Example #1
0
    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")

        incoming_key = hap_hkdf(self.shared_key, self.CIPHER_SALT, self.IN_CIPHER_INFO)
        self.in_cipher = CHACHA20_POLY1305(incoming_key, "python")
Example #2
0
   def _set_ciphers(self):

      outgoing_key = hap_hkdf(self.shared_key, self.CIPHER_SALT, self.OUT_CIPHER_INFO)
      self.out_cipher = CHACHA20_POLY1305(outgoing_key, "python")

      incoming_key = hap_hkdf(self.shared_key, self.CIPHER_SALT, self.IN_CIPHER_INFO)
      self.in_cipher = CHACHA20_POLY1305(incoming_key, "python")
Example #3
0
    def _pairing_five(self, client_username, client_ltpk, encryption_key):
        """At that point we know the client has the accessory password and has a valid key
        pair. Add it as a pair and send a sever proof.

        Parameters are as for _pairing_four.
        """
        logging.debug("Pairing [5/5]")
        session_key = self._accessory_driver.srp_verifier.get_session_key()
        output_key = hap_hkdf(long_to_bytes(session_key), PAIRING_5_SALT,
                              PAIRING_5_INFO)

        server_public = self._state.public_key.to_bytes()
        mac = self._state.mac.encode()

        material = output_key + mac + server_public
        private_key = self._state.private_key
        server_proof = private_key.sign(material)

        message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac,
                             HAP_TLV_TAGS.PUBLIC_KEY, server_public,
                             HAP_TLV_TAGS.PROOF, server_proof)

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

        client_uuid = uuid.UUID(str(client_username, "utf-8"))
        should_confirm = self._accessory_driver.pair(client_uuid, client_ltpk)

        if not should_confirm:
            self.send_response(500)
            return

        return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x06',
                          HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message)
Example #4
0
    def _pairing_three(self, tlv_objects):
        """Expand the SRP session key to obtain a new key. Use it to verify and decrypt
            the recieved data. Continue to step four.

        @param tlv_objects: The TLV data received from the client.
        @type tlv_object: dict
        """
        logger.debug("Pairing [3/5]")
        encrypted_data = tlv_objects[HAP_TLV_TAGS.ENCRYPTED_DATA]

        session_key = self.accessory_handler.srp_verifier.get_session_key()
        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"")
        assert decrypted_data is not None

        dec_tlv_objects = tlv.decode(bytes(decrypted_data))
        client_username = dec_tlv_objects[HAP_TLV_TAGS.USERNAME]
        client_ltpk = dec_tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY]
        client_proof = dec_tlv_objects[HAP_TLV_TAGS.PROOF]

        self._pairing_four(client_username, client_ltpk, client_proof,
                           hkdf_enc_key)
    def test_seal(self):
        aead = CHACHA20_POLY1305(
            bytearray(
                b'\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f'
                b'\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
            ), "python")

        plaintext = bytearray(
            b'Ladies and Gentlemen of the class of \'99: If I could offer you o'
            b'nly one tip for the future, sunscreen would be it.')

        aad = bytearray(b'\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7')

        nonce = bytearray(b'\x07\x00\x00\x00\x40\x41\x42\x43\x44\x45\x46\x47')

        ciphertext = aead.seal(nonce, plaintext, aad)

        self.assertEqual(
            ciphertext,
            bytearray(
                b'\xd3\x1a\x8d\x34\x64\x8e\x60\xdb\x7b\x86\xaf\xbc\x53\xef\x7e\xc2'
                b'\xa4\xad\xed\x51\x29\x6e\x08\xfe\xa9\xe2\xb5\xa7\x36\xee\x62\xd6'
                b'\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12\x82\xfa\xfb\x69\xda\x92\x72\x8b'
                b'\x1a\x71\xde\x0a\x9e\x06\x0b\x29\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36'
                b'\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c\x98\x03\xae\xe3\x28\x09\x1b\x58'
                b'\xfa\xb3\x24\xe4\xfa\xd6\x75\x94\x55\x85\x80\x8b\x48\x31\xd7\xbc'
                b'\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d\xe5\x76\xd2\x65\x86\xce\xc6\x4b'
                b'\x61\x16'
                b'\x1a\xe1\x0b\x59\x4f\x09\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60\x06\x91'
            ))
Example #6
0
    def _pair_verify_two(self, tlv_objects):
        """Verify the client proof and upgrade to encrypted transport.

        @param tlv_objects: The TLV data received from the client.
        @type tlv_object: dict
        """
        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"")
        assert decrypted_data is not None  # TODO:

        dec_tlv_objects = tlv.decode(bytes(decrypted_data))
        client_username = dec_tlv_objects[HAP_TLV_TAGS.USERNAME]
        material = self.enc_context["client_public"] \
            + client_username \
            + self.enc_context["public_key"].serialize()

        client_uuid = uuid.UUID(str(client_username, "ascii"))
        perm_client_public = self.state.paired_clients.get(client_uuid)
        if perm_client_public is None:
            logger.debug(
                "Client %s attempted pair verify without being paired first.",
                client_uuid)
            self.send_response(200)
            self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE)
            data = tlv.encode(HAP_TLV_TAGS.ERROR_CODE,
                              HAP_OPERATION_CODE.INVALID_REQUEST)
            self.end_response(data)
            return

        verifying_key = ed25519.VerifyingKey(perm_client_public)
        try:
            verifying_key.verify(dec_tlv_objects[HAP_TLV_TAGS.PROOF], material)
        except ed25519.BadSignatureError:
            logger.error("Bad signature, abort.")
            self.send_response(200)
            self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE)
            data = tlv.encode(HAP_TLV_TAGS.ERROR_CODE,
                              HAP_OPERATION_CODE.INVALID_REQUEST)
            self.end_response(data)
            return

        logger.debug(
            "Pair verify with client '%s' completed. Switching to "
            "encrypted transport.", self.client_address)

        data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x04')
        self.send_response(200)
        self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE)
        self.end_response(data)
        self._upgrade_to_encrypted()
        del self.enc_context
    def test_open(self):
        #RFC 7539 Appendix A.5

        key = bytearray(
            b'\x1c\x92\x40\xa5\xeb\x55\xd3\x8a\xf3\x33\x88\x86\x04\xf6\xb5\xf0'
            b'\x47\x39\x17\xc1\x40\x2b\x80\x09\x9d\xca\x5c\xbc\x20\x70\x75\xc0'
        )

        ciphertext = bytearray(
            b'\x64\xa0\x86\x15\x75\x86\x1a\xf4\x60\xf0\x62\xc7\x9b\xe6\x43\xbd'
            b'\x5e\x80\x5c\xfd\x34\x5c\xf3\x89\xf1\x08\x67\x0a\xc7\x6c\x8c\xb2'
            b'\x4c\x6c\xfc\x18\x75\x5d\x43\xee\xa0\x9e\xe9\x4e\x38\x2d\x26\xb0'
            b'\xbd\xb7\xb7\x3c\x32\x1b\x01\x00\xd4\xf0\x3b\x7f\x35\x58\x94\xcf'
            b'\x33\x2f\x83\x0e\x71\x0b\x97\xce\x98\xc8\xa8\x4a\xbd\x0b\x94\x81'
            b'\x14\xad\x17\x6e\x00\x8d\x33\xbd\x60\xf9\x82\xb1\xff\x37\xc8\x55'
            b'\x97\x97\xa0\x6e\xf4\xf0\xef\x61\xc1\x86\x32\x4e\x2b\x35\x06\x38'
            b'\x36\x06\x90\x7b\x6a\x7c\x02\xb0\xf9\xf6\x15\x7b\x53\xc8\x67\xe4'
            b'\xb9\x16\x6c\x76\x7b\x80\x4d\x46\xa5\x9b\x52\x16\xcd\xe7\xa4\xe9'
            b'\x90\x40\xc5\xa4\x04\x33\x22\x5e\xe2\x82\xa1\xb0\xa0\x6c\x52\x3e'
            b'\xaf\x45\x34\xd7\xf8\x3f\xa1\x15\x5b\x00\x47\x71\x8c\xbc\x54\x6a'
            b'\x0d\x07\x2b\x04\xb3\x56\x4e\xea\x1b\x42\x22\x73\xf5\x48\x27\x1a'
            b'\x0b\xb2\x31\x60\x53\xfa\x76\x99\x19\x55\xeb\xd6\x31\x59\x43\x4e'
            b'\xce\xbb\x4e\x46\x6d\xae\x5a\x10\x73\xa6\x72\x76\x27\x09\x7a\x10'
            b'\x49\xe6\x17\xd9\x1d\x36\x10\x94\xfa\x68\xf0\xff\x77\x98\x71\x30'
            b'\x30\x5b\xea\xba\x2e\xda\x04\xdf\x99\x7b\x71\x4d\x6c\x6f\x2c\x29'
            b'\xa6\xad\x5c\xb4\x02\x2b\x02\x70\x9b')

        nonce = bytearray(b'\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08')

        aad = bytearray(b'\xf3\x33\x88\x86\x00\x00\x00\x00\x00\x00\x4e\x91')

        tag = bytearray(
            b'\xee\xad\x9d\x67\x89\x0c\xbb\x22\x39\x23\x36\xfe\xa1\x85\x1f\x38'
        )

        aead = CHACHA20_POLY1305(key, "python")

        plaintext = aead.open(nonce, ciphertext + tag, aad)

        self.assertEqual(
            plaintext,
            bytearray(
                b'Internet-Drafts are draft documents valid for a maximum of six m'
                b'onths and may be updated, replaced, or obsoleted by other docume'
                b'nts at any time. It is inappropriate to use Internet-Drafts as r'
                b'eference material or to cite them other than as /'
                b'\xe2\x80\x9cwork in progress\x2e\x2f\xe2\x80\x9d'))
Example #8
0
    def _pair_verify_one(self, tlv_objects):
        """Generate new session key pair and send a proof to the client.

        @param tlv_objects: The TLV data received from the client.
        @type tlv_object: dict
        """
        logging.debug("Pair verify [1/2].")
        client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY]

        private_key = curve25519.Private()
        public_key = private_key.get_public()
        shared_key = private_key.get_shared_key(
            curve25519.Public(client_public),
            # Key is hashed before being returned, we don't want it; This fixes that.
            lambda x: x)

        mac = self._state.mac.encode()
        material = public_key.serialize() + mac + client_public
        server_proof = self._state.private_key.sign(material)

        output_key = hap_hkdf(shared_key, PVERIFY_1_SALT, PVERIFY_1_INFO)

        enc_context = {
            "client_public": client_public,
            "private_key": private_key,
            "public_key": public_key,
            "shared_key": shared_key,
            "pre_session_key": output_key
        }

        message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF,
                             server_proof)

        cipher = CHACHA20_POLY1305(output_key, "python")
        aead_message = bytes(
            cipher.seal(PVERIFY_1_NONCE, bytearray(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())
        return data, enc_context
Example #9
0
   def _pair_verify_one(self, tlv_objects):
      """Generate new session key pair and send a proof to the client.

      @param tlv_objects: The TLV data received from the client.
      @type tlv_object: dict
      """
      logger.debug("Pair verify [1/2].")
      client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY]

      private_key = curve25519.Private()
      public_key = private_key.get_public()
      shared_key = private_key.get_shared_key(
                        curve25519.Public(client_public),
            # Why not randomly hash the key before returning it
            # so that it is incompatible with another
            # implementation and force clients to use hacks?
                        lambda x: x)

      mac = self.accessory.mac.encode()
      material = public_key.serialize() + mac + client_public
      server_proof = self.accessory.private_key.sign(material)

      output_key = hap_hkdf(shared_key, self.PVERIFY_1_SALT, self.PVERIFY_1_INFO)

      self._set_encryption_ctx(client_public, private_key, public_key,
                               shared_key, output_key)

      message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac,
                           HAP_TLV_TAGS.PROOF, server_proof)

      cipher = CHACHA20_POLY1305(output_key, "python")
      aead_message = bytes(
         cipher.seal(self.PVERIFY_1_NONCE, bytearray(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())
      self.send_response(200)
      self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE)
      self.end_response(data)
Example #10
0
    def _pair_verify_two(self, tlv_objects, enc_context):
        """Verify the client proof and upgrade to encrypted transport.

        @param tlv_objects: The TLV data received from the client.
        @type tlv_object: dict
        """
        logging.debug('Pair verify [2/2]')
        encrypted_data = tlv_objects[HAP_TLV_TAGS.ENCRYPTED_DATA]
        cipher = CHACHA20_POLY1305(enc_context['pre_session_key'], 'python')
        decrypted_data = cipher.open(PVERIFY_2_NONCE,
                                     bytearray(encrypted_data), b'')
        assert decrypted_data is not None  # TODO:

        dec_tlv_objects = tlv.decode(bytes(decrypted_data))
        client_username = dec_tlv_objects[HAP_TLV_TAGS.USERNAME]
        material = enc_context['client_public'] \
            + client_username \
            + enc_context['public_key'].serialize()

        client_uuid = uuid.UUID(str(client_username, 'ascii'))
        perm_client_public = self._state.paired_clients.get(client_uuid)
        if perm_client_public is None:
            logging.debug(
                'Client %s attempted pair verify without being paired first.',
                client_uuid)
            return tlv.encode(HAP_TLV_TAGS.ERROR_CODE,
                              HAP_OPERATION_CODE.INVALID_REQUEST)

        verifying_key = ed25519.VerifyingKey(perm_client_public)
        try:
            verifying_key.verify(dec_tlv_objects[HAP_TLV_TAGS.PROOF], material)
        except ed25519.BadSignatureError:
            logging.error('Bad signature, abort.')
            return tlv.encode(HAP_TLV_TAGS.ERROR_CODE,
                              HAP_OPERATION_CODE.INVALID_REQUEST)

        return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x04'), None
Example #11
0
 def __init__(self, out_key, in_key):
     """Initialize a new Chacha20Cipher."""
     self._enc_out = CHACHA20_POLY1305(out_key, 'python')
     self._enc_in = CHACHA20_POLY1305(in_key, 'python')
     self._out_counter = 0
     self._in_counter = 0
Example #12
0
def unpack_addr_payload(ciphertext, hdpass):
    plaintext = CHACHA20_POLY1305(hdpass,
                                  'python').open(b'serokellfore', ciphertext,
                                                 b'')
    if plaintext:
        return cbor.loads(bytes(plaintext))
Example #13
0
def pack_addr_payload(path, hdpass):
    'packHDAddressAttr'
    plaintext = cbor.dumps(cbor.VarList(path))
    return bytes(
        CHACHA20_POLY1305(hdpass, 'python').seal(b'serokellfore', plaintext,
                                                 b''))
    def test_open_with_too_short_ciphertext(self):
        aead = CHACHA20_POLY1305(bytearray(256 // 8), "python")

        plaintext = aead.open(bytearray(96 // 8), bytearray(15), bytearray(0))

        self.assertIsNone(plaintext)
 def test___init___with_unsupported_implementation(self):
     with self.assertRaises(ValueError):
         CHACHA20_POLY1305(bytearray(256 // 8), "pycrypto")
 def test___init___with_invalid_key_size(self):
     with self.assertRaises(ValueError):
         CHACHA20_POLY1305(bytearray(128 // 8), "python")
    def test___init__(self):
        aead = CHACHA20_POLY1305(bytearray(256 // 8), "python")

        self.assertIsNotNone(aead)
    def test_open_with_invalid_tag(self):
        aead = CHACHA20_POLY1305(bytearray(256 // 8), "python")

        plaintext = aead.open(bytearray(96 // 8), bytearray(32), bytearray(0))

        self.assertIsNone(plaintext)
    def test_seal_with_invalid_nonce(self):
        aead = CHACHA20_POLY1305(bytearray(256 // 8), "python")

        with self.assertRaises(ValueError):
            aead.seal(bytearray(16), bytearray(10), bytearray(10))
    def test_open_with_invalid_size_nonce(self):
        aead = CHACHA20_POLY1305(bytearray(256 // 8), "python")

        with self.assertRaises(ValueError):
            aead.open(bytearray(128 // 8), bytearray(64), bytearray(0))