Exemple #1
0
    def shared_secret(private_key: 'CK', public_key: 'CK') -> bytes:
        """ Compute the shared secret. """

        if public_key.crv == X25519:
            d = X25519PrivateKey.from_private_bytes(private_key.d)
            x = X25519PublicKey.from_public_bytes(public_key.x)
            secret = d.exchange(x)
        elif public_key.crv == X448:
            d = X448PrivateKey.from_private_bytes(private_key.d)

            x = X448PublicKey.from_public_bytes(public_key.x)
            secret = d.exchange(x)
        elif public_key.crv == P256:
            d = ec.derive_private_key(int(hexlify(private_key.d), 16),
                                      SECP256R1(), default_backend())

            x = ec.EllipticCurvePublicNumbers(int(hexlify(public_key.x), 16),
                                              int(hexlify(public_key.y), 16),
                                              SECP256R1())
            x = x.public_key()
            secret = d.exchange(ec.ECDH(), x)
        else:
            raise CoseIllegalCurve(f"{public_key.crv} is unsupported")

        return secret
Exemple #2
0
def get_cert():
    domain = get_env("ACME_DOMAIN")
    rr = get_env("ACME_RR")

    k = generate_private_key(SECP256R1(), default_backend())
    jwk = generate_jwk(k)
    thumbprint = b64(sha256(dumps(jwk, sort_keys=True, separators=(",", ":")).encode("utf-8")))
    csr, key = generate_csr(domain)

    directory = get_directory()
    nonce = head_new_nonce(directory)
    nonce, kid = post_new_account(directory, nonce, k, jwk)
    nonce, order, authorization, finalize = post_new_order(directory, nonce, k, kid, domain)
    nonce, challenge, token = get_authorization(directory, nonce, k, kid, authorization)

    update_rr(domain, rr, b64(sha256("{}.{}".format(token, thumbprint).encode("ascii"))))
    nonce = post_chanllenge(directory, nonce, k, kid, challenge)
    nonce = check_status(directory, nonce, k, kid, order)

    nonce, certificate = post_finalize(directory, nonce, k, kid, finalize, csr)
    nonce, cert = get_certificate(directory, nonce, k, kid, certificate)

    nonce = post_account(directory, nonce, k, kid)

    save_cert(cert, key)
Exemple #3
0
    def webauthnGet(self, resp, raw_id, authData, signature, user_handle, client_data_hash):
        logging.debug('===assertion===')
        if authData:
            logging.debug('authData')
            if authData[:32].hex() == hashlib.sha256(rpid_host.encode()).hexdigest():
                logging.debug('\trpidHash ' + str(authData[:32].hex()))
                flags = int.from_bytes(authData[32:33], 'big')
                logging.debug(f'\tflags {bin(flags)}, {bin(flags & 0b0100_0000)}, {bin(flags & 0b1000_0000)}')
                logging.debug('\tsigCount ' + str(int.from_bytes(authData[33:37], 'big')))

                with open(f"./keys/{base64.urlsafe_b64encode(raw_id).decode()}.cbor") as f:
                    public_key = cbor.loads(base64.b64decode(f.readline().encode()))
                if public_key[3] == -7:
                    # TODO: 他のアルゴリズムへの対応
                    verification_data = authData + client_data_hash
                    x = int(codecs.encode(public_key[-2], 'hex'), 16)
                    y = int(codecs.encode(public_key[-3], 'hex'), 16)
                    credential_public_key = EllipticCurvePublicNumbers(x, y, SECP256R1()).public_key(backend=default_backend())
                    try:
                        credential_public_key.verify(signature, verification_data, ECDSA(SHA256()))
                        resp.media = {'status': 'ok'}
                        logging.info('[success] navigator.credentials.get')
                    except InvalidSignature as e:
                        logging.info(f'InvalidSignature {e}')
                        resp.media = {'status': 'ng'}
                else:
                    logging.info('not support type')
                    resp.media = {'status': 'ng'}
            else:
                logging.info('not match RPID')
                logging.debug(f'{authData[:32].hex()}, {hashlib.sha256(rpid_host.encode()).hexdigest()} [{authData[:32].hex() == hashlib.sha256(rpid_host.encode()).hexdigest()}]')
                resp.media = {'status': 'ng'}
        else:
            logging.info('not find authData')
            resp.media = {'status': 'ng'}
Exemple #4
0
    def test_verify_new_api(self):
        import math

        from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1

        from josepy.jwa import ES256
        key = mock.MagicMock(key_size=256, curve=SECP256R1())
        ES256.verify(key, "message", b'\x00' * math.ceil(key.key_size / 8) * 2)
        self.assertIs(key.verify.called, True)
Exemple #5
0
    def test_sign_new_api(self):
        from cryptography.hazmat.primitives.asymmetric.ec import SECP256R1

        from josepy.jwa import ES256
        key = mock.MagicMock(curve=SECP256R1())
        with mock.patch("josepy.jwa.decode_dss_signature") as decode_patch:
            decode_patch.return_value = (0, 0)
            ES256.sign(key, "message")
        self.assertIs(key.sign.called, True)
Exemple #6
0
def get_ec2_curve(crv_id: COSECRV) -> EllipticCurve:
    """Turn an EC2 COSE crv identifier into a corresponding curve"""
    if crv_id == COSECRV.P256:
        return SECP256R1()
    elif crv_id == COSECRV.P384:
        return SECP384R1()
    elif crv_id == COSECRV.P521:
        return SECP521R1()

    raise UnsupportedEC2Curve(f"Unrecognized EC2 curve {crv_id}")
Exemple #7
0
def test_ecc_public_numbers_to_bytes():
    """Test conversion ECC public numbers to bytes."""
    ecc = EllipticCurvePublicNumbers(0x1234567890ABCDEF, 0xEFCDAB9078563412, SECP256R1())
    assert (
        ecc_public_numbers_to_bytes(ecc)
        == b"\x12\x34\x56\x78\x90\xab\xcd\xef\xef\xcd\xab\x90\x78\x56\x34\x12"
    )
    assert (
        ecc_public_numbers_to_bytes(ecc, 8)
        == b"\x12\x34\x56\x78\x90\xab\xcd\xef\xef\xcd\xab\x90\x78\x56\x34\x12"
    )
Exemple #8
0
def cryptography_ec2_public_key(
        credential_public_key: EC2CredentialPublicKey) -> PublicKey:
    x = int.from_bytes(credential_public_key.x, 'big')
    y = int.from_bytes(credential_public_key.y, 'big')

    curve = None
    if credential_public_key.crv.name == 'P_256': curve = SECP256R1()
    elif credential_public_key.crv.name == 'P_384': curve = SECP384R1()
    elif credential_public_key.crv.name == 'P_521': curve = SECP521R1()
    else:
        raise UnimplementedError(
            'Unsupported cryptography EC2 curve {}'.format(
                credential_public_key.crv.name))

    ecpn = EllipticCurvePublicNumbers(x, y, curve)
    return ecpn.public_key(default_backend())
    def generate_secpr1_keys(secrp1_type):
        # TODO fix
        raise Exception(
            "secpXXXr1 has some bugs, the received application data can't be encrypted"
        )
        if secrp1_type == "secp256r1":
            curve = SECP256R1()
            coordinate_byte_size = 32
        elif secrp1_type == "secp384r1":
            curve = SECP384R1()
            coordinate_byte_size = 48
        elif secrp1_type == "secp521r1":
            curve = SECP521R1()
            coordinate_byte_size = 66
        else:
            raise Exception("Unknown secpr1 type {}".format(secrp1_type))

        private_key = generate_private_key(curve, default_backend())
        private_key_bytes = private_key.private_bytes(
            encoding=Encoding.DER,
            format=PrivateFormat.PKCS8,
            encryption_algorithm=NoEncryption(),
        )
        public_key = private_key.public_key()
        public_key_bytes = public_key.public_bytes(
            encoding=Encoding.DER, format=PublicFormat.SubjectPublicKeyInfo)

        # The key must be shared in a specific format so we generate this for this curve
        # https://tlswg.org/tls13-spec/antoine_address/draft-ietf-tls-tls13.html#ecdhe-param
        # For secp256r1, secp384r1, and secp521r1, the contents are the serialized value of the following struct:
        # struct {
        #     uint8 legacy_form = 4;
        #     opaque X[coordinate_length];
        #     opaque Y[coordinate_length];
        # } UncompressedPointRepresentation;
        x = private_key.private_numbers().public_numbers.x
        y = private_key.private_numbers().public_numbers.y
        shared_key = (
            b"\x04" +
            x.to_bytes(coordinate_byte_size, Crypto_Helper.ENDINESS) +
            y.to_bytes(coordinate_byte_size, Crypto_Helper.ENDINESS))
        return private_key_bytes, public_key_bytes, shared_key
Exemple #10
0
    def _ecdh(cls, curve: 'CoseCurve', private_key: 'EC2', public_key: 'EC2') -> bytes:
        if curve == curves.P256():
            curve_instance = SECP256R1()
        elif curve == curves.P384():
            curve_instance = SECP384R1()
        elif curve == curves.P521():
            curve_instance = SECP521R1()
        else:
            raise CoseIllegalCurve()

        d_value = int(hexlify(private_key.d), 16)
        x_value = int(hexlify(public_key.x), 16)
        y_value = int(hexlify(public_key.y), 16)

        d = ec.derive_private_key(d_value, curve_instance, default_backend())
        p = ec.EllipticCurvePublicNumbers(x_value, y_value, curve_instance)
        p = p.public_key(default_backend())

        shared_key = d.exchange(ECDH(), p)
        return shared_key
Exemple #11
0
def _decode_public_key(key_bytes):
    '''Decode a packed SECP256r1 public key into an EllipticCurvePublicKey
    '''

    # Parsing this structure by hand, following SEC1, section 2.3.4
    # An alternative is to hack on the OpenSSL CFFI bindings so we
    # can call EC_POINT_oct2point on the contents of key_bytes. Please
    # believe me when I say that this is much simpler - mainly because
    # we can make assumptions about /exactly/ which EC curve we're
    # using!)
    if key_bytes[0] != '\x04':
        raise AuthenticationRejectedException('XXX bad public key.')

    # x and y coordinates are each 32-bytes long, encoded as big-endian binary
    # strings. Without calling unsupported C API functions (i.e.
    # _PyLong_FromByteArray), converting to hex-encoding and then parsing
    # seems to be the simplest way to make these into python big-integers.
    curve = SECP256R1()
    x = long_(key_bytes[1:33].encode('hex'), 16)
    y = long_(key_bytes[33:].encode('hex'), 16)

    return EllipticCurvePublicNumbers(x, y, curve)
Exemple #12
0
    def generate_key(crv: Union[Type['CoseCurve'], str, int],
                     optional_params: dict = None) -> 'EC2Key':
        """
        Generate a random EC2Key COSE key object.

        :param crv: Specify an :class:`~cose.attributes.algorithms.CoseEllipticCurves`.
        :param optional_params: Optional key attributes for the :class:`~cose.keys.ec2.EC2Key` object, e.g., \
        :class:`~cose.keys.keyparam.KpAlg` or  :class:`~cose.keys.keyparam.KpKid`.

        :return: An COSE `EC2Key` key.
        """

        if type(crv) == str or type(crv) == int:
            crv = CoseCurve.from_id(crv)

        if crv == P256:
            curve_obj = SECP256R1()
        elif crv == P384:
            curve_obj = SECP384R1()
        elif crv == P521:
            curve_obj = SECP521R1()
        else:
            raise CoseIllegalCurve(f'Illegal COSE curve: {crv}')

        private_key = ec.generate_private_key(curve_obj,
                                              backend=default_backend())
        d_value = private_key.private_numbers().private_value
        x_coor = private_key.public_key().public_numbers().x
        y_coor = private_key.public_key().public_numbers().y

        return EC2Key(crv=crv,
                      d=d_value.to_bytes((d_value.bit_length() + 7) // 8,
                                         byteorder="big"),
                      x=x_coor.to_bytes((x_coor.bit_length() + 7) // 8,
                                        byteorder="big"),
                      y=y_coor.to_bytes((y_coor.bit_length() + 7) // 8,
                                        byteorder="big"),
                      optional_params=optional_params)
Exemple #13
0
def cryptography_ec2_public_key(
        credential_public_key: EC2CredentialPublicKey) -> EC2PublicKey:
    """Convert an `EC2CredentialPublicKey` into a cryptography `EC2PublicKey`.

    Args:
      credential_public_key (EC2CredentialPublicKey): The key to convert.

    Returns:
      A cryptography `EC2PublicKey`.

    Raises:
      UnimplementedError: If the conversion logic for the given type of
        CredentialPublicKey has not been implemented.
      PublicKeyConversionError: If the provided key could not be converted
        into a valid cryptography `EC2PublicKey`.
    """
    x = int.from_bytes(credential_public_key.x, 'big')
    y = int.from_bytes(credential_public_key.y, 'big')

    curve: Optional[Union[SECP256R1, SECP384R1, SECP521R1]] = None
    if credential_public_key.crv.name == 'P_256': curve = SECP256R1()
    elif credential_public_key.crv.name == 'P_384': curve = SECP384R1()
    elif credential_public_key.crv.name == 'P_521': curve = SECP521R1()
    else:
        raise UnimplementedError(
            'Unsupported cryptography EC2 curve {}'.format(
                credential_public_key.crv.name))

    assert curve is not None

    ecpn = EllipticCurvePublicNumbers(x, y, curve)

    try:
        return ecpn.public_key(default_backend())
    except ValueError:
        raise PublicKeyConversionError('Invalid EC2 public key')
def test_attest_android_key():
    client_data_hash = b'client-data-hash'
    auth_data = b'auth-data'

    certificate, private_key, public_key = generate_elliptic_curve_x509_certificate_android(
        SECP256R1(), client_data_hash)
    public_numbers = private_key.private_numbers().public_numbers

    x = public_numbers.x.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')
    y = public_numbers.y.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')
    public_key_android = b'\x04' + x + y
    verification_data = auth_data + client_data_hash

    credential_public_key = EC2CredentialPublicKey(
        x=x,
        y=y,
        kty=COSEKeyType.Name.EC2,
        crv=EC2Curve.Name.P_256,
    )

    attested_credential_data = AttestedCredentialData(
        aaguid=TEST_AAGUID,
        credential_id_length=TEST_CREDENTIAL_ID_LENGTH,
        credential_id=TEST_CREDENTIAL_ID,
        credential_public_key=credential_public_key,
    )

    signature = generate_signature(private_key, verification_data)

    invalid_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256, sig=b'', x5c=[])

    att_obj = AttestationObject(
        auth_data=AuthenticatorData(
            rp_id_hash=TEST_RP_ID_HASH,
            flags=0,
            sign_count=0,
            attested_credential_data=attested_credential_data,
            extensions=None),
        fmt=AttestationStatementFormatIdentifier.FIDO_U2F,
        att_stmt=invalid_att_stmt)

    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)

    der_certificate = certificate.public_bytes(Encoding.DER)
    valid_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=signature,
        x5c=[der_certificate])

    att_obj.att_stmt = valid_att_stmt
    assert attest(valid_att_stmt, att_obj, auth_data,
                  client_data_hash) == (AttestationType.BASIC, [certificate])

    unverified_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=b'bad-signature',
        x5c=[der_certificate])
    att_obj.att_stmt = unverified_att_stmt
    with pytest.raises(AttestationError):
        attest(unverified_att_stmt, att_obj, auth_data, client_data_hash)

    invalid_pk = rsa.generate_private_key(65537, 4096, default_backend())
    invalid_pubk = invalid_pk.public_key()
    invalid_certificate = generate_x509_certificate(invalid_pubk, invalid_pk,
                                                    hashes.SHA256())

    invalid_signature = invalid_pk.sign(verification_data, PKCS1v15(),
                                        hashes.SHA256())
    invalid_der_certificate = invalid_certificate.public_bytes(Encoding.DER)

    invalid_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=invalid_signature,
        x5c=[invalid_der_certificate])
    att_obj.att_stmt = invalid_att_stmt
    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)

    att_obj.att_stmt = valid_att_stmt

    while True:
        random_pk = generate_private_key(SECP256R1(), default_backend())
        random_public_numbers = random_pk.private_numbers().public_numbers
        rx = random_public_numbers.x.to_bytes(P_256_COORDINATE_BYTE_LENGTH,
                                              'big')
        ry = random_public_numbers.y.to_bytes(P_256_COORDINATE_BYTE_LENGTH,
                                              'big')

        if rx != x or ry != y:
            break

    att_obj.auth_data.attested_credential_data.credential_public_key = (
        EC2CredentialPublicKey(
            x=rx,
            y=ry,
            kty=COSEKeyType.Name.EC2,
            crv=EC2Curve.Name.P_256,
        ))

    with pytest.raises(AttestationError):
        attest(valid_att_stmt, att_obj, auth_data, client_data_hash)

    invalid_certificate, invalid_pk, invalid_pubk = generate_elliptic_curve_x509_certificate(
        SECP256R1())
    invalid_public_numbers = invalid_pk.private_numbers().public_numbers

    x = invalid_public_numbers.x.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')
    y = invalid_public_numbers.y.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')

    att_obj.auth_data.attested_credential_data.credential_public_key = (
        EC2CredentialPublicKey(
            x=x,
            y=y,
            kty=COSEKeyType.Name.EC2,
            crv=EC2Curve.Name.P_256,
        ))

    invalid_signature = generate_signature(invalid_pk, verification_data)
    invalid_der_certificate = invalid_certificate.public_bytes(Encoding.DER)
    invalid_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=invalid_signature,
        x5c=[invalid_der_certificate])

    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)

    invalid_certificate, invalid_pk, invalid_pubk = generate_elliptic_curve_x509_certificate_android_raw(
        SECP256R1(), b'invalid')
    invalid_public_numbers = invalid_pk.private_numbers().public_numbers

    x = invalid_public_numbers.x.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')
    y = invalid_public_numbers.y.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')

    att_obj.auth_data.attested_credential_data.credential_public_key = (
        EC2CredentialPublicKey(
            x=x,
            y=y,
            kty=COSEKeyType.Name.EC2,
            crv=EC2Curve.Name.P_256,
        ))

    invalid_signature = generate_signature(invalid_pk, verification_data)
    invalid_der_certificate = invalid_certificate.public_bytes(Encoding.DER)
    invalid_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=invalid_signature,
        x5c=[invalid_der_certificate])

    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)

    okp_pk = Ed25519PrivateKey.generate()
    okp_pubk = okp_pk.public_key()

    okp_certificate = generate_x509_certificate(
        okp_pubk, okp_pk, None, generate_android_extensions(client_data_hash))

    x = okp_pubk.public_bytes(Encoding.Raw, PublicFormat.Raw)

    att_obj.auth_data.attested_credential_data.credential_public_key = (
        OKPCredentialPublicKey(
            x=x,
            kty=COSEKeyType.Name.OKP,
            crv=OKPCurve.Name.ED25519,
        ))

    okp_signature = okp_pk.sign(verification_data)
    okp_der_certificate = okp_certificate.public_bytes(Encoding.DER)
    okp_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=okp_signature,
        x5c=[okp_der_certificate])

    assert attest(okp_att_stmt, att_obj, auth_data,
                  client_data_hash) == (AttestationType.BASIC,
                                        [okp_certificate])

    okp_pk = Ed448PrivateKey.generate()
    okp_pubk = okp_pk.public_key()

    okp_certificate = generate_x509_certificate(
        okp_pubk, okp_pk, None, generate_android_extensions(client_data_hash))

    x = okp_pubk.public_bytes(Encoding.Raw, PublicFormat.Raw)

    att_obj.auth_data.attested_credential_data.credential_public_key = (
        OKPCredentialPublicKey(
            x=x,
            kty=COSEKeyType.Name.OKP,
            crv=OKPCurve.Name.ED448,
        ))

    okp_signature = okp_pk.sign(verification_data)
    okp_der_certificate = okp_certificate.public_bytes(Encoding.DER)
    okp_att_stmt = AndroidKeyAttestationStatement(
        alg=COSEAlgorithmIdentifier.Name.ES256,
        sig=okp_signature,
        x5c=[okp_der_certificate])

    assert attest(okp_att_stmt, att_obj, auth_data,
                  client_data_hash) == (AttestationType.BASIC,
                                        [okp_certificate])
Exemple #15
0
    def _get_curve_instance(self):
        if self.crv == JWKCrv.P256:
            return SECP256R1()

        raise NotImplementedError
Exemple #16
0
    def _verify_attestation_statement(self, fmt, att_stmt, auth_data,
                                      client_data_hash):
        '''Verification procedure: The procedure for verifying an attestation statement,
        which takes the following verification procedure inputs:

            * attStmt: The attestation statement structure
            * authenticatorData: The authenticator data claimed to have been used for
                                 the attestation
            * clientDataHash: The hash of the serialized client data

        The procedure returns either:

            * An error indicating that the attestation is invalid, or
            * The attestation type, and the trust path. This attestation trust path is
              either empty (in case of self attestation), an identifier of an ECDAA-Issuer
              public key (in the case of ECDAA), or a set of X.509 certificates.

        TODO:
        Verification of attestation objects requires that the Relying Party has a trusted
        method of determining acceptable trust anchors in step 15 above. Also, if
        certificates are being used, the Relying Party MUST have access to certificate
        status information for the intermediate CA certificates. The Relying Party MUST
        also be able to build the attestation certificate chain if the client did not
        provide this chain in the attestation information.
        '''
        if fmt == 'fido-u2f':
            # Step 1.
            #
            # Verify that attStmt is valid CBOR conforming to the syntax
            # defined above and perform CBOR decoding on it to extract the
            # contained fields.
            if 'x5c' not in att_stmt or 'sig' not in att_stmt:
                raise RegistrationRejectedException(
                    'Attestation statement must be a valid CBOR object.')

            # Step 2.
            #
            # Let attCert be the value of the first element of x5c. Let certificate
            # public key be the public key conveyed by attCert. If certificate public
            # key is not an Elliptic Curve (EC) public key over the P-256 curve,
            # terminate this algorithm and return an appropriate error.
            att_cert = att_stmt.get('x5c')[0]
            x509_att_cert = load_der_x509_certificate(att_cert,
                                                      default_backend())
            certificate_public_key = x509_att_cert.public_key()
            if not isinstance(certificate_public_key.curve, SECP256R1):
                raise RegistrationRejectedException(
                    'Bad certificate public key.')

            # Step 3.
            #
            # Extract the claimed rpIdHash from authenticatorData, and the
            # claimed credentialId and credentialPublicKey from
            # authenticatorData.attestedCredentialData.
            attestation_data = auth_data[37:]
            aaguid = attestation_data[:16]  # noqa: F841
            credential_id_len = struct.unpack('!H', attestation_data[16:18])[0]
            cred_id = attestation_data[18:18 + credential_id_len]
            credential_pub_key = attestation_data[18 + credential_id_len:]

            # The credential public key encoded in COSE_Key format, as defined in Section 7
            # of [RFC8152], using the CTAP2 canonical CBOR encoding form. The COSE_Key-encoded
            # credential public key MUST contain the optional "alg" parameter and MUST NOT
            # contain any other optional parameters. The "alg" parameter MUST contain a
            # COSEAlgorithmIdentifier value. The encoded credential public key MUST also
            # contain any additional required parameters stipulated by the relevant key type
            # specification, i.e., required for the key type "kty" and algorithm "alg" (see
            # Section 8 of [RFC8152]).
            cpk = cbor2.loads(credential_pub_key)

            # Credential public key parameter names via the COSE_Key spec (for ES256).
            alg_key = 3
            x_key = -2
            y_key = -3

            if alg_key not in cpk:
                raise RegistrationRejectedException(
                    "Credential public key missing required algorithm parameter."
                )

            required_keys = {alg_key, x_key, y_key}
            cpk_keys = cpk.keys()

            if not set(cpk_keys).issuperset(required_keys):
                raise RegistrationRejectedException(
                    'Credential public key must match COSE_Key spec.')

            # A COSEAlgorithmIdentifier's value is a number identifying
            # a cryptographic algorithm. The algorithm identifiers SHOULD
            # be values registered in the IANA COSE Algorithms registry
            # [IANA-COSE-ALGS-REG], for instance, -7 for "ES256" and -257
            # for "RS256".
            # https://www.iana.org/assignments/cose/cose.xhtml#algorithms

            # For now we are only supporting ES256 as an algorithm.
            ES256 = -7
            if cpk[alg_key] != ES256:
                raise RegistrationRejectedException('Unsupported algorithm.')

            # Step 4.
            #
            # Convert the COSE_KEY formatted credentialPublicKey (see Section 7
            # of [RFC8152]) to CTAP1/U2F public Key format [FIDO-CTAP].

            # Let publicKeyU2F represent the result of the conversion operation
            # and set its first byte to 0x04. Note: This signifies uncompressed
            # ECC key format.
            public_key_u2f = ''  # 0x04 byte prepended in `_encode_public_key` function.

            # Extract the value corresponding to the "-2" key (representing x coordinate)
            # from credentialPublicKey, confirm its size to be of 32 bytes and concatenate
            # it with publicKeyU2F. If size differs or "-2" key is not found, terminate
            # this algorithm and return an appropriate error.
            x = cpk[x_key].encode('hex')
            if len(x) != 64:
                raise RegistrationRejectedException('Bad public key.')
            x_long = long_(x, 16)

            # Extract the value corresponding to the "-3" key (representing y coordinate)
            # from credentialPublicKey, confirm its size to be of 32 bytes and concatenate
            # it with publicKeyU2F. If size differs or "-3" key is not found, terminate
            # this algorithm and return an appropriate error.
            y = cpk[y_key].encode('hex')
            if len(y) != 64:
                raise RegistrationRejectedException('Bad public key.')
            y_long = long_(y, 16)

            user_ec = EllipticCurvePublicNumbers(
                x_long, y_long,
                SECP256R1()).public_key(backend=default_backend())
            public_key_u2f = _encode_public_key(user_ec)

            # Step 5.
            #
            # Let verificationData be the concatenation of (0x00 || rpIdHash ||
            # clientDataHash || credentialId || publicKeyU2F) (see Section 4.3
            # of [FIDO-U2F-Message-Formats]).
            auth_data_rp_id_hash = _get_auth_data_rp_id_hash(auth_data)
            signature = att_stmt['sig']
            verification_data = ''.join([
                '\0', auth_data_rp_id_hash, client_data_hash, cred_id,
                public_key_u2f
            ])

            # Step 6.
            #
            # Verify the sig using verificationData and certificate public
            # key per [SEC1].
            try:
                certificate_public_key.verify(signature, verification_data,
                                              ECDSA(SHA256()))
            except InvalidSignature:
                raise RegistrationRejectedException(
                    'Invalid signature received.')

            # Step 7.
            #
            # If successful, return attestation type Basic with the
            # attestation trust path set to x5c.
            attestation_type = AT_BASIC
            trust_path = [x509_att_cert]
            return (attestation_type, trust_path, public_key_u2f, cred_id)
        elif fmt == 'none':
            # `none` - indicates that the Relying Party is not interested in
            # authenticator attestation.
            if not self.none_attestation_permitted:
                raise RegistrationRejectedException(
                    'Authenticator attestation is required.')

            attestation_data = auth_data[37:]
            credential_id_len = struct.unpack('!H', attestation_data[16:18])[0]
            cred_id = attestation_data[18:18 + credential_id_len]
            credential_pub_key = attestation_data[18 + credential_id_len:]

            cpk = cbor2.loads(credential_pub_key)

            alg_key = 3
            x_key = -2
            y_key = -3

            if alg_key not in cpk:
                raise RegistrationRejectedException(
                    "Credential public key missing required algorithm parameter."
                )

            required_keys = {alg_key, x_key, y_key}
            cpk_keys = cpk.keys()

            if not set(cpk_keys).issuperset(required_keys):
                raise RegistrationRejectedException(
                    'Credential public key must match COSE_Key spec.')

            public_key_u2f = ''

            x = cpk[x_key].encode('hex')
            if len(x) != 64:
                raise RegistrationRejectedException('Bad public key.')
            x_long = long_(x, 16)

            y = cpk[y_key].encode('hex')
            if len(y) != 64:
                raise RegistrationRejectedException('Bad public key.')
            y_long = long_(y, 16)

            ES256 = -7
            if cpk[alg_key] != ES256:
                raise RegistrationRejectedException('Unsupported algorithm.')

            user_ec = EllipticCurvePublicNumbers(
                x_long, y_long,
                SECP256R1()).public_key(backend=default_backend())
            public_key_u2f = _encode_public_key(user_ec)

            # Step 1.
            #
            # Return attestation type None with an empty trust path.
            attestation_type = AT_NONE
            trust_path = []
            return (attestation_type, trust_path, public_key_u2f, cred_id)
        else:
            raise RegistrationRejectedException('Invalid format.')
def test_attest_fido_u2f():
    client_data_hash = b'client-data-hash'

    certificate, private_key, public_key = generate_elliptic_curve_x509_certificate(
        SECP256R1())
    public_numbers = private_key.private_numbers().public_numbers

    x = public_numbers.x.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')
    y = public_numbers.y.to_bytes(P_256_COORDINATE_BYTE_LENGTH, 'big')
    public_key_u2f = b'\x04' + x + y
    verification_data = b''.join([
        b'\x00',
        TEST_RP_ID_HASH,
        client_data_hash,
        TEST_CREDENTIAL_ID,
        public_key_u2f,
    ])

    credential_public_key = EC2CredentialPublicKey(
        x=x,
        y=y,
        kty=COSEKeyType.Name.EC2,
        crv=EC2Curve.Name.P_256,
    )

    attested_credential_data = AttestedCredentialData(
        aaguid=TEST_AAGUID,
        credential_id_length=TEST_CREDENTIAL_ID_LENGTH,
        credential_id=TEST_CREDENTIAL_ID,
        credential_public_key=credential_public_key,
    )

    signature = generate_signature(private_key, verification_data)

    auth_data = b'auth-data'
    invalid_att_stmt = FIDOU2FAttestationStatement(sig=b'', x5c=[b'', b''])

    att_obj = AttestationObject(
        auth_data=AuthenticatorData(
            rp_id_hash=TEST_RP_ID_HASH,
            flags=0,
            sign_count=0,
            attested_credential_data=attested_credential_data,
            extensions=None),
        fmt=AttestationStatementFormatIdentifier.FIDO_U2F,
        att_stmt=invalid_att_stmt)

    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)

    der_certificate = certificate.public_bytes(Encoding.DER)
    valid_att_stmt = FIDOU2FAttestationStatement(sig=signature,
                                                 x5c=[der_certificate])

    att_obj.att_stmt = valid_att_stmt
    assert attest(valid_att_stmt, att_obj, auth_data,
                  client_data_hash) == (AttestationType.BASIC, [certificate])

    unverified_att_stmt = FIDOU2FAttestationStatement(sig=b'bad-signature',
                                                      x5c=[der_certificate])
    att_obj.att_stmt = unverified_att_stmt
    with pytest.raises(AttestationError):
        attest(unverified_att_stmt, att_obj, auth_data, client_data_hash)

    invalid_pk = rsa.generate_private_key(65537, 4096, default_backend())
    invalid_pubk = invalid_pk.public_key()
    invalid_certificate = generate_x509_certificate(invalid_pubk, invalid_pk,
                                                    hashes.SHA256())

    invalid_signature = invalid_pk.sign(verification_data, PKCS1v15(),
                                        hashes.SHA256())
    invalid_der_certificate = invalid_certificate.public_bytes(Encoding.DER)
    invalid_att_stmt = FIDOU2FAttestationStatement(
        sig=invalid_signature, x5c=[invalid_der_certificate])

    att_obj.att_stmt = invalid_att_stmt
    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)

    invalid_certificate, invalid_pk, invalid_pubk = generate_elliptic_curve_x509_certificate(
        SECP384R1())
    invalid_signature = generate_signature(invalid_pk, verification_data)
    invalid_der_certificate = invalid_certificate.public_bytes(Encoding.DER)
    invalid_att_stmt = FIDOU2FAttestationStatement(
        sig=invalid_signature, x5c=[invalid_der_certificate])

    att_obj.att_stmt = invalid_att_stmt
    with pytest.raises(AttestationError):
        attest(invalid_att_stmt, att_obj, auth_data, client_data_hash)
Exemple #18
0
def keygen():
    return generate_private_key(SECP256R1(), default_backend())
Exemple #19
0
    def webauthnCreate(self, resp, raw_id, attestation, client_data_hash):
        logging.debug('===attestation===')
        authData = attestation.get('authData')
        if authData:
            logging.debug('authData')
            if authData[:32].hex() == hashlib.sha256(rpid_host.encode()).hexdigest():
                logging.debug('\trpidHash ' + str(authData[:32].hex()))
                flags = int.from_bytes(authData[32:33], 'big')
                logging.debug(f'\tflags {bin(flags)}, {bin(flags & 0b0100_0000)}, {bin(flags & 0b1000_0000)}')
                logging.debug('\tsigCount ' + str(int.from_bytes(authData[33:37], 'big')))
                if flags & 0b0100_0000:
                    logging.debug('attestedCredentialData')
                    logging.debug('\taaguid ' + str(int.from_bytes(authData[37:53], 'big')))
                    credentialIdLength = int.from_bytes(authData[53:55], 'big')
                    logging.debug('\tcredentialIdLength ' + str(credentialIdLength))
                    credentialId = authData[55:55+credentialIdLength]
                    logging.debug('\tcredentialId ' + str(base64.b64encode(credentialId)))
                    credentialPublicKey = cbor.loads(authData[55+credentialIdLength:])
                    logging.debug('\tcredentialPublicKey')
                    logging.debug('\t\tkty ' + str(credentialPublicKey[1]))
                    logging.debug('\t\talg ' + str(credentialPublicKey[3]))
                    logging.debug('\t\tcrv ' + str(credentialPublicKey[-1]))
                    logging.debug('\t\tx ' + str(credentialPublicKey[-2]))
                    logging.debug('\t\ty ' + str(credentialPublicKey[-3]))
                if flags & 0b1000_0000:
                    logging.debug('extensions ' + str(cbor.loads(bytes(authData[37:]))))
                    logging.info('not support flags')
                    resp.media = {'status': 'ng'}
                    return

                logging.debug('fmt ' + str(attestation.get('fmt')))
                attStmt = attestation.get('attStmt')
                logging.debug('attStmt')
                for k in attStmt.keys():
                    logging.debug(f'\t{k} {attStmt[k]}')

                if attestation.get('fmt') == 'packed' and 'x5c' not in attStmt:
                    # TODO: 他のパターンへの対応
                    # TODO: 他のアルゴリズムへの対応
                    signature = attStmt['sig']
                    verification_data = authData + client_data_hash
                    x = int(codecs.encode(credentialPublicKey[-2], 'hex'), 16)
                    y = int(codecs.encode(credentialPublicKey[-3], 'hex'), 16)
                    credential_public_key = EllipticCurvePublicNumbers(x, y, SECP256R1()).public_key(backend=default_backend())
                    try:
                        credential_public_key.verify(signature, verification_data, ECDSA(SHA256()))
                        resp.media = {'status': 'ok'}
                        logging.info('[success] navigator.credentials.create')
                        with open(f'keys/{base64.urlsafe_b64encode(raw_id).decode()}.cbor', 'w') as f:
                            f.write(f'{base64.b64encode(authData[55+credentialIdLength:]).decode()}')
                    except InvalidSignature as e:
                        logging.info(f'InvalidSignature {e}')
                        resp.media = {'status': 'ng'}
                else:
                    logging.info('not support type')
                    resp.media = {'status': 'ng'}
            else:
                logging.info('not match RPID')
                logging.debug(f'{authData[:32].hex()}, {hashlib.sha256(rpid_host.encode()).hexdigest()} [{authData[:32].hex() == hashlib.sha256(rpid_host.encode()).hexdigest()}]')
                resp.media = {'status': 'ng'}
        else:
            logging.info('not find authData')
            resp.media = {'status': 'ng'}
Exemple #20
0
    def verify(self):
        try:
            # Step 1.
            #
            # Perform JSON deserialization on the clientDataJSON field
            # of the AuthenticatorAttestationResponse object to extract
            # the client data C claimed as collected during the credential
            # creation.
            credential_id = self.registration_response.get('id')
            raw_id = self.registration_response.get('rawId')
            attestation_object = self.registration_response.get('attObj')
            client_data = self.registration_response.get('clientData')
            credential_type = self.registration_response.get('type')
            decoded_cd = _webauthn_b64_decode(client_data)
            cd = json.loads(decoded_cd)

            # Step 2.
            #
            # Verify that the type in C is the string webauthn.create.
            received_type = cd.get('type')
            if not _verify_type(received_type, TYPE_CREATE):
                raise RegistrationRejectedException('Invalid type.')

            # Step 3.
            #
            # Verify that the challenge in C matches the challenge that
            # was sent to the authenticator in the create() call.
            received_challenge = cd.get('challenge')
            if not _verify_challenge(received_challenge, self.challenge):
                raise RegistrationRejectedException(
                    'Unable to verify challenge.')

            # Step 4.
            #
            # Verify that the origin in C matches the Relying Party's origin.
            if not _verify_origin(cd, self.origin):
                raise RegistrationRejectedException('Unable to verify origin.')

            # Step 5.
            #
            # Verify that the tokenBindingId in C matches the Token
            # Binding ID for the TLS connection over which the
            # attestation was obtained.
            if not _verify_token_binding_id(cd):
                raise RegistrationRejectedException(
                    'Unable to verify token binding ID.')

            # Step 6.
            #
            # Verify that the clientExtensions in C is a proper subset
            # of the extensions requested by the RP and that the
            # authenticatorExtensions in C is also a proper subset of
            # the extensions requested by the RP.
            if not _verify_client_extensions(cd):
                raise RegistrationRejectedException(
                    'Unable to verify client extensions.')
            if not _verify_authenticator_extensions(cd):
                raise RegistrationRejectedException(
                    'Unable to verify authenticator extensions.')

            # Step 7.
            #
            # Compute the hash of clientDataJSON using the algorithm
            # identified by C.hashAlgorithm.
            client_data_hash = _get_client_data_hash(cd, decoded_cd)

            # Step 8.
            #
            # Perform CBOR decoding on the attestationObject field of
            # the AuthenticatorAttestationResponse structure to obtain
            # the attestation statement format fmt, the authenticator
            # data authData, and the attestation statement attStmt.
            att_obj = cbor2.loads(_webauthn_b64_decode(attestation_object))
            att_stmt = att_obj.get('attStmt')
            auth_data = att_obj.get('authData')
            fmt = att_obj.get('fmt')
            if not auth_data or len(auth_data) < 37:
                raise RegistrationRejectedException(
                    'Auth data must be at least 37 bytes.')

            # Step 9.
            #
            # Verify that the RP ID hash in authData is indeed the
            # SHA-256 hash of the RP ID expected by the RP.
            auth_data_rp_id_hash = _get_auth_data_rp_id_hash(auth_data)
            if not _verify_rp_id_hash(auth_data_rp_id_hash, self.rp_id):
                raise RegistrationRejectedException(
                    'Unable to verify RP ID hash.')

            # Step 10.
            #
            # Determine the attestation statement format by performing
            # an USASCII case-sensitive match on fmt against the set of
            # supported WebAuthn Attestation Statement Format Identifier
            # values. The up-to-date list of registered WebAuthn
            # Attestation Statement Format Identifier values is maintained
            # in the in the IANA registry of the same name
            # [WebAuthn-Registries].
            if not _verify_attestation_statement_format(fmt):
                raise RegistrationRejectedException(
                    'Unable to verify attestation statement format.')

            # From authenticatorData, extract the claimed RP ID hash, the
            # claimed credential ID and the claimed credential public key.
            attestation_data = auth_data[37:]
            aaguid = attestation_data[:16]
            credential_id_len = struct.unpack('!H', attestation_data[16:18])[0]
            cred_id = attestation_data[18:18 + credential_id_len]
            b64_cred_id = _webauthn_b64_encode(cred_id)
            credential_pub_key = attestation_data[18 + credential_id_len:]

            # The [=credential public key=] encoded in COSE_Key format, as
            # defined in Section 7 of [[#RFC8152]], using the
            # [=CTAP canonical CBOR encoding form=].

            # The COSE_Key-encoded [=credential public key=] MUST contain the optional "alg"
            # parameter and MUST NOT contain any other optional parameters
            # The "alg" parameter MUST contain a {{COSEAlgorithmIdentifier}} value.

            # The encoded [=credential public key=] MUST also contain any additional
            # required parameters stipulated by the relevant key type specification,
            # i.e., required for the key type "kty" and algorithm "alg"
            # (see Section 8 of[[RFC8152]]).
            cpk = cbor2.loads(credential_pub_key)

            # Credential public key parameter names via the COSE_Key spec (for ES256).
            alg_key = 3
            x_key = -2
            y_key = -3

            if alg_key not in cpk:
                raise RegistrationRejectedException(
                    "Credential public key missing required algorithm parameter."
                )

            required_keys = {alg_key, x_key, y_key}
            cpk_keys = cpk.keys()

            if not set(cpk_keys).issuperset(required_keys):
                raise RegistrationRejectedException(
                    'Credential public key must match COSE_Key spec.')

            # A COSEAlgorithmIdentifier's value is a number identifying
            # a cryptographic algorithm. The algorithm identifiers SHOULD
            # be values registered in the IANA COSE Algorithms registry
            # [IANA-COSE-ALGS-REG], for instance, -7 for "ES256" and -257
            # for "RS256".
            # https://www.iana.org/assignments/cose/cose.xhtml#algorithms

            # For now we are only supporting ES256 as an algorithm.
            ES256 = -7
            if cpk[alg_key] != ES256:
                raise RegistrationRejectedException('Unsupported algorithm.')

            x = long(cpk[x_key].encode('hex'), 16)
            y = long(cpk[y_key].encode('hex'), 16)
            user_ec = EllipticCurvePublicNumbers(
                x, y, SECP256R1()).public_key(backend=default_backend())
            encoded_user_pub_key = _encode_public_key(user_ec)

            # Verify public key length [65 bytes].
            if len(encoded_user_pub_key) != 65:
                raise RegistrationRejectedException('Bad public key.')

            if fmt == 'fido-u2f':
                # Step 11.
                #
                # Verify that attStmt is a correct, validly-signed attestation
                # statement, using the attestation statement format fmt's
                # verification procedure given authenticator data authData and
                # the hash of the serialized client data computed in step 6.

                # Verify that the given attestation statement is valid CBOR
                # conforming to the syntax defined above.
                if 'x5c' not in att_stmt or 'sig' not in att_stmt:
                    raise RegistrationRejectedException(
                        'Attestation statement must be a valid CBOR object.')
                # If x5c is not a certificate for an ECDSA public key over the
                # P-256 curve, stop verification and return an error.

                # Let authenticatorData denote the authenticator data claimed
                # to have been used for the attestation, and let clientDataHash
                # denote the hash of the serialized client data.

                # If clientDataHash is 256 bits long, set tbsHash to this value.
                # Otherwise set tbsHash to the SHA-256 hash of clientDataHash.
                if len(client_data_hash) == 32:
                    tbs_hash = client_data_hash
                else:
                    tbs_hash = hashlib.sha256(client_data_hash).digest()

                # Generate the claimed to-be-signed data as specified in
                # [FIDO-U2F-Message-Formats] section 4.3, with the application
                # parameter set to the claimed RP ID hash, the challenge
                # parameter set to tbsHash, the key handle parameter set to
                # the claimed credential ID of the given credential, and the
                # user public key parameter set to the claimed credential
                # public key.
                cert = att_stmt.get('x5c')[0]
                x509_attestation_cert = load_der_x509_certificate(
                    cert, default_backend())
                public_key = x509_attestation_cert.public_key()
                signature = att_stmt['sig']
                bytes_to_sign = ''.join([
                    '\0', auth_data_rp_id_hash, tbs_hash, cred_id,
                    encoded_user_pub_key
                ])

                # Verify that the sig is a valid ECDSA P-256 signature over the
                # to-be-signed data constructed above.

                # The signature is to be verified by the relying party using the
                # public key certified in the attestation certificate. The relying
                # party should also verify that the attestation certificate was
                # issued by a trusted certification authority.
                try:
                    public_key.verify(signature, bytes_to_sign,
                                      ECDSA(SHA256()))
                except InvalidSignature:
                    raise RegistrationRejectedException(
                        'Invalid signature received.')

                # If successful, return attestation type Basic with the trust
                # path set to x5c.
                # Possible attestation types: Basic, Privacy CA,
                #                             Self Attestation, ECDAA
                attestation_type = AT_BASIC
                trust_path = x509_attestation_cert

                # Step 12.
                #
                # If validation is successful, obtain a list of acceptable trust
                # anchors (attestation root certificates or ECDAA-Issuer public
                # keys) for that attestation type and attestation statement format
                # fmt, from a trusted source or from policy. For example, the FIDO
                # Metadata Service [FIDOMetadataService] provides one way to obtain
                # such information, using the AAGUID in the attestation data
                # contained in authData.
                trust_anchors = _get_trust_anchors(attestation_type, fmt,
                                                   self.trust_anchor_dir)
                if not trust_anchors and self.trusted_attestation_cert_required:
                    raise RegistrationRejectedException(
                        'No trust anchors available to verify attestation certificate.'
                    )

                # Step 13.
                #
                # Assess the attestation trustworthiness using the outputs of the
                # verification procedure in step 10, as follows:
                #
                #     * If self attestation was used, check if self attestation is
                #       acceptable under Relying Party policy.
                #     * If ECDAA was used, verify that the identifier of the
                #       ECDAA-Issuer public key used is included in the set of
                #       acceptable trust anchors obtained in step 11.
                #     * Otherwise, use the X.509 certificates returned by the
                #       verification procedure to verify that the attestation
                #       public key correctly chains up to an acceptable root
                #       certificate.
                if attestation_type == AT_SELF_ATTESTATION:
                    if not self.self_attestation_permitted:
                        raise RegistrationRejectedException(
                            'Self attestation is not permitted.')
                elif attestation_type == AT_PRIVACY_CA:
                    raise NotImplementedError(
                        'Privacy CA attestation type is not currently supported.'
                    )
                elif attestation_type == AT_ECDAA:
                    raise NotImplementedError(
                        'ECDAA attestation type is not currently supported.')
                elif attestation_type == AT_BASIC:
                    if self.trusted_attestation_cert_required:
                        if not _is_trusted_attestation_cert(
                                trust_path, trust_anchors):
                            raise RegistrationRejectedException(
                                'Untrusted attestation certificate.')
                else:
                    raise RegistrationRejectedException(
                        'Unknown attestation type.')

                # Step 14.
                #
                # If the attestation statement attStmt verified successfully and is
                # found to be trustworthy, then register the new credential with the
                # account that was denoted in the options.user passed to create(),
                # by associating it with the credential ID and credential public key
                # contained in authData's attestation data, as appropriate for the
                # Relying Party's systems.

                # Step 15.
                #
                # If the attestation statement attStmt successfully verified but is
                # not trustworthy per step 12 above, the Relying Party SHOULD fail
                # the registration ceremony.
                #
                #     NOTE: However, if permitted by policy, the Relying Party MAY
                #           register the credential ID and credential public key but
                #           treat the credential as one with self attestation (see
                #           5.3.3 Attestation Types). If doing so, the Relying Party
                #           is asserting there is no cryptographic proof that the
                #           public key credential has been generated by a particular
                #           authenticator model. See [FIDOSecRef] and [UAFProtocol]
                #           for a more detailed discussion.
            elif fmt == 'none':
                # `none` - indicates that the Relying Party is not interested in
                # authenticator attestation.
                if not self.none_attestation_permitted:
                    raise RegistrationRejectedException(
                        'Authenticator attestation is required.')
            else:
                raise RegistrationRejectedException('Invalid format.')

            sc = auth_data[33:37]
            sign_count = struct.unpack('!I', sc)[0]

            credential = WebAuthnCredential(
                self.rp_id, self.origin, b64_cred_id,
                base64.b64encode(encoded_user_pub_key), sign_count)

            return credential

        except Exception as e:
            raise RegistrationRejectedException(
                'Registration rejected. Error: {}.'.format(e))