]> git.basschouten.com Git - openhab-addons.git/blob
29f3648956ad823b8db51d7e662cc16e87882b84
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.knx.internal.security;
14
15 import static org.junit.jupiter.api.Assertions.*;
16
17 import java.io.File;
18 import java.util.Map;
19 import java.util.Optional;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.junit.jupiter.api.Test;
23 import org.openhab.binding.knx.internal.handler.KNXBridgeBaseThingHandler;
24
25 import tuwien.auto.calimero.GroupAddress;
26 import tuwien.auto.calimero.IndividualAddress;
27 import tuwien.auto.calimero.knxnetip.SecureConnection;
28 import tuwien.auto.calimero.secure.Keyring;
29 import tuwien.auto.calimero.secure.KnxSecureException;
30 import tuwien.auto.calimero.secure.Security;
31
32 /**
33  *
34  * @author Holger Friedrich - initial contribution
35  *
36  */
37 @NonNullByDefault
38 public class KNXSecurityTest {
39
40     @Test
41     public void testCalimeroKeyring() {
42         @SuppressWarnings("null")
43         final String testFile = getClass().getClassLoader().getResource("misc" + File.separator + "openhab6.knxkeys")
44                 .toString();
45         final String passwordString = "habopen";
46
47         final char[] password = passwordString.toCharArray();
48         assertNotEquals("", testFile);
49
50         Keyring keys = Keyring.load(testFile);
51
52         // System.out.println(keys.devices().toString());
53         // System.out.println(keys.groups().toString());
54         // System.out.println(keys.interfaces().toString());
55
56         GroupAddress ga = new GroupAddress(8, 0, 0);
57         byte[] key800enc = keys.groups().get(ga);
58         assertNotNull(key800enc);
59         if (key800enc != null) {
60             assertNotEquals(0, key800enc.length);
61         }
62         byte[] key800dec = keys.decryptKey(key800enc, password);
63         assertEquals(16, key800dec.length);
64
65         IndividualAddress nopa = new IndividualAddress(2, 8, 20);
66         Keyring.Device nodev = keys.devices().get(nopa);
67         assertNull(nodev);
68
69         IndividualAddress pa = new IndividualAddress(1, 1, 42);
70         Keyring.Device dev = keys.devices().get(pa);
71         assertNotNull(dev);
72         // cannot check this for dummy test file, needs real device to be included
73         // assertNotEquals(0, dev.sequenceNumber());
74
75         Security openhabSecurity = Security.newSecurity();
76         openhabSecurity.useKeyring(keys, password);
77         Map<GroupAddress, byte[]> groupKeys = openhabSecurity.groupKeys();
78         assertEquals(3, groupKeys.size());
79         groupKeys.remove(ga);
80         assertEquals(2, groupKeys.size());
81         openhabSecurity.useKeyring(keys, password);
82         Map<GroupAddress, byte[]> groupKeys2 = openhabSecurity.groupKeys();
83         assertEquals(3, groupKeys2.size());
84         assertEquals(3, groupKeys.size());
85         ga = new GroupAddress(1, 0, 0);
86         groupKeys.put(ga, new byte[1]);
87         assertEquals(4, groupKeys2.size());
88         assertEquals(4, groupKeys.size());
89         openhabSecurity.useKeyring(keys, password);
90         assertEquals(4, groupKeys2.size());
91         assertEquals(4, groupKeys.size());
92     }
93
94     // check tunnel settings, this file does not contain any key
95     @Test
96     public void testSecurityHelperEmpty() {
97         @SuppressWarnings("null")
98         final String testFile = getClass().getClassLoader()
99                 .getResource("misc" + File.separator + "openhab6-minimal-ipif.knxkeys").toString();
100         final String passwordString = "habopen";
101
102         final char[] password = passwordString.toCharArray();
103         assertNotEquals("", testFile);
104
105         Keyring keys = Keyring.load(testFile);
106         Security openhabSecurity = Security.newSecurity();
107         openhabSecurity.useKeyring(keys, password);
108
109         assertThrows(KnxSecureException.class, () -> {
110             KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
111         });
112         assertTrue(KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.ofNullable(keys), passwordString)
113                 .isEmpty());
114
115         // now check tunnel (expected to fail, not included)
116         IndividualAddress secureTunnelSourceAddr = new IndividualAddress(2, 8, 20);
117         assertThrows(KnxSecureException.class, () -> {
118             KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
119                     secureTunnelSourceAddr);
120         });
121         assertTrue(KNXBridgeBaseThingHandler
122                 .secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, secureTunnelSourceAddr)
123                 .isEmpty());
124     }
125
126     // check tunnel settings, this file does not contain any key
127     @Test
128     public void testSecurityHelperRouterKey() {
129         @SuppressWarnings("null")
130         final String testFile = getClass().getClassLoader()
131                 .getResource("misc" + File.separator + "openhab6-minimal-sipr.knxkeys").toString();
132         final String passwordString = "habopen";
133
134         final char[] password = passwordString.toCharArray();
135         assertNotEquals("", testFile);
136
137         Keyring keys = Keyring.load(testFile);
138         Security openhabSecurity = Security.newSecurity();
139         openhabSecurity.useKeyring(keys, password);
140
141         assertThrows(KnxSecureException.class, () -> {
142             KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
143         });
144         assertTrue(KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.ofNullable(keys), passwordString)
145                 .isPresent());
146
147         // now check tunnel (expected to fail, not included)
148         IndividualAddress secureTunnelSourceAddr = new IndividualAddress(2, 8, 20);
149         assertThrows(KnxSecureException.class, () -> {
150             KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
151                     secureTunnelSourceAddr);
152         });
153         assertTrue(KNXBridgeBaseThingHandler
154                 .secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, secureTunnelSourceAddr)
155                 .isEmpty());
156     }
157
158     // check tunnel settings, this file contains a secure interface, but no router password
159     @Test
160     public void testSecurityHelperTunnelKey() {
161         @SuppressWarnings("null")
162         final String testFile = getClass().getClassLoader()
163                 .getResource("misc" + File.separator + "openhab6-minimal-sipif.knxkeys").toString();
164         final String passwordString = "habopen";
165
166         final char[] password = passwordString.toCharArray();
167         assertNotEquals("", testFile);
168
169         Keyring keys = Keyring.load(testFile);
170         Security openhabSecurity = Security.newSecurity();
171         openhabSecurity.useKeyring(keys, password);
172
173         assertThrows(KnxSecureException.class, () -> {
174             KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
175         });
176         assertTrue(KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.ofNullable(keys), passwordString)
177                 .isEmpty());
178
179         // now check tunnel
180         IndividualAddress secureTunnelSourceAddr = new IndividualAddress(1, 1, 2);
181         assertThrows(KnxSecureException.class, () -> {
182             KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
183                     secureTunnelSourceAddr);
184         });
185         assertTrue(KNXBridgeBaseThingHandler
186                 .secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, secureTunnelSourceAddr)
187                 .isPresent());
188     }
189
190     @Test
191     public void testSecurityHelpers() {
192         @SuppressWarnings("null")
193         final String testFile = getClass().getClassLoader().getResource("misc" + File.separator + "openhab6.knxkeys")
194                 .toString();
195         final String passwordString = "habopen";
196
197         final char[] password = passwordString.toCharArray();
198         assertNotEquals("", testFile);
199
200         Keyring keys = Keyring.load(testFile);
201         // this is done during load() in v2.5, but check it once....
202         assertTrue(keys.verifySignature(password));
203
204         Security openhabSecurity = Security.newSecurity();
205         openhabSecurity.useKeyring(keys, password);
206
207         // now check router settings:
208         assertThrows(KnxSecureException.class, () -> {
209             KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.empty(), passwordString);
210         });
211         String bbKeyHex = "D947B12DDECAD528B1D5A88FD347F284";
212         byte[] bbKeyParsedLower = KNXBridgeBaseThingHandler.secHelperParseBackboneKey(bbKeyHex.toLowerCase());
213         byte[] bbKeyParsedUpper = KNXBridgeBaseThingHandler.secHelperParseBackboneKey(bbKeyHex);
214         Optional<byte[]> bbKeyRead = KNXBridgeBaseThingHandler.secHelperReadBackboneKey(Optional.ofNullable(keys),
215                 passwordString);
216         assertEquals(16, bbKeyParsedUpper.length);
217         assertArrayEquals(bbKeyParsedUpper, bbKeyParsedLower);
218         assertTrue(bbKeyRead.isPresent());
219         assertArrayEquals(bbKeyParsedUpper, bbKeyRead.get());
220         // System.out.print("Backbone key: \"");
221         // for (byte i : backboneGroupKey)
222         // System.out.print(String.format("%02X", i));
223         // System.out.println("\"");
224
225         // now check tunnel settings:
226         IndividualAddress secureTunnelSourceAddr = new IndividualAddress(1, 1, 2);
227         IndividualAddress noSecureTunnelSourceAddr = new IndividualAddress(2, 8, 20);
228         assertThrows(KnxSecureException.class, () -> {
229             KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.empty(), passwordString,
230                     secureTunnelSourceAddr);
231         });
232         assertTrue(KNXBridgeBaseThingHandler
233                 .secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString, noSecureTunnelSourceAddr)
234                 .isEmpty());
235
236         var config = KNXBridgeBaseThingHandler.secHelperReadTunnelConfig(Optional.ofNullable(keys), passwordString,
237                 secureTunnelSourceAddr);
238         assertTrue(config.isPresent());
239         assertEquals(2, config.get().user);
240
241         assertArrayEquals(SecureConnection.hashUserPassword("mytunnel1".toCharArray()), config.get().userKey);
242         assertArrayEquals(SecureConnection.hashDeviceAuthenticationPassword("myauthcode".toCharArray()),
243                 config.get().devKey);
244
245         // secure group addresses should contain at least one address marked as "surrogate"
246         final String secureAddresses = KNXBridgeBaseThingHandler.secHelperGetSecureGroupAddresses(openhabSecurity);
247         assertTrue(secureAddresses.contains("(S)"));
248         assertTrue(secureAddresses.contains("8/4/0"));
249     }
250 }