def _get_pairing_data(resp): pairing_message = CryptoPairingMessage.cryptoPairingMessage tlv = tlv8.read_tlv(resp.Extensions[pairing_message].pairingData) if tlv8.TLV_ERROR in tlv: error = int.from_bytes(tlv[tlv8.TLV_ERROR], byteorder="little") raise exceptions.AuthenticationError("got error: " + str(error)) return tlv
def step4(self, encrypted_data): """Last pairing step.""" chacha = chacha20.Chacha20Cipher(self._session_key, self._session_key) decrypted_tlv_bytes = chacha.decrypt(encrypted_data, nounce="PS-Msg06".encode()) if not decrypted_tlv_bytes: raise exceptions.AuthenticationError("data decrypt failed") decrypted_tlv = tlv8.read_tlv(decrypted_tlv_bytes) _LOGGER.debug("PS-Msg06: %s", decrypted_tlv) atv_identifier = decrypted_tlv[tlv8.TLV_IDENTIFIER] atv_signature = decrypted_tlv[tlv8.TLV_SIGNATURE] atv_pub_key = decrypted_tlv[tlv8.TLV_PUBLIC_KEY] log_binary( _LOGGER, "Device", Identifier=atv_identifier, Signature=atv_signature, Public=atv_pub_key, ) # TODO: verify signature here return Credentials( atv_pub_key, self._auth_private, atv_identifier, self.pairing_id )
def verify1(self, credentials, session_pub_key, encrypted): """First verification step.""" # No additional hashing used self._shared = self._verify_private.get_shared_key( curve25519.Public(session_pub_key), hashfunc=lambda x: x) session_key = hkdf_expand('Pair-Verify-Encrypt-Salt', 'Pair-Verify-Encrypt-Info', self._shared) chacha = chacha20.Chacha20Cipher(session_key, session_key) decrypted_tlv = tlv8.read_tlv( chacha.decrypt(encrypted, nounce='PV-Msg02'.encode())) identifier = decrypted_tlv[tlv8.TLV_IDENTIFIER] signature = decrypted_tlv[tlv8.TLV_SIGNATURE] if identifier != credentials.atv_id: raise Exception('incorrect device response') # TODO: new exception info = session_pub_key + \ bytes(identifier) + self._verify_public.serialize() ltpk = VerifyingKey(bytes(credentials.ltpk)) ltpk.verify(bytes(signature), bytes(info)) # throws if no match device_info = self._verify_public.serialize() + \ credentials.client_id + session_pub_key device_signature = SigningKey(credentials.ltsk).sign(device_info) tlv = tlv8.write_tlv({tlv8.TLV_IDENTIFIER: credentials.client_id, tlv8.TLV_SIGNATURE: device_signature}) return chacha.encrypt(tlv, nounce='PV-Msg03'.encode())
def handle_crypto_pairing(self, message, inner): """Handle incoming crypto pairing message.""" _LOGGER.debug('Received crypto pairing message') pairing_data = tlv8.read_tlv(inner.pairingData) seqno = pairing_data[tlv8.TLV_SEQ_NO][0] # Work-around for now to support "tries" to auth before pairing if seqno == 1: if tlv8.TLV_PUBLIC_KEY in pairing_data: self.has_paired = True elif tlv8.TLV_METHOD in pairing_data: self.has_paired = False suffix = 'paired' if self.has_paired else 'pairing' method = '_seqno{0}_{1}'.format(seqno, suffix) getattr(self, method)(pairing_data)
def verify1(self, credentials, session_pub_key, encrypted): """First verification step.""" self._shared = self._verify_private.exchange( X25519PublicKey.from_public_bytes(session_pub_key) ) session_key = hkdf_expand( "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", self._shared ) chacha = chacha20.Chacha20Cipher(session_key, session_key) decrypted_tlv = tlv8.read_tlv( chacha.decrypt(encrypted, nounce="PV-Msg02".encode()) ) identifier = decrypted_tlv[tlv8.TLV_IDENTIFIER] signature = decrypted_tlv[tlv8.TLV_SIGNATURE] if identifier != credentials.atv_id: raise exceptions.AuthenticationError("incorrect device response") info = session_pub_key + bytes(identifier) + self._public_bytes ltpk = Ed25519PublicKey.from_public_bytes(bytes(credentials.ltpk)) try: ltpk.verify(bytes(signature), bytes(info)) except InvalidSignature as ex: raise exceptions.AuthenticationError("signature error") from ex device_info = self._public_bytes + credentials.client_id + session_pub_key device_signature = Ed25519PrivateKey.from_private_bytes(credentials.ltsk).sign( device_info ) tlv = tlv8.write_tlv( { tlv8.TLV_IDENTIFIER: credentials.client_id, tlv8.TLV_SIGNATURE: device_signature, } ) return chacha.encrypt(tlv, nounce="PV-Msg03".encode())
def step4(self, encrypted_data): """Last pairing step.""" chacha = chacha20.Chacha20Cipher(self._session_key, self._session_key) decrypted_tlv_bytes = chacha.decrypt( encrypted_data, nounce='PS-Msg06'.encode()) if not decrypted_tlv_bytes: raise Exception('data decrypt failed') # TODO: new exception decrypted_tlv = tlv8.read_tlv(decrypted_tlv_bytes) _LOGGER.debug('PS-Msg06: %s', decrypted_tlv) atv_identifier = decrypted_tlv[tlv8.TLV_IDENTIFIER] atv_signature = decrypted_tlv[tlv8.TLV_SIGNATURE] atv_pub_key = decrypted_tlv[tlv8.TLV_PUBLIC_KEY] log_binary(_LOGGER, 'Device', Identifier=atv_identifier, Signature=atv_signature, Public=atv_pub_key) # TODO: verify signature here return Credentials(atv_pub_key, self._signing_key.to_seed(), atv_identifier, self.pairing_id)
def test_read_key_larger_than_255_bytes(self): self.assertEqual(read_tlv(LARGE_KEY_OUT), LARGE_KEY_IN)
def test_read_two_keys(self): self.assertEqual(read_tlv(DOUBLE_KEY_OUT), DOUBLE_KEY_IN)
def test_read_single_key(self): self.assertEqual(read_tlv(SINGLE_KEY_OUT), SINGLE_KEY_IN)
def _get_pairing_data(resp): pairing_message = CryptoPairingMessage.cryptoPairingMessage return tlv8.read_tlv(resp.Extensions[pairing_message].pairingData)
def handle_crypto_pairing(self, message, _): """Handle incoming crypto pairing message.""" _LOGGER.debug('Received crypto pairing message') pairing_data = tlv8.read_tlv(message.inner().pairingData) seqno = pairing_data[tlv8.TLV_SEQ_NO][0] getattr(self, "_seqno_" + str(seqno))(pairing_data)