Example #1
0
    def test_long_values_decode_bytearray_to_list(self):
        example = bytearray.fromhex('060103' + ('09FF' + 255 * '61' + '092D' +
                                                45 * '61') + '010568656c6c6f')
        expected = [[6, bytearray(b'\x03')], [9, bytearray(300 * b'a')],
                    [1, bytearray(b'hello')]]

        data = TLV.decode_bytearray(example)
        self.assertListEqual(data, expected)
Example #2
0
 def test_separator_list(self):
     val = [
         [TLV.kTLVType_State, TLV.M3],
         TLV.kTLVType_Separator_Pair,
         [TLV.kTLVType_State, TLV.M4],
     ]
     res = TLV.decode_bytearray(TLV.encode_list(val))
     self.assertEqual(val, res)
Example #3
0
 def test_long_values_2(self):
     val = [
         [TLV.kTLVType_State, TLV.M3],
         [TLV.kTLVType_Certificate, (150 * 'a' + 150 * 'b').encode()],
         [TLV.kTLVType_Identifier, 'hello'.encode()],
     ]
     res = TLV.decode_bytearray(TLV.encode_list(val))
     self.assertEqual(val, res)
Example #4
0
def perform_pair_setup_part2(pin, ios_pairing_id, write_fun, salt,
                             server_public_key):
    """
    Performs a pair setup operation as described in chapter 4.7 page 39 ff.

    :param pin: the setup code from the accessory
    :param ios_pairing_id: the id of the simulated ios device
    :param write_fun: a function that takes a bytes representation of a TLV, the expected keys as list and returns
        decoded TLV as list
    :return: a dict with the ios device's part of the pairing information
    :raises UnavailableError: if the device is already paired
    :raises MaxTriesError: if the device received more than 100 unsuccessful pairing attempts
    :raises BusyError: if a parallel pairing is ongoing
    :raises AuthenticationError: if the verification of the device's SRP proof fails
    :raises MaxPeersError: if the device cannot accept an additional pairing
    :raises IllegalData: if the verification of the accessory's data fails
    """

    srp_client = SrpClient('Pair-Setup', pin)
    srp_client.set_salt(salt)
    srp_client.set_server_public_key(server_public_key)
    client_pub_key = srp_client.get_public_key()
    client_proof = srp_client.get_proof()

    response_tlv = TLV.encode_list([
        (TLV.kTLVType_State, TLV.M3),
        (TLV.kTLVType_PublicKey, SrpClient.to_byte_array(client_pub_key)),
        (TLV.kTLVType_Proof, SrpClient.to_byte_array(client_proof)),
    ])

    step4_expectations = [
        TLV.kTLVType_State, TLV.kTLVType_Error, TLV.kTLVType_Proof
    ]
    response_tlv = write_fun(response_tlv, step4_expectations)

    #
    # Step #5 ios --> accessory (Exchange Request) (see page 43)
    #
    logging.debug('#5 ios -> accessory: send SRP exchange request')

    # M4 Verification (page 43)
    response_tlv = TLV.reorder(response_tlv, step4_expectations)
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][1] == TLV.M4, \
        'perform_pair_setup: State not M4'
    if response_tlv[1][0] == TLV.kTLVType_Error:
        error_handler(response_tlv[1][1], 'step 5')

    assert response_tlv[1][
        0] == TLV.kTLVType_Proof, 'perform_pair_setup: Not a proof'
    if not srp_client.verify_servers_proof(response_tlv[1][1]):
        raise AuthenticationError('Step #5: wrong proof!')

    # M5 Request generation (page 44)
    session_key = srp_client.get_session_key()

    ios_device_ltsk, ios_device_ltpk = ed25519.create_keypair()

    # reversed:
    #   Pair-Setup-Encrypt-Salt instead of Pair-Setup-Controller-Sign-Salt
    #   Pair-Setup-Encrypt-Info instead of Pair-Setup-Controller-Sign-Info
    hkdf_inst = hkdf.Hkdf('Pair-Setup-Controller-Sign-Salt'.encode(),
                          SrpClient.to_byte_array(session_key),
                          hash=hashlib.sha512)
    ios_device_x = hkdf_inst.expand('Pair-Setup-Controller-Sign-Info'.encode(),
                                    32)

    hkdf_inst = hkdf.Hkdf('Pair-Setup-Encrypt-Salt'.encode(),
                          SrpClient.to_byte_array(session_key),
                          hash=hashlib.sha512)
    session_key = hkdf_inst.expand('Pair-Setup-Encrypt-Info'.encode(), 32)

    ios_device_pairing_id = ios_pairing_id.encode()
    ios_device_info = ios_device_x + ios_device_pairing_id + ios_device_ltpk.to_bytes(
    )

    ios_device_signature = ios_device_ltsk.sign(ios_device_info)

    sub_tlv = [(TLV.kTLVType_Identifier, ios_device_pairing_id),
               (TLV.kTLVType_PublicKey, ios_device_ltpk.to_bytes()),
               (TLV.kTLVType_Signature, ios_device_signature)]
    sub_tlv_b = TLV.encode_list(sub_tlv)

    # taking tge iOSDeviceX as key was reversed from
    # https://github.com/KhaosT/HAP-NodeJS/blob/2ea9d761d9bd7593dd1949fec621ab085af5e567/lib/HAPServer.js
    # function handlePairStepFive calling encryption.encryptAndSeal
    encrypted_data_with_auth_tag = chacha20_aead_encrypt(
        bytes(), session_key, 'PS-Msg05'.encode(), bytes([0, 0, 0, 0]),
        sub_tlv_b)
    tmp = bytearray(encrypted_data_with_auth_tag[0])
    tmp += encrypted_data_with_auth_tag[1]

    response_tlv = [(TLV.kTLVType_State, TLV.M5),
                    (TLV.kTLVType_EncryptedData, tmp)]
    body = TLV.encode_list(response_tlv)

    step6_expectations = [
        TLV.kTLVType_State, TLV.kTLVType_Error, TLV.kTLVType_EncryptedData
    ]
    response_tlv = write_fun(body, step6_expectations)

    #
    # Step #7 ios (Verification) (page 47)
    #
    response_tlv = TLV.reorder(response_tlv, step6_expectations)
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][
        1] == TLV.M6, 'perform_pair_setup: State not M6'
    if response_tlv[1][0] == TLV.kTLVType_Error:
        error_handler(response_tlv[1][1], 'step 7')

    assert response_tlv[1][
        0] == TLV.kTLVType_EncryptedData, 'perform_pair_setup: No encrypted data'
    decrypted_data = chacha20_aead_decrypt(bytes(), session_key,
                                           'PS-Msg06'.encode(),
                                           bytes([0, 0, 0,
                                                  0]), response_tlv[1][1])
    if decrypted_data is False:
        raise homekit.exception.IllegalData('step 7')

    response_tlv = TLV.decode_bytearray(decrypted_data)
    response_tlv = TLV.reorder(response_tlv, [
        TLV.kTLVType_Identifier, TLV.kTLVType_PublicKey, TLV.kTLVType_Signature
    ])

    assert response_tlv[2][
        0] == TLV.kTLVType_Signature, 'perform_pair_setup: No signature'
    accessory_sig = response_tlv[2][1]

    assert response_tlv[0][
        0] == TLV.kTLVType_Identifier, 'perform_pair_setup: No identifier'
    accessory_pairing_id = response_tlv[0][1]

    assert response_tlv[1][
        0] == TLV.kTLVType_PublicKey, 'perform_pair_setup: No public key'
    accessory_ltpk = response_tlv[1][1]

    hkdf_inst = hkdf.Hkdf('Pair-Setup-Accessory-Sign-Salt'.encode(),
                          SrpClient.to_byte_array(
                              srp_client.get_session_key()),
                          hash=hashlib.sha512)
    accessory_x = hkdf_inst.expand('Pair-Setup-Accessory-Sign-Info'.encode(),
                                   32)

    accessory_info = accessory_x + accessory_pairing_id + accessory_ltpk

    e25519s = ed25519.VerifyingKey(bytes(response_tlv[1][1]))
    try:
        e25519s.verify(bytes(accessory_sig), bytes(accessory_info))
    except AssertionError:
        raise InvalidSignatureError('step #7')

    return {
        '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()[:64],
        'iOSDeviceLTPK': hexlify(ios_device_ltpk.to_bytes()).decode()
    }