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