Skip to content

Commit

Permalink
Merge pull request #95 from Jc2k/supported-25519
Browse files Browse the repository at this point in the history
Avoid py25519 dependency with cryptography.
  • Loading branch information
jlusiardi committed Jan 20, 2019
2 parents ab52150 + c4dae87 commit 8317041
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 34 deletions.
50 changes: 30 additions & 20 deletions homekit/accessoryserver.py
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -652,24 +654,24 @@ 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
accessory_info = accessory_spk + self.server.data.accessory_pairing_id_bytes + \
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -1051,28 +1057,32 @@ 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),
hash=hashlib.sha512)
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)

# 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)
Expand Down
31 changes: 19 additions & 12 deletions homekit/protocol/__init__.py
Expand Up @@ -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
Expand Down Expand Up @@ -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()
}

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
@@ -1,4 +1,4 @@
zeroconf
py25519
hkdf
ed25519
cryptography
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -40,9 +40,9 @@
],
install_requires=[
'zeroconf',
'py25519',
'hkdf',
'ed25519',
'cryptography',
],
license='Apache License 2.0',
long_description=long_description,
Expand Down

0 comments on commit 8317041

Please sign in to comment.