Skip to content

Add prf extension #413

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 12 commits into from
Apr 4, 2025
Merged
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* Added overloaded setter `RelyingPartyBuilder.origins(Optional<Set<String>>)`.
* Added support for the CTAP2 `credProtect` extension.
* Added support for the `prf` 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 @@ -31,6 +31,7 @@
import com.yubico.webauthn.StartAssertionOptions;
import com.yubico.webauthn.extension.appid.AppId;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import lombok.Builder;
Expand All @@ -55,15 +56,18 @@ public class AssertionExtensionInputs implements ExtensionInputs {

private final AppId appid;
private final Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob;
private final Extensions.Prf.PrfAuthenticationInput prf;
private final Boolean uvm;

@JsonCreator
private AssertionExtensionInputs(
@JsonProperty("appid") AppId appid,
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob,
@JsonProperty("prf") Extensions.Prf.PrfAuthenticationInput prf,
@JsonProperty("uvm") Boolean uvm) {
this.appid = appid;
this.largeBlob = largeBlob;
this.prf = prf;
this.uvm = (uvm != null && uvm) ? true : null;
}

Expand All @@ -78,6 +82,7 @@ public AssertionExtensionInputs merge(AssertionExtensionInputs other) {
return new AssertionExtensionInputs(
this.appid != null ? this.appid : other.appid,
this.largeBlob != null ? this.largeBlob : other.largeBlob,
this.prf != null ? this.prf : other.prf,
this.uvm != null ? this.uvm : other.uvm);
}

Expand All @@ -95,6 +100,9 @@ public Set<String> getExtensionIds() {
if (largeBlob != null) {
ids.add(Extensions.LargeBlob.EXTENSION_ID);
}
if (prf != null) {
ids.add(Extensions.Prf.EXTENSION_ID);
}
if (getUvm()) {
ids.add(Extensions.Uvm.EXTENSION_ID);
}
Expand Down Expand Up @@ -172,6 +180,37 @@ public AssertionExtensionInputsBuilder largeBlob(
return this;
}

/**
* Enable the Pseudo-random function extension (<code>prf</code>).
*
* <p>This extension allows a Relying Party to evaluate outputs from a pseudo-random function
* (PRF) associated with a credential.
*
* <p>Use the {@link com.yubico.webauthn.data.Extensions.Prf.PrfAuthenticationInput} factory
* functions to construct the argument:
*
* <ul>
* <li>Use {@link Extensions.Prf.PrfAuthenticationInput#eval(Extensions.Prf.PrfValues)} to use
* the same PRF input for all credentials.
* <li>Use {@link Extensions.Prf.PrfAuthenticationInput#evalByCredential(Map)} to use
* different PRF inputs for different credentials.
* <li>Use {@link Extensions.Prf.PrfAuthenticationInput#evalByCredentialWithFallback(Map,
* Extensions.Prf.PrfValues)} to use different PRF inputs for different credentials, but
* with a "fallback" input for credentials without their own input.
* </ul>
*
* @see Extensions.Prf.PrfAuthenticationInput#eval(Extensions.Prf.PrfValues)
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredential(Map)
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredentialWithFallback(Map,
* Extensions.Prf.PrfValues)
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
* Pseudo-random function extension (prf)</a>
*/
public AssertionExtensionInputsBuilder prf(Extensions.Prf.PrfAuthenticationInput prf) {
this.prf = prf;
return this;
}

/**
* Enable the User Verification Method Extension (<code>uvm</code>).
*
Expand Down Expand Up @@ -233,6 +272,31 @@ private Extensions.LargeBlob.LargeBlobAuthenticationInput getLargeBlobJson() {
: null;
}

/**
* The input to the Pseudo-random function extension (<code>prf</code>), if any.
*
* <p>This extension allows a Relying Party to evaluate outputs from a pseudo-random function
* (PRF) associated with a credential.
*
* @see Extensions.Prf.PrfAuthenticationInput#eval(Extensions.Prf.PrfValues)
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredential(Map)
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredentialWithFallback(Map,
* Extensions.Prf.PrfValues)
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
* Pseudo-random function extension (prf)</a>
*/
public Optional<Extensions.Prf.PrfAuthenticationInput> getPrf() {
return Optional.ofNullable(prf);
}

/** For JSON serialization, to omit false and null values. */
@JsonProperty("prf")
private Extensions.Prf.PrfAuthenticationInput getPrfJson() {
return prf != null && (prf.getEval().isPresent() || prf.getEvalByCredential().isPresent())
? prf
: null;
}

/**
* @return <code>true</code> if the User Verification Method Extension (<code>uvm</code>) is
* enabled, <code>false</code> otherwise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/**
* Contains <a
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator
* extension outputs</a> from a <code>navigator.credentials.create()</code> operation.
* extension outputs</a> from a <code>navigator.credentials.get()</code> operation.
*
* <p>Note that there is no guarantee that any extension input present in {@link
* RegistrationExtensionInputs} will have a corresponding output present here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,16 @@ public class ClientAssertionExtensionOutputs implements ClientExtensionOutputs {

private final Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob;

private final Extensions.Prf.PrfAuthenticationOutput prf;

@JsonCreator
private ClientAssertionExtensionOutputs(
@JsonProperty("appid") Boolean appid,
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob) {
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob,
@JsonProperty("prf") Extensions.Prf.PrfAuthenticationOutput prf) {
this.appid = appid;
this.largeBlob = largeBlob;
this.prf = prf;
}

@Override
Expand All @@ -84,6 +88,9 @@ public Set<String> getExtensionIds() {
if (largeBlob != null) {
ids.add(Extensions.LargeBlob.EXTENSION_ID);
}
if (prf != null) {
ids.add(Extensions.Prf.EXTENSION_ID);
}
return ids;
}

Expand All @@ -105,7 +112,7 @@ public Optional<Boolean> getAppid() {
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">Large blob
* storage (<code>largeBlob</code>) extension</a>, if any.
*
* @see com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobRegistrationOutput
* @see com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobAuthenticationOutput
* @see <a
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5.Large
* blob storage extension (largeBlob)</a>
Expand All @@ -114,6 +121,19 @@ public Optional<Extensions.LargeBlob.LargeBlobAuthenticationOutput> getLargeBlob
return Optional.ofNullable(largeBlob);
}

/**
* The extension output for the <a
* href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">Pseudo-random function
* (<code>prf</code>) extension</a>, if any.
*
* @see com.yubico.webauthn.data.Extensions.Prf.PrfAuthenticationOutput
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
* Pseudo-random function extension (prf)</a>
*/
public Optional<Extensions.Prf.PrfAuthenticationOutput> getPrf() {
return Optional.ofNullable(prf);
}

public static class ClientAssertionExtensionOutputsBuilder {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,19 @@ public class ClientRegistrationExtensionOutputs implements ClientExtensionOutput

private final Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob;

private final Extensions.Prf.PrfRegistrationOutput prf;

@JsonCreator
private ClientRegistrationExtensionOutputs(
@JsonProperty("appidExclude") Boolean appidExclude,
@JsonProperty("credProps")
Extensions.CredentialProperties.CredentialPropertiesOutput credProps,
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob) {
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob,
@JsonProperty("prf") Extensions.Prf.PrfRegistrationOutput prf) {
this.appidExclude = appidExclude;
this.credProps = credProps;
this.largeBlob = largeBlob;
this.prf = prf;
}

@Override
Expand All @@ -82,6 +86,9 @@ public Set<String> getExtensionIds() {
if (largeBlob != null) {
ids.add(Extensions.LargeBlob.EXTENSION_ID);
}
if (prf != null) {
ids.add(Extensions.Prf.EXTENSION_ID);
}
return ids;
}

Expand Down Expand Up @@ -127,4 +134,17 @@ public Optional<Extensions.CredentialProperties.CredentialPropertiesOutput> getC
public Optional<Extensions.LargeBlob.LargeBlobRegistrationOutput> getLargeBlob() {
return Optional.ofNullable(largeBlob);
}

/**
* The extension output for the <a
* href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">Pseudo-random function
* (<code>prf</code>) extension</a>, if any.
*
* @see com.yubico.webauthn.data.Extensions.Prf.PrfRegistrationOutput
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
* Pseudo-random function extension (prf)</a>
*/
public Optional<Extensions.Prf.PrfRegistrationOutput> getPrf() {
return Optional.ofNullable(prf);
}
}
Loading