]> git.basschouten.com Git - openhab-addons.git/blob
c669248db0ea5d4a71b822d9cf283b212e18ea1b
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.handler;
14
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.concurrent.Executors;
17 import java.util.concurrent.ScheduledExecutorService;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.knx.internal.client.KNXClient;
22 import org.openhab.binding.knx.internal.client.StatusUpdateCallback;
23 import org.openhab.core.common.ThreadPoolManager;
24 import org.openhab.core.thing.Bridge;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.ThingStatusDetail;
28 import org.openhab.core.thing.binding.BaseBridgeHandler;
29 import org.openhab.core.types.Command;
30
31 import tuwien.auto.calimero.IndividualAddress;
32 import tuwien.auto.calimero.knxnetip.SecureConnection;
33 import tuwien.auto.calimero.mgmt.Destination;
34 import tuwien.auto.calimero.secure.KnxSecureException;
35
36 /**
37  * The {@link KNXBridgeBaseThingHandler} is responsible for handling commands, which are
38  * sent to one of the channels.
39  *
40  * @author Simon Kaufmann - Initial contribution and API
41  * @author Holger Friedrich - KNX Secure configuration
42  */
43 @NonNullByDefault
44 public abstract class KNXBridgeBaseThingHandler extends BaseBridgeHandler implements StatusUpdateCallback {
45
46     public static class SecureTunnelConfig {
47         public SecureTunnelConfig() {
48             devKey = new byte[0];
49             userKey = new byte[0];
50             user = 0;
51         }
52
53         public byte[] devKey;
54         public byte[] userKey;
55         public int user = 0;
56     }
57
58     public static class SecureRoutingConfig {
59         public SecureRoutingConfig() {
60             backboneGroupKey = new byte[0];
61             latencyToleranceMs = 0;
62         }
63
64         public byte[] backboneGroupKey;
65         public long latencyToleranceMs = 0;
66     }
67
68     protected ConcurrentHashMap<IndividualAddress, Destination> destinations = new ConcurrentHashMap<>();
69     private final ScheduledExecutorService knxScheduler = ThreadPoolManager.getScheduledPool("knx");
70     private final ScheduledExecutorService backgroundScheduler = Executors.newSingleThreadScheduledExecutor();
71     protected SecureRoutingConfig secureRouting;
72     protected SecureTunnelConfig secureTunnel;
73
74     public KNXBridgeBaseThingHandler(Bridge bridge) {
75         super(bridge);
76         secureRouting = new SecureRoutingConfig();
77         secureTunnel = new SecureTunnelConfig();
78     }
79
80     protected abstract KNXClient getClient();
81
82     /***
83      * Initialize KNX secure if configured (full interface)
84      *
85      * @param cRouterBackboneGroupKey shared key for secure router mode.
86      * @param cTunnelDevAuth device password for IP interface in tunnel mode.
87      * @param cTunnelUser user id for tunnel mode. Must be an integer >0.
88      * @param cTunnelPassword user password for tunnel mode.
89      * @return
90      */
91     protected boolean initializeSecurity(String cRouterBackboneGroupKey, String cTunnelDevAuth, String cTunnelUser,
92             String cTunnelPassword) throws KnxSecureException {
93         secureRouting = new SecureRoutingConfig();
94         secureTunnel = new SecureTunnelConfig();
95
96         boolean securityInitialized = false;
97
98         // step 1: secure routing, backbone group key manually specified in OH config
99         if (!cRouterBackboneGroupKey.isBlank()) {
100             // provided in config
101             String key = cRouterBackboneGroupKey.trim().replaceFirst("^0x", "").trim().replace(" ", "");
102             if (!key.isEmpty()) {
103                 // helper may throw KnxSecureException
104                 secureRouting.backboneGroupKey = secHelperParseBackboneKey(key);
105                 securityInitialized = true;
106             }
107         }
108
109         // step 2: check if valid tunnel parameters are specified in config
110         if (!cTunnelDevAuth.isBlank()) {
111             secureTunnel.devKey = SecureConnection.hashDeviceAuthenticationPassword(cTunnelDevAuth.toCharArray());
112             securityInitialized = true;
113         }
114         if (!cTunnelPassword.isBlank()) {
115             secureTunnel.userKey = SecureConnection.hashUserPassword(cTunnelPassword.toCharArray());
116             securityInitialized = true;
117         }
118         if (!cTunnelUser.isBlank()) {
119             String user = cTunnelUser.trim();
120             try {
121                 secureTunnel.user = Integer.decode(user);
122             } catch (NumberFormatException e) {
123                 throw new KnxSecureException("tunnelUser must be a number >0");
124             }
125             if (secureTunnel.user <= 0) {
126                 throw new KnxSecureException("tunnelUser must be a number >0");
127             }
128             securityInitialized = true;
129         }
130
131         // step 5: router: load latencyTolerance
132         // default to 2000ms
133         // this parameter is currently not exposed in config, it may later be set by using the keyring
134         secureRouting.latencyToleranceMs = 2000;
135
136         return securityInitialized;
137     }
138
139     /***
140      * converts hex string (32 characters) to byte[16]
141      *
142      * @param hexstring 32 characters hex
143      * @return key in byte array format
144      */
145     public static byte[] secHelperParseBackboneKey(String hexstring) throws KnxSecureException {
146         if (hexstring.length() != 32) {
147             throw new KnxSecureException("backbone key must be 32 characters (16 byte hex notation)");
148         }
149
150         byte[] parsed = new byte[16];
151         try {
152             for (byte i = 0; i < 16; i++) {
153                 parsed[i] = (byte) Integer.parseInt(hexstring.substring(2 * i, 2 * i + 2), 16);
154             }
155         } catch (NumberFormatException e) {
156             throw new KnxSecureException("backbone key configured, cannot parse hex string, illegal character", e);
157         }
158         return parsed;
159     }
160
161     @Override
162     public void handleCommand(ChannelUID channelUID, Command command) {
163         // Nothing to do here
164     }
165
166     public ScheduledExecutorService getScheduler() {
167         return knxScheduler;
168     }
169
170     public ScheduledExecutorService getBackgroundScheduler() {
171         return backgroundScheduler;
172     }
173
174     @Override
175     public void updateStatus(ThingStatus status) {
176         super.updateStatus(status);
177     }
178
179     @Override
180     public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
181         super.updateStatus(status, statusDetail, description);
182     }
183 }