diff --git a/homekit/accessoryserver.py b/homekit/accessoryserver.py index df3458fe..4b311d42 100644 --- a/homekit/accessoryserver.py +++ b/homekit/accessoryserver.py @@ -24,11 +24,13 @@ from socketserver import ThreadingMixIn import hkdf -import py25519 from zeroconf import Zeroconf, ServiceInfo import socket import sys import logging +import ed25519 + +from cryptography.hazmat.primitives.asymmetric import x25519 from homekit.crypto.chacha20poly1305 import chacha20_aead_decrypt, chacha20_aead_encrypt from homekit.crypto.srp import SrpServer @@ -234,7 +236,7 @@ def accessory_ltpk(self) -> bytes: def set_accessory_keys(self, accessory_ltpk: bytes, accessory_ltsk: bytes): self.data['accessory_ltpk'] = binascii.hexlify(accessory_ltpk).decode() - self.data['accessory_ltsk'] = binascii.hexlify(accessory_ltsk).decode() + self.data['accessory_ltsk'] = binascii.hexlify(accessory_ltsk).decode()[:64] self._save_data() @property @@ -652,16 +654,16 @@ def _post_pair_verify(self): self.log_message('Step #2 /pair-verify') # 1) generate new curve25519 key pair - accessory_session_key = py25519.Key25519() - accessory_spk = accessory_session_key.public_key().pubkey + accessory_session_key = x25519.X25519PrivateKey.generate() + accessory_spk = accessory_session_key.public_key().public_bytes() self.server.sessions[self.session_id]['accessory_pub_key'] = accessory_spk # 2) generate shared secret - ios_device_curve25519_pub_key_bytes = d_req[1][1] + ios_device_curve25519_pub_key_bytes = bytes(d_req[1][1]) self.server.sessions[self.session_id]['ios_device_pub_key'] = ios_device_curve25519_pub_key_bytes - ios_device_curve25519_pub_key = py25519.Key25519(pubkey=bytes(ios_device_curve25519_pub_key_bytes), - verifyingkey=bytes()) - shared_secret = accessory_session_key.get_ecdh_key(ios_device_curve25519_pub_key) + ios_device_curve25519_pub_key = x25519.X25519PublicKey.from_public_bytes(ios_device_curve25519_pub_key_bytes) + + shared_secret = accessory_session_key.exchange(ios_device_curve25519_pub_key) self.server.sessions[self.session_id]['shared_secret'] = shared_secret # 3) generate accessory info @@ -669,7 +671,7 @@ def _post_pair_verify(self): ios_device_curve25519_pub_key_bytes # 4) sign accessory info for accessory signature - accessory_ltsk = py25519.Key25519(secretkey=self.server.data.accessory_ltsk) + accessory_ltsk = ed25519.SigningKey(self.server.data.accessory_ltsk + self.server.data.accessory_ltpk) accessory_signature = accessory_ltsk.sign(accessory_info) # 5) sub tlv @@ -731,14 +733,16 @@ def _post_pair_verify(self): self.send_error_reply(TLV.M4, TLV.kTLVError_Authentication) self.log_error('error in step #4: not paired %s %s', d_res, self.server.sessions) return - ios_device_ltpk = py25519.Key25519(pubkey=bytes(), verifyingkey=ios_device_ltpk_bytes) + ios_device_lptk = ed25519.VerifyingKey(ios_device_ltpk_bytes) # 4) verify ios_device_info ios_device_sig = d1[1][1] ios_device_curve25519_pub_key_bytes = self.server.sessions[self.session_id]['ios_device_pub_key'] accessory_spk = self.server.sessions[self.session_id]['accessory_pub_key'] ios_device_info = ios_device_curve25519_pub_key_bytes + ios_device_pairing_id + accessory_spk - if not ios_device_ltpk.verify(bytes(ios_device_sig), bytes(ios_device_info)): + try: + ios_device_lptk.verify(bytes(ios_device_sig), bytes(ios_device_info)) + except ed25519.BadSignatureError: self.send_error_reply(TLV.M4, TLV.kTLVError_Authentication) self.log_error('error in step #4: signature %s %s', d_res, self.server.sessions) return @@ -1039,8 +1043,10 @@ def _post_pair_setup(self): # 5) verify signature ios_device_sig = d_req_2[2][1] # should be [TLV.kTLVType_Signature - verify_key = py25519.Key25519(pubkey=bytes(), verifyingkey=bytes(ios_device_ltpk)) - if not verify_key.verify(bytes(ios_device_sig), bytes(ios_device_info)): + verify_key = ed25519.VerifyingKey(bytes(ios_device_ltpk)) + try: + verify_key.verify(bytes(ios_device_sig), bytes(ios_device_info)) + except ed25519.BadSignatureError: self.send_error_reply(TLV.M6, TLV.kTLVError_Authentication) self.log_error('error in step #6 %s %s', d_res, self.server.sessions) return @@ -1051,12 +1057,16 @@ def _post_pair_setup(self): # Response Generation # 1) generate accessoryLTPK if not existing if self.server.data.accessory_ltsk is None or self.server.data.accessory_ltpk is None: - accessory_ltsk = py25519.Key25519() - accessory_ltpk = accessory_ltsk.verifyingkey - self.server.data.set_accessory_keys(accessory_ltpk, accessory_ltsk.secretkey) + accessory_ltsk, accessory_ltpk = ed25519.create_keypair() + self.server.data.set_accessory_keys( + accessory_ltpk.to_bytes(), + accessory_ltsk.to_bytes(), + ) else: - accessory_ltsk = py25519.Key25519(self.server.data.accessory_ltsk) - accessory_ltpk = accessory_ltsk.verifyingkey + accessory_ltsk = ed25519.SigningKey( + self.server.data.accessory_ltsk + self.server.data.accessory_ltpk + ) + accessory_ltpk = ed25519.VerifyingKey(self.server.data.accessory_ltpk) # 2) derive AccessoryX hkdf_inst = hkdf.Hkdf('Pair-Setup-Accessory-Sign-Salt'.encode(), SrpServer.to_byte_array(shared_secret), @@ -1064,7 +1074,7 @@ def _post_pair_setup(self): accessory_x = hkdf_inst.expand('Pair-Setup-Accessory-Sign-Info'.encode(), 32) # 3) - accessory_info = accessory_x + self.server.data.accessory_pairing_id_bytes + accessory_ltpk + accessory_info = accessory_x + self.server.data.accessory_pairing_id_bytes + accessory_ltpk.to_bytes() # 4) generate signature accessory_signature = accessory_ltsk.sign(accessory_info) @@ -1072,7 +1082,7 @@ def _post_pair_setup(self): # 5) construct sub_tlv sub_tlv = [ (TLV.kTLVType_Identifier, self.server.data.accessory_pairing_id_bytes), - (TLV.kTLVType_PublicKey, accessory_ltpk), + (TLV.kTLVType_PublicKey, accessory_ltpk.to_bytes()), (TLV.kTLVType_Signature, accessory_signature) ] sub_tlv_b = TLV.encode_list(sub_tlv) diff --git a/homekit/protocol/__init__.py b/homekit/protocol/__init__.py index 0663f386..f6484188 100644 --- a/homekit/protocol/__init__.py +++ b/homekit/protocol/__init__.py @@ -17,8 +17,9 @@ import hashlib import ed25519 import hkdf -import py25519 from binascii import hexlify +from cryptography.hazmat.primitives.asymmetric import x25519 + from homekit.protocol.tlv import TLV from homekit.exceptions import IncorrectPairingIdError, InvalidAuthTagError, InvalidSignatureError, UnavailableError, \ AuthenticationError, InvalidError, BusyError, MaxTriesError, MaxPeersError, BackoffError @@ -219,7 +220,7 @@ def perform_pair_setup(connection, pin, ios_pairing_id): 'AccessoryPairingID': response_tlv[0][1].decode(), 'AccessoryLTPK': hexlify(response_tlv[1][1]).decode(), 'iOSPairingId': ios_pairing_id, - 'iOSDeviceLTSK': ios_device_ltsk.to_ascii(encoding='hex').decode(), + 'iOSDeviceLTSK': ios_device_ltsk.to_ascii(encoding='hex').decode()[:64], 'iOSDeviceLTPK': hexlify(ios_device_ltpk.to_bytes()).decode() } @@ -243,11 +244,12 @@ def get_session_keys(conn, pairing_data): # # Step #1 ios --> accessory (send verify start Request) (page 47) # - ios_key = py25519.Key25519() + ios_key = x25519.X25519PrivateKey.generate() + ios_key_pub = ios_key.public_key().public_bytes() request_tlv = TLV.encode_list([ (TLV.kTLVType_State, TLV.M1), - (TLV.kTLVType_PublicKey, ios_key.pubkey) + (TLV.kTLVType_PublicKey, ios_key_pub) ]) conn.request('POST', '/pair-verify', request_tlv, headers) @@ -263,9 +265,11 @@ def get_session_keys(conn, pairing_data): assert response_tlv[2][0] == TLV.kTLVType_EncryptedData, 'get_session_keys: no encrypted data' # 1) generate shared secret - accessorys_session_pub_key_bytes = response_tlv[1][1] - shared_secret = ios_key.get_ecdh_key( - py25519.Key25519(pubkey=bytes(accessorys_session_pub_key_bytes), verifyingkey=bytes())) + accessorys_session_pub_key_bytes = bytes(response_tlv[1][1]) + accessorys_session_pub_key = x25519.X25519PublicKey.from_public_bytes( + accessorys_session_pub_key_bytes + ) + shared_secret = ios_key.exchange(accessorys_session_pub_key) # 2) derive session key hkdf_inst = hkdf.Hkdf('Pair-Verify-Encrypt-Salt'.encode(), shared_secret, hash=hashlib.sha512) @@ -288,21 +292,24 @@ def get_session_keys(conn, pairing_data): if pairing_data['AccessoryPairingID'] != accessory_name: raise IncorrectPairingIdError('step 3') - accessory_ltpk = py25519.Key25519(pubkey=bytes(), verifyingkey=bytes.fromhex(pairing_data['AccessoryLTPK'])) + accessory_ltpk = ed25519.VerifyingKey(bytes.fromhex(pairing_data['AccessoryLTPK'])) # 6) verify accessory's signature accessory_sig = d1[1][1] accessory_session_pub_key_bytes = response_tlv[1][1] - accessory_info = accessory_session_pub_key_bytes + accessory_name.encode() + ios_key.pubkey - if not accessory_ltpk.verify(bytes(accessory_sig), bytes(accessory_info)): + accessory_info = accessory_session_pub_key_bytes + accessory_name.encode() + ios_key_pub + try: + accessory_ltpk.verify(bytes(accessory_sig), bytes(accessory_info)) + except ed25519.BadSignatureError: raise InvalidSignatureError('step 3') # 7) create iOSDeviceInfo - ios_device_info = ios_key.pubkey + pairing_data['iOSPairingId'].encode() + accessorys_session_pub_key_bytes + ios_device_info = ios_key_pub + pairing_data['iOSPairingId'].encode() + accessorys_session_pub_key_bytes # 8) sign iOSDeviceInfo with long term secret key ios_device_ltsk_h = pairing_data['iOSDeviceLTSK'] - ios_device_ltsk = py25519.Key25519(secretkey=bytes.fromhex(ios_device_ltsk_h)) + ios_device_ltpk_h = pairing_data['iOSDeviceLTPK'] + ios_device_ltsk = ed25519.SigningKey(bytes.fromhex(ios_device_ltsk_h) + bytes.fromhex(ios_device_ltpk_h)) ios_device_signature = ios_device_ltsk.sign(ios_device_info) # 9) construct sub tlv diff --git a/requirements.txt b/requirements.txt index 2a9bc332..cf169f27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ zeroconf -py25519 hkdf ed25519 +cryptography diff --git a/setup.py b/setup.py index 7feb85bf..cf0ec42f 100644 --- a/setup.py +++ b/setup.py @@ -40,9 +40,9 @@ ], install_requires=[ 'zeroconf', - 'py25519', 'hkdf', 'ed25519', + 'cryptography', ], license='Apache License 2.0', long_description=long_description,