Skip to content

Commit 70d2e65

Browse files
committed
PASE succeeds
1 parent 997a65c commit 70d2e65

File tree

4 files changed

+95
-33
lines changed

4 files changed

+95
-33
lines changed

circuitmatter/__init__.py

Lines changed: 94 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919

2020
__version__ = "0.0.0"
2121

22+
# Section 3.6
23+
24+
CRYPTO_SYMMETRIC_KEY_LENGTH_BITS = 128
25+
CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES = 16
26+
CRYPTO_AEAD_MIC_LENGTH_BITS = 128
27+
CRYPTO_AEAD_MIC_LENGTH_BYTES = 16
28+
CRYPTO_AEAD_NONCE_LENGTH_BYTES = 13
29+
30+
2231
# Section 4.11.2
2332
MSG_COUNTER_WINDOW_SIZE = 32
2433
MSG_COUNTER_SYNC_REQ_JITTER_MS = 500
@@ -386,10 +395,8 @@ def send(self, message):
386395
message.destination_node_id = self.ephemeral_initiator_node_id
387396
if message.message_counter is None:
388397
message.message_counter = next(self.message_counter)
389-
print(message)
390398
buf = memoryview(bytearray(1280))
391399
nbytes = message.encode_into(buf)
392-
print(nbytes, buf[:nbytes].hex(" "))
393400
self.socket.sendto(buf[:nbytes], self.node_ipaddress)
394401

395402

@@ -559,6 +566,8 @@ def encode_into(self, buffer):
559566
offset = self.application_payload.encode_into(buffer, offset)
560567
buffer[offset] = 0x18
561568
offset += 1
569+
elif isinstance(self.application_payload, StatusReport):
570+
offset = self.application_payload.encode_into(buffer, offset)
562571
else:
563572
buffer[offset : offset + len(self.application_payload)] = (
564573
self.application_payload
@@ -676,6 +685,23 @@ class GeneralCode(enum.IntEnum):
676685
"""Message size is larger than the recipient can handle."""
677686

678687

688+
class SecureChannelProtocolCode(enum.IntEnum):
689+
SESSION_ESTABLISHMENT_SUCCESS = 0x0000
690+
"""Indication that the last session establishment message was successfully processed."""
691+
692+
NO_SHARED_TRUST_ROOTS = 0x0001
693+
"""Failure to find a common set of shared roots."""
694+
695+
INVALID_PARAMETER = 0x0002
696+
"""Generic failure during session establishment."""
697+
698+
CLOSE_SESSION = 0x0003
699+
"""Indication that the sender will close the current session."""
700+
701+
BUSY = 0x0004
702+
"""Indication that the sender cannot currently fulfill the request."""
703+
704+
679705
class StatusReport:
680706
def __init__(self):
681707
self.clear()
@@ -686,6 +712,9 @@ def clear(self):
686712
self.protocol_code = 0
687713
self.protocol_data = None
688714

715+
def __len__(self):
716+
return 8 + len(self.protocol_data) if self.protocol_data else 0
717+
689718
def encode_into(self, buffer, offset=0) -> int:
690719
struct.pack_into(
691720
"<HIH",
@@ -702,15 +731,14 @@ def encode_into(self, buffer, offset=0) -> int:
702731
return offset
703732

704733
def decode(self, buffer):
705-
print(buffer.hex(" "))
706734
self.general_code, self.protocol_id, self.protocol_code = struct.unpack_from(
707735
"<HIH", buffer
708736
)
709737
self.general_code = GeneralCode(self.general_code)
710738
self.protocol_data = buffer[8:]
711739

712740
def __str__(self):
713-
return f"StatusReport: General Code: {self.general_code!r}, Protocol ID: {self.protocol_id}, Protocol Code: {self.protocol_code}, Protocol Data: {self.protocol_data.hex()}"
741+
return f"StatusReport: General Code: {self.general_code!r}, Protocol ID: {self.protocol_id}, Protocol Code: {self.protocol_code}, Protocol Data: {self.protocol_data.hex() if self.protocol_data else None}"
714742

715743

716744
class SessionManager:
@@ -918,11 +946,9 @@ def Crypto_Transcript(context, pA, pB, Z, V, w0) -> bytes:
918946
offset = 0
919947
for e in elements:
920948
struct.pack_into("<Q", tt, offset, len(e))
921-
print(offset, 8, hex(tt[offset]))
922949
offset += 8
923950

924951
tt[offset : offset + len(e)] = e
925-
print(offset, len(e), e.hex(" "))
926952
offset += len(e)
927953
return tt
928954

@@ -975,13 +1001,12 @@ def KDF(salt, key, info):
9751001

9761002
def Crypto_P2(tt, pA, pB) -> tuple[bytes, bytes, bytes]:
9771003
KaKe = Crypto_Hash(tt)
978-
print(f"KaKe[{len(KaKe)}]", KaKe.hex(" "))
9791004
Ka = KaKe[: CRYPTO_HASH_LEN_BYTES // 2]
9801005
Ke = KaKe[CRYPTO_HASH_LEN_BYTES // 2 :]
9811006
# https://github.com/project-chip/connectedhomeip/blob/c88d5cf83cd3e3323ac196630acc34f196a2f405/src/crypto/CHIPCryptoPAL.cpp#L458-L468
9821007
KcAKcB = KDF(None, Ka, b"ConfirmationKeys")
983-
KcA = KcAKcB[:CRYPTO_GROUP_SIZE_BYTES]
984-
KcB = KcAKcB[CRYPTO_GROUP_SIZE_BYTES:]
1008+
KcA = KcAKcB[: CRYPTO_HASH_LEN_BYTES // 2]
1009+
KcB = KcAKcB[CRYPTO_HASH_LEN_BYTES // 2 :]
9851010
cA = Crypto_HMAC(KcA, pB)
9861011
cB = Crypto_HMAC(KcB, pA)
9871012
return (cA, cB, Ke)
@@ -1112,16 +1137,6 @@ def process_packet(self, address, data):
11121137
exchange.commissioning_hash = hashlib.sha256(
11131138
b"CHIP PAKE V1 Commissioning"
11141139
)
1115-
print(
1116-
"commissioning hash",
1117-
hex(b"CHIP PAKE V1 Commissioning"[0]),
1118-
len(b"CHIP PAKE V1 Commissioning"),
1119-
)
1120-
print(
1121-
"Commissioning hash",
1122-
hex(message.application_payload[0]),
1123-
len(message.application_payload),
1124-
)
11251140
exchange.commissioning_hash.update(message.application_payload)
11261141
if request.passcodeId == 0:
11271142
pass
@@ -1136,6 +1151,7 @@ def process_packet(self, address, data):
11361151
response.responderRandom = os.urandom(32)
11371152
session_context = self.manager.new_context()
11381153
response.responderSessionId = session_context.local_session_id
1154+
exchange.secure_session_context = session_context
11391155
session_context.peer_session_id = request.initiatorSessionId
11401156
if not request.hasPBKDFParameters:
11411157
params = Crypto_PBKDFParameterSet()
@@ -1144,7 +1160,6 @@ def process_packet(self, address, data):
11441160
response.pbkdf_parameters = params
11451161

11461162
encoded = b"\x15" + response.encode() + b"\x18"
1147-
print("Commissioning hash", hex(encoded[0]), len(encoded))
11481163
exchange.commissioning_hash.update(encoded)
11491164
exchange.send(
11501165
ProtocolId.SECURE_CHANNEL,
@@ -1157,26 +1172,24 @@ def process_packet(self, address, data):
11571172
elif protocol_opcode == SecureProtocolOpcode.PASE_PAKE1:
11581173
print("Received PASE PAKE1")
11591174
pake1 = PAKE1(message.application_payload[1:-1])
1160-
# print(pake1)
11611175
pake2 = PAKE2()
1162-
print("verifier", self.nonvolatile["verifier"])
11631176
verifier = binascii.a2b_base64(self.nonvolatile["verifier"])
11641177
w0 = memoryview(verifier)[:CRYPTO_GROUP_SIZE_BYTES]
11651178
L = memoryview(verifier)[CRYPTO_GROUP_SIZE_BYTES:]
11661179
L = Point.from_bytes(NIST256p.curve, L)
1167-
print("w0", w0.hex(" "))
11681180
w0 = int.from_bytes(w0, byteorder="big")
1169-
print("L", L)
11701181
y, Y = Crypto_pB(w0, L)
11711182
# pB is Y encoded uncompressed
11721183
# pA is X encoded uncompressed
11731184
pake2.pB = Y.to_bytes("uncompressed")
11741185
h = NIST256p.curve.cofactor()
11751186
# Use negation because the class doesn't support subtraction. 🤦
1176-
Z = h * y * (Y + (-(w0 * N)))
1187+
X = Point.from_bytes(NIST256p.curve, pake1.pA)
1188+
Z = h * y * (X + (-(w0 * M)))
1189+
# Z is wrong. V is right
11771190
V = h * y * L
11781191
context = exchange.commissioning_hash.digest()
1179-
print("context", context.hex(" "))
1192+
del exchange.commissioning_hash
11801193
tt = Crypto_Transcript(
11811194
context,
11821195
pake1.pA,
@@ -1185,18 +1198,70 @@ def process_packet(self, address, data):
11851198
V.to_bytes("uncompressed"),
11861199
w0.to_bytes(NIST256p.baselen, byteorder="big"),
11871200
)
1188-
print("transcript", len(tt))
11891201
cA, cB, Ke = Crypto_P2(tt, pake1.pA, pake2.pB)
11901202
pake2.cB = cB
1191-
# print("sending pake2 back")
1192-
# print(pake2)
1203+
exchange.cA = cA
1204+
exchange.Ke = Ke
11931205
exchange.send(
11941206
ProtocolId.SECURE_CHANNEL, SecureProtocolOpcode.PASE_PAKE2, pake2
11951207
)
11961208
elif protocol_opcode == SecureProtocolOpcode.PASE_PAKE2:
11971209
print("Received PASE PAKE2")
1210+
raise NotImplementedError("Implement SPAKE2+ prover")
11981211
elif protocol_opcode == SecureProtocolOpcode.PASE_PAKE3:
11991212
print("Received PASE PAKE3")
1213+
pake3 = PAKE3(message.application_payload[1:-1])
1214+
if pake3.cA != exchange.cA:
1215+
del exchange.cA
1216+
del exchange.Ke
1217+
print("cA mismatch")
1218+
error_status = StatusReport()
1219+
error_status.general_code = GeneralCode.FAILURE
1220+
error_status.protocol_id = ProtocolId.SECURE_CHANNEL
1221+
error_status.protocol_code = (
1222+
SecureChannelProtocolCode.INVALID_PARAMETER
1223+
)
1224+
exchange.send(
1225+
ProtocolId.SECURE_CHANNEL,
1226+
SecureProtocolOpcode.STATUS_REPORT,
1227+
error_status,
1228+
)
1229+
else:
1230+
exchange.session.session_timestamp = time.monotonic()
1231+
status_ok = StatusReport()
1232+
status_ok.general_code = GeneralCode.SUCCESS
1233+
status_ok.protocol_id = ProtocolId.SECURE_CHANNEL
1234+
status_ok.protocol_code = (
1235+
SecureChannelProtocolCode.SESSION_ESTABLISHMENT_SUCCESS
1236+
)
1237+
exchange.send(
1238+
ProtocolId.SECURE_CHANNEL,
1239+
SecureProtocolOpcode.STATUS_REPORT,
1240+
status_ok,
1241+
)
1242+
1243+
# Fully initialize the secure session context we'll use going
1244+
# forwards.
1245+
secure_session_context = exchange.secure_session_context
1246+
1247+
# Compute session keys
1248+
keys = Crypto_KDF(
1249+
exchange.Ke,
1250+
b"",
1251+
b"SessionKeys",
1252+
3 * CRYPTO_SYMMETRIC_KEY_LENGTH_BITS,
1253+
)
1254+
secure_session_context.i2r_key = keys[
1255+
:CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES
1256+
]
1257+
secure_session_context.r2i_key = keys[
1258+
CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES : 2
1259+
* CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES
1260+
]
1261+
secure_session_context.attestation_challenge = keys[
1262+
2 * CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES :
1263+
]
1264+
print("PASE succeeded")
12001265
elif protocol_opcode == SecureProtocolOpcode.CASE_SIGMA1:
12011266
print("Received CASE Sigma1")
12021267
elif protocol_opcode == SecureProtocolOpcode.CASE_SIGMA2:

circuitmatter/__main__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ def recvfrom_into(self, buffer, nbytes=None):
3333
def sendto(self, data, address):
3434
if address is None:
3535
raise ValueError("Address must be set")
36-
print("sendto", address, data.hex(" "))
3736
return len(data)
3837

3938

circuitmatter/tlv.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,7 @@ def scan_until(self, tag):
128128
value_length = 1
129129
else: # Float
130130
value_offset = length_offset
131-
print(value_offset)
132131
value_length = 4 << (element_type & 0x1)
133-
print(value_length)
134132
elif (
135133
element_category == 3 or element_category == 4
136134
): # UTF-8 String or Octet String

test_data/recorded_packets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[["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"]]
1+
[["receive", 19144358180711, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45718, 0, 0], "BAAAAGpkOgQwkwDA2XDoNAUgd1gAABUwASAqDGF6b0TLjYSHpiEP8ULWWJUbuZ1RXNOefpJ4KFje9SUCPPQkAwAoBDUFJQH0ASUCLAElA6APJAQRJAULJgYAAAMBJAcBGBg="], ["receive", 19144364732600, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45718, 0, 0], "BAAAAGtkOgQwkwDA2XDoNAUid1gAABUwAUEEf8gHR/MR/oQGRHJRxZirzEa42qHq4qH8aIioXnNkXrWr8d2LZRb3nIYMKWt+QDmTvIp+9L79mr68C+OrH3WiYBg="], ["receive", 19144379606119, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45718, 0, 0], "BAAAAGxkOgQwkwDA2XDoNAUkd1gAABUwASC8iRG3Ehkfyze+iIPOsRNSstOwlc67pZmCj1WLxvY0kBg="], ["receive", 19144379820804, ["fd98:bbab:bd61:8040:642:1aff:fe0c:9f2a", 45718, 0, 0], "AAEAAJslbgoieWTcgmp1jDKhidqXMqcxlhS1MBWeoTOdBWCmqQMUUKBjJiUx3DB2FWrCLJbwXxHwYmtYsReJ9DNcXrt7d46v984TaDqT+tt+ZjdRxOVJDP/wDlTrHWTZ/5ybvBXRBzZrtSkRC0K47EyiRQ/KXpyzAY1w66SPk/2IGjI="]]

0 commit comments

Comments
 (0)