Search in sources :

Example 1 with COSEAlgorithmIdentifier

use of com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier in project webauthn4j by webauthn4j.

the class PackedAttestationStatementValidator method validateSelfAttestation.

@SuppressWarnings("SameReturnValue")
@NonNull
private AttestationType validateSelfAttestation(@NonNull CoreRegistrationObject registrationObject, @NonNull byte[] sig, @NonNull COSEAlgorithmIdentifier alg, @NonNull byte[] attrToBeSigned) {
    // noinspection ConstantConditions as null check is already done in caller
    COSEKey coseKey = registrationObject.getAttestationObject().getAuthenticatorData().getAttestedCredentialData().getCOSEKey();
    // Validate that alg matches the algorithm of the coseKey in authenticatorData.
    COSEAlgorithmIdentifier credentialPublicKeyAlgorithm = coseKey.getAlgorithm();
    if (!alg.equals(credentialPublicKeyAlgorithm)) {
        throw new BadAlgorithmException("`alg` in attestation statement doesn't match the algorithm of the coseKey in authenticatorData.");
    }
    // noinspection ConstantConditions as null check is already done in caller
    if (!verifySignature(coseKey.getPublicKey(), alg, sig, attrToBeSigned)) {
        throw new BadSignatureException("`sig` in attestation statement is not valid signature over the concatenation of authenticatorData and clientDataHash.");
    }
    // If successful, return attestation type Self and empty attestation trust path.
    return AttestationType.SELF;
}
Also used : BadSignatureException(com.webauthn4j.validator.exception.BadSignatureException) BadAlgorithmException(com.webauthn4j.validator.exception.BadAlgorithmException) COSEKey(com.webauthn4j.data.attestation.authenticator.COSEKey) COSEAlgorithmIdentifier(com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier) NonNull(org.checkerframework.checker.nullness.qual.NonNull)

Example 2 with COSEAlgorithmIdentifier

use of com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier in project webauthn4j by webauthn4j.

the class PackedAttestationStatementValidator method validate.

@Override
@NonNull
public AttestationType validate(@NonNull CoreRegistrationObject registrationObject) {
    AssertUtil.notNull(registrationObject, "registrationObject must not be null");
    if (!supports(registrationObject)) {
        throw new IllegalArgumentException("Specified format is not supported by " + this.getClass().getName());
    }
    PackedAttestationStatement attestationStatement = (PackedAttestationStatement) registrationObject.getAttestationObject().getAttestationStatement();
    validateAttestationStatementNotNull(attestationStatement);
    byte[] sig = attestationStatement.getSig();
    COSEAlgorithmIdentifier alg = attestationStatement.getAlg();
    byte[] attrToBeSigned = getAttToBeSigned(registrationObject);
    // If x5c is present,
    if (attestationStatement.getX5c() != null) {
        return validateX5c(registrationObject, attestationStatement, sig, alg, attrToBeSigned);
    } else // If x5c is not present, self attestation is in use.
    {
        return validateSelfAttestation(registrationObject, sig, alg, attrToBeSigned);
    }
}
Also used : PackedAttestationStatement(com.webauthn4j.data.attestation.statement.PackedAttestationStatement) COSEAlgorithmIdentifier(com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier) NonNull(org.checkerframework.checker.nullness.qual.NonNull)

Example 3 with COSEAlgorithmIdentifier

use of com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier in project webauthn4j by webauthn4j.

the class CoreRegistrationDataValidator method validate.

/**
 * It is up to caller responsibility to inject challenge into clientData and validate it equals to challenge stored in server side
 *
 * @param registrationData       registration data
 * @param registrationParameters registration parameters
 */
// as null check is done by BeanAssertUtil#validate
@SuppressWarnings("ConstantConditions")
public void validate(@NonNull CoreRegistrationData registrationData, @NonNull CoreRegistrationParameters registrationParameters) {
    // spec| Step1
    // spec| Let options be a new PublicKeyCredentialCreationOptions structure configured to the Relying Party's needs for the ceremony.
    // (This step is done on client slide and out of WebAuthn4J responsibility.)
    // spec| Step2
    // spec| Call navigator.credentials.create() and pass options as the publicKey option. Let credential be the result of the successfully resolved promise.
    // spec| If the promise is rejected, abort the ceremony with a user-visible error,
    // spec| or otherwise guide the user experience as might be determinable from the context available in the rejected promise.
    // spec| For example if the promise is rejected with an error code equivalent to "InvalidStateError",
    // spec| the user might be instructed to use a different authenticator.
    // spec| For information on different error contexts and the circumstances leading to them, see § 6.3.2 The authenticatorMakeCredential Operation.
    // (This step is done on client slide and out of WebAuthn4J responsibility.)
    // spec| Step3
    // spec| Let response be credential.response. If response is not an instance of AuthenticatorAttestationResponse, abort the ceremony with a user-visible error.
    // (This step is done on client slide and out of WebAuthn4J responsibility.)
    // spec| Step4
    // spec| Let clientExtensionResults be the result of calling credential.getClientExtensionResults().
    // (This step is only applicable to WebAuthn)
    // spec| Step5
    // spec| Let JSONtext be the result of running UTF-8 decode on the value of response.clientDataJSON.
    // (This step is only applicable to WebAuthn)
    BeanAssertUtil.validate(registrationData);
    AssertUtil.notNull(registrationParameters, "registrationParameters must not be null");
    // spec| Step6
    // spec| Let C, the client data claimed as collected during the credential creation,
    // spec| be the result of running an implementation-specific JSON parser on JSONtext.
    // (This step is only applicable to WebAuthn)
    AttestationObject attestationObject = registrationData.getAttestationObject();
    validateAuthenticatorDataField(attestationObject.getAuthenticatorData());
    CoreServerProperty serverProperty = registrationParameters.getServerProperty();
    CoreRegistrationObject registrationObject = createCoreRegistrationObject(registrationData, registrationParameters);
    AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authenticatorData = attestationObject.getAuthenticatorData();
    COSEKey coseKey = authenticatorData.getAttestedCredentialData().getCOSEKey();
    validateCOSEKey(coseKey);
    // spec| Step7
    // spec| Verify that the value of C.type is webauthn.create.
    // (This step is only applicable to WebAuthn)
    // spec| Step8
    // spec| Verify that the value of C.challenge equals the base64url encoding of options.challenge.
    // (This step is only applicable to WebAuthn)
    // spec| Step9
    // spec| Verify that the value of C.origin matches the Relying Party's origin.
    // (This step is only applicable to WebAuthn)
    // spec| Step10
    // spec| Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over
    // spec| which the assertion was obtained. If Token Binding was used on that TLS connection, also verify that
    // spec| C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection.
    // (This step is only applicable to WebAuthn)
    // spec| Step11
    // spec| Let hash be the result of computing a hash over response.clientDataJSON using SHA-256.
    // spec| Step12
    // spec| Perform CBOR decoding on the attestationObject field of the AuthenticatorAttestationResponse structure to
    // spec| obtain the attestation statement format fmt, the authenticator data authData, and the attestation statement attStmt.
    // (This step is done on caller.)
    // spec| Step13
    // spec| Verify that the rpIdHash in authData is the SHA-256 hash of the RP ID expected by the Relying Party.
    rpIdHashValidator.validate(authenticatorData.getRpIdHash(), serverProperty);
    // spec| Step14, 15
    // spec| Verify that the User Present bit of the flags in authData is set.
    // spec| If user verification is required for this registration, verify that the User Verified bit of the flags in authData is set.
    validateUVUPFlags(authenticatorData, registrationParameters.isUserVerificationRequired(), registrationParameters.isUserPresenceRequired());
    // spec| Step16
    // spec| Verify that the "alg" parameter in the credential public key in authData matches the alg attribute of one of the items in options.pubKeyCredParams.
    COSEAlgorithmIdentifier alg = authenticatorData.getAttestedCredentialData().getCOSEKey().getAlgorithm();
    List<PublicKeyCredentialParameters> pubKeyCredParams = registrationParameters.getPubKeyCredParams();
    validateAlg(alg, pubKeyCredParams);
    // spec| Step17
    // spec| Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected,
    // spec| considering the client extension input values that were given in options.extensions and any specific policy of the Relying Party regarding unsolicited extensions,
    // spec| i.e., those that were not specified as part of options.extensions.
    // spec| In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use.
    AuthenticationExtensionsAuthenticatorOutputs<RegistrationExtensionAuthenticatorOutput> authenticationExtensionsAuthenticatorOutputs = authenticatorData.getExtensions();
    authenticatorExtensionValidator.validate(authenticationExtensionsAuthenticatorOutputs);
    // spec| Step18-21
    attestationValidator.validate(registrationObject);
    // validate with custom logic
    for (CustomCoreRegistrationValidator customRegistrationValidator : customRegistrationValidators) {
        customRegistrationValidator.validate(registrationObject);
    }
}
Also used : CoreServerProperty(com.webauthn4j.server.CoreServerProperty) COSEKey(com.webauthn4j.data.attestation.authenticator.COSEKey) PublicKeyCredentialParameters(com.webauthn4j.data.PublicKeyCredentialParameters) COSEAlgorithmIdentifier(com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier) AttestationObject(com.webauthn4j.data.attestation.AttestationObject) RegistrationExtensionAuthenticatorOutput(com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput)

Example 4 with COSEAlgorithmIdentifier

use of com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier in project webauthn4j by webauthn4j.

the class RegistrationDataValidator method validate.

// as null check is done by BeanAssertUtil#validate
@SuppressWarnings("ConstantConditions")
public void validate(@NonNull RegistrationData registrationData, @NonNull RegistrationParameters registrationParameters) {
    // spec| Step1
    // spec| Let options be a new PublicKeyCredentialCreationOptions structure configured to the Relying Party's needs for the ceremony.
    // (This step is done on client slide and out of WebAuthn4J responsibility.)
    // spec| Step2
    // spec| Call navigator.credentials.create() and pass options as the publicKey option. Let credential be the result of the successfully resolved promise.
    // spec| If the promise is rejected, abort the ceremony with a user-visible error,
    // spec| or otherwise guide the user experience as might be determinable from the context available in the rejected promise.
    // spec| For example if the promise is rejected with an error code equivalent to "InvalidStateError",
    // spec| the user might be instructed to use a different authenticator.
    // spec| For information on different error contexts and the circumstances leading to them, see § 6.3.2 The authenticatorMakeCredential Operation.
    // (This step is done on client slide and out of WebAuthn4J responsibility.)
    // spec| Step3
    // spec| Let response be credential.response. If response is not an instance of AuthenticatorAttestationResponse, abort the ceremony with a user-visible error.
    // (This step is done on client slide and out of WebAuthn4J responsibility.)
    // spec| Step4
    // spec| Let clientExtensionResults be the result of calling credential.getClientExtensionResults().
    // spec| Step5
    // spec| Let JSONtext be the result of running UTF-8 decode on the value of response.clientDataJSON.
    // (This step is done on caller.)
    BeanAssertUtil.validate(registrationData);
    AssertUtil.notNull(registrationParameters, "registrationParameters must not be null");
    byte[] clientDataBytes = registrationData.getCollectedClientDataBytes();
    byte[] attestationObjectBytes = registrationData.getAttestationObjectBytes();
    // spec| Step6
    // spec| Let C, the client data claimed as collected during the credential creation,
    // spec| be the result of running an implementation-specific JSON parser on JSONtext.
    CollectedClientData collectedClientData = registrationData.getCollectedClientData();
    AttestationObject attestationObject = registrationData.getAttestationObject();
    Set<AuthenticatorTransport> transports = registrationData.getTransports();
    AuthenticationExtensionsClientOutputs<RegistrationExtensionClientOutput> clientExtensions = registrationData.getClientExtensions();
    validateAuthenticatorDataField(attestationObject.getAuthenticatorData());
    ServerProperty serverProperty = registrationParameters.getServerProperty();
    RegistrationObject registrationObject = new RegistrationObject(attestationObject, attestationObjectBytes, collectedClientData, clientDataBytes, clientExtensions, transports, serverProperty);
    AuthenticatorData<RegistrationExtensionAuthenticatorOutput> authenticatorData = attestationObject.getAuthenticatorData();
    // spec| Verify that the value of C.type is webauthn.create.
    if (!Objects.equals(collectedClientData.getType(), ClientDataType.WEBAUTHN_CREATE)) {
        throw new InconsistentClientDataTypeException("ClientData.type must be 'create' on registration, but it isn't.");
    }
    // spec| Step8
    // spec| Verify that the value of C.challenge equals the base64url encoding of options.challenge.
    challengeValidator.validate(collectedClientData, serverProperty);
    // spec| Step9
    // spec| Verify that the value of C.origin matches the Relying Party's origin.
    originValidator.validate(registrationObject);
    // spec| Step10
    // spec| Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over
    // spec| which the assertion was obtained. If Token Binding was used on that TLS connection, also verify that
    // spec| C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection.
    tokenBindingValidator.validate(collectedClientData.getTokenBinding(), serverProperty.getTokenBindingId());
    // spec| Step11
    // spec| Let hash be the result of computing a hash over response.clientDataJSON using SHA-256.
    // spec| Step12
    // spec| Perform CBOR decoding on the attestationObject field of the AuthenticatorAttestationResponse structure to
    // spec| obtain the attestation statement format fmt, the authenticator data authData, and the attestation statement attStmt.
    // (This step is done on caller.)
    // spec| Step13
    // spec| Verify that the rpIdHash in authData is the SHA-256 hash of the RP ID expected by the Relying Party.
    rpIdHashValidator.validate(authenticatorData.getRpIdHash(), serverProperty);
    // spec| Step14, 15
    // spec| Verify that the User Present bit of the flags in authData is set.
    // spec| If user verification is required for this registration, verify that the User Verified bit of the flags in authData is set.
    validateUVUPFlags(authenticatorData, registrationParameters.isUserVerificationRequired(), registrationParameters.isUserPresenceRequired());
    // spec| Step16
    // spec| Verify that the "alg" parameter in the credential public key in authData matches the alg attribute of one of the items in options.pubKeyCredParams.
    COSEAlgorithmIdentifier alg = authenticatorData.getAttestedCredentialData().getCOSEKey().getAlgorithm();
    List<PublicKeyCredentialParameters> pubKeyCredParams = registrationParameters.getPubKeyCredParams();
    validateAlg(alg, pubKeyCredParams);
    // spec| Step17
    // spec| Verify that the values of the client extension outputs in clientExtensionResults and the authenticator extension outputs in the extensions in authData are as expected,
    // spec| considering the client extension input values that were given in options.extensions and any specific policy of the Relying Party regarding unsolicited extensions,
    // spec| i.e., those that were not specified as part of options.extensions.
    // spec| In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use.
    AuthenticationExtensionsAuthenticatorOutputs<RegistrationExtensionAuthenticatorOutput> authenticationExtensionsAuthenticatorOutputs = authenticatorData.getExtensions();
    clientExtensionValidator.validate(clientExtensions);
    authenticatorExtensionValidator.validate(authenticationExtensionsAuthenticatorOutputs);
    // spec| Step18-21
    attestationValidator.validate(registrationObject);
    // validate with custom logic
    for (CustomRegistrationValidator customRegistrationValidator : customRegistrationValidators) {
        customRegistrationValidator.validate(registrationObject);
    }
}
Also used : ServerProperty(com.webauthn4j.server.ServerProperty) RegistrationExtensionAuthenticatorOutput(com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput) RegistrationExtensionClientOutput(com.webauthn4j.data.extension.client.RegistrationExtensionClientOutput) CollectedClientData(com.webauthn4j.data.client.CollectedClientData) PublicKeyCredentialParameters(com.webauthn4j.data.PublicKeyCredentialParameters) COSEAlgorithmIdentifier(com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier) AttestationObject(com.webauthn4j.data.attestation.AttestationObject) AuthenticatorTransport(com.webauthn4j.data.AuthenticatorTransport)

Example 5 with COSEAlgorithmIdentifier

use of com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier in project webauthn4j by webauthn4j.

the class TestDataUtil method createEC2COSEKey.

private static EC2COSEKey createEC2COSEKey(ECPublicKey publicKey, BigInteger d) {
    ECPoint ecPoint = publicKey.getW();
    EllipticCurve ellipticCurve = publicKey.getParams().getCurve();
    Curve curve;
    COSEAlgorithmIdentifier coseAlgorithmIdentifier;
    switch(ellipticCurve.getField().getFieldSize()) {
        case 256:
            curve = Curve.SECP256R1;
            coseAlgorithmIdentifier = COSEAlgorithmIdentifier.ES256;
            break;
        case 384:
            curve = Curve.SECP384R1;
            coseAlgorithmIdentifier = COSEAlgorithmIdentifier.ES384;
            break;
        case 521:
            curve = Curve.SECP521R1;
            coseAlgorithmIdentifier = COSEAlgorithmIdentifier.ES512;
            break;
        default:
            throw new IllegalStateException("We can only support secp256r1, secp384r1 or secp521r1");
    }
    byte[] x = ECUtil.convertToFixedByteArray(curve.getSize(), ecPoint.getAffineX());
    byte[] y = ECUtil.convertToFixedByteArray(curve.getSize(), ecPoint.getAffineY());
    return new EC2COSEKey(null, coseAlgorithmIdentifier, null, curve, x, y, d == null ? null : d.toByteArray());
}
Also used : EllipticCurve(java.security.spec.EllipticCurve) COSEAlgorithmIdentifier(com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier) EllipticCurve(java.security.spec.EllipticCurve) ECPoint(java.security.spec.ECPoint)

Aggregations

COSEAlgorithmIdentifier (com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier)6 PublicKeyCredentialParameters (com.webauthn4j.data.PublicKeyCredentialParameters)2 AttestationObject (com.webauthn4j.data.attestation.AttestationObject)2 COSEKey (com.webauthn4j.data.attestation.authenticator.COSEKey)2 RegistrationExtensionAuthenticatorOutput (com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput)2 NonNull (org.checkerframework.checker.nullness.qual.NonNull)2 AuthenticatorTransport (com.webauthn4j.data.AuthenticatorTransport)1 PackedAttestationStatement (com.webauthn4j.data.attestation.statement.PackedAttestationStatement)1 CollectedClientData (com.webauthn4j.data.client.CollectedClientData)1 RegistrationExtensionClientOutput (com.webauthn4j.data.extension.client.RegistrationExtensionClientOutput)1 CoreServerProperty (com.webauthn4j.server.CoreServerProperty)1 ServerProperty (com.webauthn4j.server.ServerProperty)1 BadAlgorithmException (com.webauthn4j.validator.exception.BadAlgorithmException)1 BadSignatureException (com.webauthn4j.validator.exception.BadSignatureException)1 ECPoint (java.security.spec.ECPoint)1 EllipticCurve (java.security.spec.EllipticCurve)1 Test (org.junit.jupiter.api.Test)1