use of com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput in project webauthn4j by webauthn4j.
the class AuthenticationDataValidator method validate.
// as null check is done by BeanAssertUtil#validate
@SuppressWarnings("ConstantConditions")
public void validate(@NonNull AuthenticationData authenticationData, @NonNull AuthenticationParameters authenticationParameters) {
BeanAssertUtil.validate(authenticationData);
AssertUtil.notNull(authenticationParameters, "authenticationParameters must not be null");
// spec| Step1
// spec| Let options be a new PublicKeyCredentialRequestOptions 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.get() 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, or otherwise guide the user experience as might be determinable
// spec| from the context available in the rejected promise. For information on different error contexts and the circumstances leading to them,
// spec| see § 6.3.3 The authenticatorGetAssertion 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 AuthenticatorAssertionResponse, 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().
AuthenticationExtensionsClientOutputs<AuthenticationExtensionClientOutput> clientExtensions = authenticationData.getClientExtensions();
// spec| Step5
// spec| If options.allowCredentials is not empty, verify that credential.id identifies one of the public key credentials listed in options.allowCredentials.
byte[] credentialId = authenticationData.getCredentialId();
List<byte[]> allowCredentials = authenticationParameters.getAllowCredentials();
validateCredentialId(credentialId, allowCredentials);
// spec| Step6
// spec| Identify the user being authenticated and verify that this user is the owner of the public key credential source credentialSource identified by credential.id:
// spec| - If the user was identified before the authentication ceremony was initiated,
// spec| verify that the identified user is the owner of credentialSource. If credential.response.userHandle is present,
// spec| let userHandle be its value. Verify that userHandle also maps to the same user.
// spec| - If the user was not identified before the authentication ceremony was initiated,
// spec| verify that response.userHandle is present, and that the user identified by this value is the owner of credentialSource.
// (This step is out of WebAuthn4J scope. It's caller's responsibility.)
// spec| Step7
// spec| Using credential’s id attribute (or the corresponding rawId, if base64url encoding is inappropriate for your use case),
// spec| look up the corresponding credential public key and let credentialPublicKey be that credential public key.
// (This step is out of WebAuthn4J scope. It's caller's responsibility.)
// spec| Step8
// spec| Let cData, aData and sig denote the value of credential’s response's clientDataJSON, authenticatorData,
// spec| and signature respectively.
byte[] cData = authenticationData.getCollectedClientDataBytes();
byte[] aData = authenticationData.getAuthenticatorDataBytes();
// spec| Step9
// spec| Let JSONtext be the result of running UTF-8 decode on the value of cData.
// (This step is done on caller.)
// spec| Step10
// spec| Let C, the client data claimed as used for the signature, be the result of running an implementation-specific JSON parser on JSONtext.
// (In the spec, claimed as "C", but use "collectedClientData" here)
CollectedClientData collectedClientData = authenticationData.getCollectedClientData();
AuthenticatorData<AuthenticationExtensionAuthenticatorOutput> authenticatorData = authenticationData.getAuthenticatorData();
ServerProperty serverProperty = authenticationParameters.getServerProperty();
BeanAssertUtil.validate(collectedClientData);
BeanAssertUtil.validate(authenticatorData);
validateAuthenticatorData(authenticatorData);
Authenticator authenticator = authenticationParameters.getAuthenticator();
AuthenticationObject authenticationObject = new AuthenticationObject(credentialId, authenticatorData, aData, collectedClientData, cData, clientExtensions, serverProperty, authenticator);
// spec| Verify that the value of C.type is the string webauthn.get.
if (!Objects.equals(collectedClientData.getType(), ClientDataType.WEBAUTHN_GET)) {
throw new InconsistentClientDataTypeException("ClientData.type must be 'get' on authentication, but it isn't.");
}
// spec| Step12
// spec| Verify that the value of C.challenge matches the challenge that was sent to the authenticator in
// spec| the PublicKeyCredentialRequestOptions passed to the get() call.
challengeValidator.validate(collectedClientData, serverProperty);
// spec| Step13
// spec| Verify that the value of C.origin matches the Relying Party's origin.
originValidator.validate(authenticationObject);
// Verify cross origin, which is not defined in the spec
validateClientDataCrossOrigin(collectedClientData);
// spec| Step14
// spec| Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over
// spec| which the attestation was obtained. If Token Binding was used on that TLS connection,
// spec| also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection.
tokenBindingValidator.validate(collectedClientData.getTokenBinding(), serverProperty.getTokenBindingId());
// spec| Step15
// 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| Verify that the User Present bit of the flags in authData is set.
if (authenticationParameters.isUserPresenceRequired() && !authenticatorData.isFlagUP()) {
throw new UserNotPresentException("Validator is configured to check user present, but UP flag in authenticatorData is not set.");
}
// spec| If user verification is required for this assertion, verify that the User Verified bit of the flags in authData is set.
if (authenticationParameters.isUserVerificationRequired() && !authenticatorData.isFlagUV()) {
throw new UserNotVerifiedException("Validator is configured to check user verified, but UV flag in authenticatorData is not set.");
}
// spec| Step18
// spec| Verify that the values of the client extension outputs in clientExtensionResults and the authenticator
// spec| extension outputs in the extensions in authData are as expected, considering the client extension input
// spec| values that were given as the extensions option in the get() call. In particular, any extension identifier
// spec| values in the clientExtensionResults and the extensions in authData MUST be also be present as extension
// spec| identifier values in the extensions member of options, i.e., no extensions are present that were not requested.
// spec| In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use.
AuthenticationExtensionsAuthenticatorOutputs<AuthenticationExtensionAuthenticatorOutput> authenticationExtensionsAuthenticatorOutputs = authenticatorData.getExtensions();
clientExtensionValidator.validate(clientExtensions);
authenticatorExtensionValidator.validate(authenticationExtensionsAuthenticatorOutputs);
// spec| Step19
// spec| Let hash be the result of computing a hash over the cData using SHA-256.
// spec| Step20
// spec| Using the credential public key, validate that sig is a valid signature over
// spec| the binary concatenation of the authenticatorData and the hash of the collectedClientData.
assertionSignatureValidator.validate(authenticationData, authenticator.getAttestedCredentialData().getCOSEKey());
// spec| Step21
// spec| Let storedSignCount be the stored signature counter value associated with credential.id.
// spec| If authData.signCount is nonzero or storedSignCount is nonzero, then run the following sub-step:
long presentedSignCount = authenticatorData.getSignCount();
long storedSignCount = authenticator.getCounter();
if (presentedSignCount > 0 || storedSignCount > 0) {
// spec| greater than storedSignCount:
if (presentedSignCount > storedSignCount) {
// spec| Update storedSignCount to be the value of authData.signCount.
// (caller need to update the signature counter value based on the value set in the Authenticator instance)
authenticator.setCounter(presentedSignCount);
} else // spec| less than or equal to storedSignCount:
// spec| This is a signal that the authenticator may be cloned, i.e. at least two copies of the credential private key may exist and are being used in parallel.
// spec| Relying Parties should incorporate this information into their risk scoring.
// spec| Whether the Relying Party updates storedSignCount in this case, or not, or fails the authentication ceremony or not, is Relying Party-specific.
{
maliciousCounterValueHandler.maliciousCounterValueDetected(authenticationObject);
}
}
for (CustomAuthenticationValidator customAuthenticationValidator : customAuthenticationValidators) {
customAuthenticationValidator.validate(authenticationObject);
}
// spec| Step18
// spec| If all the above steps are successful, continue with the authentication ceremony as appropriate. Otherwise, fail the authentication ceremony.
}
use of com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput in project webauthn4j by webauthn4j.
the class CoreAuthenticationDataValidator method createCoreAuthenticationObject.
@NonNull
protected CoreAuthenticationObject createCoreAuthenticationObject(@NonNull CoreAuthenticationData authenticationData, @NonNull CoreAuthenticationParameters authenticationParameters) {
byte[] credentialId = authenticationData.getCredentialId();
AuthenticatorData<AuthenticationExtensionAuthenticatorOutput> authenticatorData = authenticationData.getAuthenticatorData();
byte[] authenticatorDataBytes = authenticationData.getAuthenticatorDataBytes();
byte[] clientDataHash = authenticationData.getClientDataHash();
CoreServerProperty serverProperty = authenticationParameters.getServerProperty();
CoreAuthenticator authenticator = authenticationParameters.getAuthenticator();
AssertUtil.notNull(authenticatorData, "authenticatorData must not be null");
return new CoreAuthenticationObject(credentialId, authenticatorData, authenticatorDataBytes, clientDataHash, serverProperty, authenticator);
}
use of com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput in project webauthn4j by webauthn4j.
the class CoreAuthenticationDataValidator method validate.
/**
* It is up to caller responsibility to inject challenge into clientData and validate it equals to challenge stored in server side
*
* @param authenticationData authentication data
* @param authenticationParameters authentication parameters
*/
// as null check is done by BeanAssertUtil#validate
@SuppressWarnings("ConstantConditions")
public void validate(@NonNull CoreAuthenticationData authenticationData, @NonNull CoreAuthenticationParameters authenticationParameters) {
BeanAssertUtil.validate(authenticationData);
AssertUtil.notNull(authenticationParameters, "authenticationParameters must not be null");
// spec| Step1
// spec| Let options be a new PublicKeyCredentialRequestOptions 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.get() 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, or otherwise guide the user experience as might be determinable
// spec| from the context available in the rejected promise. For information on different error contexts and the circumstances leading to them,
// spec| see § 6.3.3 The authenticatorGetAssertion 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 AuthenticatorAssertionResponse, 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| If options.allowCredentials is not empty, verify that credential.id identifies one of the public key credentials listed in options.allowCredentials.
byte[] credentialId = authenticationData.getCredentialId();
List<byte[]> allowCredentials = authenticationParameters.getAllowCredentials();
validateCredentialId(credentialId, allowCredentials);
// spec| Step6
// spec| Identify the user being authenticated and verify that this user is the owner of the public key credential source credentialSource identified by credential.id:
// spec| - If the user was identified before the authentication ceremony was initiated,
// spec| verify that the identified user is the owner of credentialSource. If credential.response.userHandle is present,
// spec| let userHandle be its value. Verify that userHandle also maps to the same user.
// spec| - If the user was not identified before the authentication ceremony was initiated,
// spec| verify that response.userHandle is present, and that the user identified by this value is the owner of credentialSource.
// (This step is out of WebAuthn4J scope. It's caller's responsibility.)
// spec| Step7
// spec| Using credential’s id attribute (or the corresponding rawId, if base64url encoding is inappropriate for your use case),
// spec| look up the corresponding credential public key and let credentialPublicKey be that credential public key.
// (This step is out of WebAuthn4J scope. It's caller's responsibility.)
// spec| Step8
// spec| Let cData, aData and sig denote the value of credential’s response's clientDataJSON, authenticatorData,
// spec| and signature respectively.
// (This step is only applicable to WebAuthn)
// spec| Step9
// spec| Let JSONtext be the result of running UTF-8 decode on the value of cData.
// (This step is done on caller.)
// spec| Step10
// spec| Let C, the client data claimed as used for the signature, be the result of running an implementation-specific JSON parser on JSONtext.
// (This step is only applicable to WebAuthn)
AuthenticatorData<AuthenticationExtensionAuthenticatorOutput> authenticatorData = authenticationData.getAuthenticatorData();
CoreServerProperty serverProperty = authenticationParameters.getServerProperty();
BeanAssertUtil.validate(authenticatorData);
validateAuthenticatorData(authenticatorData);
CoreAuthenticator authenticator = authenticationParameters.getAuthenticator();
CoreAuthenticationObject authenticationObject = createCoreAuthenticationObject(authenticationData, authenticationParameters);
// spec| Step11
// spec| Verify that the value of C.type is the string webauthn.get.
// (This step is only applicable to WebAuthn)
// spec| Step12
// spec| Verify that the value of C.challenge matches the challenge that was sent to the authenticator in
// spec| the PublicKeyCredentialRequestOptions passed to the get() call.
// (This step is only applicable to WebAuthn)
// spec| Step13
// spec| Verify that the value of C.origin matches the Relying Party's origin.
// (This step is only applicable to WebAuthn)
// Verify cross origin, which is not defined in the spec
// (This step is only applicable to WebAuthn)
// spec| Step14
// spec| Verify that the value of C.tokenBinding.status matches the state of Token Binding for the TLS connection over
// spec| which the attestation was obtained. If Token Binding was used on that TLS connection,
// spec| also verify that C.tokenBinding.id matches the base64url encoding of the Token Binding ID for the connection.
// (This step is only applicable to WebAuthn)
// spec| Step15
// 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| Verify that the User Present bit of the flags in authData is set.
if (authenticationParameters.isUserPresenceRequired() && !authenticatorData.isFlagUP()) {
throw new UserNotPresentException("Validator is configured to check user present, but UP flag in authenticatorData is not set.");
}
// spec| If user verification is required for this assertion, verify that the User Verified bit of the flags in authData is set.
if (authenticationParameters.isUserVerificationRequired() && !authenticatorData.isFlagUV()) {
throw new UserNotVerifiedException("Validator is configured to check user verified, but UV flag in authenticatorData is not set.");
}
// spec| Step18
// spec| Verify that the values of the client extension outputs in clientExtensionResults and the authenticator
// spec| extension outputs in the extensions in authData are as expected, considering the client extension input
// spec| values that were given as the extensions option in the get() call. In particular, any extension identifier
// spec| values in the clientExtensionResults and the extensions in authData MUST be also be present as extension
// spec| identifier values in the extensions member of options, i.e., no extensions are present that were not requested.
// spec| In the general case, the meaning of "are as expected" is specific to the Relying Party and which extensions are in use.
AuthenticationExtensionsAuthenticatorOutputs<AuthenticationExtensionAuthenticatorOutput> authenticationExtensionsAuthenticatorOutputs = authenticatorData.getExtensions();
// (This clientExtensionResults verification is only applicable to WebAuthn)
authenticatorExtensionValidator.validate(authenticationExtensionsAuthenticatorOutputs);
// spec| Step19
// spec| Let hash be the result of computing a hash over the cData using SHA-256.
// spec| Step20
// spec| Using the credential public key, validate that sig is a valid signature over
// spec| the binary concatenation of the authenticatorData and the hash of the collectedClientData.
assertionSignatureValidator.validate(authenticationData, authenticator.getAttestedCredentialData().getCOSEKey());
// spec| Step21
// spec| Let storedSignCount be the stored signature counter value associated with credential.id.
// spec| If authData.signCount is nonzero or storedSignCount is nonzero, then run the following sub-step:
long presentedSignCount = authenticatorData.getSignCount();
long storedSignCount = authenticator.getCounter();
if (presentedSignCount > 0 || storedSignCount > 0) {
// spec| greater than storedSignCount:
if (presentedSignCount > storedSignCount) {
// spec| Update storedSignCount to be the value of authData.signCount.
// (caller need to update the signature counter value based on the value set in the Authenticator instance)
authenticator.setCounter(presentedSignCount);
} else // spec| less than or equal to storedSignCount:
// spec| This is a signal that the authenticator may be cloned, i.e. at least two copies of the credential private key may exist and are being used in parallel.
// spec| Relying Parties should incorporate this information into their risk scoring.
// spec| Whether the Relying Party updates storedSignCount in this case, or not, or fails the authentication ceremony or not, is Relying Party-specific.
{
coreMaliciousCounterValueHandler.maliciousCounterValueDetected(authenticationObject);
}
}
for (CustomCoreAuthenticationValidator customAuthenticationValidator : customAuthenticationValidators) {
customAuthenticationValidator.validate(authenticationObject);
}
// spec| Step18
// spec| If all the above steps are successful, continue with the authentication ceremony as appropriate. Otherwise, fail the authentication ceremony.
}
use of com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput in project webauthn4j by webauthn4j.
the class DeviceCheckAssertionManager method parse.
@SuppressWarnings("squid:S1130")
@NonNull
public DCAssertionData parse(@NonNull DCAssertionRequest dcAssertionRequest) throws DataConversionException {
AssertUtil.notNull(dcAssertionRequest, "dcAssertionRequest must not be null");
byte[] credentialId = dcAssertionRequest.getKeyId();
DCAssertion assertion = cborConverter.readValue(dcAssertionRequest.getAssertion(), DCAssertion.class);
byte[] authenticatorDataBytes = assertion == null ? null : assertion.getAuthenticatorData();
AuthenticatorData<AuthenticationExtensionAuthenticatorOutput> authenticatorData = authenticatorDataBytes == null ? null : authenticatorDataConverter.convert(authenticatorDataBytes);
byte[] clientDataHash = dcAssertionRequest.getClientDataHash();
byte[] signature = assertion == null ? null : assertion.getSignature();
return new DCAssertionData(credentialId, authenticatorData, authenticatorDataBytes, clientDataHash, signature);
}
use of com.webauthn4j.data.extension.authenticator.AuthenticationExtensionAuthenticatorOutput in project webauthn4j by webauthn4j.
the class FIDOAuthenticatorCoreAuthenticationValidationTest method validate_test.
@Test
void validate_test() {
String rpId = "example.com";
long timeout = 0;
Challenge challenge = new DefaultChallenge();
// create
AttestationObject attestationObject = createAttestationObject(rpId, challenge);
// get
PublicKeyCredentialRequestOptions credentialRequestOptions = new PublicKeyCredentialRequestOptions(challenge, timeout, rpId, null, UserVerificationRequirement.REQUIRED, null);
PublicKeyCredential<AuthenticatorAssertionResponse, AuthenticationExtensionClientOutput> credential = clientPlatform.get(credentialRequestOptions);
AuthenticatorAssertionResponse authenticationRequest = credential.getAuthenticatorResponse();
ServerProperty serverProperty = new ServerProperty(origin, rpId, challenge, null);
Authenticator authenticator = TestDataUtil.createAuthenticator(attestationObject);
AuthenticatorData<AuthenticationExtensionAuthenticatorOutput> authenticationData = authenticatorDataConverter.convert(authenticationRequest.getAuthenticatorData());
CoreAuthenticationData coreAuthenticationData = new CoreAuthenticationData(credential.getRawId(), authenticationData, authenticationRequest.getAuthenticatorData(), MessageDigestUtil.createSHA256().digest(authenticationRequest.getClientDataJSON()), authenticationRequest.getSignature());
AuthenticationParameters authenticationParameters = new AuthenticationParameters(serverProperty, authenticator, null, true);
target.validate(coreAuthenticationData, authenticationParameters);
}
Aggregations