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_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 test_seedweed_vectors_get_assertion( self, solo, device, ): import seedweed from binascii import hexlify version = b'\x01' for i, v in enumerate(seedweed.load_test_vectors(shortlist=True)): print(f'{i}) Enter user presence THREE times.') ext_key_cmd = 0x62 solo.send_data_hid(ext_key_cmd, version + v['seed'] + b'') allow_list = [{"id": v['credential_id'], "type": "public-key"}] ga_req = FidoRequest(rp={ "id": v['rp_id'], "name": "seedweed" }, allow_list=allow_list) ga_res = device.sendGA(*ga_req.toGA()) # print(v) # print(ga_res.auth_data + ga_req.cdh) # assert ga_res.auth_data.rp_id_hash == reg.auth_data.rp_id_hash assert ga_res.credential["id"] == v['credential_id'] # reg.auth_data.credential_data.credential_id seedweed.conformance.verify_get_assertion( v, convert_der_sig_to_padded_binary(ga_res.signature), # ga_res.signature, ga_res.auth_data + ga_req.cdh, )
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_missing_keyAgreement(self, device, cipher, sharedSecret): key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, (salt3,)) req = FidoRequest(extensions={"hmac-secret": {2: salt_enc, 3: salt_auth}}) with pytest.raises(CtapError): device.sendGA(*req.toGA())
def test_missing_saltEnc(self, device, cipher, sharedSecret): key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, (salt3,)) req = FidoRequest(extensions={"hmac-secret": {1: key_agreement, 3: salt_auth}}) with pytest.raises(CtapError) as e: device.sendGA(*req.toGA()) assert e.value.code == CtapError.ERR.MISSING_PARAMETER
def test_invalid_salt_length(self, device, 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}} ) with pytest.raises(CtapError) as e: device.sendGA(*req.toGA()) assert e.value.code == CtapError.ERR.INVALID_LENGTH
def test_authenticate_ctap1_through_ctap2(self, device, RegRes): req = FidoRequest(allow_list=[{ "id": RegRes.key_handle, "type": "public-key" }]) auth = device.sendGA(*req.toGA()) credential_data = AttestedCredentialData.from_ctap1( RegRes.key_handle, RegRes.public_key) auth.verify(req.cdh, credential_data.public_key) assert auth.credential["id"] == RegRes.key_handle
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_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_bad_auth(self, device, cipher, sharedSecret): key_agreement, shared_secret = sharedSecret salt_enc, salt_auth = get_salt_params(cipher, shared_secret, (salt3,)) bad_auth = list(salt_auth[:]) bad_auth[len(bad_auth) // 2] = bad_auth[len(bad_auth) // 2] ^ 1 bad_auth = bytes(bad_auth) req = FidoRequest( extensions={"hmac-secret": {1: key_agreement, 2: salt_enc, 3: bad_auth}} ) with pytest.raises(CtapError) as e: device.sendGA(*req.toGA()) assert e.value.code == CtapError.ERR.EXTENSION_FIRST
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)