19
19
20
20
__version__ = "0.0.0"
21
21
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
+
22
31
# Section 4.11.2
23
32
MSG_COUNTER_WINDOW_SIZE = 32
24
33
MSG_COUNTER_SYNC_REQ_JITTER_MS = 500
@@ -386,10 +395,8 @@ def send(self, message):
386
395
message .destination_node_id = self .ephemeral_initiator_node_id
387
396
if message .message_counter is None :
388
397
message .message_counter = next (self .message_counter )
389
- print (message )
390
398
buf = memoryview (bytearray (1280 ))
391
399
nbytes = message .encode_into (buf )
392
- print (nbytes , buf [:nbytes ].hex (" " ))
393
400
self .socket .sendto (buf [:nbytes ], self .node_ipaddress )
394
401
395
402
@@ -559,6 +566,8 @@ def encode_into(self, buffer):
559
566
offset = self .application_payload .encode_into (buffer , offset )
560
567
buffer [offset ] = 0x18
561
568
offset += 1
569
+ elif isinstance (self .application_payload , StatusReport ):
570
+ offset = self .application_payload .encode_into (buffer , offset )
562
571
else :
563
572
buffer [offset : offset + len (self .application_payload )] = (
564
573
self .application_payload
@@ -676,6 +685,23 @@ class GeneralCode(enum.IntEnum):
676
685
"""Message size is larger than the recipient can handle."""
677
686
678
687
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
+
679
705
class StatusReport :
680
706
def __init__ (self ):
681
707
self .clear ()
@@ -686,6 +712,9 @@ def clear(self):
686
712
self .protocol_code = 0
687
713
self .protocol_data = None
688
714
715
+ def __len__ (self ):
716
+ return 8 + len (self .protocol_data ) if self .protocol_data else 0
717
+
689
718
def encode_into (self , buffer , offset = 0 ) -> int :
690
719
struct .pack_into (
691
720
"<HIH" ,
@@ -702,15 +731,14 @@ def encode_into(self, buffer, offset=0) -> int:
702
731
return offset
703
732
704
733
def decode (self , buffer ):
705
- print (buffer .hex (" " ))
706
734
self .general_code , self .protocol_id , self .protocol_code = struct .unpack_from (
707
735
"<HIH" , buffer
708
736
)
709
737
self .general_code = GeneralCode (self .general_code )
710
738
self .protocol_data = buffer [8 :]
711
739
712
740
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 } "
714
742
715
743
716
744
class SessionManager :
@@ -918,11 +946,9 @@ def Crypto_Transcript(context, pA, pB, Z, V, w0) -> bytes:
918
946
offset = 0
919
947
for e in elements :
920
948
struct .pack_into ("<Q" , tt , offset , len (e ))
921
- print (offset , 8 , hex (tt [offset ]))
922
949
offset += 8
923
950
924
951
tt [offset : offset + len (e )] = e
925
- print (offset , len (e ), e .hex (" " ))
926
952
offset += len (e )
927
953
return tt
928
954
@@ -975,13 +1001,12 @@ def KDF(salt, key, info):
975
1001
976
1002
def Crypto_P2 (tt , pA , pB ) -> tuple [bytes , bytes , bytes ]:
977
1003
KaKe = Crypto_Hash (tt )
978
- print (f"KaKe[{ len (KaKe )} ]" , KaKe .hex (" " ))
979
1004
Ka = KaKe [: CRYPTO_HASH_LEN_BYTES // 2 ]
980
1005
Ke = KaKe [CRYPTO_HASH_LEN_BYTES // 2 :]
981
1006
# https://github.com/project-chip/connectedhomeip/blob/c88d5cf83cd3e3323ac196630acc34f196a2f405/src/crypto/CHIPCryptoPAL.cpp#L458-L468
982
1007
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 :]
985
1010
cA = Crypto_HMAC (KcA , pB )
986
1011
cB = Crypto_HMAC (KcB , pA )
987
1012
return (cA , cB , Ke )
@@ -1112,16 +1137,6 @@ def process_packet(self, address, data):
1112
1137
exchange .commissioning_hash = hashlib .sha256 (
1113
1138
b"CHIP PAKE V1 Commissioning"
1114
1139
)
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
- )
1125
1140
exchange .commissioning_hash .update (message .application_payload )
1126
1141
if request .passcodeId == 0 :
1127
1142
pass
@@ -1136,6 +1151,7 @@ def process_packet(self, address, data):
1136
1151
response .responderRandom = os .urandom (32 )
1137
1152
session_context = self .manager .new_context ()
1138
1153
response .responderSessionId = session_context .local_session_id
1154
+ exchange .secure_session_context = session_context
1139
1155
session_context .peer_session_id = request .initiatorSessionId
1140
1156
if not request .hasPBKDFParameters :
1141
1157
params = Crypto_PBKDFParameterSet ()
@@ -1144,7 +1160,6 @@ def process_packet(self, address, data):
1144
1160
response .pbkdf_parameters = params
1145
1161
1146
1162
encoded = b"\x15 " + response .encode () + b"\x18 "
1147
- print ("Commissioning hash" , hex (encoded [0 ]), len (encoded ))
1148
1163
exchange .commissioning_hash .update (encoded )
1149
1164
exchange .send (
1150
1165
ProtocolId .SECURE_CHANNEL ,
@@ -1157,26 +1172,24 @@ def process_packet(self, address, data):
1157
1172
elif protocol_opcode == SecureProtocolOpcode .PASE_PAKE1 :
1158
1173
print ("Received PASE PAKE1" )
1159
1174
pake1 = PAKE1 (message .application_payload [1 :- 1 ])
1160
- # print(pake1)
1161
1175
pake2 = PAKE2 ()
1162
- print ("verifier" , self .nonvolatile ["verifier" ])
1163
1176
verifier = binascii .a2b_base64 (self .nonvolatile ["verifier" ])
1164
1177
w0 = memoryview (verifier )[:CRYPTO_GROUP_SIZE_BYTES ]
1165
1178
L = memoryview (verifier )[CRYPTO_GROUP_SIZE_BYTES :]
1166
1179
L = Point .from_bytes (NIST256p .curve , L )
1167
- print ("w0" , w0 .hex (" " ))
1168
1180
w0 = int .from_bytes (w0 , byteorder = "big" )
1169
- print ("L" , L )
1170
1181
y , Y = Crypto_pB (w0 , L )
1171
1182
# pB is Y encoded uncompressed
1172
1183
# pA is X encoded uncompressed
1173
1184
pake2 .pB = Y .to_bytes ("uncompressed" )
1174
1185
h = NIST256p .curve .cofactor ()
1175
1186
# 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
1177
1190
V = h * y * L
1178
1191
context = exchange .commissioning_hash .digest ()
1179
- print ( "context" , context . hex ( " " ))
1192
+ del exchange . commissioning_hash
1180
1193
tt = Crypto_Transcript (
1181
1194
context ,
1182
1195
pake1 .pA ,
@@ -1185,18 +1198,70 @@ def process_packet(self, address, data):
1185
1198
V .to_bytes ("uncompressed" ),
1186
1199
w0 .to_bytes (NIST256p .baselen , byteorder = "big" ),
1187
1200
)
1188
- print ("transcript" , len (tt ))
1189
1201
cA , cB , Ke = Crypto_P2 (tt , pake1 .pA , pake2 .pB )
1190
1202
pake2 .cB = cB
1191
- # print("sending pake2 back")
1192
- # print(pake2)
1203
+ exchange . cA = cA
1204
+ exchange . Ke = Ke
1193
1205
exchange .send (
1194
1206
ProtocolId .SECURE_CHANNEL , SecureProtocolOpcode .PASE_PAKE2 , pake2
1195
1207
)
1196
1208
elif protocol_opcode == SecureProtocolOpcode .PASE_PAKE2 :
1197
1209
print ("Received PASE PAKE2" )
1210
+ raise NotImplementedError ("Implement SPAKE2+ prover" )
1198
1211
elif protocol_opcode == SecureProtocolOpcode .PASE_PAKE3 :
1199
1212
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" )
1200
1265
elif protocol_opcode == SecureProtocolOpcode .CASE_SIGMA1 :
1201
1266
print ("Received CASE Sigma1" )
1202
1267
elif protocol_opcode == SecureProtocolOpcode .CASE_SIGMA2 :
0 commit comments