1
+ /*
2
+ * Copyright (c) 2020-2021 dresden elektronik ingenieurtechnik gmbh.
3
+ * All rights reserved.
4
+ *
5
+ * The software in this package is published under the terms of the BSD
6
+ * style license a copy of which has been included with this distribution in
7
+ * the LICENSE.txt file.
8
+ *
9
+ */
10
+
1
11
#include < QLibrary>
2
- #include < array>
12
+ #include < QDataStream>
13
+
3
14
#ifdef HAS_OPENSSL
4
- #include < openssl/aes.h>
5
- #include < openssl/evp.h>
15
+ #define OPEN_SSL_VERSION_MIN 0x10100000L
16
+
17
+ #include < openssl/opensslv.h>
18
+ #if OPENSSL_VERSION_NUMBER >= OPEN_SSL_VERSION_MIN
19
+ #define HAS_RECENT_OPENSSL
20
+ #endif
21
+ #endif
22
+
23
+ #ifdef HAS_RECENT_OPENSSL
24
+ #include < openssl/aes.h>
25
+ #include < openssl/evp.h>
6
26
#endif
7
27
#include < string>
28
+ #include " deconz/aps_controller.h"
29
+ #include " deconz/dbg_trace.h"
30
+ #include " deconz/green_power.h"
31
+ #include " deconz/zcl.h"
8
32
#include " green_power.h"
33
+ #include " resource.h"
34
+
35
+ #define GP_PAIR_INTERVAL_SECONDS (60 * 15 )
9
36
10
37
// this code is based on
11
38
// https://github.com/Smanar/Zigbee_firmware/blob/master/Encryption.cpp
12
- #define OPEN_SSL_VERSION_MIN 0x10100000
13
39
14
40
#define AES_KEYLENGTH 128
15
41
#define AES_BLOCK_SIZE 16
16
42
17
- const unsigned char defaultTCLinkKey[] = { 0x5A , 0x69 , 0x67 , 0x42 , 0x65 , 0x65 , 0x41 , 0x6C , 0x6C , 0x69 , 0x61 , 0x6E , 0x63 , 0x65 , 0x30 , 0x39 };
18
43
19
44
// From https://github.com/Koenkk/zigbee-herdsman/blob/master/src/controller/greenPower.ts
20
45
/* !
@@ -23,7 +48,8 @@ GpKey_t GP_DecryptSecurityKey(quint32 sourceID, const GpKey_t &securityKey)
23
48
{
24
49
GpKey_t result = { 0 };
25
50
26
- #ifdef HAS_OPENSSL
51
+ #ifdef HAS_RECENT_OPENSSL
52
+ const unsigned char defaultTCLinkKey[] = { 0x5A , 0x69 , 0x67 , 0x42 , 0x65 , 0x65 , 0x41 , 0x6C , 0x6C , 0x69 , 0x61 , 0x6E , 0x63 , 0x65 , 0x30 , 0x39 };
27
53
28
54
unsigned char nonce[13 ]; // u8 source address, u32 frame counter, u8 security control
29
55
unsigned char sourceIDInBytes[4 ];
@@ -53,7 +79,7 @@ GpKey_t GP_DecryptSecurityKey(quint32 sourceID, const GpKey_t &securityKey)
53
79
54
80
if (!libCrypto.load () || !libSsl.load ())
55
81
{
56
- DBG_Printf (DBG_INFO , " OpenSSl library for ZGP encryption not found\n " );
82
+ DBG_Printf (DBG_ZGP , " [ZGP] OpenSSl library for ZGP encryption not found\n " );
57
83
return result;
58
84
}
59
85
@@ -80,11 +106,11 @@ GpKey_t GP_DecryptSecurityKey(quint32 sourceID, const GpKey_t &securityKey)
80
106
_EVP_CIPHER_CTX_free &&
81
107
_EVP_aes_128_ccm)
82
108
{
83
- DBG_Printf (DBG_INFO , " OpenSSl version 0x%08X loaded\n " , openSslVersion);
109
+ DBG_Printf (DBG_ZGP , " [ZGP] OpenSSl version 0x%08X loaded\n " , openSslVersion);
84
110
}
85
111
else
86
112
{
87
- DBG_Printf (DBG_INFO , " OpenSSl library version 0x%08X for ZGP encryption resolve symbols failed\n " , openSslVersion);
113
+ DBG_Printf (DBG_ZGP , " [ZGP] OpenSSl library version 0x%08X for ZGP encryption resolve symbols failed\n " , openSslVersion);
88
114
return result;
89
115
}
90
116
@@ -110,7 +136,10 @@ GpKey_t GP_DecryptSecurityKey(quint32 sourceID, const GpKey_t &securityKey)
110
136
111
137
std::copy (encryptBuf.begin (), encryptBuf.begin () + result.size (), result.begin ());
112
138
113
- #endif // HAS_OPENSSL
139
+ #else
140
+ Q_UNUSED (securityKey)
141
+ DBG_Printf (DBG_ERROR, " [ZGP] failed to decrypt GPDKey for 0x%08X, OpenSSL is not available or too old\n " , unsigned (sourceID));
142
+ #endif // HAS_RECENT_OPENSSL
114
143
115
144
return result;
116
145
}
@@ -161,11 +190,11 @@ bool GP_SendProxyCommissioningMode(deCONZ::ApsController *apsCtrl, quint8 zclSeq
161
190
// broadcast
162
191
if (apsCtrl->apsdeDataRequest (req) == deCONZ::Success)
163
192
{
164
- DBG_Printf (DBG_INFO , " send GP proxy commissioning mode\n " );
193
+ DBG_Printf (DBG_ZGP , " [ZGP] send GP proxy commissioning mode\n " );
165
194
return true ;
166
195
}
167
196
168
- DBG_Printf (DBG_INFO , " send GP proxy commissioning mode failed\n " );
197
+ DBG_Printf (DBG_ZGP , " [ZGP] send GP proxy commissioning mode failed\n " );
169
198
return false ;
170
199
}
171
200
@@ -204,7 +233,7 @@ bool GP_SendPairing(quint32 gpdSrcId, quint16 sinkGroupId, quint8 deviceId, quin
204
233
// 4: remove gpd
205
234
// 5..6: communication mode
206
235
// 7: gpd fixed
207
- quint8 options0 = 0xc8 ; // bits 0..7: add sink, enter commissioning mode, exit on window expire
236
+ quint8 options0 = 0x48 ; // bits 0..7: add sink, enter commissioning mode, exit on window expire
208
237
209
238
// 0 / 8: gpd mac seq number capabilities
210
239
// 1..2 / 9..10: security level
@@ -245,10 +274,131 @@ bool GP_SendPairing(quint32 gpdSrcId, quint16 sinkGroupId, quint8 deviceId, quin
245
274
// broadcast
246
275
if (apsCtrl->apsdeDataRequest (req) == deCONZ::Success)
247
276
{
248
- DBG_Printf (DBG_INFO, " send GP pairing to 0x%04X\n " , gppShortAddress);
277
+ DBG_Printf (DBG_ZGP, " [ZGP] send GP pairing to 0x%04X\n " , gppShortAddress);
278
+ return true ;
279
+ }
280
+
281
+ DBG_Printf (DBG_ZGP, " [ZGP] send GP pairing to 0x%04X failed\n " , gppShortAddress);
282
+ return false ;
283
+ }
284
+
285
+
286
+ // TODO remove TMP_ functions after alarm systems PR is merged, where these functions are in utils.h
287
+ static bool TMP_isHexChar (char ch)
288
+ {
289
+ return ((ch >= ' 0' && ch <= ' 9' ) || (ch >= ' a' && ch <= ' f' ) || (ch >= ' A' && ch <= ' F' ));
290
+ }
291
+
292
+ static quint64 TMP_extAddressFromUniqueId (const QString &uniqueId)
293
+ {
294
+ quint64 result = 0 ;
295
+
296
+ if (uniqueId.size () < 23 )
297
+ {
298
+ return result;
299
+ }
300
+
301
+ // 28:6d:97:00:01:06:41:79-01-0500 31 characters
302
+ int pos = 0 ;
303
+ char buf[16 + 1 ];
304
+
305
+ for (auto ch : uniqueId)
306
+ {
307
+ if (ch != ' :' )
308
+ {
309
+ buf[pos] = ch.toLatin1 ();
310
+
311
+ if (!TMP_isHexChar (buf[pos]))
312
+ {
313
+ return result;
314
+ }
315
+ pos++;
316
+ }
317
+
318
+ if (pos == 16 )
319
+ {
320
+ buf[pos] = ' \0 ' ;
321
+ break ;
322
+ }
323
+ }
324
+
325
+ if (pos == 16 )
326
+ {
327
+ result = strtoull (buf, nullptr , 16 );
328
+ }
329
+
330
+ return result;
331
+ }
332
+
333
+ /* ! For already paired ZGP devices a Pair command needs to be send periodically every \c GP_PAIR_INTERVAL_SECONDS
334
+ in order to keep ZGP Proxy entrys alive.
335
+
336
+ Each ZGP device keeps track of when the last Pair command was sent and the current device frame counter.
337
+ */
338
+ bool GP_SendPairingIfNeeded (Resource *resource, deCONZ::ApsController *apsCtrl, quint8 zclSeqNo)
339
+ {
340
+ if (!resource || !apsCtrl)
341
+ {
342
+ return false ;
343
+ }
344
+
345
+ ResourceItem *gpdLastpair = resource->item (RStateGPDLastPair);
346
+ if (!gpdLastpair)
347
+ {
348
+ return false ;
349
+ }
350
+
351
+ const deCONZ::SteadyTimeRef now = deCONZ::steadyTimeRef ();
352
+
353
+ if (now - deCONZ::SteadyTimeRef{gpdLastpair->toNumber ()} < deCONZ::TimeSeconds{GP_PAIR_INTERVAL_SECONDS})
354
+ {
355
+ return false ;
356
+ }
357
+
358
+ // the GPDKey must be known to send pair command
359
+ ResourceItem *gpdKey = resource->item (RConfigGPDKey);
360
+
361
+ if (!gpdKey || gpdKey->toString ().isEmpty ())
362
+ {
363
+ return false ;
364
+ }
365
+
366
+ ResourceItem *frameCounter = resource->item (RStateGPDFrameCounter);
367
+ ResourceItem *gpdDeviceId = resource->item (RConfigGPDDeviceId);
368
+ ResourceItem *uniqueId = resource->item (RAttrUniqueId);
369
+
370
+ if (!gpdKey || !frameCounter || !gpdDeviceId || !uniqueId)
371
+ {
372
+ return false ;
373
+ }
374
+
375
+ auto srcGpdId = TMP_extAddressFromUniqueId (uniqueId->toString ());
376
+
377
+ if (srcGpdId == 0 || srcGpdId > UINT32_MAX)
378
+ {
379
+ return false ; // should not happen
380
+ }
381
+
382
+ GpKey_t key;
383
+
384
+ {
385
+ QByteArray arr = QByteArray::fromHex (gpdKey->toString ().toLocal8Bit ());
386
+ DBG_Assert (arr.size () == int (key.size ()));
387
+ if (arr.size () != int (key.size ()))
388
+ {
389
+ return false ;
390
+ }
391
+
392
+ memcpy (key.data (), arr.constData (), key.size ());
393
+ }
394
+
395
+ quint8 deviceId = gpdDeviceId->toNumber () & 0xFF ;
396
+
397
+ if (GP_SendPairing (quint32 (srcGpdId), GP_DEFAULT_PROXY_GROUP, deviceId, frameCounter->toNumber (), key, apsCtrl, zclSeqNo, deCONZ::BroadcastRouters))
398
+ {
399
+ gpdLastpair->setValue (now.ref );
249
400
return true ;
250
401
}
251
402
252
- DBG_Printf (DBG_INFO, " send GP pairing to 0x%04X failed\n " , gppShortAddress);
253
403
return false ;
254
404
}
0 commit comments