Skip to content

Commit d434110

Browse files
authored
Merge pull request #413 from Yubico/prf-extension
Add prf extension
2 parents 349ebec + 538e565 commit d434110

13 files changed

+934
-7
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* Added overloaded setter `RelyingPartyBuilder.origins(Optional<Set<String>>)`.
44
* Added support for the CTAP2 `credProtect` extension.
5+
* Added support for the `prf` extension.
56
* (Experimental) Added a new suite of interfaces, starting with
67
`CredentialRepositoryV2`. `RelyingParty` can now be configured with a
78
`CredentialRepositoryV2` instance instead of a `CredentialRepository`

webauthn-server-core/src/main/java/com/yubico/webauthn/data/AssertionExtensionInputs.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.yubico.webauthn.StartAssertionOptions;
3232
import com.yubico.webauthn.extension.appid.AppId;
3333
import java.util.HashSet;
34+
import java.util.Map;
3435
import java.util.Optional;
3536
import java.util.Set;
3637
import lombok.Builder;
@@ -55,15 +56,18 @@ public class AssertionExtensionInputs implements ExtensionInputs {
5556

5657
private final AppId appid;
5758
private final Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob;
59+
private final Extensions.Prf.PrfAuthenticationInput prf;
5860
private final Boolean uvm;
5961

6062
@JsonCreator
6163
private AssertionExtensionInputs(
6264
@JsonProperty("appid") AppId appid,
6365
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob,
66+
@JsonProperty("prf") Extensions.Prf.PrfAuthenticationInput prf,
6467
@JsonProperty("uvm") Boolean uvm) {
6568
this.appid = appid;
6669
this.largeBlob = largeBlob;
70+
this.prf = prf;
6771
this.uvm = (uvm != null && uvm) ? true : null;
6872
}
6973

@@ -78,6 +82,7 @@ public AssertionExtensionInputs merge(AssertionExtensionInputs other) {
7882
return new AssertionExtensionInputs(
7983
this.appid != null ? this.appid : other.appid,
8084
this.largeBlob != null ? this.largeBlob : other.largeBlob,
85+
this.prf != null ? this.prf : other.prf,
8186
this.uvm != null ? this.uvm : other.uvm);
8287
}
8388

@@ -95,6 +100,9 @@ public Set<String> getExtensionIds() {
95100
if (largeBlob != null) {
96101
ids.add(Extensions.LargeBlob.EXTENSION_ID);
97102
}
103+
if (prf != null) {
104+
ids.add(Extensions.Prf.EXTENSION_ID);
105+
}
98106
if (getUvm()) {
99107
ids.add(Extensions.Uvm.EXTENSION_ID);
100108
}
@@ -172,6 +180,37 @@ public AssertionExtensionInputsBuilder largeBlob(
172180
return this;
173181
}
174182

183+
/**
184+
* Enable the Pseudo-random function extension (<code>prf</code>).
185+
*
186+
* <p>This extension allows a Relying Party to evaluate outputs from a pseudo-random function
187+
* (PRF) associated with a credential.
188+
*
189+
* <p>Use the {@link com.yubico.webauthn.data.Extensions.Prf.PrfAuthenticationInput} factory
190+
* functions to construct the argument:
191+
*
192+
* <ul>
193+
* <li>Use {@link Extensions.Prf.PrfAuthenticationInput#eval(Extensions.Prf.PrfValues)} to use
194+
* the same PRF input for all credentials.
195+
* <li>Use {@link Extensions.Prf.PrfAuthenticationInput#evalByCredential(Map)} to use
196+
* different PRF inputs for different credentials.
197+
* <li>Use {@link Extensions.Prf.PrfAuthenticationInput#evalByCredentialWithFallback(Map,
198+
* Extensions.Prf.PrfValues)} to use different PRF inputs for different credentials, but
199+
* with a "fallback" input for credentials without their own input.
200+
* </ul>
201+
*
202+
* @see Extensions.Prf.PrfAuthenticationInput#eval(Extensions.Prf.PrfValues)
203+
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredential(Map)
204+
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredentialWithFallback(Map,
205+
* Extensions.Prf.PrfValues)
206+
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
207+
* Pseudo-random function extension (prf)</a>
208+
*/
209+
public AssertionExtensionInputsBuilder prf(Extensions.Prf.PrfAuthenticationInput prf) {
210+
this.prf = prf;
211+
return this;
212+
}
213+
175214
/**
176215
* Enable the User Verification Method Extension (<code>uvm</code>).
177216
*
@@ -233,6 +272,31 @@ private Extensions.LargeBlob.LargeBlobAuthenticationInput getLargeBlobJson() {
233272
: null;
234273
}
235274

275+
/**
276+
* The input to the Pseudo-random function extension (<code>prf</code>), if any.
277+
*
278+
* <p>This extension allows a Relying Party to evaluate outputs from a pseudo-random function
279+
* (PRF) associated with a credential.
280+
*
281+
* @see Extensions.Prf.PrfAuthenticationInput#eval(Extensions.Prf.PrfValues)
282+
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredential(Map)
283+
* @see Extensions.Prf.PrfAuthenticationInput#evalByCredentialWithFallback(Map,
284+
* Extensions.Prf.PrfValues)
285+
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
286+
* Pseudo-random function extension (prf)</a>
287+
*/
288+
public Optional<Extensions.Prf.PrfAuthenticationInput> getPrf() {
289+
return Optional.ofNullable(prf);
290+
}
291+
292+
/** For JSON serialization, to omit false and null values. */
293+
@JsonProperty("prf")
294+
private Extensions.Prf.PrfAuthenticationInput getPrfJson() {
295+
return prf != null && (prf.getEval().isPresent() || prf.getEvalByCredential().isPresent())
296+
? prf
297+
: null;
298+
}
299+
236300
/**
237301
* @return <code>true</code> if the User Verification Method Extension (<code>uvm</code>) is
238302
* enabled, <code>false</code> otherwise.

webauthn-server-core/src/main/java/com/yubico/webauthn/data/AuthenticatorAssertionExtensionOutputs.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
/**
4242
* Contains <a
4343
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator
44-
* extension outputs</a> from a <code>navigator.credentials.create()</code> operation.
44+
* extension outputs</a> from a <code>navigator.credentials.get()</code> operation.
4545
*
4646
* <p>Note that there is no guarantee that any extension input present in {@link
4747
* RegistrationExtensionInputs} will have a corresponding output present here.

webauthn-server-core/src/main/java/com/yubico/webauthn/data/ClientAssertionExtensionOutputs.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,16 @@ public class ClientAssertionExtensionOutputs implements ClientExtensionOutputs {
6666

6767
private final Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob;
6868

69+
private final Extensions.Prf.PrfAuthenticationOutput prf;
70+
6971
@JsonCreator
7072
private ClientAssertionExtensionOutputs(
7173
@JsonProperty("appid") Boolean appid,
72-
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob) {
74+
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob,
75+
@JsonProperty("prf") Extensions.Prf.PrfAuthenticationOutput prf) {
7376
this.appid = appid;
7477
this.largeBlob = largeBlob;
78+
this.prf = prf;
7579
}
7680

7781
@Override
@@ -84,6 +88,9 @@ public Set<String> getExtensionIds() {
8488
if (largeBlob != null) {
8589
ids.add(Extensions.LargeBlob.EXTENSION_ID);
8690
}
91+
if (prf != null) {
92+
ids.add(Extensions.Prf.EXTENSION_ID);
93+
}
8794
return ids;
8895
}
8996

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

124+
/**
125+
* The extension output for the <a
126+
* href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">Pseudo-random function
127+
* (<code>prf</code>) extension</a>, if any.
128+
*
129+
* @see com.yubico.webauthn.data.Extensions.Prf.PrfAuthenticationOutput
130+
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
131+
* Pseudo-random function extension (prf)</a>
132+
*/
133+
public Optional<Extensions.Prf.PrfAuthenticationOutput> getPrf() {
134+
return Optional.ofNullable(prf);
135+
}
136+
117137
public static class ClientAssertionExtensionOutputsBuilder {
118138

119139
/**

webauthn-server-core/src/main/java/com/yubico/webauthn/data/ClientRegistrationExtensionOutputs.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,19 @@ public class ClientRegistrationExtensionOutputs implements ClientExtensionOutput
5858

5959
private final Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob;
6060

61+
private final Extensions.Prf.PrfRegistrationOutput prf;
62+
6163
@JsonCreator
6264
private ClientRegistrationExtensionOutputs(
6365
@JsonProperty("appidExclude") Boolean appidExclude,
6466
@JsonProperty("credProps")
6567
Extensions.CredentialProperties.CredentialPropertiesOutput credProps,
66-
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob) {
68+
@JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob,
69+
@JsonProperty("prf") Extensions.Prf.PrfRegistrationOutput prf) {
6770
this.appidExclude = appidExclude;
6871
this.credProps = credProps;
6972
this.largeBlob = largeBlob;
73+
this.prf = prf;
7074
}
7175

7276
@Override
@@ -82,6 +86,9 @@ public Set<String> getExtensionIds() {
8286
if (largeBlob != null) {
8387
ids.add(Extensions.LargeBlob.EXTENSION_ID);
8488
}
89+
if (prf != null) {
90+
ids.add(Extensions.Prf.EXTENSION_ID);
91+
}
8592
return ids;
8693
}
8794

@@ -127,4 +134,17 @@ public Optional<Extensions.CredentialProperties.CredentialPropertiesOutput> getC
127134
public Optional<Extensions.LargeBlob.LargeBlobRegistrationOutput> getLargeBlob() {
128135
return Optional.ofNullable(largeBlob);
129136
}
137+
138+
/**
139+
* The extension output for the <a
140+
* href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">Pseudo-random function
141+
* (<code>prf</code>) extension</a>, if any.
142+
*
143+
* @see com.yubico.webauthn.data.Extensions.Prf.PrfRegistrationOutput
144+
* @see <a href="https://www.w3.org/TR/2025/WD-webauthn-3-20250127/#prf-extension">§10.1.4.
145+
* Pseudo-random function extension (prf)</a>
146+
*/
147+
public Optional<Extensions.Prf.PrfRegistrationOutput> getPrf() {
148+
return Optional.ofNullable(prf);
149+
}
130150
}

0 commit comments

Comments
 (0)