Ejemplo n.º 1
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'}
Ejemplo n.º 2
0
 def loads_public_key(cls, obj):
     curve = cls.DSS_CURVES[obj['crv']]()
     public_numbers = EllipticCurvePublicNumbers(
         base64_to_int(obj['x']),
         base64_to_int(obj['y']),
         curve,
     )
     return public_numbers.public_key(default_backend())
Ejemplo n.º 3
0
 def load_public_key(self):
     curve = self.DSS_CURVES[self._dict_data['crv']]()
     public_numbers = EllipticCurvePublicNumbers(
         base64_to_int(self._dict_data['x']),
         base64_to_int(self._dict_data['y']),
         curve,
     )
     return public_numbers.public_key(default_backend())
Ejemplo n.º 4
0
 def __init__(self, json):
     super().__init__(json)
     decoded_x = bytes_to_int(urlsafe_b64decode(self.x))
     decoded_y = bytes_to_int(urlsafe_b64decode(self.y))
     ec_public_key = EllipticCurvePublicNumbers(
         decoded_x, decoded_y,
         self._get_curve_instance()).public_key(default_backend())
     self.public_key = ec_public_key.public_bytes(
         Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode()
Ejemplo n.º 5
0
    def __init__(self, x, y, d=None, kid=None, curve=None):
        super(EllipticCurveKey, self).__init__()

        self._kid = kid or str(uuid.uuid4())
        self._default_algo = _curve_to_default_algo[curve]
        curve_cls = _kv_crv_to_crypto_cls[curve]

        public_numbers = EllipticCurvePublicNumbers(x, y, curve_cls())
        self._public_key = public_numbers.public_key(default_backend())
        self._private_key = None
        if d is not None:
            private_numbers = EllipticCurvePrivateNumbers(d, public_numbers)
            self._private_key = private_numbers.private_key(default_backend())
Ejemplo n.º 6
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())
Ejemplo n.º 7
0
    def pubkey_from_json(json_pubkey):
        root = json.loads(json_pubkey.decode())

        x = int(root['x'], 16)
        y = int(root['y'], 16)

        return EllipticCurvePublicNumbers(x, y, SECP521R1()).public_key(backend=default_backend())
Ejemplo n.º 8
0
    def loads(self, obj):
        for k in ['crv', 'x', 'y']:
            if k not in obj:
                raise ValueError('Not a elliptic curve key')

        curve = self.DSS_CURVES[obj['crv']]()
        public_numbers = EllipticCurvePublicNumbers(
            base64_to_int(obj['x']),
            base64_to_int(obj['y']),
            curve,
        )
        if 'd' in obj:
            private_numbers = EllipticCurvePrivateNumbers(
                base64_to_int(obj['d']), public_numbers)
            return private_numbers.private_key(default_backend())
        return public_numbers.public_key(default_backend())
Ejemplo n.º 9
0
    def pubkey_from_xml(xml):
        root = ET.fromstring(xml)

        x = int(root[1][0].attrib['Value'])
        y = int(root[1][1].attrib['Value'])
        return EllipticCurvePublicNumbers(
            x, y, SECP521R1()).public_key(backend=default_backend())
    def __init__(self, x, y, kid=None, curve=None):
        super(EllipticCurveKey, self).__init__()

        self._kid = kid or str(uuid.uuid4())
        self._default_algo = _curve_to_default_algo[curve]
        curve_cls = _kv_crv_to_crypto_cls[curve]
        self._ec_impl = EllipticCurvePublicNumbers(
            x, y, curve_cls()).public_key(default_backend())
Ejemplo n.º 11
0
def pubkey_from_tpm2b_public(public: bytes) -> pubkey_type:
    (public, rest) = _extract_tpm2b(public)
    if len(rest) != 0:
        raise ValueError("More in tpm2b_public than tpmt_public")
    # Extract type, [nameAlg], and [objectAttributes] (we don't care about the
    #  latter two)
    (alg_type, _, _) = struct.unpack(">HHI", public[0:8])
    # Ignore the authPolicy
    (_, sym_parms) = _extract_tpm2b(public[8:])
    # Ignore the non-asym-alg parameters
    (sym_mode, ) = struct.unpack(">H", sym_parms[0:2])
    # Ignore the sym_mode and keybits (4 bytes), possibly symmetric (2) and sign
    #  scheme (2)
    to_skip = 4 + 2  # sym_mode, keybits and sign scheme
    if sym_mode != TPM2_ALG_NULL:
        to_skip = to_skip + 2
    asym_parms = sym_parms[to_skip:]

    # Handle fields
    if alg_type == TPM_ALG_RSA:
        (keybits, exponent) = struct.unpack(">HI", asym_parms[0:6])
        if exponent == 0:
            exponent = 65537
        (modulus, _) = _extract_tpm2b(asym_parms[6:])
        if (len(modulus) * 8) != keybits:
            raise ValueError(
                f"Misparsed either modulus or keybits: {len(modulus)}*8 != {keybits}"
            )
        bmodulus = int.from_bytes(modulus, byteorder="big")

        numbers = RSAPublicNumbers(exponent, bmodulus)
        return numbers.public_key(backend=default_backend())

    if alg_type == TPM_ALG_ECC:
        (curve, _) = struct.unpack(">HH", asym_parms[0:4])
        asym_x = asym_parms[4:]
        curve = _curve_from_curve_id(curve)

        (x, asym_y) = _extract_tpm2b(asym_x)
        (y, rest) = _extract_tpm2b(asym_y)
        if len(rest) != 0:
            raise ValueError("Misparsed: more contents after X and Y")

        if (len(x) * 8) != curve.key_size:
            raise ValueError(
                f"Misparsed either X or curve: {len(x)}*8 != {curve.key_size}")
        if (len(y) * 8) != curve.key_size:
            raise ValueError(
                f"Misparsed either Y or curve curve: {len(y)}*8 != {curve.key_size}"
            )

        bx = int.from_bytes(x, byteorder="big")
        by = int.from_bytes(y, byteorder="big")

        numbers = EllipticCurvePublicNumbers(bx, by, curve)
        return numbers.public_key(backend=default_backend())

    raise ValueError(f"Invalid tpm2b_public type: {alg_type}")
Ejemplo n.º 12
0
def get_crypto_ecdsa_pubkey_from_bitcoin_hex(bitcoin_hex_key):
    """
    Given a bitcoin hex string key returns the corresponding cryptography ECDSA key object
    :param bitcoin_hex_key: the h
    :return: cryptography ECDSA key object
    """
    bin_ecdsa_public_key = extract_bin_ecdsa_pubkey(bitcoin_hex_key)
    numbers = EllipticCurvePublicNumbers.from_encoded_point(ec.SECP256K1(), b'\x04' + bin_ecdsa_public_key)
    return numbers.public_key(backend=default_backend())
Ejemplo n.º 13
0
def public_ecc_to_crypto(public):
    cid = public.parameters.eccDetail.curveID
    curve = curveid_to_curve(cid)
    if curve is None:
        raise Exception("Unable to find curve for curveid {x}".format(cid))
    xbytes = buffer_to_bytes(public.unique.ecc.x)
    x = int.from_bytes(xbytes, 'big')
    ybytes = buffer_to_bytes(public.unique.ecc.y)
    y = int.from_bytes(ybytes, 'big')
    key = EllipticCurvePublicNumbers(x, y, curve).public_key(default_backend())
    return key
Ejemplo n.º 14
0
def jwk_dict_to_public_key(jwk):
    """ Converts the specified JWK into a public key. """
    jwkest_key = keyrep(jwk)
    if isinstance(jwkest_key, RSAKey):
        pycrypto_key = jwkest_key.key
        return RSAPublicNumbers(e=pycrypto_key.e,
                                n=pycrypto_key.n).public_key(default_backend())
    elif isinstance(jwkest_key, ECKey):
        x, y = jwkest_key.get_key()
        return EllipticCurvePublicNumbers(x, y, jwkest_key.curve).public_key(
            default_backend())

    raise Exception("Unsupported kind of JWK: %s", str(type(jwkest_key)))
Ejemplo n.º 15
0
Archivo: util.py Proyecto: IO42630/ace
def ecdh_cose_to_key(ckey: bytes):
    decoded = loads(ckey)

    kty = decoded[CoseKey.KTY]
    curve = _ecdh_curves[decoded[CoseKey.CRV]]
    x = int(decoded[CoseKey.X].hex(), 16)
    y = int(decoded[CoseKey.Y].hex(), 16)

    numbers = EllipticCurvePublicNumbers(x, y, curve())

    key = backend.load_elliptic_curve_public_numbers(numbers)

    return key
Ejemplo n.º 16
0
def validate_message(message):
    """Validates the entire message using the signature found in the message footer."""
    encoded_point = base64.b64decode(
        message.header.encryption_context['aws-crypto-public-key'])
    public_numbers = EllipticCurvePublicNumbers.from_encoded_point(
        ec.SECP384R1(), encoded_point)
    public_key = public_numbers.public_key(default_backend())
    verifier = public_key.verifier(message.footer.signature,
                                   ec.ECDSA(hashes.SHA384()))

    verifier.update(message.serialize_authenticated_fields())
    verifier.verify()

    logging.info("Message integrity verified.")
def decoded_public_key_to_cryptography(
    public_key: Union[DecodedOKPPublicKey, DecodedEC2PublicKey,
                      DecodedRSAPublicKey]
) -> Union[Ed25519PublicKey, EllipticCurvePublicKey, RSAPublicKey]:
    """Convert raw decoded public key parameters (crv, x, y, n, e, etc...) into
    public keys using primitives from the cryptography.io library
    """
    if isinstance(public_key, DecodedEC2PublicKey):
        """
        alg is -7 (ES256), where kty is 2 (with uncompressed points) and
        crv is 1 (P-256).
        https://www.w3.org/TR/webauthn-2/#sctn-public-key-easy
        """
        x = int(codecs.encode(public_key.x, "hex"), 16)
        y = int(codecs.encode(public_key.y, "hex"), 16)
        curve = get_ec2_curve(public_key.crv)

        ecc_pub_key = EllipticCurvePublicNumbers(x, y, curve).public_key(
            default_backend())

        return ecc_pub_key
    elif isinstance(public_key, DecodedRSAPublicKey):
        """
        alg is -257 (RS256)
        https://www.w3.org/TR/webauthn-2/#sctn-public-key-easy
        """
        e = int(codecs.encode(public_key.e, "hex"), 16)
        n = int(codecs.encode(public_key.n, "hex"), 16)

        rsa_pub_key = RSAPublicNumbers(e, n).public_key(default_backend())

        return rsa_pub_key
    elif isinstance(public_key, DecodedOKPPublicKey):
        """
        -8 (EdDSA), where crv is 6 (Ed25519).
        https://www.w3.org/TR/webauthn-2/#sctn-public-key-easy
        """
        if (public_key.alg != COSEAlgorithmIdentifier.EDDSA
                or public_key.crv != COSECRV.ED25519):
            raise UnsupportedPublicKey(
                f"OKP public key with alg {public_key.alg} and crv {public_key.crv} is not supported"
            )

        okp_pub_key = Ed25519PublicKey.from_public_bytes(public_key.x)

        return okp_pub_key
    else:
        raise UnsupportedPublicKey(
            f"Unrecognized decoded public key: {public_key}")
Ejemplo n.º 18
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')
Ejemplo n.º 19
0
def jwk_dict_to_public_key(jwk_dict):
    """
    Converts the specified JWK into a public key.
    """
    jwk = JsonWebKey.import_key(jwk_dict)

    if isinstance(jwk, RSAKey):
        rsa_pk = jwk.as_key()
        return RSAPublicNumbers(e=rsa_pk.public_numbers().e,
                                n=rsa_pk.public_numbers().n).public_key(
                                    default_backend())
    elif isinstance(jwk, ECKey):
        ec_pk = jwk.as_key()
        return EllipticCurvePublicNumbers(
            x=ec_pk.public_numbers().x,
            y=ec_pk.public_numbers().y,
            curve=ec_pk.public_numbers().curve,
        ).public_key(default_backend())

    raise Exception("Unsupported kind of JWK: %s", str(type(jwk)))
Ejemplo n.º 20
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)
Ejemplo n.º 21
0
    def _load_uncompressed_verification_key_from_encryption_context(
            self, algorithm, encryption_context):
        # pylint: disable=no-self-use
        """Loads the verification key from an uncompressed elliptic curve point.

        :param algorithm: Algorithm for which to generate signing key
        :type algorithm: aws_encryption_sdk.identifiers.Algorithm
        :param dict encryption_context: Encryption context from request
        :returns: Raw verification key
        :rtype: bytes
        """
        # If we are at this point, DefaultCryptoMaterialsManager has already confirmed that:
        #  a) a key should be loaded,
        #  b) the key field is present in the encryption context, and
        #  c) the elliptic curve in the encryption context is not compressed
        # This means that we can safely skip the safety checks that we know have already been done.
        uncompressed_point = base64.b64decode(
            encryption_context[ENCODED_SIGNER_KEY])
        public_key = EllipticCurvePublicNumbers.from_encoded_point(
            curve=algorithm.signing_algorithm_info(),
            data=uncompressed_point).public_key(default_backend())
        return public_key.public_bytes(
            encoding=serialization.Encoding.DER,
            format=serialization.PublicFormat.SubjectPublicKeyInfo)
Ejemplo n.º 22
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'}
Ejemplo n.º 23
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.')
Ejemplo n.º 24
0
def deserialize_pub_key(public_key_ser):
    numbers = EllipticCurvePublicNumbers.from_encoded_point(
        CURVE(), public_key_ser)
    return numbers.public_key(backend=BACKEND)
Ejemplo n.º 25
0
def verify_signature(signature, data, point):
    public_key = EllipticCurvePublicNumbers(
        *point, curve=SECP256K1).public_key(DEFAULT_BACKEND)

    return public_key.verify(signature, data, ECDSA_SHA256)
Ejemplo n.º 26
0
# Using OpenSSL backend for "cryptography".
BACK_END = openssl.backend

# Convert the base64url components to integers.
x_bin = base64url_decode(x_base64url)
y_bin = base64url_decode(y_base64url)

x_hex = binascii.hexlify(x_bin)
y_hex = binascii.hexlify(y_bin)

print("X: " + x_hex.decode("ascii"))
print("Y: " + y_hex.decode("ascii"))

# Convert the base64url components to integers.
x = int.from_bytes(x_bin, byteorder="big")
y = int.from_bytes(y_bin, byteorder="big")

# Acquire the public key from the public components -
public_numbers = EllipticCurvePublicNumbers(x, y, ECC_CURVE)
public_key = public_numbers.public_key(backend=BACK_END)

public_key_pem = public_key.public_bytes(Encoding.PEM,
                                         PublicFormat.SubjectPublicKeyInfo)
with open("public_key.pem", "wb") as f:
    f.write(public_key_pem)

public_key_der = public_key.public_bytes(Encoding.DER,
                                         PublicFormat.SubjectPublicKeyInfo)
with open("public_key.der", "wb") as f:
    f.write(public_key_der)
Ejemplo n.º 27
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))
Ejemplo n.º 28
0
handshake_messages = client_hello.subprotocol_messages[0].to_bytes() + TlsHandshakeMessage(TlsHandshakeTypeByte.SERVER_HELLO, server_hello).to_bytes() + TlsHandshakeMessage(TlsHandshakeTypeByte.CERTIFICATE, certs).to_bytes() + TlsHandshakeMessage(TlsHandshakeTypeByte.SERVER_KEY_EXCHANGE, serverkey).to_bytes() + TlsHandshakeMessage(TlsHandshakeTypeByte.SERVER_DONE, b"").to_bytes() + clientkey.subprotocol_messages[0].to_bytes()

def prf(secret, label, seed, size):
  seed = label + seed
  a = seed
  r = b""
  while len(r) < size:
    a = hmac.new(secret, a, sha384).digest()
    r += hmac.new(secret, a+seed, sha384).digest()
  return r[:size]

from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicNumbers
x = int(clientkey.subprotocol_messages[0].handshake_data[2:2+32].hex(), 16)
y = int(clientkey.subprotocol_messages[0].handshake_data[2+32:2+2*32].hex(), 16)
c = EllipticCurvePublicNumbers(x,y,ec.SECP256R1())
c = c.public_key(default_backend())
premaster = privkey.exchange(ec.ECDH(), c)
mastersecret = prf(premaster, b"extended master secret", sha384(handshake_messages).digest(), 48)
print("randoms")
print(client_random.hex())
print(server_random.hex())
print("premaster then master")
print(premaster.hex())
print(mastersecret.hex())

mac_key_length = 0
iv_length = 4
key_mat = 32
key_block = prf(mastersecret, b"key expansion", server_random+client_random, 2*mac_key_length + 2*key_mat + 2*iv_length)