diff --git a/circuitmatter/__init__.py b/circuitmatter/__init__.py index 4c88cc6..bb8979c 100644 --- a/circuitmatter/__init__.py +++ b/circuitmatter/__init__.py @@ -7,10 +7,10 @@ import pathlib import json import os -import pathlib +import secrets import struct import time -from ecdsa.ellipticcurve import Point +from ecdsa.ellipticcurve import AbstractPoint, Point, PointJacobi from ecdsa.curves import NIST256p from typing import Optional @@ -851,17 +851,19 @@ def process_exchange(self, message): return exchange -M = Point.from_bytes( +M = PointJacobi.from_bytes( NIST256p.curve, b"\x02\x88\x6e\x2f\x97\xac\xe4\x6e\x55\xba\x9d\xd7\x24\x25\x79\xf2\x99\x3b\x64\xe1\x6e\xf3\xdc\xab\x95\xaf\xd4\x97\x33\x3d\x8f\xa1\x2f", ) -N = Point.from_bytes( +N = PointJacobi.from_bytes( NIST256p.curve, b"\x03\xd8\xbb\xd6\xc6\x39\xc6\x29\x37\xb0\x4d\x99\x7f\x38\xc3\x77\x07\x19\xc6\x29\xd7\x01\x4d\x49\xa2\x4b\x4f\x98\xba\xa1\x29\x2b\x49", ) CRYPTO_W_SIZE_BYTES = CRYPTO_GROUP_SIZE_BYTES + 8 +# in the spake2p math P is NIST256p.generator +# in the spake2p math p is NIST256p.order def _pbkdf2(passcode, salt, iterations): ws = hashlib.pbkdf2_hmac( "sha256", struct.pack(" bytes: return b"" -def Crypto_pB(w0: bytes, L: bytes) -> bytes: - return b"" +def Crypto_pB(w0: int, L: Point) -> tuple[int, AbstractPoint]: + y = secrets.randbelow(NIST256p.order) + Y = y * NIST256p.generator + w0 * N + return y, Y def Crypto_Transcript(context, pA, pB, Z, V, w0) -> bytes: @@ -914,9 +918,11 @@ def Crypto_Transcript(context, pA, pB, Z, V, w0) -> bytes: offset = 0 for e in elements: struct.pack_into(" tuple[bytes, bytes, bytes]: KaKe = Crypto_Hash(tt) + print(f"KaKe[{len(KaKe)}]", KaKe.hex(" ")) Ka = KaKe[: CRYPTO_HASH_LEN_BYTES // 2] Ke = KaKe[CRYPTO_HASH_LEN_BYTES // 2 :] # https://github.com/project-chip/connectedhomeip/blob/c88d5cf83cd3e3323ac196630acc34f196a2f405/src/crypto/CHIPCryptoPAL.cpp#L458-L468 @@ -1087,9 +1094,9 @@ def process_packet(self, address, data): print(f"Dropping message {message.message_counter}") return - print(f"Received packet from {address}:") - print(f"{data.hex(' ')}") - print(f"Message counter {message.message_counter}") + # print(f"Received packet from {address}:") + # print(f"{data.hex(' ')}") + # print(f"Message counter {message.message_counter}") protocol_id = message.protocol_id protocol_opcode = message.protocol_opcode @@ -1105,13 +1112,23 @@ def process_packet(self, address, data): exchange.commissioning_hash = hashlib.sha256( b"CHIP PAKE V1 Commissioning" ) + print( + "commissioning hash", + hex(b"CHIP PAKE V1 Commissioning"[0]), + len(b"CHIP PAKE V1 Commissioning"), + ) + print( + "Commissioning hash", + hex(message.application_payload[0]), + len(message.application_payload), + ) exchange.commissioning_hash.update(message.application_payload) if request.passcodeId == 0: pass # Send back failure # response = StatusReport() # response.GeneralCode - print(request) + # print(request) response = PBKDFParamResponse() response.initiatorRandom = request.initiatorRandom @@ -1125,7 +1142,10 @@ def process_packet(self, address, data): params.iterations = self.nonvolatile["iteration-count"] params.salt = binascii.a2b_base64(self.nonvolatile["salt"]) response.pbkdf_parameters = params - exchange.commissioning_hash.update(response.encode()) + + encoded = b"\x15" + response.encode() + b"\x18" + print("Commissioning hash", hex(encoded[0]), len(encoded)) + exchange.commissioning_hash.update(encoded) exchange.send( ProtocolId.SECURE_CHANNEL, SecureProtocolOpcode.PBKDF_PARAM_RESPONSE, @@ -1137,20 +1157,39 @@ def process_packet(self, address, data): elif protocol_opcode == SecureProtocolOpcode.PASE_PAKE1: print("Received PASE PAKE1") pake1 = PAKE1(message.application_payload[1:-1]) - print(pake1) + # print(pake1) pake2 = PAKE2() + print("verifier", self.nonvolatile["verifier"]) verifier = binascii.a2b_base64(self.nonvolatile["verifier"]) w0 = memoryview(verifier)[:CRYPTO_GROUP_SIZE_BYTES] L = memoryview(verifier)[CRYPTO_GROUP_SIZE_BYTES:] - pake2.pB = Crypto_pB(w0, L) - # TODO: Compute these - Z = b"" - V = b"" + L = Point.from_bytes(NIST256p.curve, L) + print("w0", w0.hex(" ")) + w0 = int.from_bytes(w0, byteorder="big") + print("L", L) + y, Y = Crypto_pB(w0, L) + # pB is Y encoded uncompressed + # pA is X encoded uncompressed + pake2.pB = Y.to_bytes("uncompressed") + h = NIST256p.curve.cofactor() + # Use negation because the class doesn't support subtraction. 🤦 + Z = h * y * (Y + (-(w0 * N))) + V = h * y * L + context = exchange.commissioning_hash.digest() + print("context", context.hex(" ")) tt = Crypto_Transcript( - exchange.commissioning_hash.digest(), pake1.pA, pake2.pB, Z, V, w0 + context, + pake1.pA, + pake2.pB, + Z.to_bytes("uncompressed"), + V.to_bytes("uncompressed"), + w0.to_bytes(NIST256p.baselen, byteorder="big"), ) + print("transcript", len(tt)) cA, cB, Ke = Crypto_P2(tt, pake1.pA, pake2.pB) pake2.cB = cB + # print("sending pake2 back") + # print(pake2) exchange.send( ProtocolId.SECURE_CHANNEL, SecureProtocolOpcode.PASE_PAKE2, pake2 ) diff --git a/test_data/device_state.json b/test_data/device_state.json index 448a21f..9165230 100644 --- a/test_data/device_state.json +++ b/test_data/device_state.json @@ -1,6 +1,6 @@ { - "descriminator": 2207, + "descriminator": 1983, "iteration-count": 10000, "salt": "5uCP0ITHYzI9qBEe6hfU4HfY3y7VopSk0qNvhvznhiQ=", - "verifier": "wxAsyKc/NiJkxkXfi9zu8aVXfMR5zOTmTA2ssdg5B+wEFcTyXODL7NAqAgFIUUvIdgZL3lB7ZoHmQDTroBgAV4ZebS6l5jrklt97N418Wnypeoi9JED6aVVDpmTivkFFUw==" + "verifier": "0xGqxJFBr/ViQt3lv1Yw5F0GcPBAtFFvXB+EcIIjH5cEsjkPZHDQyFWjA6Ide+2gafYnZgIy6gJBgdJOlD8htAZKe0i6nIhT/ADsBWH4CvZcl37n/ofEEECWSEBV4vy/0A==" } diff --git a/test_data/recorded_packets.json b/test_data/recorded_packets.json index 03e705e..53e6b8b 100644 --- a/test_data/recorded_packets.json +++ b/test_data/recorded_packets.json @@ -1 +1 @@ -[["receive", 357657266155036, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45867, 0, 0], "BAAAAObOzQyqpsS5iZt1AwUg70sAABUwASBHe5PONLiOU6V4DJ7DZkokBgIvtdAKtWjuE8CDoN8dPyUCVUQkAwAoBDUFJQH0ASUCLAElA6APJAQRJAULJgYAAAMBJAcBGBg="], ["receive", 357657273325183, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45867, 0, 0], "BAAAAOfOzQyqpsS5iZt1AwUi70sAABUwAUEEZZRPQKxRLWkU0CZwUU4AJT0cfikv2BmIiAUNWeFqLcoZSI8ULSVK5Re/dNmunfoSd7YojNkgN6L+GJ3TN7ad/xg="], ["receive", 357657652704410, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45867, 0, 0], "BAAAAOfOzQyqpsS5iZt1AwUi70sAABUwAUEEZZRPQKxRLWkU0CZwUU4AJT0cfikv2BmIiAUNWeFqLcoZSI8ULSVK5Re/dNmunfoSd7YojNkgN6L+GJ3TN7ad/xg="], ["receive", 357658002105028, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45867, 0, 0], "BAAAAOfOzQyqpsS5iZt1AwUi70sAABUwAUEEZZRPQKxRLWkU0CZwUU4AJT0cfikv2BmIiAUNWeFqLcoZSI8ULSVK5Re/dNmunfoSd7YojNkgN6L+GJ3TN7ad/xg="], ["receive", 357658622312059, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45867, 0, 0], "BAAAAOfOzQyqpsS5iZt1AwUi70sAABUwAUEEZZRPQKxRLWkU0CZwUU4AJT0cfikv2BmIiAUNWeFqLcoZSI8ULSVK5Re/dNmunfoSd7YojNkgN6L+GJ3TN7ad/xg="], ["receive", 357659576369391, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45867, 0, 0], "BAAAAOfOzQyqpsS5iZt1AwUi70sAABUwAUEEZZRPQKxRLWkU0CZwUU4AJT0cfikv2BmIiAUNWeFqLcoZSI8ULSVK5Re/dNmunfoSd7YojNkgN6L+GJ3TN7ad/xg="]] +[["receive", 22019542334443, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 56921, 0, 0], "BAAAABKaPgrv6cJyqUaC6gUg/GsAABUwASB1xjIG0TZ6FcxVP4D/UaOXMLwYZk5bTZIC3ASnMUvA2iUCe9YkAwAoBDUFJQH0ASUCLAElA6APJAQRJAULJgYAAAMBJAcBGBg="], ["receive", 22019548962619, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 56921, 0, 0], "BAAAABOaPgrv6cJyqUaC6gUi/GsAABUwAUEENxA7hCA0cyjIm09EgPW/LHBoZpeY/4MF0YmZTtjPgFaVuA+qHWGWW3zv2u37ySGlKnMM4xKKTpclVmio+H8dKhg="], ["receive", 22019565022443, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 56921, 0, 0], "BAAAABSaPgrv6cJyqUaC6gVA/GsAAAEAAAAAAAIA"]]