xref: /btstack/test/mesh/mesh_crypto.py (revision bbaf89325d1a56fab5c98c0905350ca9471c5c26)
1#!/usr/bin/env python3
2# BlueKitchen GmbH (c) 2019
3
4# pip3 install pycryptodomex
5
6# implementation of the Bluetooth SIG Mesh crypto functions using pycryptodomex
7
8from Cryptodome.Cipher import AES
9from Cryptodome.Hash   import CMAC
10
11def aes128(key, n):
12    cipher = AES.new(key, AES.MODE_ECB)
13    ciphertext = cipher.encrypt(n)
14    return ciphertext
15
16def aes_cmac(key, n):
17    cobj = CMAC.new(key, ciphermod=AES)
18    cobj.update(n)
19    return cobj.digest()
20
21def aes_ccm_encrypt(key, nonce, message, additional_data, mac_len):
22    cipher = AES.new(key, AES.MODE_CCM, nonce=nonce, mac_len=mac_len)
23    cipher.update(additional_data)
24    ciphertext, tag = cipher.encrypt_and_digest(message)
25    return ciphertext, tag
26
27def aes_ccm_decrypt(key, nonce, message, additional_data, mac_len, mac_tag):
28    cipher = AES.new(key, AES.MODE_CCM, nonce=nonce, mac_len=mac_len)
29    cipher.update(additional_data)
30    try:
31        ciphertext = cipher.decrypt_and_verify(message, mac_tag)
32        return ciphertext
33    except ValueError:
34        return None
35
36def s1(m):
37    # s1(M) = AES-CMACZERO (M)
38    zero_key = bytes(16)
39    return aes_cmac(zero_key, m)
40
41def k1(n, salt, p):
42    # T = AES-CMACSALT (N)
43    t = aes_cmac(salt, n)
44    # k1(N, SALT, P) = AES-CMACT (P)
45    return aes_cmac(t, p)
46
47def k2(n, p):
48    # SALT = s1(“smk2”)
49    salt = s1(b'smk2')
50    # T = AES-CMACSALT (N)
51    t = aes_cmac(salt, n)
52    # T0 = empty string (zero length)
53    t0 = b''
54    # T1 = AES-CMACT (T0 || P || 0x01)
55    t1 = aes_cmac(t, t0 + p + b'\x01')
56    # T2 = AES-CMACT (T1 || P || 0x02)
57    t2 = aes_cmac(t, t1 + p + b'\x02')
58    # T3 = AES-CMACT (T2 || P || 0x03)
59    t3 = aes_cmac(t, t2 + p + b'\x03')
60    nid = t1[15] & 0x7f
61    encryption_key = t2
62    privacy_key = t3
63    return (nid, encryption_key, privacy_key)
64
65def k3(n):
66    # SALT = s1(“smk3”)
67    salt = s1(b'smk3')
68    # T = AES-CMACSALT (N)
69    t = aes_cmac(salt, n)
70    return aes_cmac(t, b'id64' + b'\x01')[8:]
71
72def k4(n):
73    # SALT = s1(“smk4”)
74    salt = s1(b'smk4')
75    # T = AES-CMACSALT (N)
76    t = aes_cmac(salt, n)
77    return aes_cmac(t, b'id6' + b'\x01')[15] & 0x3f
78
79def network_pecb(network_pdu, iv_index, privacy_key):
80    privacy_random = network_pdu[7:14]
81    privacy_plaintext = bytes(5) + iv_index + privacy_random
82    return aes128(privacy_key, privacy_plaintext)[0:6]
83
84def network_decrypt(network_pdu, iv_index, encryption_key, privacy_key):
85    pecb = network_pecb(network_pdu, iv_index, privacy_key)
86    deobfuscated = bytes([(a ^ b) for (a,b) in zip(pecb, network_pdu[1:7])])
87    nonce = bytes(1) + deobfuscated + bytes(2) + iv_index
88    decrypted = aes_ccm_decrypt(encryption_key, nonce, network_pdu[7:-8], b'', 8, network_pdu[-8:])
89    return network_pdu[0:1] + deobfuscated + decrypted
90
91