diff --git a/tools/pico-fido-tool.py b/tools/pico-fido-tool.py new file mode 100644 index 0000000..fc54ec8 --- /dev/null +++ b/tools/pico-fido-tool.py @@ -0,0 +1,84 @@ +from fido2.ctap2.config import Config +from fido2.ctap2 import Ctap2 +from fido2.hid import CtapHidDevice +from fido2.utils import bytes2int, int2bytes + +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat +from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 +from cryptography.hazmat.primitives import hashes + +from enum import IntEnum +from binascii import hexlify + +class VendorConfig(Config): + + class PARAM(IntEnum): + VENDOR_COMMAND_ID = 0x01 + VENDOR_AUT_KEY_AGREEMENT = 0x02 + VENDOR_AUT_CT = 0x03 + + class CMD(IntEnum): + CONFIG_AUT = 0x03e43f56b34285e2 + CONFIG_KEY_AGREEMENT = 0x1831a40f04a25ed9 + + class RESP(IntEnum): + KEY_AGREEMENT = 0x01 + + def __init__(self, ctap, pin_uv_protocol=None, pin_uv_token=None): + super().__init__(ctap, pin_uv_protocol, pin_uv_token) + + def enable_device_aut(self): + ret = self._call( + Config.CMD.VENDOR_PROTOTYPE, + { + VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_KEY_AGREEMENT, + }, + ) + peer_cose_key = ret[VendorConfig.RESP.KEY_AGREEMENT] + + sk = ec.generate_private_key(ec.SECP256R1()) + pn = sk.public_key().public_numbers() + pb = sk.public_key().public_bytes(Encoding.X962, PublicFormat.UncompressedPoint) + key_agreement = { + 1: 2, + 3: -25, # Per the spec, "although this is NOT the algorithm actually used" + -1: 1, + -2: int2bytes(pn.x, 32), + -3: int2bytes(pn.y, 32), + } + + x = bytes2int(peer_cose_key[-2]) + y = bytes2int(peer_cose_key[-3]) + pk = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()).public_key() + shared_key = sk.exchange(ec.ECDH(), pk) + + xkdf = HKDF( + algorithm=hashes.SHA256(), + length=12+32, + salt=None, + info=pb + ) + print(hexlify(shared_key)) + kdf_out = xkdf.derive(shared_key) + key_enc = kdf_out[12:] + iv = kdf_out[:12] + print(hexlify(kdf_out)) + print(hexlify(pb)) + chacha = ChaCha20Poly1305(key_enc) + ct = chacha.encrypt(iv, b"\x69"*32, pb) + self._call( + Config.CMD.VENDOR_PROTOTYPE, + { + VendorConfig.PARAM.VENDOR_COMMAND_ID: VendorConfig.CMD.CONFIG_AUT, + VendorConfig.PARAM.VENDOR_AUT_KEY_AGREEMENT: key_agreement, + VendorConfig.PARAM.VENDOR_AUT_CT: ct + }, + ) + +dev = next(CtapHidDevice.list_devices(), None) + +vcfg = VendorConfig(Ctap2(dev)) + +vcfg.enable_device_aut() \ No newline at end of file