Skip to content

Add support for credProtect extension #404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
== Version 2.7.0 (unreleased) ==

* Added support for the CTAP2 `credProtect` extension.
* (Experimental) Added a new suite of interfaces, starting with
`CredentialRepositoryV2`. `RelyingParty` can now be configured with a
`CredentialRepositoryV2` instance instead of a `CredentialRepository`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
import com.yubico.webauthn.data.CollectedClientData;
import com.yubico.webauthn.data.Extensions;
import com.yubico.webauthn.data.PublicKeyCredential;
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import com.yubico.webauthn.data.PublicKeyCredentialParameters;
Expand Down Expand Up @@ -337,7 +338,7 @@ public Step16 nextStep() {
}

@Value
class Step16 implements Step<Step18> {
class Step16 implements Step<Step17> {
private final ByteArray clientDataJsonHash;
private final AttestationObject attestation;

Expand Down Expand Up @@ -368,14 +369,28 @@ public void validate() {
}
}

@Override
public Step17 nextStep() {
return new Step17(clientDataJsonHash, attestation);
}
}

@Value
class Step17 implements Step<Step18> {
private final ByteArray clientDataJsonHash;
private final AttestationObject attestation;

@Override
public void validate() {
Extensions.CredentialProtection.validateExtensionOutput(request, response);
}

@Override
public Step18 nextStep() {
return new Step18(clientDataJsonHash, attestation);
}
}

// Nothing to do for step 17

@Value
class Step18 implements Step<Step19> {
private final ByteArray clientDataJsonHash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
import com.yubico.webauthn.data.AuthenticatorResponse;
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs;
import com.yubico.webauthn.data.Extensions;
import com.yubico.webauthn.data.PublicKeyCredential;
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor;
import com.yubico.webauthn.data.RegistrationExtensionInputs;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
Expand Down Expand Up @@ -367,6 +369,40 @@ public Optional<Boolean> isDiscoverable() {
.flatMap(credProps -> credProps.getRk());
}

/**
* Retrieve the <code>credProtect</code> extension policy that was set for the credential, if
* available.
*
* <p>If accessing this, you most likely also want to set the {@link
* RegistrationExtensionInputs.RegistrationExtensionInputsBuilder#credProtect(Extensions.CredentialProtection.CredentialProtectionInput)
* credProtect} extension input in the {@link
* StartRegistrationOptions.StartRegistrationOptionsBuilder#extensions(RegistrationExtensionInputs)
* extensions} parameter of {@link StartRegistrationOptions}.
*
* <p>This output is signed by the authenticator, and thus its trustworthiness may be evaluated
* using <a
* href="https://developers.yubico.com/java-webauthn-server/#using_attestation">authenticator
* attestation</a>.
*
* @return the <code>credProtect</code> extension policy that was set for the credential, if
* available.
* @see
* StartRegistrationOptions.StartRegistrationOptionsBuilder#extensions(RegistrationExtensionInputs)
* @see
* RegistrationExtensionInputs.RegistrationExtensionInputsBuilder#credProtect(Extensions.CredentialProtection.CredentialProtectionInput)
* @see <a
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-credProtect-extension">CTAP2
* §12.1. Credential Protection (credProtect)</a>
* @see <a href="https://developers.yubico.com/java-webauthn-server/#using_attestation">Using
* attestation</a>
*/
@JsonIgnore
public Optional<Extensions.CredentialProtection.CredentialProtectionPolicy>
getCredProtectPolicy() {
return getAuthenticatorExtensionOutputs()
.flatMap(AuthenticatorRegistrationExtensionOutputs::getCredProtect);
}

/**
* The <a
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-trust-path">attestation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,15 @@
public final class AuthenticatorRegistrationExtensionOutputs
implements AuthenticatorExtensionOutputs {

private final Extensions.CredentialProtection.CredentialProtectionPolicy credProtect;
private final List<Extensions.Uvm.UvmEntry> uvm;

@JsonCreator
private AuthenticatorRegistrationExtensionOutputs(
@JsonProperty("credProtect")
Extensions.CredentialProtection.CredentialProtectionPolicy credProtect,
@JsonProperty("uvm") List<Extensions.Uvm.UvmEntry> uvm) {
this.credProtect = credProtect;
this.uvm = uvm == null ? null : CollectionUtil.immutableList(uvm);
}

Expand Down Expand Up @@ -107,6 +111,8 @@ public static Optional<AuthenticatorRegistrationExtensionOutputs> fromAuthentica
static Optional<AuthenticatorRegistrationExtensionOutputs> fromCbor(CBORObject cbor) {
AuthenticatorRegistrationExtensionOutputsBuilder b = builder();

Extensions.CredentialProtection.parseAuthenticatorExtensionOutput(cbor)
.ifPresent(b::credProtect);
Extensions.Uvm.parseAuthenticatorExtensionOutput(cbor).ifPresent(b::uvm);

AuthenticatorRegistrationExtensionOutputs result = b.build();
Expand All @@ -122,12 +128,30 @@ static Optional<AuthenticatorRegistrationExtensionOutputs> fromCbor(CBORObject c
@EqualsAndHashCode.Include
public Set<String> getExtensionIds() {
HashSet<String> ids = new HashSet<>();
if (credProtect != null) {
ids.add(Extensions.CredentialProtection.EXTENSION_ID);
}
if (uvm != null) {
ids.add(Extensions.Uvm.EXTENSION_ID);
}
return ids;
}

/**
* @return The <a
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator
* extension output</a> for the <a
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-credProtect-extension">Credential
* Protection (credProtect) extension</a>, if any. This indicates the credential protection
* policy that was set for the credential.
* @see <a
* href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-credProtect-extension">CTAP2
* §12.1. Credential Protection (credProtect)</a>
*/
public Optional<Extensions.CredentialProtection.CredentialProtectionPolicy> getCredProtect() {
return Optional.ofNullable(credProtect);
}

/**
* @return The <a
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator
Expand Down
Loading