def test_bad_type_exclude_list_type(self, device, MCRes, GARes): req = FidoRequest(MCRes, exclude_list=GARes.request.allow_list) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC()) assert e.value.code == CtapError.ERR.CREDENTIAL_EXCLUDED
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_pubKeyCredParams(self, device, MCRes): req = FidoRequest(MCRes, key_params=None) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC()) assert e.value.code == CtapError.ERR.MISSING_PARAMETER
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 test_unsupported_algorithm(self, device, MCRes): req = FidoRequest(MCRes, key_params=[{"alg": 1337, "type": "public-key"}]) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC()) assert e.value.code == CtapError.ERR.UNSUPPORTED_ALGORITHM
def test_exclude_list2(self, device, MCRes): req = FidoRequest( MCRes, exclude_list=[{"id": b"1234", "type": "mangoPapayaCoconutNotAPublicKey"}], ) device.sendMC(*req.toMC())
def test_missing_pubKeyCredParams_type(self, device, MCRes): req = FidoRequest(MCRes, key_params=[{"alg": ES256.ALGORITHM}]) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC()) assert e.value.code == CtapError.ERR.MISSING_PARAMETER
def test_bad_type_user_displayName(self, device, MCRes): req = FidoRequest( MCRes, user={"id": "user_id", "name": "name", "displayName": 8} ) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC())
def MCHmacSecret( resetDevice, ): req = FidoRequest(extensions={"hmac-secret": True}, options={"rk": True}) res = resetDevice.sendMC(*req.toMC()) setattr(res, "request", req) return res
def test_exclude_list(self, device, MCRes): req = FidoRequest(MCRes, exclude_list=[{ "id": b"1234", "type": "rot13" }]) device.sendMC(*req.toMC())
def test_bad_type_exclude_list_type(self, device, MCRes): req = FidoRequest(MCRes, exclude_list=[{ "type": b"public-key", "id": b"1234" }]) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC())
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_multiple_key_params_eddsa(self, device, MCRes): key_params = [ {"type": "public-key", "alg": EdDSA.ALGORITHM}, {"type": "public-key", "alg": ES256.ALGORITHM}, ] req = FidoRequest(MCRes, key_params=key_params) resp = device.sendMC(*req.toMC()) assert resp.auth_data.credential_data.public_key[3] == EdDSA.ALGORITHM
def test_bad_type_pubKeyCredParams_alg(self, device, MCRes): req = FidoRequest(MCRes, key_params=[{ "alg": "7", "type": "public-key" }]) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC())
def test_missing_pubKeyCredParams_alg(self, device, MCRes): req = FidoRequest(MCRes, key_params=[{"type": "public-key"}]) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC()) assert e.value.code in [ CtapError.ERR.MISSING_PARAMETER, CtapError.ERR.UNSUPPORTED_ALGORITHM, ]
def test_bad_type_rp_name(self, device, MCRes): req = FidoRequest(MCRes, rp={ "id": "test.org", "name": 8, "icon": "icon" }) with pytest.raises(CtapError) as e: device.sendMC(*req.toMC())
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_hmac_secret_and_credProtect_make_credential( self, resetDevice, MCCredProtect): req = FidoRequest(extensions={"credProtect": 1, "hmac-secret": True}) res = resetDevice.sendMC(*req.toMC()) setattr(res, "request", req) for ext in ["credProtect", "hmac-secret"]: assert res.auth_data.extensions assert ext in res.auth_data.extensions assert res.auth_data.extensions[ext] == True
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_big_request_response(self, device, MCRes): req = FidoRequest( MCRes, exclude_list=[ { "id": b"0123456789012345678901234567890123456789012345678901234567890123456789", "type": "public-key"}, { "id": b"1123456789012345678901234567890123456789012345678901234567890123456789", "type": "public-key"}, { "id": b"2123456789012345678901234567890123456789012345678901234567890123456789", "type": "public-key"}], ) device.sendMC(*req.toMC())
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_ext_state_in_credential_id( self, solo, device, ): key_A = b'A' * 32 ext_state = b"I'm a dicekey key abc1234!!@@##" 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()) assert ext_state in mc_A_res.auth_data.credential_data.credential_id
def test_u2f(self, device, iterations): lastc = 0 chal = FidoRequest().challenge appid = FidoRequest().appid regs = [] for i in range(0, iterations): print("U2F reg + auth %d/%d (count: %02x)" % (i + 1, iterations, lastc)) reg = device.register(chal, appid) reg.verify(appid, chal) auth = device.authenticate(chal, appid, reg.key_handle) auth.verify(appid, chal, reg.public_key) regs.append(reg) # check endianness if lastc: assert (auth.counter - lastc) < 10 lastc = auth.counter if lastc > 0x80000000: print("WARNING: counter is unusually high: %04x" % lastc) assert 0 for i in range(0, iterations): auth = device.authenticate(chal, appid, regs[i].key_handle) auth.verify(appid, chal, regs[i].public_key) device.reboot() for i in range(0, iterations): auth = device.authenticate(chal, appid, regs[i].key_handle) auth.verify(appid, chal, regs[i].public_key) print("Check that all previous credentials are registered...") for i in range(0, iterations): with pytest.raises(ApduError) as e: auth = device.ctap1.authenticate(chal, appid, regs[i].key_handle, check_only=True) assert e.value.code == APDU.USE_NOT_SATISFIED
def test_ctap1_authenticate(self, MCRes, device): req = FidoRequest() key_handle = MCRes.auth_data.credential_data.credential_id res = device.authenticate(req.challenge, req.appid, key_handle) credential_data = AttestedCredentialData( MCRes.auth_data.credential_data) pubkey_string = b'\x04' + credential_data.public_key[ -2] + credential_data.public_key[-3] res.verify(req.appid, req.challenge, pubkey_string)
def test_seedweed_vectors_make_credential( 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'') mc_req = FidoRequest(rp={"id": v['rp_id'], "name": "seedweed"}) mc_res = device.sendMC(*mc_req.toMC()) seedweed.conformance.verify_make_credential( v, mc_res.auth_data.credential_data.credential_id, mc_res.auth_data.credential_data.public_key[-2] + mc_res.auth_data.credential_data.public_key[-3])
def test_ctap1_authenticate(self, MCRes, device): req = FidoRequest() key_handle = MCRes.auth_data.credential_data.credential_id if len(key_handle) <= 255: res = device.authenticate(req.challenge, req.appid, key_handle) credential_data = AttestedCredentialData( MCRes.auth_data.credential_data) pubkey_string = b'\x04' + credential_data.public_key[ -2] + credential_data.public_key[-3] res.verify(req.appid, req.challenge, pubkey_string) else: print( "ctap2 credId is longer than 255 bytes, cannot use with U2F.")
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, )