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
2332MSG_COUNTER_WINDOW_SIZE = 32
2433MSG_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+
679705class 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
716744class 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
9761002def 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 :
0 commit comments