Skip to content

Commit

Permalink
Use same get_session_keys for async and sync client
Browse files Browse the repository at this point in the history
  • Loading branch information
Jc2k committed Aug 25, 2019
1 parent c548ccb commit 23934ed
Show file tree
Hide file tree
Showing 2 changed files with 1 addition and 125 deletions.
2 changes: 1 addition & 1 deletion homekit/aio/controller/ip/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
from homekit.crypto.chacha20poly1305 import chacha20_aead_encrypt, chacha20_aead_decrypt
from homekit.exceptions import AccessoryDisconnectedError
from homekit.http_impl import HttpContentTypes
from homekit.protocol import get_session_keys
from homekit.protocol.tlv import TLV

from .crypto import get_session_keys
from .response import HttpResponse


Expand Down
124 changes: 0 additions & 124 deletions homekit/aio/controller/ip/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,127 +217,3 @@ def perform_pair_setup_part2(pin, ios_pairing_id, salt, server_public_key):
'iOSDeviceLTSK': ios_device_ltsk.to_ascii(encoding='hex').decode()[:64],
'iOSDeviceLTPK': hexlify(ios_device_ltpk.to_bytes()).decode()
}


def get_session_keys(pairing_data):
"""
HomeKit Controller side call to perform a pair verify operation as described in chapter 4.8 page 47 ff.
:param pairing_data: the paring data as returned by perform_pair_setup
:return: tuple of the session keys (controller_to_accessory_key and accessory_to_controller_key)
:raises InvalidAuthTagError: if the auth tag could not be verified,
:raises IncorrectPairingIdError: if the accessory's LTPK could not be found
:raises InvalidSignatureError: if the accessory's signature could not be verified
:raises AuthenticationError: if the secured session could not be established
"""

#
# Step #1 ios --> accessory (send verify start Request) (page 47)
#
ios_key = x25519.X25519PrivateKey.generate()
ios_key_pub = ios_key.public_key().public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)

request_tlv = [
(TLV.kTLVType_State, TLV.M1),
(TLV.kTLVType_PublicKey, ios_key_pub)
]

step2_expectations = [TLV.kTLVType_State, TLV.kTLVType_PublicKey, TLV.kTLVType_EncryptedData]
response_tlv = yield (request_tlv, step2_expectations)

#
# Step #3 ios --> accessory (send SRP verify request) (page 49)
#
response_tlv = TLV.reorder(response_tlv, step2_expectations)
assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][1] == TLV.M2, 'get_session_keys: not M2'
assert response_tlv[1][0] == TLV.kTLVType_PublicKey, 'get_session_keys: no public key'
assert response_tlv[2][0] == TLV.kTLVType_EncryptedData, 'get_session_keys: no encrypted data'

# 1) generate shared secret
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)
session_key = hkdf_inst.expand('Pair-Verify-Encrypt-Info'.encode(), 32)

# 3) verify auth tag on encrypted data and 4) decrypt
encrypted = response_tlv[2][1]
decrypted = chacha20_aead_decrypt(bytes(), session_key, 'PV-Msg02'.encode(), bytes([0, 0, 0, 0]),
encrypted)
if type(decrypted) == bool and not decrypted:
raise InvalidAuthTagError('step 3')
d1 = TLV.decode_bytes(decrypted)
d1 = TLV.reorder(d1, [TLV.kTLVType_Identifier, TLV.kTLVType_Signature])
assert d1[0][0] == TLV.kTLVType_Identifier, 'get_session_keys: no identifier'
assert d1[1][0] == TLV.kTLVType_Signature, 'get_session_keys: no signature'

# 5) look up pairing by accessory name
accessory_name = d1[0][1].decode()

if pairing_data['AccessoryPairingID'] != accessory_name:
raise IncorrectPairingIdError('step 3')

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_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_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_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
sub_tlv = TLV.encode_list([
(TLV.kTLVType_Identifier, pairing_data['iOSPairingId'].encode()),
(TLV.kTLVType_Signature, ios_device_signature)
])

# 10) encrypt and sign
encrypted_data_with_auth_tag = chacha20_aead_encrypt(bytes(), session_key, 'PV-Msg03'.encode(), bytes([0, 0, 0, 0]),
sub_tlv)
tmp = bytearray(encrypted_data_with_auth_tag[0])
tmp += encrypted_data_with_auth_tag[1]

# 11) create tlv
request_tlv = [
(TLV.kTLVType_State, TLV.M3),
(TLV.kTLVType_EncryptedData, tmp)
]

step3_expectations = [TLV.kTLVType_State, TLV.kTLVType_Error]
response_tlv = yield (request_tlv, step3_expectations)

#
# Post Step #4 verification (page 51)
#
response_tlv = TLV.reorder(response_tlv, step3_expectations)
assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][1] == TLV.M4, 'get_session_keys: not M4'
if len(response_tlv) == 2 and response_tlv[1][0] == TLV.kTLVType_Error:
error_handler(response_tlv[1][1], 'verification')

# calculate session keys
hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(), shared_secret, hash=hashlib.sha512)
controller_to_accessory_key = hkdf_inst.expand('Control-Write-Encryption-Key'.encode(), 32)

hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(), shared_secret, hash=hashlib.sha512)
accessory_to_controller_key = hkdf_inst.expand('Control-Read-Encryption-Key'.encode(), 32)

return controller_to_accessory_key, accessory_to_controller_key

0 comments on commit 23934ed

Please sign in to comment.