RelyingPartyV2.java

1
// Copyright (c) 2018, Yubico AB
2
// All rights reserved.
3
//
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are met:
6
//
7
// 1. Redistributions of source code must retain the above copyright notice, this
8
//    list of conditions and the following disclaimer.
9
//
10
// 2. Redistributions in binary form must reproduce the above copyright notice,
11
//    this list of conditions and the following disclaimer in the documentation
12
//    and/or other materials provided with the distribution.
13
//
14
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25
package com.yubico.webauthn;
26
27
import com.yubico.internal.util.CollectionUtil;
28
import com.yubico.internal.util.OptionalUtil;
29
import com.yubico.webauthn.attestation.AttestationTrustSource;
30
import com.yubico.webauthn.data.AssertionExtensionInputs;
31
import com.yubico.webauthn.data.AttestationConveyancePreference;
32
import com.yubico.webauthn.data.AuthenticatorData;
33
import com.yubico.webauthn.data.ByteArray;
34
import com.yubico.webauthn.data.CollectedClientData;
35
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
36
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions.PublicKeyCredentialCreationOptionsBuilder;
37
import com.yubico.webauthn.data.PublicKeyCredentialParameters;
38
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
39
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions.PublicKeyCredentialRequestOptionsBuilder;
40
import com.yubico.webauthn.data.RegistrationExtensionInputs;
41
import com.yubico.webauthn.data.RelyingPartyIdentity;
42
import com.yubico.webauthn.exception.AssertionFailedException;
43
import com.yubico.webauthn.exception.InvalidSignatureCountException;
44
import com.yubico.webauthn.exception.RegistrationFailedException;
45
import com.yubico.webauthn.extension.appid.AppId;
46
import java.net.MalformedURLException;
47
import java.net.URL;
48
import java.security.KeyFactory;
49
import java.security.SecureRandom;
50
import java.security.Signature;
51
import java.time.Clock;
52
import java.util.Arrays;
53
import java.util.Collections;
54
import java.util.List;
55
import java.util.Optional;
56
import java.util.Set;
57
import java.util.stream.Collectors;
58
import lombok.Builder;
59
import lombok.NonNull;
60
import lombok.Value;
61
import lombok.extern.slf4j.Slf4j;
62
63
/**
64
 * Encapsulates the four basic Web Authentication operations - start/finish registration,
65
 * start/finish authentication - along with overall operational settings for them.
66
 *
67
 * <p>This class has no mutable state. An instance of this class may therefore be thought of as a
68
 * container for specialized versions (function closures) of these four operations rather than a
69
 * stateful object.
70
 */
71
@Slf4j
72
@Builder(toBuilder = true)
73
@Value
74
public class RelyingPartyV2<C extends CredentialRecord> {
75
76
  private static final SecureRandom random = new SecureRandom();
77
78
  /**
79
   * The {@link RelyingPartyIdentity} that will be set as the {@link
80
   * PublicKeyCredentialCreationOptions#getRp() rp} parameter when initiating registration
81
   * operations, and which {@link AuthenticatorData#getRpIdHash()} will be compared against. This is
82
   * a required parameter.
83
   *
84
   * <p>A successful registration or authentication operation requires {@link
85
   * AuthenticatorData#getRpIdHash()} to exactly equal the SHA-256 hash of this member's {@link
86
   * RelyingPartyIdentity#getId() id} member. Alternatively, it may instead equal the SHA-256 hash
87
   * of {@link #getAppId() appId} if the latter is present.
88
   *
89
   * @see #startRegistration(StartRegistrationOptions)
90
   * @see PublicKeyCredentialCreationOptions
91
   */
92
  @NonNull private final RelyingPartyIdentity identity;
93
94
  /**
95
   * The allowed origins that returned authenticator responses will be compared against.
96
   *
97
   * <p>The default is the set containing only the string <code>
98
   * "https://" + {@link #getIdentity()}.getId()</code>.
99
   *
100
   * <p>If {@link RelyingPartyV2Builder#allowOriginPort(boolean) allowOriginPort} and {@link
101
   * RelyingPartyV2Builder#allowOriginSubdomain(boolean) allowOriginSubdomain} are both <code>false
102
   * </code> (the default), then a successful registration or authentication operation requires
103
   * {@link CollectedClientData#getOrigin()} to exactly equal one of these values.
104
   *
105
   * <p>If {@link RelyingPartyV2Builder#allowOriginPort(boolean) allowOriginPort} is <code>true
106
   * </code> , then the above rule is relaxed to allow any port number in {@link
107
   * CollectedClientData#getOrigin()}, regardless of any port specified.
108
   *
109
   * <p>If {@link RelyingPartyV2Builder#allowOriginSubdomain(boolean) allowOriginSubdomain} is
110
   * <code>
111
   * true</code>, then the above rule is relaxed to allow any subdomain, of any depth, of any of
112
   * these values.
113
   *
114
   * <p>For either of the above relaxations to take effect, both the allowed origin and the client
115
   * data origin must be valid URLs. Origins that are not valid URLs are matched only by exact
116
   * string equality.
117
   *
118
   * @see #getIdentity()
119
   */
120
  @NonNull private final Set<String> origins;
121
122
  /**
123
   * An abstract database which can look up credentials, usernames and user handles from usernames,
124
   * user handles and credential IDs. This is a required parameter.
125
   *
126
   * <p>This is used to look up:
127
   *
128
   * <ul>
129
   *   <li>the user handle for a user logging in via user name
130
   *   <li>the user name for a user logging in via user handle
131
   *   <li>the credential IDs to include in {@link
132
   *       PublicKeyCredentialCreationOptions#getExcludeCredentials()}
133
   *   <li>the credential IDs to include in {@link
134
   *       PublicKeyCredentialRequestOptions#getAllowCredentials()}
135
   *   <li>that the correct user owns the credential when verifying an assertion
136
   *   <li>the public key to use to verify an assertion
137
   *   <li>the stored signature counter when verifying an assertion
138
   * </ul>
139
   */
140
  @NonNull private final CredentialRepositoryV2<C> credentialRepository;
141
142
  /**
143
   * Enable support for identifying users by username.
144
   *
145
   * <p>If set, then {@link #startAssertion(StartAssertionOptions)} allows setting the {@link
146
   * StartAssertionOptions.StartAssertionOptionsBuilder#username(String) username} parameter when
147
   * starting an assertion.
148
   *
149
   * <p>By default, this is not set.
150
   *
151
   * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted
152
   *     before reaching a mature release.
153
   */
154
  @Deprecated private final UsernameRepository usernameRepository;
155
156
  /**
157
   * The extension input to set for the <code>appid</code> and <code>appidExclude</code> extensions.
158
   *
159
   * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to
160
   * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not
161
   * needed for new registrations, even of U2F authenticators.
162
   *
163
   * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will
164
   * automatically set the <code>appid</code> extension input, and {@link
165
   * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic to
166
   * also accept this AppID as an alternative to the RP ID. Likewise, {@link
167
   * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the
168
   * <code>appidExclude</code> extension input.
169
   *
170
   * <p>By default, this is not set.
171
   *
172
   * @see AssertionExtensionInputs#getAppid()
173
   * @see RegistrationExtensionInputs#getAppidExclude()
174
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1.
175
   *     FIDO AppID Extension (appid)</a>
176
   * @see <a
177
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2.
178
   *     FIDO AppID Exclusion Extension (appidExclude)</a>
179
   */
180
  @NonNull private final Optional<AppId> appId;
181
182
  /**
183
   * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation}
184
   * parameter in registration operations.
185
   *
186
   * <p>Unless your application has a concrete policy for authenticator attestation, it is
187
   * recommended to leave this parameter undefined.
188
   *
189
   * <p>If you set this, you may want to explicitly set {@link
190
   * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and {@link
191
   * RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource) attestationTrustSource}
192
   * too.
193
   *
194
   * <p>By default, this is not set.
195
   *
196
   * @see PublicKeyCredentialCreationOptions#getAttestation()
197
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
198
   *     Attestation</a>
199
   */
200
  @NonNull private final Optional<AttestationConveyancePreference> attestationConveyancePreference;
201
202
  /**
203
   * An {@link AttestationTrustSource} instance to use for looking up trust roots for authenticator
204
   * attestation. This matters only if {@link #getAttestationConveyancePreference()} is non-empty
205
   * and not set to {@link AttestationConveyancePreference#NONE}.
206
   *
207
   * <p>By default, this is not set.
208
   *
209
   * @see PublicKeyCredentialCreationOptions#getAttestation()
210
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
211
   *     Attestation</a>
212
   */
213
  @NonNull private final Optional<AttestationTrustSource> attestationTrustSource;
214
215
  /**
216
   * The argument for the {@link PublicKeyCredentialCreationOptions#getPubKeyCredParams()
217
   * pubKeyCredParams} parameter in registration operations.
218
   *
219
   * <p>This is a list of acceptable public key algorithms and their parameters, ordered from most
220
   * to least preferred.
221
   *
222
   * <p>The default is the following list, in order:
223
   *
224
   * <ol>
225
   *   <li>{@link PublicKeyCredentialParameters#ES256 ES256}
226
   *   <li>{@link PublicKeyCredentialParameters#EdDSA EdDSA}
227
   *   <li>{@link PublicKeyCredentialParameters#ES256 ES384}
228
   *   <li>{@link PublicKeyCredentialParameters#ES256 ES512}
229
   *   <li>{@link PublicKeyCredentialParameters#RS256 RS256}
230
   *   <li>{@link PublicKeyCredentialParameters#RS384 RS384}
231
   *   <li>{@link PublicKeyCredentialParameters#RS512 RS512}
232
   * </ol>
233
   *
234
   * @see PublicKeyCredentialCreationOptions#getAttestation()
235
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
236
   *     Attestation</a>
237
   */
238
  @Builder.Default @NonNull
239
  private final List<PublicKeyCredentialParameters> preferredPubkeyParams =
240
      Collections.unmodifiableList(
241
          Arrays.asList(
242
              PublicKeyCredentialParameters.ES256,
243
              PublicKeyCredentialParameters.EdDSA,
244
              PublicKeyCredentialParameters.ES384,
245
              PublicKeyCredentialParameters.ES512,
246
              PublicKeyCredentialParameters.RS256,
247
              PublicKeyCredentialParameters.RS384,
248
              PublicKeyCredentialParameters.RS512));
249
250
  /**
251
   * If <code>true</code>, the origin matching rule is relaxed to allow any port number.
252
   *
253
   * <p>The default is <code>false</code>.
254
   *
255
   * <p>Examples with <code>
256
   * origins: ["https://example.org", "https://accounts.example.org", "https://acme.com:8443"]
257
   * </code>
258
   *
259
   * <ul>
260
   *   <li>
261
   *       <p><code>allowOriginPort: false</code>
262
   *       <p>Accepted:
263
   *       <ul>
264
   *         <li><code>https://example.org</code>
265
   *         <li><code>https://accounts.example.org</code>
266
   *         <li><code>https://acme.com:8443</code>
267
   *       </ul>
268
   *       <p>Rejected:
269
   *       <ul>
270
   *         <li><code>https://example.org:8443</code>
271
   *         <li><code>https://shop.example.org</code>
272
   *         <li><code>https://acme.com</code>
273
   *         <li><code>https://acme.com:9000</code>
274
   *       </ul>
275
   *   <li>
276
   *       <p><code>allowOriginPort: true</code>
277
   *       <p>Accepted:
278
   *       <ul>
279
   *         <li><code>https://example.org</code>
280
   *         <li><code>https://example.org:8443</code>
281
   *         <li><code>https://accounts.example.org</code>
282
   *         <li><code>https://acme.com</code>
283
   *         <li><code>https://acme.com:8443</code>
284
   *         <li><code>https://acme.com:9000</code>
285
   *       </ul>
286
   *       <p>Rejected:
287
   *       <ul>
288
   *         <li><code>https://shop.example.org</code>
289
   *       </ul>
290
   * </ul>
291
   */
292
  @Builder.Default private final boolean allowOriginPort = false;
293
294
  /**
295
   * If <code>true</code>, the origin matching rule is relaxed to allow any subdomain, of any depth,
296
   * of the values of {@link RelyingPartyV2Builder#origins(Set) origins}.
297
   *
298
   * <p>The default is <code>false</code>.
299
   *
300
   * <p>Examples with <code>origins: ["https://example.org", "https://acme.com:8443"]</code>
301
   *
302
   * <ul>
303
   *   <li>
304
   *       <p><code>allowOriginSubdomain: false</code>
305
   *       <p>Accepted:
306
   *       <ul>
307
   *         <li><code>https://example.org</code>
308
   *         <li><code>https://acme.com:8443</code>
309
   *       </ul>
310
   *       <p>Rejected:
311
   *       <ul>
312
   *         <li><code>https://example.org:8443</code>
313
   *         <li><code>https://accounts.example.org</code>
314
   *         <li><code>https://acme.com</code>
315
   *         <li><code>https://eu.shop.acme.com:8443</code>
316
   *       </ul>
317
   *   <li>
318
   *       <p><code>allowOriginSubdomain: true</code>
319
   *       <p>Accepted:
320
   *       <ul>
321
   *         <li><code>https://example.org</code>
322
   *         <li><code>https://accounts.example.org</code>
323
   *         <li><code>https://acme.com:8443</code>
324
   *         <li><code>https://eu.shop.acme.com:8443</code>
325
   *       </ul>
326
   *       <p>Rejected:
327
   *       <ul>
328
   *         <li><code>https://example.org:8443</code>
329
   *         <li><code>https://acme.com</code>
330
   *       </ul>
331
   * </ul>
332
   */
333
  @Builder.Default private final boolean allowOriginSubdomain = false;
334
335
  /**
336
   * If <code>false</code>, {@link #finishRegistration(FinishRegistrationOptions)
337
   * finishRegistration} will only allow registrations where the attestation signature can be linked
338
   * to a trusted attestation root. This excludes none attestation, and self attestation unless the
339
   * self attestation key is explicitly trusted.
340
   *
341
   * <p>Regardless of the value of this option, invalid attestation statements of supported formats
342
   * will always be rejected. For example, a "packed" attestation statement with an invalid
343
   * signature will be rejected even if this option is set to <code>true</code>.
344
   *
345
   * <p>The default is <code>true</code>.
346
   */
347
  @Builder.Default private final boolean allowUntrustedAttestation = true;
348
349
  /**
350
   * If <code>true</code>, {@link #finishAssertion(FinishAssertionOptions) finishAssertion} will
351
   * succeed only if the {@link AuthenticatorData#getSignatureCounter() signature counter value} in
352
   * the response is strictly greater than the {@link RegisteredCredential#getSignatureCount()
353
   * stored signature counter value}, or if both counters are exactly zero.
354
   *
355
   * <p>The default is <code>true</code>.
356
   */
357
  @Builder.Default private final boolean validateSignatureCounter = true;
358
359
  /**
360
   * A {@link Clock} which will be used to tell the current time while verifying attestation
361
   * certificate chains.
362
   *
363
   * <p>This is intended primarily for testing, and relevant only if {@link
364
   * RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource)} is set.
365
   *
366
   * <p>The default is <code>Clock.systemUTC()</code>.
367
   */
368
  @Builder.Default @NonNull private final Clock clock = Clock.systemUTC();
369
370
  @Builder
371
  private RelyingPartyV2(
372 1 1. <init> : negated conditional → KILLED
      @NonNull RelyingPartyIdentity identity,
373
      Set<String> origins,
374 1 1. <init> : negated conditional → KILLED
      @NonNull CredentialRepositoryV2<C> credentialRepository,
375
      UsernameRepository usernameRepository,
376 1 1. <init> : negated conditional → KILLED
      @NonNull Optional<AppId> appId,
377 1 1. <init> : negated conditional → KILLED
      @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference,
378 1 1. <init> : negated conditional → KILLED
      @NonNull Optional<AttestationTrustSource> attestationTrustSource,
379
      List<PublicKeyCredentialParameters> preferredPubkeyParams,
380
      boolean allowOriginPort,
381
      boolean allowOriginSubdomain,
382
      boolean allowUntrustedAttestation,
383
      boolean validateSignatureCounter,
384
      Clock clock) {
385
    this.identity = identity;
386
    this.origins =
387 1 1. <init> : negated conditional → KILLED
        origins != null
388
            ? CollectionUtil.immutableSet(origins)
389
            : Collections.singleton("https://" + identity.getId());
390
391
    for (String origin : this.origins) {
392
      try {
393
        new URL(origin);
394
      } catch (MalformedURLException e) {
395
        log.warn(
396
            "Allowed origin is not a valid URL, it will match only by exact string equality: {}",
397
            origin);
398
      }
399
    }
400
401
    this.credentialRepository = credentialRepository;
402
    this.usernameRepository = usernameRepository;
403
    this.appId = appId;
404
    this.attestationConveyancePreference = attestationConveyancePreference;
405
    this.attestationTrustSource = attestationTrustSource;
406
    this.preferredPubkeyParams = filterAvailableAlgorithms(preferredPubkeyParams);
407
    this.allowOriginPort = allowOriginPort;
408
    this.allowOriginSubdomain = allowOriginSubdomain;
409
    this.allowUntrustedAttestation = allowUntrustedAttestation;
410
    this.validateSignatureCounter = validateSignatureCounter;
411
    this.clock = clock;
412
  }
413
414
  private static ByteArray generateChallenge() {
415
    byte[] bytes = new byte[32];
416 1 1. generateChallenge : removed call to java/security/SecureRandom::nextBytes → KILLED
    random.nextBytes(bytes);
417 1 1. generateChallenge : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::generateChallenge → KILLED
    return new ByteArray(bytes);
418
  }
419
420
  /**
421
   * Filter <code>pubKeyCredParams</code> to only contain algorithms with a {@link KeyFactory} and a
422
   * {@link Signature} available, and log a warning for every unsupported algorithm.
423
   *
424
   * @return a new {@link List} containing only the algorithms supported in the current JCA context.
425
   */
426
  private static List<PublicKeyCredentialParameters> filterAvailableAlgorithms(
427
      List<PublicKeyCredentialParameters> pubKeyCredParams) {
428 1 1. filterAvailableAlgorithms : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::filterAvailableAlgorithms → KILLED
    return RelyingParty.filterAvailableAlgorithms(pubKeyCredParams);
429
  }
430
431
  public PublicKeyCredentialCreationOptions startRegistration(
432
      StartRegistrationOptions startRegistrationOptions) {
433
    PublicKeyCredentialCreationOptionsBuilder builder =
434
        PublicKeyCredentialCreationOptions.builder()
435
            .rp(identity)
436
            .user(startRegistrationOptions.getUser())
437
            .challenge(generateChallenge())
438
            .pubKeyCredParams(preferredPubkeyParams)
439
            .excludeCredentials(
440
                credentialRepository
441
                    .getCredentialDescriptorsForUserHandle(
442
                        startRegistrationOptions.getUser().getId())
443
                    .stream()
444
                    .map(ToPublicKeyCredentialDescriptor::toPublicKeyCredentialDescriptor)
445
                    .collect(Collectors.toSet()))
446
            .authenticatorSelection(startRegistrationOptions.getAuthenticatorSelection())
447
            .extensions(
448
                startRegistrationOptions
449
                    .getExtensions()
450
                    .merge(
451
                        RegistrationExtensionInputs.builder()
452
                            .appidExclude(appId)
453
                            .credProps()
454
                            .build()))
455
            .timeout(startRegistrationOptions.getTimeout());
456 1 1. startRegistration : removed call to java/util/Optional::ifPresent → KILLED
    attestationConveyancePreference.ifPresent(builder::attestation);
457 1 1. startRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startRegistration → KILLED
    return builder.build();
458
  }
459
460
  public RegistrationResult finishRegistration(FinishRegistrationOptions finishRegistrationOptions)
461
      throws RegistrationFailedException {
462
    try {
463 1 1. finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishRegistration → KILLED
      return _finishRegistration(finishRegistrationOptions).run();
464
    } catch (IllegalArgumentException e) {
465
      throw new RegistrationFailedException(e);
466
    }
467
  }
468
469
  /**
470
   * This method is NOT part of the public API.
471
   *
472
   * <p>This method is called internally by {@link #finishRegistration(FinishRegistrationOptions)}.
473
   * It is a separate method to facilitate testing; users should call {@link
474
   * #finishRegistration(FinishRegistrationOptions)} instead of this method.
475
   */
476
  FinishRegistrationSteps _finishRegistration(FinishRegistrationOptions options) {
477 1 1. _finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishRegistration → KILLED
    return new FinishRegistrationSteps(this, options);
478
  }
479
480
  public AssertionRequest startAssertion(StartAssertionOptions startAssertionOptions) {
481 2 1. startAssertion : negated conditional → KILLED
2. startAssertion : negated conditional → KILLED
    if (startAssertionOptions.getUsername().isPresent() && usernameRepository == null) {
482
      throw new IllegalArgumentException(
483
          "StartAssertionOptions.username must not be set when usernameRepository is not configured.");
484
    }
485
486
    PublicKeyCredentialRequestOptionsBuilder pkcro =
487
        PublicKeyCredentialRequestOptions.builder()
488
            .challenge(generateChallenge())
489
            .rpId(identity.getId())
490
            .allowCredentials(
491
                OptionalUtil.orElseOptional(
492
                        startAssertionOptions.getUserHandle(),
493
                        () ->
494 1 1. lambda$startAssertion$1 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$1 → KILLED
                            Optional.ofNullable(usernameRepository)
495
                                .flatMap(
496
                                    unr ->
497 1 1. lambda$startAssertion$0 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$0 → KILLED
                                        startAssertionOptions
498
                                            .getUsername()
499
                                            .flatMap(unr::getUserHandleForUsername)))
500
                    .map(credentialRepository::getCredentialDescriptorsForUserHandle)
501
                    .map(
502
                        descriptors ->
503
                            descriptors.stream()
504
                                .map(
505
                                    ToPublicKeyCredentialDescriptor
506
                                        ::toPublicKeyCredentialDescriptor)
507 1 1. lambda$startAssertion$2 : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$2 → KILLED
                                .collect(Collectors.toList())))
508
            .extensions(
509
                startAssertionOptions
510
                    .getExtensions()
511
                    .merge(startAssertionOptions.getExtensions().toBuilder().appid(appId).build()))
512
            .timeout(startAssertionOptions.getTimeout());
513
514 1 1. startAssertion : removed call to java/util/Optional::ifPresent → KILLED
    startAssertionOptions.getUserVerification().ifPresent(pkcro::userVerification);
515
516 1 1. startAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startAssertion → KILLED
    return AssertionRequest.builder()
517
        .publicKeyCredentialRequestOptions(pkcro.build())
518
        .username(startAssertionOptions.getUsername())
519
        .userHandle(startAssertionOptions.getUserHandle())
520
        .build();
521
  }
522
523
  /**
524
   * @throws InvalidSignatureCountException if {@link
525
   *     RelyingPartyV2Builder#validateSignatureCounter(boolean) validateSignatureCounter} is <code>
526
   *     true</code>, the {@link AuthenticatorData#getSignatureCounter() signature count} in the
527
   *     response is less than or equal to the {@link RegisteredCredential#getSignatureCount()
528
   *     stored signature count}, and at least one of the signature count values is nonzero.
529
   * @throws AssertionFailedException if validation fails for any other reason.
530
   */
531
  public AssertionResultV2<C> finishAssertion(FinishAssertionOptions finishAssertionOptions)
532
      throws AssertionFailedException {
533
    try {
534 1 1. finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishAssertion → KILLED
      return _finishAssertion(finishAssertionOptions).runV2();
535
    } catch (IllegalArgumentException e) {
536
      throw new AssertionFailedException(e);
537
    }
538
  }
539
540
  /**
541
   * This method is NOT part of the public API.
542
   *
543
   * <p>This method is called internally by {@link #finishAssertion(FinishAssertionOptions)}. It is
544
   * a separate method to facilitate testing; users should call {@link
545
   * #finishAssertion(FinishAssertionOptions)} instead of this method.
546
   */
547
  FinishAssertionSteps<C> _finishAssertion(FinishAssertionOptions options) {
548 1 1. _finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishAssertion → KILLED
    return new FinishAssertionSteps<C>(this, options);
549
  }
550
551
  static <C extends CredentialRecord> RelyingPartyV2Builder<C> builder(
552
      RelyingPartyIdentity identity, CredentialRepositoryV2<C> credentialRepository) {
553 1 1. builder : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::builder → KILLED
    return new RelyingPartyV2Builder<C>()
554
        .identity(identity)
555
        .credentialRepository(credentialRepository);
556
  }
557
558
  public static class RelyingPartyV2Builder<C extends CredentialRecord> {
559
    private @NonNull Optional<AppId> appId = Optional.empty();
560
    private @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference =
561
        Optional.empty();
562
    private @NonNull Optional<AttestationTrustSource> attestationTrustSource = Optional.empty();
563
564
    /**
565
     * The extension input to set for the <code>appid</code> and <code>appidExclude</code>
566
     * extensions.
567
     *
568
     * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to
569
     * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not
570
     * needed for new registrations, even of U2F authenticators.
571
     *
572
     * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will
573
     * automatically set the <code>appid</code> extension input, and {@link
574
     * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic
575
     * to also accept this AppID as an alternative to the RP ID. Likewise, {@link
576
     * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the
577
     * <code>appidExclude</code> extension input.
578
     *
579
     * <p>By default, this is not set.
580
     *
581
     * @see AssertionExtensionInputs#getAppid()
582
     * @see RegistrationExtensionInputs#getAppidExclude()
583
     * @see <a
584
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1.
585
     *     FIDO AppID Extension (appid)</a>
586
     * @see <a
587
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2.
588
     *     FIDO AppID Exclusion Extension (appidExclude)</a>
589
     */
590 1 1. appId : negated conditional → KILLED
    public RelyingPartyV2Builder<C> appId(@NonNull Optional<AppId> appId) {
591
      this.appId = appId;
592 1 1. appId : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED
      return this;
593
    }
594
595
    /**
596
     * The extension input to set for the <code>appid</code> and <code>appidExclude</code>
597
     * extensions.
598
     *
599
     * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to
600
     * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not
601
     * needed for new registrations, even of U2F authenticators.
602
     *
603
     * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will
604
     * automatically set the <code>appid</code> extension input, and {@link
605
     * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic
606
     * to also accept this AppID as an alternative to the RP ID. Likewise, {@link
607
     * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the
608
     * <code>appidExclude</code> extension input.
609
     *
610
     * <p>By default, this is not set.
611
     *
612
     * @see AssertionExtensionInputs#getAppid()
613
     * @see RegistrationExtensionInputs#getAppidExclude()
614
     * @see <a
615
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1.
616
     *     FIDO AppID Extension (appid)</a>
617
     * @see <a
618
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2.
619
     *     FIDO AppID Exclusion Extension (appidExclude)</a>
620
     */
621 1 1. appId : negated conditional → KILLED
    public RelyingPartyV2Builder<C> appId(@NonNull AppId appId) {
622 1 1. appId : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED
      return this.appId(Optional.of(appId));
623
    }
624
625
    /**
626
     * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation}
627
     * parameter in registration operations.
628
     *
629
     * <p>Unless your application has a concrete policy for authenticator attestation, it is
630
     * recommended to leave this parameter undefined.
631
     *
632
     * <p>If you set this, you may want to explicitly set {@link
633
     * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and
634
     * {@link RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource)
635
     * attestationTrustSource} too.
636
     *
637
     * <p>By default, this is not set.
638
     *
639
     * @see PublicKeyCredentialCreationOptions#getAttestation()
640
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
641
     *     Attestation</a>
642
     */
643
    public RelyingPartyV2Builder<C> attestationConveyancePreference(
644 1 1. attestationConveyancePreference : negated conditional → KILLED
        @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference) {
645
      this.attestationConveyancePreference = attestationConveyancePreference;
646 1 1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED
      return this;
647
    }
648
649
    /**
650
     * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation}
651
     * parameter in registration operations.
652
     *
653
     * <p>Unless your application has a concrete policy for authenticator attestation, it is
654
     * recommended to leave this parameter undefined.
655
     *
656
     * <p>If you set this, you may want to explicitly set {@link
657
     * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and
658
     * {@link RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource)
659
     * attestationTrustSource} too.
660
     *
661
     * <p>By default, this is not set.
662
     *
663
     * @see PublicKeyCredentialCreationOptions#getAttestation()
664
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
665
     *     Attestation</a>
666
     */
667
    public RelyingPartyV2Builder<C> attestationConveyancePreference(
668 1 1. attestationConveyancePreference : negated conditional → KILLED
        @NonNull AttestationConveyancePreference attestationConveyancePreference) {
669 1 1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED
      return this.attestationConveyancePreference(Optional.of(attestationConveyancePreference));
670
    }
671
672
    /**
673
     * An {@link AttestationTrustSource} instance to use for looking up trust roots for
674
     * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()}
675
     * is non-empty and not set to {@link AttestationConveyancePreference#NONE}.
676
     *
677
     * <p>By default, this is not set.
678
     *
679
     * @see PublicKeyCredentialCreationOptions#getAttestation()
680
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
681
     *     Attestation</a>
682
     */
683
    public RelyingPartyV2Builder<C> attestationTrustSource(
684 1 1. attestationTrustSource : negated conditional → KILLED
        @NonNull Optional<AttestationTrustSource> attestationTrustSource) {
685
      this.attestationTrustSource = attestationTrustSource;
686 1 1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED
      return this;
687
    }
688
689
    /**
690
     * An {@link AttestationTrustSource} instance to use for looking up trust roots for
691
     * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()}
692
     * is non-empty and not set to {@link AttestationConveyancePreference#NONE}.
693
     *
694
     * <p>By default, this is not set.
695
     *
696
     * @see PublicKeyCredentialCreationOptions#getAttestation()
697
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
698
     *     Attestation</a>
699
     */
700
    public RelyingPartyV2Builder<C> attestationTrustSource(
701 1 1. attestationTrustSource : negated conditional → KILLED
        @NonNull AttestationTrustSource attestationTrustSource) {
702 1 1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED
      return this.attestationTrustSource(Optional.of(attestationTrustSource));
703
    }
704
  }
705
}

Mutations

372

1.1
Location : <init>
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

374

1.1
Location : <init>
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

376

1.1
Location : <init>
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

377

1.1
Location : <init>
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

378

1.1
Location : <init>
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

387

1.1
Location : <init>
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

416

1.1
Location : generateChallenge
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
removed call to java/security/SecureRandom::nextBytes → KILLED

417

1.1
Location : generateChallenge
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::generateChallenge → KILLED

428

1.1
Location : filterAvailableAlgorithms
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::filterAvailableAlgorithms → KILLED

456

1.1
Location : startRegistration
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
removed call to java/util/Optional::ifPresent → KILLED

457

1.1
Location : startRegistration
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startRegistration → KILLED

463

1.1
Location : finishRegistration
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishRegistration → KILLED

477

1.1
Location : _finishRegistration
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishRegistration → KILLED

481

1.1
Location : startAssertion
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

2.2
Location : startAssertion
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
negated conditional → KILLED

494

1.1
Location : lambda$startAssertion$1
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$1 → KILLED

497

1.1
Location : lambda$startAssertion$0
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$0 → KILLED

507

1.1
Location : lambda$startAssertion$2
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$2 → KILLED

514

1.1
Location : startAssertion
Killed by : com.yubico.webauthn.RelyingPartyV2AssertionSpec
removed call to java/util/Optional::ifPresent → KILLED

516

1.1
Location : startAssertion
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startAssertion → KILLED

534

1.1
Location : finishAssertion
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishAssertion → KILLED

548

1.1
Location : _finishAssertion
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishAssertion → KILLED

553

1.1
Location : builder
Killed by : com.yubico.webauthn.RelyingPartyUserIdentificationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2::builder → KILLED

590

1.1
Location : appId
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
negated conditional → KILLED

592

1.1
Location : appId
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED

621

1.1
Location : appId
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
negated conditional → KILLED

622

1.1
Location : appId
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED

644

1.1
Location : attestationConveyancePreference
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
negated conditional → KILLED

646

1.1
Location : attestationConveyancePreference
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED

668

1.1
Location : attestationConveyancePreference
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
negated conditional → KILLED

669

1.1
Location : attestationConveyancePreference
Killed by : com.yubico.webauthn.RelyingPartyStartOperationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED

684

1.1
Location : attestationTrustSource
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
negated conditional → KILLED

686

1.1
Location : attestationTrustSource
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED

701

1.1
Location : attestationTrustSource
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
negated conditional → KILLED

702

1.1
Location : attestationTrustSource
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED

Active mutators

Tests examined


Report generated by PIT 1.15.0