示例#1
0
    def _readCredentialPublicKey(self, binary, offset):
        enc, end_offset = cbor.loads_in_place(binary[offset:])

        # print(enc)
        # COSE key-encoded elliptic curve public key in EC2 format
        credPKey = {}
        credPKey.update({'kty': enc[self.COSE_KTY]})
        credPKey.update({'alg': enc[self.COSE_ALG]})
        credPKey.update({'crv': enc[self.COSE_CRV]})
        credPKey.update({'x': enc[self.COSE_X]})
        credPKey.update({'y': enc[self.COSE_Y]})
        # Validation
        if credPKey['kty'] != self.EC2_TYPE:
            raise WebAuthnException('public key not in EC2 format',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        if credPKey['alg'] != self.EC2_ES256:
            raise WebAuthnException('signature algorithm not ES256',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        if credPKey['crv'] != self.EC2_P256:
            raise WebAuthnException('curve not P-256',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        if len(credPKey['x']) != 32:
            raise WebAuthnException('Invalid X-coordinate',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        if len(credPKey['y']) != 32:
            raise WebAuthnException('Invalid Y-coordinate',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        return (credPKey, end_offset)
示例#2
0
    def __init__(self, attestation_object, authenticator_data):
        super().__init__(attestation_object, authenticator_data)
        # check u2f data
        att_stmt = self._attestation_object['attStmt']

        if 'alg' in att_stmt and att_stmt[
                'alg'] != self._SHA256_cose_identifier:  # SHA256
            raise WebAuthnException(
                'only SHA256 acceptable but got: ' + att_stmt['alg'],
                ErrCodes.INVALID_DATA)

        if 'sig' not in att_stmt:
            raise WebAuthnException('No signature found',
                                    ErrCodes.INVALID_DATA)

        if 'x5c' not in att_stmt or len(att_stmt['x5c']) < 1:
            raise WebAuthnException('invalid x5c certificate',
                                    ErrCodes.INVALID_DATA)

        self._signature = att_stmt['sig']
        self._x5c = att_stmt['x5c'][0]

        if len(att_stmt['x5c']) > 1:
            for x5c in att_stmt['x5c']:
                self._x5c_chain.append(x5c)
示例#3
0
    def __init__(self, binary):
        super().__init__()
        if not isinstance(binary, bytes) or len(binary) < 37:
            raise WebAuthnException("Invalid authenticatorData input",
                                    ErrCodes.INVALID_DATA)

        self._binary = binary
        # Read infos from binary
        # https://www.w3.org/TR/webauthn/#sec-authenticator-data
        # https://medium.com/@herrjemand/verifying-fido2-responses-4691288c8770
        #RP ID
        self._rpIdHash = binary[0:32]
        #flags (1 byte)
        flags = struct.unpack(">B", binary[32:33])[0]
        self._flags = self._readFlags(flags)
        # signature counter: 4 bytes unsigned big-endian integer.
        self._signCount = struct.unpack(">I", binary[33:37])[0]
        offset = 37
        # https://www.w3.org/TR/webauthn/#sec-attested-credential-data
        self._attestedCredentialData = None
        if self._flags['attestedDataIncluded']:
            self._attestedCredentialData, offset = self._readAttestData(
                binary, offset)

        self._extensionData = None
        if self._flags['extensionDataIncluded']:
            self._extensionData = self._readExtensionData(binary[offset:])
示例#4
0
    def __init__(self,
                 binary,
                 allowFormat=[
                     'none', 'android-key', 'packed', 'fido-u2f',
                     'android-safetynet'
                 ]):
        self.allowedFormats = allowFormat
        self.authenticatorData = {}
        self.attestationFormat = {}
        enc = cbor.loads(binary)
        #validation
        if not isinstance(enc, dict) or 'fmt' not in enc:
            raise WebAuthnException('invalid attestation format',
                                    ErrCodes.INVALID_DATA)

        if 'attStmt' not in enc:
            raise WebAuthnException(
                'invalid attestation format (attStmt not available)',
                ErrCodes.INVALID_DATA)

        if 'authData' not in enc:
            raise WebAuthnException(
                'invalid attestation format (authData not available)',
                ErrCodes.INVALID_DATA)

        self._authenticatorData = AuthenticatorData(enc['authData'])

        if enc['fmt'] not in self.allowedFormats:
            raise WebAuthnException(
                'invalid atttestation format: ' + enc['fmt'],
                ErrCodes.INVALID_DATA)

        if enc['fmt'] == 'none':
            self._attestationFormat = NoneType(enc, self._authenticatorData)
        elif enc['fmt'] == 'fido-u2f':
            self._attestationFormat = U2F(enc, self._authenticatorData)
        elif enc['fmt'] == 'packed':
            self._attestationFormat = Packed(enc, self._authenticatorData)
        elif enc['fmt'] == 'android-key':
            self._attestationFormat = AndroidKey(enc, self._authenticatorData)
        elif enc['fmt'] == 'android-safetynet':
            self._attestationFormat = AndroidSafetyNet(enc,
                                                       self._authenticatorData)
        else:
            raise WebAuthnException(
                'atttestation format: ' + enc['fmt'] + ' not supported',
                ErrCodes.INVALID_DATA)
示例#5
0
 def getPublicKeyU2F(self):
     if self._attestedCredentialData == None:
         raise WebAuthnException(
             "credential id not included in authenticator data",
             ErrCodes.CREDENTIAL_NOT_INCLUDE)
     # ECC uncompressed
     return b"\x04" + self._attestedCredentialData['credentialPublicKey'][
         'x'] + self._attestedCredentialData['credentialPublicKey']['y']
示例#6
0
    def __init__(self, attestation_object, authenticator_data):
        super().__init__(attestation_object, authenticator_data)
        att_stmt = self._attestation_object['attStmt']

        if 'ver' not in att_stmt:
            raise WebAuthnException('invalid Android Safety Net Format ',
                                    ErrCodes.INVALID_DATA)

        if 'response' not in att_stmt:
            raise WebAuthnException('invalid Android Safety Net Format ',
                                    ErrCodes.INVALID_DATA)

        response = att_stmt['response']
        # Response is a JWS [RFC7515] object in Compact Serialization.
        # JWSs have three segments separated by two period ('.') characters

        parts = response.decode('ascii').split('.')
        if len(parts) != 3:
            raise WebAuthnException('invalid JWS data', ErrCodes.INVALID_DATA)

        header = base64.b64decode(parts[0] + '===',
                                  altchars="-_").decode('ascii')
        payload = base64.b64decode(parts[1] + '===',
                                   altchars="-_").decode('ascii')
        self._signature = base64.b64decode(parts[2] + '===', altchars="-_")
        self._signedValue = bytes(parts[0] + '.' + parts[1], 'ascii')

        header = json.loads(header)
        payload = json.loads(payload)
        if 'x5c' not in header:
            raise WebAuthnException('No X.509 signature in JWS Header',
                                    ErrCodes.INVALID_DATA)

        if 'alg' not in header or header['alg'] not in ['RS256', 'ES256']:
            raise WebAuthnException('invalid JWS algorithm '.header['alg'],
                                    ErrCodes.INVALID_DATA)

        self._x5c = base64.b64decode(header['x5c'][0])
        self._payload = payload

        if len(header['x5c']) > 1:
            for x5c in header['x5c']:
                self._x5c_chain.append(base64.b64decode(x5c))
示例#7
0
    def validateAttestation(self, clientDataHash):
        cert = x509.load_pem_x509_certificate(self.getCertificatePem(),
                                              default_backend())
        public_key = cert.public_key()
        if not public_key:
            raise WebAuthnException('invalid public key: ',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        # Verify that the nonce in the response is identical to the Base64 encoding
        # of the SHA-256 hash of the concatenation of authenticatorData and clientDataHash.

        m = hashlib.sha256()
        m.update(self._authenticator_data.getBinary() + clientDataHash)
        hash_data = m.digest()
        if 'nonce' not in self._payload or bytes(
                self._payload['nonce'],
                'ascii') != base64.b64encode(hash_data):
            raise WebAuthnException('invalid nonce in JWS payload',
                                    ErrCodes.INVALID_DATA)

        if 'ctsProfileMatch' not in self._payload or not self._payload[
                'ctsProfileMatch']:
            raise WebAuthnException('invalid ctsProfileMatch in payload',
                                    ErrCodes.INVALID_DATA)

        CN = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
        if len(CN) == 0 or CN[0].value != 'attest.android.com':
            raise WebAuthnException(
                'The common name is not set to "attest.android.com"!',
                ErrCodes.INVALID_DATA)

        try:
            public_key.verify(
                self._signature, self._signedValue,
                padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                            salt_length=padding.PSS.MAX_LENGTH),
                hashes.SHA256())
            return True
        except InvalidSignature:
            return False
        except Exception as e:
            raise e
示例#8
0
文件: u2f.py 项目: cuongnv/PyWebAuthn
    def validateAttestation(self, clientDataHash):
        cert = x509.load_pem_x509_certificate(self.getCertificatePem(), default_backend())
        public_key = cert.public_key()
        if not public_key:
            raise WebAuthnException('invalid public key: ', ErrCodes.INVALID_PUBLIC_KEY)

        verify_data = b"\x00" + self._authenticator_data.getRpIdHash() + clientDataHash \
            + self._authenticator_data.getCredentialId() + self._authenticator_data.getPublicKeyU2F()
        try:
            public_key.verify(self._signature, verify_data, ec.ECDSA(hashes.SHA256()))
            return True
        except InvalidSignature:
            return False
        except Exception as e:
            raise e
示例#9
0
    def validateAttestation(self, clientDataHash):
        cert = x509.load_pem_x509_certificate(self.getCertificatePem(),
                                              default_backend())
        public_key = cert.public_key()
        if not public_key:
            raise WebAuthnException('invalid public key: ',
                                    ErrCodes.INVALID_PUBLIC_KEY)

        verify_data = self._authenticator_data.getBinary() + clientDataHash
        try:
            import base64
            # print(base64.b64encode(self._signature).decode("ascii"))
            print(
                base64.b64encode(
                    self._authenticator_data.getBinary()).decode('ascii'))
            public_key.verify(self._signature, verify_data,
                              ec.ECDSA(hashes.SHA256()))
            return True
        except InvalidSignature:
            return False
        except Exception as e:
            raise e
示例#10
0
    def _readAttestData(self, binary, offset):
        attested_CData = {}
        end_offset = offset + 16
        if (len(binary) <= 55):
            raise WebAuthnException(
                'Attested data should be present but is missing',
                ErrCodes.MISSING_ATTESTED_DATA)

        # The AAGUID of the authenticator (16 bytes)
        attested_CData.update({'aaguid': binary[37:end_offset]})

        end_offset = end_offset + 2
        # Byte length L of Credential ID, 2 bytes unsigned big-endian integer.
        length = struct.unpack('>H', binary[53:end_offset])[0]
        end_offset = end_offset + length
        attested_CData.update({'credentialId': binary[55:end_offset]})
        # extract public key
        public_key, byte_reads = self._readCredentialPublicKey(
            binary, 55 + length)
        attested_CData.update({'credentialPublicKey': public_key})
        end_offset = end_offset + byte_reads
        return (attested_CData, end_offset)
示例#11
0
 def getCredentialId(self):
     if self._attestedCredentialData == None:
         raise WebAuthnException(
             "credential id not included in authenticator data",
             ErrCodes.CREDENTIAL_NOT_INCLUDE)
     return self._attestedCredentialData["credentialId"]