def test_hmac_secret_entropy(self, device, MCHmacSecret, cipher, sharedSecret, salts): key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, salts) req = FidoRequest(extensions={ "hmac-secret": { 1: key_agreement, 2: salt_enc, 3: salt_auth } }) auth = device.sendGA(*req.toGA()) ext = auth.auth_data.extensions assert ext assert "hmac-secret" in ext assert isinstance(ext["hmac-secret"], bytes) assert len(ext["hmac-secret"]) == len(salts) * 32 verify(MCHmacSecret, auth, req.cdh) dec = cipher.decryptor() key = dec.update(ext["hmac-secret"]) + dec.finalize() print(shannon_entropy(ext["hmac-secret"])) if len(salts) == 1: assert shannon_entropy(ext["hmac-secret"]) > 4.6 assert shannon_entropy(key) > 4.6 if len(salts) == 2: assert shannon_entropy(ext["hmac-secret"]) > 5.4 assert shannon_entropy(key) > 5.4
def get_output(self, device, MCHmacSecret, cipher, sharedSecret, salts): key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, salts) req = FidoRequest(extensions={ "hmac-secret": { 1: key_agreement, 2: salt_enc, 3: salt_auth } }) auth = device.sendGA(*req.toGA()) ext = auth.auth_data.extensions assert ext assert "hmac-secret" in ext assert isinstance(ext["hmac-secret"], bytes) assert len(ext["hmac-secret"]) == len(salts) * 32 verify(MCHmacSecret, auth, req.cdh) dec = cipher.decryptor() output = dec.update(ext["hmac-secret"]) + dec.finalize() if len(salts) == 2: return (output[0:32], output[32:64]) else: return output
def test_load_external_key_invalidate_old_cred(self, solo, device, MCRes, GARes): ext_key_cmd = 0x62 verify(MCRes, GARes) print('Enter user presence THREE times.') solo.send_data_hid(ext_key_cmd, b'\x01' + b'Z' * 32 + b'dicekeys key') # Old credential should not exist now. with pytest.raises(CtapError) as e: ga_bad_req = FidoRequest(GARes) device.sendGA(*ga_bad_req.toGA()) assert (e.value.code == CtapError.ERR.NO_CREDENTIALS)
def test_backup_credential_is_generated_correctly( self, solo, device, ): import seedweed from binascii import hexlify key_A = b"A" * 32 ext_state = b"I'm a dicekey key!" version = b"\x01" ext_key_cmd = 0x62 print("Enter user presence THREE times.") solo.send_data_hid(ext_key_cmd, version + key_A + ext_state) # New credential works. mc_A_req = FidoRequest() mc_A_res = device.sendMC(*mc_A_req.toMC()) rpIdHash = sha256(mc_A_req.rp["id"].encode("utf8")) credId = mc_A_res.auth_data.credential_data.credential_id ( uniqueId, extStateInCredId, credMacInCredId, ) = seedweed.nonce_extstate_mac_from_credential_id(credId) seedweed.validate_credential_id(key_A, credId, rpIdHash) credMac = hmac_sha256(key_A, rpIdHash + version + uniqueId + ext_state) allow_list = [{ "id": mc_A_res.auth_data.credential_data.credential_id, "type": "public-key", }] ga_req = FidoRequest(allow_list=allow_list) ga_res = device.sendGA(*ga_req.toGA()) verify(mc_A_res, ga_res, ga_req.cdh) # Independently create the key and verify _, _, keypair, iterations = seedweed.keypair_from_seed_mac( key_A, credMac) assert iterations == 1 keypair.verifying_key.verify( ga_res.signature, ga_res.auth_data + ga_req.cdh, sigdecode=ecdsa.util.sigdecode_der, hashfunc=hashlib.sha256, )
def test_hmac_secret_different_with_uv(self, device, MCHmacSecret, cipher, sharedSecret): salts = [salt1] key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, salts) req = FidoRequest(extensions={ "hmac-secret": { 1: key_agreement, 2: salt_enc, 3: salt_auth } }) auth_no_uv = device.sendGA(*req.toGA()) assert (auth_no_uv.auth_data.flags & (1 << 2)) == 0 ext_no_uv = auth_no_uv.auth_data.extensions assert ext_no_uv assert "hmac-secret" in ext_no_uv assert isinstance(ext_no_uv["hmac-secret"], bytes) assert len(ext_no_uv["hmac-secret"]) == len(salts) * 32 verify(MCHmacSecret, auth_no_uv, req.cdh) # Now get same auth with UV pin = '1234' device.client.pin_protocol.set_pin(pin) pin_token = device.client.pin_protocol.get_pin_token(pin) pin_auth = hmac_sha256(pin_token, req.cdh)[:16] req = FidoRequest(req, pin_protocol=1, pin_auth=pin_auth, extensions={ "hmac-secret": { 1: key_agreement, 2: salt_enc, 3: salt_auth } }) auth_uv = device.sendGA(*req.toGA()) assert auth_uv.auth_data.flags & (1 << 2) ext_uv = auth_uv.auth_data.extensions assert ext_uv assert "hmac-secret" in ext_uv assert isinstance(ext_uv["hmac-secret"], bytes) assert len(ext_uv["hmac-secret"]) == len(salts) * 32 verify(MCHmacSecret, auth_uv, req.cdh) # Now see if the hmac-secrets are different assert ext_no_uv['hmac-secret'] != ext_uv['hmac-secret']
def test_get_next_assertion_has_extension(self, device, MCHmacSecret, cipher, sharedSecret, salts, fixed_users): """ Check that get_next_assertion properly returns extension information for multiple accounts. """ accounts = 3 regs = [] auths = [] rp = {"id": "example_2.org", "name": "ExampleRP_2"} for i in range(0, accounts): req = FidoRequest(extensions={"hmac-secret": True}, options={"rk": True}, rp=rp, user=fixed_users[i]) res = device.sendMC(*req.toMC()) regs.append(res) key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, salts) req = FidoRequest( extensions={ "hmac-secret": { 1: key_agreement, 2: salt_enc, 3: salt_auth } }, rp=rp, ) auth = device.sendGA(*req.toGA()) assert auth.number_of_credentials == accounts auths.append(auth) for i in range(0, accounts - 1): auths.append(device.ctap2.get_next_assertion()) for x in auths: assert x.auth_data.flags & (1 << 7) # has extension ext = auth.auth_data.extensions assert ext assert "hmac-secret" in ext assert isinstance(ext["hmac-secret"], bytes) assert len(ext["hmac-secret"]) == len(salts) * 32 dec = cipher.decryptor() key = dec.update(ext["hmac-secret"]) + dec.finalize() auths.reverse() for x, y in zip(regs, auths): verify(x, y, req.cdh)
def test_load_external_key( self, solo, device, ): key_A = b'A' * 32 key_B = b'B' * 32 ext_state = b"I'm a dicekey key" version = b'\x01' ext_key_cmd = 0x62 print('Enter user presence THREE times.') solo.send_data_hid(ext_key_cmd, version + key_A + ext_state) # New credential works. mc_A_req = FidoRequest() mc_A_res = device.sendMC(*mc_A_req.toMC()) allow_list = [{ "id": mc_A_res.auth_data.credential_data.credential_id, "type": "public-key" }] ga_A_req = FidoRequest(mc_A_req, allow_list=allow_list) ga_A_res = device.sendGA(*FidoRequest(ga_A_req).toGA()) verify(mc_A_res, ga_A_res, ga_A_req.cdh) # Load up Key B and verify cred A doesn't exist. print('Enter user presence THREE times.') solo.send_data_hid(ext_key_cmd, version + key_B + ext_state) with pytest.raises(CtapError) as e: ga_A_res = device.sendGA(*FidoRequest(ga_A_req).toGA()) assert (e.value.code == CtapError.ERR.NO_CREDENTIALS) # Load up Key A and verify cred A is back. print('Enter user presence THREE times.') solo.send_data_hid(ext_key_cmd, version + key_A + ext_state) ga_A_res = device.sendGA(*FidoRequest(ga_A_req).toGA()) verify(mc_A_res, ga_A_res, ga_A_req.cdh)
def test_eddsa(self, device): mc_req = FidoRequest(key_params=[{ "type": "public-key", "alg": EdDSA.ALGORITHM }]) try: mc_res = device.sendMC(*mc_req.toMC()) except CtapError as e: if e.code == CtapError.ERR.UNSUPPORTED_ALGORITHM: print("ed25519 is not supported. Skip this test.") return setattr(mc_res, "request", mc_req) allow_list = [{ "id": mc_res.auth_data.credential_data.credential_id[:], "type": "public-key", }] ga_req = FidoRequest(allow_list=allow_list) ga_res = device.sendGA(*ga_req.toGA()) setattr(ga_res, "request", ga_req) try: verify(mc_res, ga_res) except: # Print out extra details on failure from binascii import hexlify print("authdata", hexlify(ga_res.auth_data)) print("cdh", hexlify(ga_res.request.cdh)) print("sig", hexlify(ga_res.signature)) from fido2.ctap2 import AttestedCredentialData credential_data = AttestedCredentialData( mc_res.auth_data.credential_data) print("public key:", hexlify(credential_data.public_key[-2])) verify(mc_res, ga_res)