Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 32 additions & 22 deletions pyhap/hap_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
import socketserver
import threading

from tlslite.utils.chacha20_poly1305 import CHACHA20_POLY1305
from Crypto.Protocol.KDF import HKDF
from Crypto.Hash import SHA512
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes

import curve25519
import ed25519

Expand All @@ -28,6 +30,7 @@

logger = logging.getLogger(__name__)

backend = default_backend()

# Various "tag" constants for HAP's TLV encoding.
class HAP_TLV_TAGS:
Expand Down Expand Up @@ -65,7 +68,8 @@ class HAP_OPERATION_CODE:

class HAP_CRYPTO:
HKDF_KEYLEN = 32 # bytes, length of expanded HKDF keys
HKDF_HASH = SHA512 # Hash function to use in key expansion
HKDF_HASH = hashes.SHA512() # Hash function to use in key expansion
TAG_LENGTH = 16 # ChaCha20Poly1305 tag length
TLS_NONCE_LEN = 12 # bytes, length of TLS encryption nonce


Expand All @@ -76,8 +80,14 @@ def _pad_tls_nonce(nonce, total_len=HAP_CRYPTO.TLS_NONCE_LEN):

def hap_hkdf(key, salt, info):
"""Just a shorthand."""
return HKDF(key, HAP_CRYPTO.HKDF_KEYLEN, salt, HAP_CRYPTO.HKDF_HASH, context=info)

hkdf = HKDF(
algorithm=HAP_CRYPTO.HKDF_HASH,
length=HAP_CRYPTO.HKDF_KEYLEN,
salt=salt,
info=info,
backend=backend,
)
return hkdf.derive(key)

class UnprivilegedRequestException(Exception):
pass
Expand Down Expand Up @@ -313,8 +323,8 @@ def _pairing_three(self, tlv_objects):
hkdf_enc_key = hap_hkdf(long_to_bytes(session_key),
self.PAIRING_3_SALT, self.PAIRING_3_INFO)

cipher = CHACHA20_POLY1305(hkdf_enc_key, "python")
decrypted_data = cipher.open(self.PAIRING_3_NONCE, bytearray(encrypted_data), b"")
cipher = ChaCha20Poly1305(hkdf_enc_key)
decrypted_data = cipher.decrypt(self.PAIRING_3_NONCE, bytes(encrypted_data), b"")
assert decrypted_data is not None

dec_tlv_objects = tlv.decode(bytes(decrypted_data))
Expand Down Expand Up @@ -379,9 +389,9 @@ def _pairing_five(self, client_username, client_ltpk, encryption_key):
HAP_TLV_TAGS.PUBLIC_KEY, server_public,
HAP_TLV_TAGS.PROOF, server_proof)

cipher = CHACHA20_POLY1305(encryption_key, "python")
cipher = ChaCha20Poly1305(encryption_key)
aead_message = bytes(
cipher.seal(self.PAIRING_5_NONCE, bytearray(message), b""))
cipher.encrypt(self.PAIRING_5_NONCE, bytes(message), b""))

client_uuid = uuid.UUID(str(client_username, "utf-8"))
should_confirm = self.accessory_handler.pair(client_uuid, client_ltpk)
Expand Down Expand Up @@ -443,9 +453,9 @@ def _pair_verify_one(self, tlv_objects):
message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac,
HAP_TLV_TAGS.PROOF, server_proof)

cipher = CHACHA20_POLY1305(output_key, "python")
cipher = ChaCha20Poly1305(output_key)
aead_message = bytes(
cipher.seal(self.PVERIFY_1_NONCE, bytearray(message), b""))
cipher.encrypt(self.PVERIFY_1_NONCE, bytes(message), b""))
data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02',
HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message,
HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize())
Expand All @@ -461,8 +471,8 @@ def _pair_verify_two(self, tlv_objects):
"""
logger.debug("Pair verify [2/2]")
encrypted_data = tlv_objects[HAP_TLV_TAGS.ENCRYPTED_DATA]
cipher = CHACHA20_POLY1305(self.enc_context["pre_session_key"], "python")
decrypted_data = cipher.open(self.PVERIFY_2_NONCE, bytearray(encrypted_data), b"")
cipher = ChaCha20Poly1305(self.enc_context["pre_session_key"])
decrypted_data = cipher.decrypt(self.PVERIFY_2_NONCE, bytes(encrypted_data), b"")
assert decrypted_data is not None # TODO:

dec_tlv_objects = tlv.decode(bytes(decrypted_data))
Expand Down Expand Up @@ -683,10 +693,10 @@ def makefile(self, *args, **kwargs):
def _set_ciphers(self):
"""Generate out/inbound encryption keys and initialise respective ciphers."""
outgoing_key = hap_hkdf(self.shared_key, self.CIPHER_SALT, self.OUT_CIPHER_INFO)
self.out_cipher = CHACHA20_POLY1305(outgoing_key, "python")
self.out_cipher = ChaCha20Poly1305(outgoing_key)

incoming_key = hap_hkdf(self.shared_key, self.CIPHER_SALT, self.IN_CIPHER_INFO)
self.in_cipher = CHACHA20_POLY1305(incoming_key, "python")
self.in_cipher = ChaCha20Poly1305(incoming_key)

# socket.socket interface

Expand Down Expand Up @@ -730,7 +740,7 @@ def recv(self, buflen=1042, flags=0):
# Init. info about the block we just started.
# Note we are setting the total length to block_length + mac length
self.curr_in_total = \
struct.unpack("H", block_length_bytes)[0] + self.in_cipher.tagLength
struct.unpack("H", block_length_bytes)[0] + HAP_CRYPTO.TAG_LENGTH
self.num_in_recv = 0
self.curr_in_block = b""
buflen -= self.LENGTH_LENGTH
Expand All @@ -747,9 +757,9 @@ def recv(self, buflen=1042, flags=0):
# We read a whole block. Decrypt it and append it to the result.
nonce = _pad_tls_nonce(struct.pack("Q", self.in_count))
# Note we are removing the mac length from the total length
block_length = self.curr_in_total - self.in_cipher.tagLength
plaintext = self.in_cipher.open(
nonce, bytearray(self.curr_in_block),
block_length = self.curr_in_total - HAP_CRYPTO.TAG_LENGTH
plaintext = self.in_cipher.decrypt(
nonce, bytes(self.curr_in_block),
struct.pack("H", block_length))
result += plaintext
self.in_count += 1
Expand All @@ -776,10 +786,10 @@ def sendall(self, data, flags=0):
while offset < total:
length = min(total - offset, self.MAX_BLOCK_LENGTH)
length_bytes = struct.pack("H", length)
block = bytearray(data[offset: offset + length])
block = bytes(data[offset: offset + length])
nonce = _pad_tls_nonce(struct.pack("Q", self.out_count))
ciphertext = length_bytes \
+ self.out_cipher.seal(nonce, block, length_bytes)
+ self.out_cipher.encrypt(nonce, block, length_bytes)
offset += length
self.out_count += 1
result += ciphertext
Expand Down
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
curve25519-donna
ed25519
pycryptodome
tlslite-ng
cryptography
zeroconf
3 changes: 1 addition & 2 deletions requirements_all.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
base36
curve25519-donna
ed25519
pycryptodome
cryptography
pyqrcode
tlslite-ng
zeroconf
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
REQUIRES = [
'curve25519-donna',
'ed25519',
'pycryptodome',
'tlslite-ng',
'cryptography',
'zeroconf',
]

Expand Down