Skip to content

Commit

Permalink
Switch to cryptography to resolve performance concerns.
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Mar 27, 2020
1 parent c8ffd07 commit 52a1718
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 28 deletions.
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

0 comments on commit 52a1718

Please sign in to comment.