Exemplo n.º 1
0
def main():
    args = sys.argv[1:]
    for arg in args:
        print("// Path: m/44'/866'/0'")
        print("MnemonicToRistretto {")
        print(f"    phrase: \"{arg}\",")
        print("    account_index: 0,")

        mnemo = mnemonic.Mnemonic("english")
        master_seed = mnemo.to_seed(arg, "")

        # manually build the path to m/usage/cointype/acctidx
        # for us, usage is BIP-44, cointype is MobileCoin
        # m
        k, c = seed2hdnode(master_seed, b"ed25519 seed", 'ed25519')
        # m/44
        k, c = derive(k, c, 44 + privdev, 'ed25519')
        # m/44/866
        k, c = derive(k, c, 866 + privdev, 'ed25519')
        # m/44/866/0
        acct0, _c = derive(k, c, 0 + privdev, 'ed25519')

        kdf = hkdf.Hkdf(b"mobilecoin-ristretto255-view", acct0, hashlib.sha512)
        key = kdf.expand(length=64)

        print(f"    view_hex: \"{key.hex()}\",")

        kdf = hkdf.Hkdf(b"mobilecoin-ristretto255-spend", acct0,
                        hashlib.sha512)
        key = kdf.expand(length=64)

        print(f"    spend_hex: \"{key.hex()}\",")
        print("},")

        print("// Path: m/44'/866'/1'")
        print("MnemonicToRistretto {")
        print(f"    phrase: \"{arg}\",")
        print("    account_index: 1,")

        # m/44/866/1
        acct1, _c = derive(k, c, 1 + privdev, 'ed25519')

        kdf = hkdf.Hkdf(b"mobilecoin-ristretto255-view", acct1, hashlib.sha512)
        key = kdf.expand(length=64)

        print(f"    view_hex: \"{key.hex()}\",")

        kdf = hkdf.Hkdf(b"mobilecoin-ristretto255-spend", acct1,
                        hashlib.sha512)
        key = kdf.expand(length=64)

        print(f"    spend_hex: \"{key.hex()}\",")
        print("},")
Exemplo n.º 2
0
    def receiveM2(self, data):
        logger.info("Receive M2")

        pvDict = OPACK.decode(data)
        tlv = TLV8Box.decodeFromData(pvDict["pd"]).toDict()
        searching_device_pub_key = x25519.X25519PublicKey.from_public_bytes(
            bytes(tlv[0x03]))
        self.shared_secret = self.session_keys.private.exchange(
            searching_device_pub_key)

        hkdf_inst = hkdf.Hkdf("Pair-Verify-Encrypt-Salt".encode(),
                              self.shared_secret,
                              hash=hashlib.sha512)
        key = hkdf_inst.expand("Pair-Verify-Encrypt-Info".encode(), 32)

        encryptedData = bytes(tlv[0x05])[:-16]
        authKey = bytes(tlv[0x05])[-16:]
        nonce = "PV-Msg02".encode()

        cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
        plainData = cipher.decrypt_and_verify(encryptedData, authKey)
        plainTLV = TLV8Box.decodeFromData(plainData).toDict()

        # plainTLV contains authentication data:
        # {
        #    9: Signature over public keys of grantor and requestor
        #   10: Apple ID certificate (NSDataCompressed)
        #   20: Apple ID validation record (NSDataCompressed)
        # }
        logger.debug(f"Identity TLV: {plainTLV}")

        self.sendM3()
Exemplo n.º 3
0
def check_class_tv(tv):
	'''Test HKDF output via wrapper class'''

	kdf = hkdf.Hkdf(tv["salt"], tv["IKM"], tv["hash"])
	test_okm = kdf.expand(tv["info"], tv["L"])

	print "%s (via wrapper class)" % tv
	print "PRK: %s" % ("match" if kdf._prk == tv["PRK"] else "FAIL")
	print "OKM: %s" % ("match" if test_okm == tv["OKM"] else "FAIL")
	print

	assert_equals(kdf._prk, tv["PRK"])
	assert_equals(test_okm, tv["OKM"])
Exemplo n.º 4
0
def get_privkey(ln_dir, known_pubkey: str):
    hsm_secret = open(f"{ln_dir}/hsm_secret", "rb").read()
    logger.debug(f"hsm_secret: {hsm_secret}")

    # To generate the node private key, apply hkdf to string b"nodeid"
    salt = bytes([0]) or b"\x00"
    key = hkdf.Hkdf(salt, hsm_secret, hash=hashlib.sha256).expand(b"nodeid")

    # Small chance of the key produced not being valid to secp256k1 parameters, so test
    # and increase the salt until it is valid.
    i = 0
    privkey = b""
    while True and i < 1000:
        if i == 999:
            logger.error(
                "No valid secp256k1 key found for hsm secret after 1000 salts")
            return False
        try:
            privkey = secp256k1.PrivateKey(key)
        except Exception:
            # invalid key
            i += 1
            salt = bytes([i])
            key = hkdf.Hkdf(salt, hsm_secret,
                            hash=hashlib.sha256).expand(b"nodeid")
        else:
            # Key valid under secp256k1
            break

    # Check public key derived from the private key against node id
    if not privkey.pubkey.serialize().hex() == known_pubkey:
        logger.warning(
            f"Valid secp265k1 derived pubkey doesn't appear to match "
            f"lightning node id:")
        logger.warning(f"generated: {privkey.pubkey.serialize().hex()}")
        logger.warning(f"actual:    {known_pubkey}")
    return key.hex()
Exemplo n.º 5
0
def expand_argon2_secret(pkm: bytes,
                         context: bytes,
                         salt: Optional[bytes] = None) -> bytes:
    """

    Expand ``pkm`` and ``context`` into a key of length ``bytes`` using
    HKDF's expand function based on HMAC SHA-512). See the HKDF draft RFC and paper for usage notes.

    :param pkm:
    :param context:
    :param salt:
    :return:
    """
    kdf = hkdf.Hkdf(salt=salt, input_key_material=pkm, hash=hashlib.sha512)
    key = kdf.expand(info=context, length=32)
    return key
Exemplo n.º 6
0
    def sendM3(self):
        logger.info("Send M3")

        hkdf_inst = hkdf.Hkdf("Pair-Verify-Encrypt-Salt".encode(),
                              self.shared_secret,
                              hash=hashlib.sha512)
        key = hkdf_inst.expand("Pair-Verify-Encrypt-Info".encode(), 32)

        nonce = "PV-Msg03".encode()
        payload = bytes()
        cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
        _ciphertext, authTag = cipher.encrypt_and_digest(payload)

        tlv8_bytes = TLV8Box([TLV8(0x05, authTag),
                              TLV8(0x06, b"\x03")]).encode()

        opack_bytes = OPACK.encode({
            "pd": tlv8_bytes,
        })
        self.send(0x13, opack_bytes)
Exemplo n.º 7
0
def encrypt():
    req = request.json
    CaaS_creds = req['CaaS_creds']
    UserList = req['UserList']
    ciphertext = base64.b64decode(req['Ciphertext'])

    response.content_type = "application/json"
    rv = dict()

    # Abort if any of the people in the list don't have CaaS accounts
    for user in UserList:
        if not user_exists(user):
            rv['Status'] = "Unsuccessful"
            rv['Error'] = "User {0} not found. Aborted.".format(user)
            return rv

    # Add sender to user list
    UserList.append(CaaS_creds['username'])

    # Sort user list
    UserList = sorted(UserList)

    hashes = ['']

    # Calculate the iterative hashes
    for j in range(len(UserList)):
        s = hashlib.sha512()
        s.update(UserList[j] + hashes[j])
        h = s.hexdigest()
        hashes.append(h)

    # Obtain key via HMAC-based key derivation function (hkdf)
    kdf = hkdf.Hkdf(binascii.unhexlify(hashes[-1]), Xp, hash=hashlib.sha512)
    key = kdf.expand(b"context1", 32)

    ctr = os.urandom(16)
    e = AES.new(key, AES.MODE_CTR, counter=lambda: ctr)
    CaaS_encrypted_text = e.encrypt(ciphertext)
    rv['ctr'] = base64.b64encode(str(ctr))
    rv['enc_p'] = base64.b64encode(CaaS_encrypted_text)
    return rv
Exemplo n.º 8
0
    def sendPWS3(self):
        logger.info("Send PWS3")

        hkdf_inst = hkdf.Hkdf("WriteKeySalt".encode(),
                              self.shared_secret,
                              hash=hashlib.sha512)
        key = hkdf_inst.expand("WriteKeyInfo".encode(), 32)

        opack_bytes = OPACK.encode({
            "op": 5,
            "nw": self.ssid,
            "psk": self.psk,
        })

        aad = bytes(b"\x06\x07")
        nonce = bytes(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")

        cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce)
        cipher.update(aad)
        encryptedData, authTag = cipher.encrypt_and_digest(opack_bytes)
        encryptedData += authTag

        self.send(0x06, encryptedData)
Exemplo n.º 9
0
def get_session_keys(pairing_data):
    """
    HomeKit Controller state machine to perform a pair verify operation as described in chapter 4.8 page 47 ff.
    :param pairing_data: the paring data as returned by perform_pair_setup
    :return: tuple of the session keys (controller_to_accessory_key and  accessory_to_controller_key)
    :raises InvalidAuthTagError: if the auth tag could not be verified,
    :raises IncorrectPairingIdError: if the accessory's LTPK could not be found
    :raises InvalidSignatureError: if the accessory's signature could not be verified
    :raises AuthenticationError: if the secured session could not be established
    """

    #
    # Step #1 ios --> accessory (send verify start Request) (page 47)
    #
    ios_key = x25519.X25519PrivateKey.generate()
    ios_key_pub = ios_key.public_key().public_bytes(
        encoding=serialization.Encoding.Raw,
        format=serialization.PublicFormat.Raw)

    request_tlv = [(TLV.kTLVType_State, TLV.M1),
                   (TLV.kTLVType_PublicKey, ios_key_pub)]

    step2_expectations = [
        TLV.kTLVType_State, TLV.kTLVType_PublicKey, TLV.kTLVType_EncryptedData
    ]
    response_tlv = yield (request_tlv, step2_expectations)

    #
    # Step #3 ios --> accessory (send SRP verify request)  (page 49)
    #
    response_tlv = TLV.reorder(response_tlv, step2_expectations)
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][
        1] == TLV.M2, 'get_session_keys: not M2'
    assert response_tlv[1][
        0] == TLV.kTLVType_PublicKey, 'get_session_keys: no public key'
    assert response_tlv[2][
        0] == TLV.kTLVType_EncryptedData, 'get_session_keys: no encrypted data'

    # 1) generate shared secret
    accessorys_session_pub_key_bytes = bytes(response_tlv[1][1])
    accessorys_session_pub_key = x25519.X25519PublicKey.from_public_bytes(
        accessorys_session_pub_key_bytes)
    shared_secret = ios_key.exchange(accessorys_session_pub_key)

    # 2) derive session key
    hkdf_inst = hkdf.Hkdf('Pair-Verify-Encrypt-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    session_key = hkdf_inst.expand('Pair-Verify-Encrypt-Info'.encode(), 32)

    # 3) verify auth tag on encrypted data and 4) decrypt
    encrypted = response_tlv[2][1]
    decrypted = chacha20_aead_decrypt(bytes(),
                                      session_key, 'PV-Msg02'.encode(),
                                      bytes([0, 0, 0, 0]), encrypted)
    if type(decrypted) == bool and not decrypted:
        raise InvalidAuthTagError('step 3')
    d1 = TLV.decode_bytes(decrypted)
    d1 = TLV.reorder(d1, [TLV.kTLVType_Identifier, TLV.kTLVType_Signature])
    assert d1[0][
        0] == TLV.kTLVType_Identifier, 'get_session_keys: no identifier'
    assert d1[1][0] == TLV.kTLVType_Signature, 'get_session_keys: no signature'

    # 5) look up pairing by accessory name
    accessory_name = d1[0][1].decode()

    if pairing_data['AccessoryPairingID'] != accessory_name:
        raise IncorrectPairingIdError('step 3')

    accessory_ltpk = ed25519.VerifyingKey(
        bytes.fromhex(pairing_data['AccessoryLTPK']))

    # 6) verify accessory's signature
    accessory_sig = d1[1][1]
    accessory_session_pub_key_bytes = response_tlv[1][1]
    accessory_info = accessory_session_pub_key_bytes + accessory_name.encode(
    ) + ios_key_pub
    try:
        accessory_ltpk.verify(bytes(accessory_sig), bytes(accessory_info))
    except ed25519.BadSignatureError:
        raise InvalidSignatureError('step 3')

    # 7) create iOSDeviceInfo
    ios_device_info = ios_key_pub + pairing_data['iOSPairingId'].encode(
    ) + accessorys_session_pub_key_bytes

    # 8) sign iOSDeviceInfo with long term secret key
    ios_device_ltsk_h = pairing_data['iOSDeviceLTSK']
    ios_device_ltpk_h = pairing_data['iOSDeviceLTPK']
    ios_device_ltsk = ed25519.SigningKey(
        bytes.fromhex(ios_device_ltsk_h) + bytes.fromhex(ios_device_ltpk_h))
    ios_device_signature = ios_device_ltsk.sign(ios_device_info)

    # 9) construct sub tlv
    sub_tlv = TLV.encode_list([(TLV.kTLVType_Identifier,
                                pairing_data['iOSPairingId'].encode()),
                               (TLV.kTLVType_Signature, ios_device_signature)])

    # 10) encrypt and sign
    encrypted_data_with_auth_tag = chacha20_aead_encrypt(
        bytes(), session_key, 'PV-Msg03'.encode(), bytes([0, 0, 0, 0]),
        sub_tlv)
    tmp = bytearray(encrypted_data_with_auth_tag[0])
    tmp += encrypted_data_with_auth_tag[1]

    # 11) create tlv
    request_tlv = [(TLV.kTLVType_State, TLV.M3),
                   (TLV.kTLVType_EncryptedData, tmp)]

    step3_expectations = [TLV.kTLVType_State, TLV.kTLVType_Error]
    response_tlv = yield (request_tlv, step3_expectations)

    #
    #   Post Step #4 verification (page 51)
    #
    response_tlv = TLV.reorder(response_tlv, step3_expectations)
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][
        1] == TLV.M4, 'get_session_keys: not M4'
    if len(response_tlv) == 2 and response_tlv[1][0] == TLV.kTLVType_Error:
        error_handler(response_tlv[1][1], 'verification')

    # calculate session keys
    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    controller_to_accessory_key = hkdf_inst.expand(
        'Control-Write-Encryption-Key'.encode(), 32)

    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    accessory_to_controller_key = hkdf_inst.expand(
        'Control-Read-Encryption-Key'.encode(), 32)

    return controller_to_accessory_key, accessory_to_controller_key
Exemplo n.º 10
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()
    }
Exemplo n.º 11
0
def hkdf_sha256(key, salt, info, length):
    kdf = hkdf.Hkdf(salt, key, hash=hashlib.sha256)
    return kdf.expand(info, length)
Exemplo n.º 12
0
def get_session_keys(conn, pairing_data):
    """
    Perform a pair verify operation as described in chapter 4.8 page 47 ff.

    :param conn: the http connection to the target accessory
    :param pairing_data: the paring data as returned by perform_pair_setup
    :returns: tuple of the session keys (controller_to_accessory_key and
              accessory_to_controller_key)
    """
    headers = {
        'Content-Type': 'application/pairing+tlv8'
    }

    # Step #1 ios --> accessory (send verify start request) (page 47)
    ios_key = PrivateKey.generate()

    request_tlv = TLV.encode_dict({
        TLV.kTLVType_State: TLV.M1,
        TLV.kTLVType_PublicKey: bytes(ios_key.public_key),
    })

    try:
        conn.request('POST', '/pair-verify', request_tlv, headers)
        resp = conn.getresponse()
    except (TimeoutError, HTTPException, OSError):
        return None

    response_tlv = TLV.decode_bytes(resp.read())

    # Step #3 ios --> accessory (send SRP verify request)  (page 49)
    if TLV.kTLVType_State not in response_tlv:
        return None

    if response_tlv[TLV.kTLVType_State] != TLV.M2:
        return None

    if TLV.kTLVType_PublicKey not in response_tlv:
        return None

    if TLV.kTLVType_EncryptedData not in response_tlv:
        return None

    # 1) generate shared secret
    accessorys_session_pub_key_bytes = \
        bytes(response_tlv[TLV.kTLVType_PublicKey])
    shared_secret = crypto_scalarmult(
        bytes(ios_key),
        bytes(PublicKey(accessorys_session_pub_key_bytes)))

    # 2) derive session key
    hkdf_inst = hkdf.Hkdf('Pair-Verify-Encrypt-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    session_key = hkdf_inst.expand('Pair-Verify-Encrypt-Info'.encode(), 32)

    # 3) verify authtag on encrypted data and 4) decrypt
    encrypted = response_tlv[TLV.kTLVType_EncryptedData]
    decrypted = crypto_aead_chacha20poly1305_ietf_decrypt(
        bytes(encrypted),
        bytes(),
        bytes([0, 0, 0, 0]) + 'PV-Msg02'.encode(),
        session_key)

    if not decrypted:
        return None

    d1 = TLV.decode_bytes(decrypted)

    if TLV.kTLVType_Identifier not in d1:
        return None

    if TLV.kTLVType_Signature not in d1:
        return None

    # 5) look up pairing by accessory name
    accessory_name = d1[TLV.kTLVType_Identifier].decode()

    if pairing_data['AccessoryPairingID'] != accessory_name:
        return None

    accessory_ltpk = VerifyKey(bytes.fromhex(pairing_data['AccessoryLTPK']))

    # 6) verify accessory's signature
    accessory_sig = d1[TLV.kTLVType_Signature]
    accessory_session_pub_key_bytes = response_tlv[TLV.kTLVType_PublicKey]
    accessory_info = accessory_session_pub_key_bytes + \
        accessory_name.encode() + bytes(ios_key.public_key)
    try:
        accessory_ltpk.verify(bytes(accessory_info), bytes(accessory_sig))
    except BadSignatureError:
        return None

    # 7) create iOSDeviceInfo
    ios_device_info = bytes(ios_key.public_key) + \
        pairing_data['iOSPairingID'].encode() + \
        accessorys_session_pub_key_bytes

    # 8) sign iOSDeviceInfo with long term secret key
    ios_device_ltsk_h = pairing_data['iOSDeviceLTSK']
    ios_device_ltsk = SigningKey(bytes.fromhex(ios_device_ltsk_h))
    ios_device_signature = ios_device_ltsk.sign(ios_device_info).signature

    # 9) construct sub tlv
    sub_tlv = TLV.encode_dict({
        TLV.kTLVType_Identifier: pairing_data['iOSPairingID'].encode(),
        TLV.kTLVType_Signature: ios_device_signature
    })

    # 10) encrypt and sign
    ciphertext = crypto_aead_chacha20poly1305_ietf_encrypt(
        bytes(sub_tlv),
        bytes(),
        bytes([0, 0, 0, 0]) + 'PV-Msg03'.encode(),
        session_key)
    tmp = ciphertext

    # 11) create tlv
    request_tlv = TLV.encode_dict({
        TLV.kTLVType_State: TLV.M3,
        TLV.kTLVType_EncryptedData: tmp
    })

    # 12) send to accessory
    try:
        conn.request('POST', '/pair-verify', request_tlv, headers)
        resp = conn.getresponse()
    except (TimeoutError, HTTPException, OSError):
        return None

    response_tlv = TLV.decode_bytes(resp.read())

    # Post Step #4 verification (page 51)
    if TLV.kTLVType_State not in response_tlv:
        return None

    if response_tlv[TLV.kTLVType_State] != TLV.M4:
        return None

    if TLV.kTLVType_Error in response_tlv:
        return None

    # calculate session keys
    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    controller_to_accessory_key = \
        hkdf_inst.expand('Control-Write-Encryption-Key'.encode(), 32)

    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    accessory_to_controller_key = \
        hkdf_inst.expand('Control-Read-Encryption-Key'.encode(), 32)

    return controller_to_accessory_key, accessory_to_controller_key
Exemplo n.º 13
0
def perform_pair_setup(connection, pin, ios_pairing_id):
    """
    Perform a pair setup operation as described in chapter 4.7 page 39 ff.

    :param connection: the http connection to the target accessory
    :param pin: the setup code from the accessory
    :param ios_pairing_id: the id of the simulated ios device
    :returns: a dict with the ios device's part of the pairing information
    """
    headers = {
        'Content-Type': 'application/pairing+tlv8'
    }

    # Step #1 ios --> accessory (send SRP start request) (see page 39)
    request_tlv = TLV.encode_dict({
        TLV.kTLVType_State: TLV.M1,
        TLV.kTLVType_Method: TLV.PairSetup
    })

    try:
        connection.request('POST', '/pair-setup', request_tlv, headers)
        resp = connection.getresponse()
    except (TimeoutError, HTTPException, OSError):
        return None

    response_tlv = TLV.decode_bytes(resp.read())

    # Step #3 ios --> accessory (send SRP verify request) (see page 41)
    if TLV.kTLVType_State not in response_tlv:
        return None

    if response_tlv[TLV.kTLVType_State] != TLV.M2:
        return None

    if TLV.kTLVType_Error in response_tlv:
        return None

    srp_client = SrpClient('Pair-Setup', pin)
    srp_client.set_salt(response_tlv[TLV.kTLVType_Salt])
    srp_client.set_server_public_key(response_tlv[TLV.kTLVType_PublicKey])
    client_pub_key = srp_client.get_public_key()
    client_proof = srp_client.get_proof()

    response_tlv = TLV.encode_dict({
        TLV.kTLVType_State: TLV.M3,
        TLV.kTLVType_PublicKey: SrpClient.to_byte_array(client_pub_key),
        TLV.kTLVType_Proof: SrpClient.to_byte_array(client_proof),
    })

    try:
        connection.request('POST', '/pair-setup', response_tlv, headers)
        resp = connection.getresponse()
    except (TimeoutError, HTTPException, OSError):
        return None

    response_tlv = TLV.decode_bytes(resp.read())

    # Step #5 ios --> accessory (exchange request) (see page 43)

    # M4 Verification (page 43)
    if TLV.kTLVType_State not in response_tlv:
        return None

    if response_tlv[TLV.kTLVType_State] != TLV.M4:
        return None

    if TLV.kTLVType_Error in response_tlv:
        return None

    if TLV.kTLVType_Proof not in response_tlv:
        return None

    if not srp_client.verify_servers_proof(response_tlv[TLV.kTLVType_Proof]):
        return None

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

    ios_device_ltsk = SigningKey.generate()
    ios_device_ltpk = ios_device_ltsk.verify_key

    # 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 + bytes(ios_device_ltpk)

    ios_device_signature = ios_device_ltsk.sign(ios_device_info).signature

    sub_tlv = {
        TLV.kTLVType_Identifier: ios_device_pairing_id,
        TLV.kTLVType_PublicKey: bytes(ios_device_ltpk),
        TLV.kTLVType_Signature: ios_device_signature
    }
    sub_tlv_b = TLV.encode_dict(sub_tlv)

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

    response_tlv = {
        TLV.kTLVType_State: TLV.M5,
        TLV.kTLVType_EncryptedData: tmp
    }
    body = TLV.encode_dict(response_tlv)

    try:
        connection.request('POST', '/pair-setup', body, headers)
        resp = connection.getresponse()
    except (TimeoutError, HTTPException, OSError):
        return None

    response_tlv = TLV.decode_bytes(resp.read())

    # Step #7 ios (verification) (page 47)
    if response_tlv[TLV.kTLVType_State] != TLV.M6:
        return None

    if TLV.kTLVType_Error in response_tlv:
        return None

    if TLV.kTLVType_EncryptedData not in response_tlv:
        return None

    decrypted_data = crypto_aead_chacha20poly1305_ietf_decrypt(
        bytes(response_tlv[TLV.kTLVType_EncryptedData]),
        bytes(),
        bytes([0, 0, 0, 0]) + 'PS-Msg06'.encode(),
        session_key)
    if not decrypted_data:
        return None

    decrypted_data = bytearray(decrypted_data)
    response_tlv = TLV.decode_bytearray(decrypted_data)

    if TLV.kTLVType_Signature not in response_tlv:
        return None

    accessory_sig = response_tlv[TLV.kTLVType_Signature]

    if TLV.kTLVType_Identifier not in response_tlv:
        return None

    accessory_pairing_id = response_tlv[TLV.kTLVType_Identifier]

    if TLV.kTLVType_PublicKey not in response_tlv:
        return None

    accessory_ltpk = response_tlv[TLV.kTLVType_PublicKey]
    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 = VerifyKey(bytes(response_tlv[TLV.kTLVType_PublicKey]))
    e25519s.verify(bytes(accessory_info), bytes(accessory_sig))

    return {
        'AccessoryPairingID': response_tlv[TLV.kTLVType_Identifier].decode(),
        'AccessoryLTPK':
            hexlify(response_tlv[TLV.kTLVType_PublicKey]).decode(),
        'iOSPairingID': ios_pairing_id,
        'iOSDeviceLTSK': hexlify(bytes(ios_device_ltsk)).decode(),
        'iOSDeviceLTPK': hexlify(bytes(ios_device_ltpk)).decode(),
    }
Exemplo n.º 14
0
def request_pin_setup(connection, pin):
    """
    Requests a Pin from device
    """
    headers = {'Content-Type': 'application/pairing+tlv8'}

    #
    # Step #1 ios --> accessory (send SRP start Request) (see page 39)
    #
    request_tlv = TLV.encode_list([(TLV.kTLVType_State, TLV.M1),
                                   (TLV.kTLVType_Method, TLV.PairSetup)])

    #
    # Step #3 ios --> accessory (send SRP verify request) (see page 41)
    #

    connection.request('POST', '/pair-setup', request_tlv, headers)
    resp = connection.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    response_tlv = TLV.reorder(response_tlv, [
        TLV.kTLVType_State, TLV.kTLVType_Error, TLV.kTLVType_PublicKey,
        TLV.kTLVType_Salt
    ])
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][
        1] == TLV.M2, 'perform_pair_setup: State not M2'

    # the errors here can be:
    #  * kTLVError_Unavailable: Device is paired
    #  * kTLVError_MaxTries: More than 100 unsuccessfull attempts
    #  * kTLVError_Busy: There is already a pairing going on
    # if response_tlv[1][0] == TLV.kTLVType_Error:
    #     error_handler(response_tlv[1][1], 'step 3')

    assert response_tlv[1][
        0] == TLV.kTLVType_PublicKey, 'perform_pair_setup: Not a public key'
    assert response_tlv[2][
        0] == TLV.kTLVType_Salt, 'perform_pair_setup: Not a salt'

    srp_client = SrpClient('Pair-Setup', pin)
    srp_client.set_salt(response_tlv[2][1])
    srp_client.set_server_public_key(response_tlv[1][1])
    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)),
    ])

    connection.request('POST', '/pair-setup', response_tlv, headers)
    resp = connection.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    # Step #5 ios --> accessory (Exchange Request) (see page 43)
    #

    # M4 Verification (page 43)
    # response_tlv = TLV.reorder(response_tlv, [TLV.kTLVType_State, TLV.kTLVType_Error, TLV.kTLVType_Proof])
    # 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_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)

    connection.request('POST', '/pair-setup', body, headers)
    resp = connection.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())
Exemplo n.º 15
0
def hash_hkdf(algo, ikm, length=0, info=b'', salt=b''):
    h = getattr(hashlib, algo)
    kdf = hkdf.Hkdf(salt, ikm, h)
    if length == 0:
        length = h().digest_size
    return kdf.expand(info, length)
Exemplo n.º 16
0
def get_session_keys(conn, pairing_data):
    """
    Performs a pair verify operation as described in chapter 4.8 page 47 ff.

    :param conn: the http connection to the target accessory
    :param pairing_data: the paring data as returned by perform_pair_setup
    :return: tuple of the session keys (controller_to_accessory_key and  accessory_to_controller_key)
    """
    headers = {'Content-Type': 'application/pairing+tlv8'}

    #
    # Step #1 ios --> accessory (send verify start Request) (page 47)
    #
    ios_key = py25519.Key25519()

    request_tlv = TLV.encode_list([(TLV.kTLVType_State, TLV.M1),
                                   (TLV.kTLVType_PublicKey, ios_key.pubkey)])

    conn.request('POST', '/pair-verify', request_tlv, headers)
    resp = conn.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    # Step #3 ios --> accessory (send SRP verify request)  (page 49)
    #
    assert TLV.kTLVType_State in response_tlv, response_tlv
    assert response_tlv[TLV.kTLVType_State] == TLV.M2
    assert TLV.kTLVType_PublicKey in response_tlv, response_tlv
    assert TLV.kTLVType_EncryptedData in response_tlv, response_tlv

    # 1) generate shared secret
    accessorys_session_pub_key_bytes = response_tlv[TLV.kTLVType_PublicKey]
    shared_secret = ios_key.get_ecdh_key(
        py25519.Key25519(pubkey=bytes(accessorys_session_pub_key_bytes),
                         verifyingkey=bytes()))

    # 2) derive session key
    hkdf_inst = hkdf.Hkdf('Pair-Verify-Encrypt-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    session_key = hkdf_inst.expand('Pair-Verify-Encrypt-Info'.encode(), 32)

    # 3) verify authtag on encrypted data and 4) decrypt
    encrypted = response_tlv[TLV.kTLVType_EncryptedData]
    decrypted = chacha20_aead_decrypt(bytes(),
                                      session_key, 'PV-Msg02'.encode(),
                                      bytes([0, 0, 0, 0]), encrypted)
    if decrypted == False:
        raise homekit.exception.InvalidAuth("step 3")
    d1 = TLV.decode_bytes(decrypted)
    assert TLV.kTLVType_Identifier in d1
    assert TLV.kTLVType_Signature in d1

    # 5) look up pairing by accessory name
    accessory_name = d1[TLV.kTLVType_Identifier].decode()

    if pairing_data['AccessoryPairingID'] != accessory_name:
        raise homekit.exception.IncorrectPairingID("step 3")

    accessory_ltpk = py25519.Key25519(pubkey=bytes(),
                                      verifyingkey=bytes.fromhex(
                                          pairing_data['AccessoryLTPK']))

    # 6) verify accessory's signature
    accessory_sig = d1[TLV.kTLVType_Signature]
    accessory_session_pub_key_bytes = response_tlv[TLV.kTLVType_PublicKey]
    accessory_info = accessory_session_pub_key_bytes + accessory_name.encode(
    ) + ios_key.pubkey
    if not accessory_ltpk.verify(bytes(accessory_sig), bytes(accessory_info)):
        raise homekit.exception.InvalidSignature("step 3")

    # 7) create iOSDeviceInfo
    ios_device_info = ios_key.pubkey + pairing_data['iOSPairingId'].encode(
    ) + accessorys_session_pub_key_bytes

    # 8) sign iOSDeviceInfo with long term secret key
    ios_device_ltsk_h = pairing_data['iOSDeviceLTSK']
    ios_device_ltsk = py25519.Key25519(
        secretkey=bytes.fromhex(ios_device_ltsk_h))
    ios_device_signature = ios_device_ltsk.sign(ios_device_info)

    # 9) construct sub tlv
    sub_tlv = TLV.encode_list([(TLV.kTLVType_Identifier,
                                pairing_data['iOSPairingId'].encode()),
                               (TLV.kTLVType_Signature, ios_device_signature)])

    # 10) encrypt and sign
    encrypted_data_with_auth_tag = chacha20_aead_encrypt(
        bytes(), session_key, 'PV-Msg03'.encode(), bytes([0, 0, 0, 0]),
        sub_tlv)
    tmp = bytearray(encrypted_data_with_auth_tag[0])
    tmp += encrypted_data_with_auth_tag[1]

    # 11) create tlv
    request_tlv = TLV.encode_list([(TLV.kTLVType_State, TLV.M3),
                                   (TLV.kTLVType_EncryptedData, tmp)])

    # 12) send to accessory
    conn.request('POST', '/pair-verify', request_tlv, headers)
    resp = conn.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    #   Post Step #4 verification (page 51)
    #
    if TLV.kTLVType_Error in response_tlv:
        error_handler(response_tlv[TLV.kTLVType_Error], "verification")
    assert TLV.kTLVType_State in response_tlv
    assert response_tlv[TLV.kTLVType_State] == TLV.M4

    # calculate session keys
    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    controller_to_accessory_key = hkdf_inst.expand(
        'Control-Write-Encryption-Key'.encode(), 32)

    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    accessory_to_controller_key = hkdf_inst.expand(
        'Control-Read-Encryption-Key'.encode(), 32)

    return controller_to_accessory_key, accessory_to_controller_key
Exemplo n.º 17
0
 def _derive_subkey(self, salt: bytes):
     return hkdf.Hkdf(salt, self.key,
                      hashlib.sha1).expand(self.INFO, self.KEY_SIZE)
Exemplo n.º 18
0
def perform_pair_setup(connection, pin, ios_pairing_id):
    """
    Performs a pair setup operation as described in chapter 4.7 page 39 ff.

    :param connection: the http connection to the target accessory
    :param pin: the setup code from the accessory
    :param ios_pairing_id: the id of the simulated ios device
    :return: a dict with the ios device's part of the pairing information
    """
    headers = {'Content-Type': 'application/pairing+tlv8'}

    #
    # Step #1 ios --> accessory (send SRP start Request) (see page 39)
    #
    request_tlv = TLV.encode_list([(TLV.kTLVType_State, TLV.M1),
                                   (TLV.kTLVType_Method, TLV.PairSetup)])

    connection.request('POST', '/pair-setup', request_tlv, headers)
    resp = connection.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    # Step #3 ios --> accessory (send SRP verify request) (see page 41)
    #
    assert TLV.kTLVType_State in response_tlv, response_tlv
    assert response_tlv[TLV.kTLVType_State] == TLV.M2
    if TLV.kTLVType_Error in response_tlv:
        error_handler(response_tlv[TLV.kTLVType_Error], "step 3")

    srp_client = SrpClient('Pair-Setup', pin)
    srp_client.set_salt(response_tlv[TLV.kTLVType_Salt])
    srp_client.set_server_public_key(response_tlv[TLV.kTLVType_PublicKey])
    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)),
    ])

    connection.request('POST', '/pair-setup', response_tlv, headers)
    resp = connection.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    # Step #5 ios --> accessory (Exchange Request) (see page 43)
    #

    # M4 Verification (page 43)
    assert TLV.kTLVType_State in response_tlv, response_tlv
    assert response_tlv[TLV.kTLVType_State] == TLV.M4
    if TLV.kTLVType_Error in response_tlv:
        error_handler(response_tlv[TLV.kTLVType_Error], "step 5")

    assert TLV.kTLVType_Proof in response_tlv
    if not srp_client.verify_servers_proof(response_tlv[TLV.kTLVType_Proof]):
        print('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)

    connection.request('POST', '/pair-setup', body, headers)
    resp = connection.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    # Step #7 ios (Verification) (page 47)
    #
    assert response_tlv[TLV.kTLVType_State] == TLV.M6
    if TLV.kTLVType_Error in response_tlv:
        error_handler(response_tlv[TLV.kTLVType_Error], "step 7")

    assert TLV.kTLVType_EncryptedData in response_tlv
    decrypted_data = chacha20_aead_decrypt(
        bytes(), session_key, 'PS-Msg06'.encode(), bytes([0, 0, 0, 0]),
        response_tlv[TLV.kTLVType_EncryptedData])
    if decrypted_data == False:
        raise homekit.exception.IllegalData("step 7")

    response_tlv = TLV.decode_bytearray(decrypted_data)
    assert TLV.kTLVType_Signature in response_tlv
    accessory_sig = response_tlv[TLV.kTLVType_Signature]

    assert TLV.kTLVType_Identifier in response_tlv
    accessory_pairing_id = response_tlv[TLV.kTLVType_Identifier]

    assert TLV.kTLVType_PublicKey in response_tlv
    accessory_ltpk = response_tlv[TLV.kTLVType_PublicKey]
    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[TLV.kTLVType_PublicKey]))
    e25519s.verify(bytes(accessory_sig), bytes(accessory_info))

    return {
        'AccessoryPairingID': response_tlv[TLV.kTLVType_Identifier].decode(),
        'AccessoryLTPK':
        hexlify(response_tlv[TLV.kTLVType_PublicKey]).decode(),
        'iOSPairingId': ios_pairing_id,
        'iOSDeviceLTSK': ios_device_ltsk.to_ascii(encoding='hex').decode(),
        'iOSDeviceLTPK': hexlify(ios_device_ltpk.to_bytes()).decode()
    }
print "uid:\t<{}>".format(hexlify(uid))
print "kB:\t<{}>".format(hexlify(kB))

# Validate OAuth client details, and fetch scoped-key metadata.

scoped_key_identifier = 'app_key:{}'.format(urlquote(urlorigin(redirect_uri)))
print "scoped_key_identifier:\t'{}'".format(scoped_key_identifier)

print "key_rotation_secret:\t<{}>".format(hexlify(key_rotation_secret))

print "key_rotation_timestamp:\t{}".format(key_rotation_timestamp)

# Calculate the scoped key and its fingerprint via HKDF.

context = 'identity.mozilla.com/picl/v1/scoped_key\n' + scoped_key_identifier
ks_bytes = hkdf.Hkdf(uid, kB + key_rotation_secret).expand(context, 16 + 32)

kSfp = ks_bytes[:16]
kS = ks_bytes[16:]

assert len(kS) == 32

print "kSfp:\t<{}>".format(hexlify(kSfp))
print "kS:\t<{}>".format(hexlify(kS))

# Serialize into a JSON key bundle payload.

keys_bundle = json_encode({
    "app_key": {
        "kid": "{}-{}".format(key_rotation_timestamp, b64url_encode(kSfp)),
        "k": b64url_encode(kS),
Exemplo n.º 20
0
 def _derive_subkey(self, salt: bytes) -> bytes:
     return hkdf.Hkdf(salt, self.master_key,
                      sha1).expand(self.info, self.KEY_SIZE)
Exemplo n.º 21
0
    def _post_pair_verify(self):
        d_req = TLV.decode_bytes(self.body)

        d_res = {}

        if d_req[TLV.kTLVType_State] == TLV.M1:
            # step #2 Accessory -> iOS Device Verify Start Response
            if HomeKitRequestHandler.DEBUG_PAIR_VERIFY:
                self.log_message('Step #2 /pair-verify')

            # 1) generate new curve25519 key pair
            accessory_session_key = py25519.Key25519()
            accessory_spk = accessory_session_key.public_key().pubkey
            self.server.sessions[self.session_id]['accessory_pub_key'] = accessory_spk

            # 2) generate shared secret
            ios_device_curve25519_pub_key_bytes = d_req[TLV.kTLVType_PublicKey]
            self.server.sessions[self.session_id]['ios_device_pub_key'] = ios_device_curve25519_pub_key_bytes
            ios_device_curve25519_pub_key = py25519.Key25519(pubkey=bytes(ios_device_curve25519_pub_key_bytes),
                                                             verifyingkey=bytes())
            shared_secret = accessory_session_key.get_ecdh_key(ios_device_curve25519_pub_key)
            self.server.sessions[self.session_id]['shared_secret'] = shared_secret

            # 3) generate accessory info
            accessory_info = accessory_spk + self.server.data.accessory_pairing_id_bytes + \
                             ios_device_curve25519_pub_key_bytes

            # 4) sign accessory info for accessory signature
            accessory_ltsk = py25519.Key25519(secretkey=self.server.data.accessory_ltsk)
            accessory_signature = accessory_ltsk.sign(accessory_info)

            # 5) sub tlv
            sub_tlv = {
                TLV.kTLVType_Identifier: self.server.data.accessory_pairing_id_bytes,
                TLV.kTLVType_Signature: accessory_signature
            }
            sub_tlv_b = TLV.encode_dict(sub_tlv)

            # 6) derive session key
            hkdf_inst = hkdf.Hkdf('Pair-Verify-Encrypt-Salt'.encode(), shared_secret, hash=hashlib.sha512)
            session_key = hkdf_inst.expand('Pair-Verify-Encrypt-Info'.encode(), 32)
            self.server.sessions[self.session_id]['session_key'] = session_key

            # 7) encrypt sub tlv
            encrypted_data_with_auth_tag = chacha20_aead_encrypt(bytes(),
                                                                 session_key,
                                                                 'PV-Msg02'.encode(),
                                                                 bytes([0, 0, 0, 0]),
                                                                 sub_tlv_b)
            tmp = bytearray(encrypted_data_with_auth_tag[0])
            tmp += encrypted_data_with_auth_tag[1]

            # 8) construct result tlv
            d_res[TLV.kTLVType_State] = TLV.M2
            d_res[TLV.kTLVType_PublicKey] = accessory_spk
            d_res[TLV.kTLVType_EncryptedData] = tmp

            self._send_response_tlv(d_res)
            if HomeKitRequestHandler.DEBUG_PAIR_VERIFY:
                self.log_message('after step #2\n%s', TLV.to_string(d_res))
            return

        if d_req[TLV.kTLVType_State] == TLV.M3:
            # step #4 Accessory -> iOS Device Verify Finish Response
            if HomeKitRequestHandler.DEBUG_PAIR_VERIFY:
                self.log_message('Step #4 /pair-verify')

            session_key = self.server.sessions[self.session_id]['session_key']

            # 1) verify ios' authtag
            # 2) decrypt
            encrypted = d_req[TLV.kTLVType_EncryptedData]
            decrypted = chacha20_aead_decrypt(bytes(), session_key, 'PV-Msg03'.encode(), bytes([0, 0, 0, 0]),
                                              encrypted)
            if decrypted == False:
                self.send_error_reply(TLV.M4, TLV.kTLVError_Authentication)
                print('error in step #4: authtag', d_res, self.server.sessions)
                return
            d1 = TLV.decode_bytes(decrypted)
            assert TLV.kTLVType_Identifier in d1
            assert TLV.kTLVType_Signature in d1

            # 3) get ios_device_ltpk
            ios_device_pairing_id = d1[TLV.kTLVType_Identifier]
            self.server.sessions[self.session_id]['ios_device_pairing_id'] = ios_device_pairing_id
            ios_device_ltpk_bytes = self.server.data.get_peer_key(ios_device_pairing_id)
            if ios_device_ltpk_bytes is None:
                self.send_error_reply(TLV.M4, TLV.kTLVError_Authentication)
                print('error in step #4: not paired', d_res, self.server.sessions)
                return
            ios_device_ltpk = py25519.Key25519(pubkey=bytes(), verifyingkey=ios_device_ltpk_bytes)

            # 4) verify ios_device_info
            ios_device_sig = d1[TLV.kTLVType_Signature]
            ios_device_curve25519_pub_key_bytes = self.server.sessions[self.session_id]['ios_device_pub_key']
            accessory_spk = self.server.sessions[self.session_id]['accessory_pub_key']
            ios_device_info = ios_device_curve25519_pub_key_bytes + ios_device_pairing_id + accessory_spk
            if not ios_device_ltpk.verify(bytes(ios_device_sig), bytes(ios_device_info)):
                self.send_error_reply(TLV.M4, TLV.kTLVError_Authentication)
                print('error in step #4: signature', d_res, self.server.sessions)
                return

            #
            shared_secret = self.server.sessions[self.session_id]['shared_secret']
            hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(), shared_secret, hash=hashlib.sha512)
            controller_to_accessory_key = hkdf_inst.expand('Control-Write-Encryption-Key'.encode(), 32)
            self.server.sessions[self.session_id]['controller_to_accessory_key'] = controller_to_accessory_key
            self.server.sessions[self.session_id]['controller_to_accessory_count'] = 0

            hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(), shared_secret, hash=hashlib.sha512)
            accessory_to_controller_key = hkdf_inst.expand('Control-Read-Encryption-Key'.encode(), 32)
            self.server.sessions[self.session_id]['accessory_to_controller_key'] = accessory_to_controller_key
            self.server.sessions[self.session_id]['accessory_to_controller_count'] = 0

            d_res[TLV.kTLVType_State] = TLV.M4

            self._send_response_tlv(d_res)
            if HomeKitRequestHandler.DEBUG_PAIR_VERIFY:
                self.log_message('after step #4\n%s', TLV.to_string(d_res))
            return

        self.send_error(HttpStatusCodes.METHOD_NOT_ALLOWED)
Exemplo n.º 22
0
    def _post_pair_setup(self):
        d_req = TLV.decode_bytes(self.body)
        self.log_message('POST /pair-setup request body:\n%s', TLV.to_string(d_req))

        d_res = {}

        if d_req[TLV.kTLVType_State] == TLV.M1:
            # step #2 Accessory -> iOS Device SRP Start Response
            self.log_message('Step #2 /pair-setup')

            # 1) Check if paired
            if self.server.data.is_paired:
                self.send_error_reply(TLV.M2, TLV.kTLVError_Unavailable)
                return

            # 2) Check if over 100 attempts
            if self.server.data.unsuccessful_tries > 100:
                self.log_error('to many failed attempts')
                self.send_error_reply(TLV.M2, TLV.kTLVError_MaxTries)
                return

            # 3) Check if already in pairing
            if False:
                self.send_error_reply(TLV.M2, TLV.kTLVError_Busy)
                return

            # 4) 5) 7) Create in SRP Session, set username and password
            server = SrpServer('Pair-Setup', self.server.data.setup_code)

            # 6) create salt
            salt = server.get_salt()

            # 8) show setup code to user
            sc = self.server.data.setup_code
            sc_str = 'Setup Code\n┌─' + '─' * len(sc) + '─┐\n│ ' + sc + ' │\n└─' + '─' * len(sc) + '─┘'
            self.log_message(sc_str)

            # 9) create public key
            public_key = server.get_public_key()

            # 10) create response tlv and send response
            d_res[TLV.kTLVType_State] = TLV.M2
            d_res[TLV.kTLVType_PublicKey] = SrpServer.to_byte_array(public_key)
            d_res[TLV.kTLVType_Salt] = SrpServer.to_byte_array(salt)
            self._send_response_tlv(d_res)

            # store session
            self.server.sessions[self.session_id]['srp'] = server
            self.log_message('after step #2:\n%s', TLV.to_string(d_res))
            return

        if d_req[TLV.kTLVType_State] == TLV.M3:
            # step #4 Accessory -> iOS Device SRP Verify Response
            self.log_message('Step #4 /pair-setup')

            # 1) use ios pub key to compute shared secret key
            ios_pub_key = bytes_to_mpz(d_req[TLV.kTLVType_PublicKey])
            server = self.server.sessions[self.session_id]['srp']
            server.set_client_public_key(ios_pub_key)

            hkdf_inst = hkdf.Hkdf('Pair-Setup-Encrypt-Salt'.encode(), SrpServer.to_byte_array(server.get_session_key()),
                                  hash=hashlib.sha512)
            session_key = hkdf_inst.expand('Pair-Setup-Encrypt-Info'.encode(), 32)
            self.server.sessions[self.session_id]['session_key'] = session_key

            # 2) verify ios proof
            ios_proof = bytes_to_mpz(d_req[TLV.kTLVType_Proof])
            if not server.verify_clients_proof(ios_proof):
                d_res[TLV.kTLVType_State] = TLV.M4
                d_res[TLV.kTLVType_Error] = TLV.kTLVError_Authentication

                self._send_response_tlv(d_res)
                print('error in step #4', d_res, self.server.sessions)
                return
            else:
                self.log_message('ios proof was verified')

            # 3) generate accessory proof
            accessory_proof = server.get_proof(ios_proof)

            # 4) create response tlv
            d_res[TLV.kTLVType_State] = TLV.M4
            d_res[TLV.kTLVType_Proof] = SrpServer.to_byte_array(accessory_proof)

            # 5) send response tlv
            self._send_response_tlv(d_res)

            self.log_message('after step #4:\n%s', TLV.to_string(d_res))
            return

        if d_req[TLV.kTLVType_State] == TLV.M5:
            # step #6 Accessory -> iOS Device Exchange Response
            self.log_message('Step #6 /pair-setup')

            # 1) Verify the iOS device's authTag
            # done by chacha20_aead_decrypt

            # 2) decrypt and test
            encrypted_data = d_req[TLV.kTLVType_EncryptedData]
            decrypted_data = chacha20_aead_decrypt(bytes(), self.server.sessions[self.session_id]['session_key'],
                                                   'PS-Msg05'.encode(), bytes([0, 0, 0, 0]),
                                                   encrypted_data)
            if decrypted_data == False:
                d_res[TLV.kTLVType_State] = TLV.M6
                d_res[TLV.kTLVType_Error] = TLV.kTLVError_Authentication

                self.send_error_reply(TLV.M6, TLV.kTLVError_Authentication)
                print('error in step #6', d_res, self.server.sessions)
                return

            d_req_2 = TLV.decode_bytearray(decrypted_data)

            # 3) Derive ios_device_x
            shared_secret = self.server.sessions[self.session_id]['srp'].get_session_key()
            hkdf_inst = hkdf.Hkdf('Pair-Setup-Controller-Sign-Salt'.encode(), SrpServer.to_byte_array(shared_secret),
                                  hash=hashlib.sha512)
            ios_device_x = hkdf_inst.expand('Pair-Setup-Controller-Sign-Info'.encode(), 32)

            # 4) construct ios_device_info
            ios_device_pairing_id = d_req_2[TLV.kTLVType_Identifier]
            ios_device_ltpk = d_req_2[TLV.kTLVType_PublicKey]
            ios_device_info = ios_device_x + ios_device_pairing_id + ios_device_ltpk

            # 5) verify signature
            ios_device_sig = d_req_2[TLV.kTLVType_Signature]

            verify_key = py25519.Key25519(pubkey=bytes(), verifyingkey=bytes(ios_device_ltpk))
            if not verify_key.verify(bytes(ios_device_sig), bytes(ios_device_info)):
                self.send_error_reply(TLV.M6, TLV.kTLVError_Authentication)
                print('error in step #6', d_res, self.server.sessions)
                return

            # 6) save ios_device_pairing_id and ios_device_ltpk
            self.server.data.add_peer(ios_device_pairing_id, ios_device_ltpk)

            # Response Generation
            # 1) generate accessoryLTPK if not existing
            if self.server.data.accessory_ltsk is None or self.server.data.accessory_ltpk is None:
                accessory_ltsk = py25519.Key25519()
                accessory_ltpk = accessory_ltsk.verifyingkey
                self.server.data.set_accessory_keys(accessory_ltpk, accessory_ltsk.secretkey)
            else:
                accessory_ltsk = py25519.Key25519(self.server.data.accessory_ltsk)
                accessory_ltpk = accessory_ltsk.verifyingkey

            # 2) derive AccessoryX
            hkdf_inst = hkdf.Hkdf('Pair-Setup-Accessory-Sign-Salt'.encode(), SrpServer.to_byte_array(shared_secret),
                                  hash=hashlib.sha512)
            accessory_x = hkdf_inst.expand('Pair-Setup-Accessory-Sign-Info'.encode(), 32)

            # 3)
            accessory_info = accessory_x + self.server.data.accessory_pairing_id_bytes + accessory_ltpk

            # 4) generate signature
            accessory_signature = accessory_ltsk.sign(accessory_info)

            # 5) construct sub_tlv
            sub_tlv = {
                TLV.kTLVType_Identifier: self.server.data.accessory_pairing_id_bytes,
                TLV.kTLVType_PublicKey: accessory_ltpk,
                TLV.kTLVType_Signature: accessory_signature
            }
            sub_tlv_b = TLV.encode_dict(sub_tlv)

            # 6) encrypt sub_tlv
            encrypted_data_with_auth_tag = chacha20_aead_encrypt(bytes(),
                                                                 self.server.sessions[self.session_id]['session_key'],
                                                                 'PS-Msg06'.encode(),
                                                                 bytes([0, 0, 0, 0]),
                                                                 sub_tlv_b)
            tmp = bytearray(encrypted_data_with_auth_tag[0])
            tmp += encrypted_data_with_auth_tag[1]

            # 7) send response
            self.server.publish_device()
            d_res = dict()
            d_res[TLV.kTLVType_State] = TLV.M6
            d_res[TLV.kTLVType_EncryptedData] = tmp

            self._send_response_tlv(d_res)
            self.log_message('after step #6:\n%s', TLV.to_string(d_res))
            return

        self.send_error(HttpStatusCodes.METHOD_NOT_ALLOWED)
Exemplo n.º 23
0
#
# This takes a SLIP-0010 output (specifically, what is termed the "Ed25519
# private key" by the spec) and runs it through a pair of KDF instances to
# create the view and spend outputs.
#
# This code should not be re-used in a production system.
#

import hkdf
import hashlib
import sys

args = sys.argv[1:]
print(args)
for arg in args:
    print("SlipToRistretto {")
    print(f"    slip10_hex: \"{arg}\",")

    slip10 = bytes.fromhex(arg)
    kdf = hkdf.Hkdf(b"mobilecoin-ristretto255-view", slip10, hashlib.sha512)
    key = kdf.expand(length=64)

    print(f"    view_hex: \"{key.hex()}\",")

    kdf = hkdf.Hkdf(b"mobilecoin-ristretto255-spend", slip10, hashlib.sha512)
    key = kdf.expand(length=64)

    print(f"    spend_hex: \"{key.hex()}\",")
    print("},")

Exemplo n.º 24
0
def hkdf(key, salt, info, output_length):
    f = hkdf_i.Hkdf(salt, key)
    return f.expand(info, output_length)
Exemplo n.º 25
0
def get_session_keys(conn, pairing_data):
    """
    HomeKit Controller side call to perform a pair verify operation as described in chapter 4.8 page 47 ff.

    :param conn: the http_impl connection to the target accessory
    :param pairing_data: the paring data as returned by perform_pair_setup
    :return: tuple of the session keys (controller_to_accessory_key and  accessory_to_controller_key)
    :raises InvalidAuthTagError: if the auth tag could not be verified,
    :raises IncorrectPairingIdError: if the accessory's LTPK could not be found
    :raises InvalidSignatureError: if the accessory's signature could not be verified
    :raises AuthenticationError: if the secured session could not be established
    """
    headers = {'Content-Type': 'application/pairing+tlv8'}

    #
    # Step #1 ios --> accessory (send verify start Request) (page 47)
    #
    ios_key = py25519.Key25519()

    request_tlv = TLV.encode_list([(TLV.kTLVType_State, TLV.M1),
                                   (TLV.kTLVType_PublicKey, ios_key.pubkey)])

    conn.request('POST', '/pair-verify', request_tlv, headers)
    resp = conn.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    # Step #3 ios --> accessory (send SRP verify request)  (page 49)
    #
    response_tlv = TLV.reorder(response_tlv, [
        TLV.kTLVType_State, TLV.kTLVType_PublicKey, TLV.kTLVType_EncryptedData
    ])
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][
        1] == TLV.M2, 'get_session_keys: not M2'
    assert response_tlv[1][
        0] == TLV.kTLVType_PublicKey, 'get_session_keys: no public key'
    assert response_tlv[2][
        0] == TLV.kTLVType_EncryptedData, 'get_session_keys: no encrypted data'

    # 1) generate shared secret
    accessorys_session_pub_key_bytes = response_tlv[1][1]
    shared_secret = ios_key.get_ecdh_key(
        py25519.Key25519(pubkey=bytes(accessorys_session_pub_key_bytes),
                         verifyingkey=bytes()))

    # 2) derive session key
    hkdf_inst = hkdf.Hkdf('Pair-Verify-Encrypt-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    session_key = hkdf_inst.expand('Pair-Verify-Encrypt-Info'.encode(), 32)

    # 3) verify auth tag on encrypted data and 4) decrypt
    encrypted = response_tlv[2][1]
    decrypted = chacha20_aead_decrypt(bytes(),
                                      session_key, 'PV-Msg02'.encode(),
                                      bytes([0, 0, 0, 0]), encrypted)
    if type(decrypted) == bool and not decrypted:
        raise InvalidAuthTagError('step 3')
    d1 = TLV.decode_bytes(decrypted)
    d1 = TLV.reorder(d1, [TLV.kTLVType_Identifier, TLV.kTLVType_Signature])
    assert d1[0][
        0] == TLV.kTLVType_Identifier, 'get_session_keys: no identifier'
    assert d1[1][0] == TLV.kTLVType_Signature, 'get_session_keys: no signature'

    # 5) look up pairing by accessory name
    accessory_name = d1[0][1].decode()

    if pairing_data['AccessoryPairingID'] != accessory_name:
        raise IncorrectPairingIdError('step 3')

    accessory_ltpk = py25519.Key25519(pubkey=bytes(),
                                      verifyingkey=bytes.fromhex(
                                          pairing_data['AccessoryLTPK']))

    # 6) verify accessory's signature
    accessory_sig = d1[1][1]
    accessory_session_pub_key_bytes = response_tlv[1][1]
    accessory_info = accessory_session_pub_key_bytes + accessory_name.encode(
    ) + ios_key.pubkey
    if not accessory_ltpk.verify(bytes(accessory_sig), bytes(accessory_info)):
        raise InvalidSignatureError('step 3')

    # 7) create iOSDeviceInfo
    ios_device_info = ios_key.pubkey + pairing_data['iOSPairingId'].encode(
    ) + accessorys_session_pub_key_bytes

    # 8) sign iOSDeviceInfo with long term secret key
    ios_device_ltsk_h = pairing_data['iOSDeviceLTSK']
    ios_device_ltsk = py25519.Key25519(
        secretkey=bytes.fromhex(ios_device_ltsk_h))
    ios_device_signature = ios_device_ltsk.sign(ios_device_info)

    # 9) construct sub tlv
    sub_tlv = TLV.encode_list([(TLV.kTLVType_Identifier,
                                pairing_data['iOSPairingId'].encode()),
                               (TLV.kTLVType_Signature, ios_device_signature)])

    # 10) encrypt and sign
    encrypted_data_with_auth_tag = chacha20_aead_encrypt(
        bytes(), session_key, 'PV-Msg03'.encode(), bytes([0, 0, 0, 0]),
        sub_tlv)
    tmp = bytearray(encrypted_data_with_auth_tag[0])
    tmp += encrypted_data_with_auth_tag[1]

    # 11) create tlv
    request_tlv = TLV.encode_list([(TLV.kTLVType_State, TLV.M3),
                                   (TLV.kTLVType_EncryptedData, tmp)])

    # 12) send to accessory
    conn.request('POST', '/pair-verify', request_tlv, headers)
    resp = conn.getresponse()
    response_tlv = TLV.decode_bytes(resp.read())

    #
    #   Post Step #4 verification (page 51)
    #
    response_tlv = TLV.reorder(response_tlv,
                               [TLV.kTLVType_State, TLV.kTLVType_Error])
    assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][
        1] == TLV.M4, 'get_session_keys: not M4'
    if len(response_tlv) == 2 and response_tlv[1][0] == TLV.kTLVType_Error:
        error_handler(response_tlv[1][1], 'verification')

    # calculate session keys
    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    controller_to_accessory_key = hkdf_inst.expand(
        'Control-Write-Encryption-Key'.encode(), 32)

    hkdf_inst = hkdf.Hkdf('Control-Salt'.encode(),
                          shared_secret,
                          hash=hashlib.sha512)
    accessory_to_controller_key = hkdf_inst.expand(
        'Control-Read-Encryption-Key'.encode(), 32)

    return controller_to_accessory_key, accessory_to_controller_key