def make_credential(self, pin=None): rp = {"id": self.host, "name": "example site"} user = {"id": self.user_id, "name": "example user"} challenge = b"Y2hhbGxlbmdl" options = PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -7 }], ) attest, data = self.client.make_credential(options, pin=pin) try: attest.verify(data.hash) except AttributeError: verifier = Attestation.for_type(attest.fmt) verifier().verify(attest.att_statement, attest.auth_data, data.hash) print("Register valid") x5c = attest.att_statement["x5c"][0] cert = x509.load_der_x509_certificate(x5c, default_backend()) return cert
def test_make_credential_ctap1(self): dev = mock.Mock() dev.capabilities = 0 # No CTAP2 client = Fido2Client(dev, APP_ID) ctap1_mock = mock.MagicMock() ctap1_mock.get_version.return_value = "U2F_V2" ctap1_mock.register.return_value = REG_DATA client._backend.ctap1 = ctap1_mock response = client.make_credential( PublicKeyCredentialCreationOptions(rp, user, challenge, [{ "type": "public-key", "alg": -7 }])) self.assertIsInstance(response.attestation_object, AttestationObject) self.assertIsInstance(response.client_data, CollectedClientData) client_data = response.client_data ctap1_mock.register.assert_called_with(client_data.hash, sha256(rp["id"].encode())) self.assertEqual(client_data.origin, APP_ID) self.assertEqual(client_data.type, "webauthn.create") self.assertEqual(client_data.challenge, challenge) self.assertEqual(response.attestation_object.fmt, "fido-u2f")
def test_make_credential_ctap1(self): dev = mock.Mock() dev.capabilities = 0 # No CTAP2 client = Fido2Client(dev, APP_ID) client.ctap1 = mock.MagicMock() client.ctap1.get_version.return_value = "U2F_V2" client.ctap1.register.return_value = REG_DATA attestation, client_data = client.make_credential( PublicKeyCredentialCreationOptions(rp, user, challenge, [{ "type": "public-key", "alg": -7 }])) self.assertIsInstance(attestation, AttestationObject) self.assertIsInstance(client_data, ClientData) client.ctap1.register.assert_called_with(client_data.hash, sha256(rp["id"].encode())) self.assertEqual(client_data.get("origin"), APP_ID) self.assertEqual(client_data.get("type"), "webauthn.create") self.assertEqual(client_data.challenge, challenge) self.assertEqual(attestation.fmt, "fido-u2f")
def test_make_credential_existing_key(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) ctap2.make_credential.side_effect = CtapError( CtapError.ERR.CREDENTIAL_EXCLUDED) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) try: client.make_credential( PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -7 }], authenticator_selection={ "userVerification": "discouraged" }, )) self.fail("make_credential did not raise error") except ClientError as e: self.assertEqual(e.code, ClientError.ERR.DEVICE_INELIGIBLE) ctap2.get_info.assert_called_with() ctap2.make_credential.assert_called_once()
def _make_credential(self, client, user): pub_key_cred_params = [PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, cose.ES256.ALGORITHM)] options = PublicKeyCredentialCreationOptions(self._rp, user, self._challenge, pub_key_cred_params, timeout=self._timeout_ms) pin = self._get_pin_from_client(client) self._attestation, self._client_data = client.make_credential(options, event=self._event, on_keepalive=self.on_keepalive, pin=pin) self._event.set()
def gen_key(self): name = input('Real name: ') email = input('Email address: ') username = "******".format(name, email) created = int(time.time()) rp = {"id": RP_ID, "name": "OpenPGP"} user = {"id": struct.pack('>I', created), "name": username} challenge = secrets.token_bytes(32) options = PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -7 }], authenticator_selection={"require_resident_key": True}) attestation_object, client_data = self.client.make_credential( options, pin=self.pin) statement = attestation_object.att_statement auth_data = attestation_object.auth_data attestation = Attestation.for_type("packed")() attestation.verify(statement, auth_data, client_data.hash) cred_id = auth_data.credential_data.credential_id pubkey_x = auth_data.credential_data.public_key[-2] pubkey_y = auth_data.credential_data.public_key[-3] pubkey = (pubkey_x, pubkey_y) pubkey_pkt = self._pubkey_packet(pubkey, created) userid_pkt = self._userid_packet(username) fp = self._fingerprint(pubkey_pkt) key_id = fp[-8:] print("Key ID: {}".format(key_id.hex().upper())) print("Key fingerprint: {}\n".format(fp.hex().upper())) hashed_prefix = b'\x99\x00\x52' + pubkey_pkt[2:] hashed_prefix += b'\xb4' + struct.pack( '>I', len(userid_pkt) - 2) + userid_pkt[2:] hashed_subpkts = [ SubPacket(0x21, b'\x04' + fp), SubPacket(0x1B, b'\x03'), # key flags SubPacket(0x02, struct.pack('>I', created)) ] unhashed_subpkts = [SubPacket(0x10, key_id)] # issuer sig_pkt = self._signature_packet_key(cred_id, hashed_prefix, hashed_subpkts, unhashed_subpkts) armored = self._ascii_armor(pubkey_pkt + userid_pkt + sig_pkt) print( '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n{}-----END PGP PUBLIC KEY BLOCK-----' .format(armored))
def test_creation_options(self): o = PublicKeyCredentialCreationOptions( {"id": "example.com", "name": "Example"}, {"id": b"user_id", "name": "A. User"}, b"request_challenge", [{"type": "public-key", "alg": -7}], 10000, [{"type": "public-key", "id": b"credential_id"}], { "authenticatorAttachment": "platform", "requireResidentKey": True, "userVerification": "required", }, "direct", ) self.assertEqual(o.rp, {"id": "example.com", "name": "Example"}) self.assertEqual(o.user, {"id": b"user_id", "name": "A. User"}) self.assertIsNone(o.extensions) o = PublicKeyCredentialCreationOptions( {"id": "example.com", "name": "Example"}, {"id": b"user_id", "name": "A. User"}, b"request_challenge", [{"type": "public-key", "alg": -7}], ) self.assertIsNone(o.timeout) self.assertIsNone(o.authenticator_selection) self.assertIsNone(o.attestation) with self.assertRaises(ValueError): PublicKeyCredentialCreationOptions( {"id": "example.com", "name": "Example"}, {"id": b"user_id", "name": "A. User"}, b"request_challenge", [{"type": "public-key", "alg": -7}], attestation="invalid", )
def test_make_credential_ctap2(self, PatchedCtap2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info.from_dict(cbor.decode(_INFO_NO_PIN)) ctap2.info = ctap2.get_info() ctap2.make_credential.return_value = AttestationResponse.from_dict( cbor.decode(_MC_RESP)) PatchedCtap2.return_value = ctap2 client = Fido2Client(dev, APP_ID) response = client.make_credential( PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -7 }], timeout=1000, authenticator_selection={"userVerification": "discouraged"}, )) self.assertIsInstance(response.attestation_object, AttestationObject) self.assertIsInstance(response.client_data, CollectedClientData) ctap2.make_credential.assert_called_with( response.client_data.hash, rp, user, [{ "type": "public-key", "alg": -7 }], None, None, None, None, None, event=mock.ANY, on_keepalive=mock.ANY, ) self.assertEqual(response.client_data.origin, APP_ID) self.assertEqual(response.client_data.type, "webauthn.create") self.assertEqual(response.client_data.challenge, challenge)
def make_credential(self, options, **kwargs): """Creates a credential. :param options: PublicKeyCredentialCreationOptions data. :param pin: (optional) Used if PIN verification is required. :param threading.Event event: (optional) Signal to abort the operation. :param on_keepalive: (optional) function to call with CTAP status updates. """ options = PublicKeyCredentialCreationOptions._wrap(options) pin = kwargs.get("pin") event = kwargs.get("event", Event()) if options.timeout: timer = Timer(options.timeout / 1000, event.set) timer.daemon = True timer.start() self._verify_rp_id(options.rp.id) client_data = self._build_client_data(WEBAUTHN_TYPE.MAKE_CREDENTIAL, options.challenge) selection = options.authenticator_selection or AuthenticatorSelectionCriteria( ) try: return ( self._do_make_credential( client_data, options.rp, options.user, options.pub_key_cred_params, options.exclude_credentials, options.extensions, selection.require_resident_key, self._get_ctap_uv(selection.user_verification, pin is not None), pin, event, kwargs.get("on_keepalive"), ), client_data, ) except CtapError as e: raise _ctap2client_err(e) finally: if options.timeout: timer.cancel()
def test_make_credential_ctap2(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) ctap2.make_credential.return_value = AttestationObject(_MC_RESP) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) attestation, client_data = client.make_credential( PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -7 }], timeout=1000, authenticator_selection={"userVerification": "discouraged"}, )) self.assertIsInstance(attestation, AttestationObject) self.assertIsInstance(client_data, ClientData) ctap2.get_info.assert_called_with() ctap2.make_credential.assert_called_with( client_data.hash, rp, user, [{ "type": "public-key", "alg": -7 }], None, None, None, None, None, mock.ANY, None, ) self.assertEqual(client_data.get("origin"), APP_ID) self.assertEqual(client_data.get("type"), "webauthn.create") self.assertEqual(client_data.challenge, challenge)
def make_credential( host="solokeys.dev", user_id="they", serial=None, pin=None, prompt="Touch your authenticator to generate a credential...", output=True, udp=False, ): user_id = user_id.encode() client = solo.client.find(solo_serial=serial, udp=udp).client rp = {"id": host, "name": "Example RP"} client.host = host client.origin = f"https://{client.host}" client.user_id = user_id user = {"id": user_id, "name": "A. User"} challenge = secrets.token_bytes(32) if prompt: print(prompt) hmac_ext = HmacSecretExtension(client.ctap2) options = PublicKeyCredentialCreationOptions( rp, user, challenge, [{ "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -7 }], extensions=hmac_ext.create_dict(), ) attestation_object, client_data = client.make_credential(options, pin=pin) credential = attestation_object.auth_data.credential_data credential_id = credential.credential_id if output: print(credential_id.hex()) return credential_id
def test_make_credential_wrong_app_id(self, PatchedCTAP2): dev = mock.Mock() dev.capabilities = CAPABILITY.CBOR ctap2 = mock.MagicMock() ctap2.get_info.return_value = Info(_INFO_NO_PIN) PatchedCTAP2.return_value = ctap2 client = Fido2Client(dev, APP_ID) try: client.make_credential( PublicKeyCredentialCreationOptions( {"id": "bar.example.com", "name": "Invalid RP"}, user, challenge, [{"type": "public-key", "alg": -7}], ) ) self.fail("make_credential did not raise error") except ClientError as e: self.assertEqual(e.code, ClientError.ERR.BAD_REQUEST)
def create_credential(client, token, host="localhost", silent=False): rp = {"id": host, "name": "TOTP Token"} user = {"id": token.encode(), "name": "%s token" % token} challenge = secrets.token_bytes(32) options = PublicKeyCredentialCreationOptions( rp, user, challenge, [{"type": "public-key", "alg": -8}, {"type": "public-key", "alg": -7}], extensions={"hmacCreateSecret": True}, authenticator_selection={"require_resident_key": True}, ) if not silent: print("Please press security key to create new credential") result = client.make_credential(options) credential = result.attestation_object.auth_data.credential_data credential_id = credential.credential_id return credential_id.hex()