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
            .hints(startRegistrationOptions.getHints());
457 1 1. startRegistration : removed call to java/util/Optional::ifPresent → KILLED
    attestationConveyancePreference.ifPresent(builder::attestation);
458 1 1. startRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startRegistration → KILLED
    return builder.build();
459
  }
460
461
  public RegistrationResult finishRegistration(FinishRegistrationOptions finishRegistrationOptions)
462
      throws RegistrationFailedException {
463
    try {
464 1 1. finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishRegistration → KILLED
      return _finishRegistration(finishRegistrationOptions).run();
465
    } catch (IllegalArgumentException e) {
466
      throw new RegistrationFailedException(e);
467
    }
468
  }
469
470
  /**
471
   * This method is NOT part of the public API.
472
   *
473
   * <p>This method is called internally by {@link #finishRegistration(FinishRegistrationOptions)}.
474
   * It is a separate method to facilitate testing; users should call {@link
475
   * #finishRegistration(FinishRegistrationOptions)} instead of this method.
476
   */
477
  FinishRegistrationSteps _finishRegistration(FinishRegistrationOptions options) {
478 1 1. _finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishRegistration → KILLED
    return new FinishRegistrationSteps(this, options);
479
  }
480
481
  public AssertionRequest startAssertion(StartAssertionOptions startAssertionOptions) {
482 2 1. startAssertion : negated conditional → KILLED
2. startAssertion : negated conditional → KILLED
    if (startAssertionOptions.getUsername().isPresent() && usernameRepository == null) {
483
      throw new IllegalArgumentException(
484
          "StartAssertionOptions.username must not be set when usernameRepository is not configured.");
485
    }
486
487
    PublicKeyCredentialRequestOptionsBuilder pkcro =
488
        PublicKeyCredentialRequestOptions.builder()
489
            .challenge(generateChallenge())
490
            .rpId(identity.getId())
491
            .allowCredentials(
492
                OptionalUtil.orElseOptional(
493
                        startAssertionOptions.getUserHandle(),
494
                        () ->
495 1 1. lambda$startAssertion$1 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$1 → KILLED
                            Optional.ofNullable(usernameRepository)
496
                                .flatMap(
497
                                    unr ->
498 1 1. lambda$startAssertion$0 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$0 → KILLED
                                        startAssertionOptions
499
                                            .getUsername()
500
                                            .flatMap(unr::getUserHandleForUsername)))
501
                    .map(credentialRepository::getCredentialDescriptorsForUserHandle)
502
                    .map(
503
                        descriptors ->
504
                            descriptors.stream()
505
                                .map(
506
                                    ToPublicKeyCredentialDescriptor
507
                                        ::toPublicKeyCredentialDescriptor)
508 1 1. lambda$startAssertion$2 : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$2 → KILLED
                                .collect(Collectors.toList())))
509
            .extensions(
510
                startAssertionOptions
511
                    .getExtensions()
512
                    .merge(startAssertionOptions.getExtensions().toBuilder().appid(appId).build()))
513
            .timeout(startAssertionOptions.getTimeout())
514
            .hints(startAssertionOptions.getHints());
515
516 1 1. startAssertion : removed call to java/util/Optional::ifPresent → KILLED
    startAssertionOptions.getUserVerification().ifPresent(pkcro::userVerification);
517
518 1 1. startAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startAssertion → KILLED
    return AssertionRequest.builder()
519
        .publicKeyCredentialRequestOptions(pkcro.build())
520
        .username(startAssertionOptions.getUsername())
521
        .userHandle(startAssertionOptions.getUserHandle())
522
        .build();
523
  }
524
525
  /**
526
   * @throws InvalidSignatureCountException if {@link
527
   *     RelyingPartyV2Builder#validateSignatureCounter(boolean) validateSignatureCounter} is <code>
528
   *     true</code>, the {@link AuthenticatorData#getSignatureCounter() signature count} in the
529
   *     response is less than or equal to the {@link RegisteredCredential#getSignatureCount()
530
   *     stored signature count}, and at least one of the signature count values is nonzero.
531
   * @throws AssertionFailedException if validation fails for any other reason.
532
   */
533
  public AssertionResultV2<C> finishAssertion(FinishAssertionOptions finishAssertionOptions)
534
      throws AssertionFailedException {
535
    try {
536 1 1. finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishAssertion → KILLED
      return _finishAssertion(finishAssertionOptions).runV2();
537
    } catch (IllegalArgumentException e) {
538
      throw new AssertionFailedException(e);
539
    }
540
  }
541
542
  /**
543
   * This method is NOT part of the public API.
544
   *
545
   * <p>This method is called internally by {@link #finishAssertion(FinishAssertionOptions)}. It is
546
   * a separate method to facilitate testing; users should call {@link
547
   * #finishAssertion(FinishAssertionOptions)} instead of this method.
548
   */
549
  FinishAssertionSteps<C> _finishAssertion(FinishAssertionOptions options) {
550 1 1. _finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishAssertion → KILLED
    return new FinishAssertionSteps<C>(this, options);
551
  }
552
553
  static <C extends CredentialRecord> RelyingPartyV2Builder<C> builder(
554
      RelyingPartyIdentity identity, CredentialRepositoryV2<C> credentialRepository) {
555 1 1. builder : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::builder → KILLED
    return new RelyingPartyV2Builder<C>()
556
        .identity(identity)
557
        .credentialRepository(credentialRepository);
558
  }
559
560
  public static class RelyingPartyV2Builder<C extends CredentialRecord> {
561
    private @NonNull Optional<AppId> appId = Optional.empty();
562
    private @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference =
563
        Optional.empty();
564
    private @NonNull Optional<AttestationTrustSource> attestationTrustSource = Optional.empty();
565
566
    /**
567
     * The extension input to set for the <code>appid</code> and <code>appidExclude</code>
568
     * extensions.
569
     *
570
     * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to
571
     * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not
572
     * needed for new registrations, even of U2F authenticators.
573
     *
574
     * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will
575
     * automatically set the <code>appid</code> extension input, and {@link
576
     * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic
577
     * to also accept this AppID as an alternative to the RP ID. Likewise, {@link
578
     * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the
579
     * <code>appidExclude</code> extension input.
580
     *
581
     * <p>By default, this is not set.
582
     *
583
     * @see AssertionExtensionInputs#getAppid()
584
     * @see RegistrationExtensionInputs#getAppidExclude()
585
     * @see <a
586
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1.
587
     *     FIDO AppID Extension (appid)</a>
588
     * @see <a
589
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2.
590
     *     FIDO AppID Exclusion Extension (appidExclude)</a>
591
     */
592 1 1. appId : negated conditional → KILLED
    public RelyingPartyV2Builder<C> appId(@NonNull Optional<AppId> appId) {
593
      this.appId = appId;
594 1 1. appId : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED
      return this;
595
    }
596
597
    /**
598
     * The extension input to set for the <code>appid</code> and <code>appidExclude</code>
599
     * extensions.
600
     *
601
     * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to
602
     * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not
603
     * needed for new registrations, even of U2F authenticators.
604
     *
605
     * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will
606
     * automatically set the <code>appid</code> extension input, and {@link
607
     * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic
608
     * to also accept this AppID as an alternative to the RP ID. Likewise, {@link
609
     * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the
610
     * <code>appidExclude</code> extension input.
611
     *
612
     * <p>By default, this is not set.
613
     *
614
     * @see AssertionExtensionInputs#getAppid()
615
     * @see RegistrationExtensionInputs#getAppidExclude()
616
     * @see <a
617
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1.
618
     *     FIDO AppID Extension (appid)</a>
619
     * @see <a
620
     *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2.
621
     *     FIDO AppID Exclusion Extension (appidExclude)</a>
622
     */
623 1 1. appId : negated conditional → KILLED
    public RelyingPartyV2Builder<C> appId(@NonNull AppId appId) {
624 1 1. appId : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED
      return this.appId(Optional.of(appId));
625
    }
626
627
    /**
628
     * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation}
629
     * parameter in registration operations.
630
     *
631
     * <p>Unless your application has a concrete policy for authenticator attestation, it is
632
     * recommended to leave this parameter undefined.
633
     *
634
     * <p>If you set this, you may want to explicitly set {@link
635
     * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and
636
     * {@link RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource)
637
     * attestationTrustSource} too.
638
     *
639
     * <p>By default, this is not set.
640
     *
641
     * @see PublicKeyCredentialCreationOptions#getAttestation()
642
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
643
     *     Attestation</a>
644
     */
645
    public RelyingPartyV2Builder<C> attestationConveyancePreference(
646 1 1. attestationConveyancePreference : negated conditional → KILLED
        @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference) {
647
      this.attestationConveyancePreference = attestationConveyancePreference;
648 1 1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED
      return this;
649
    }
650
651
    /**
652
     * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation}
653
     * parameter in registration operations.
654
     *
655
     * <p>Unless your application has a concrete policy for authenticator attestation, it is
656
     * recommended to leave this parameter undefined.
657
     *
658
     * <p>If you set this, you may want to explicitly set {@link
659
     * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and
660
     * {@link RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource)
661
     * attestationTrustSource} too.
662
     *
663
     * <p>By default, this is not set.
664
     *
665
     * @see PublicKeyCredentialCreationOptions#getAttestation()
666
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
667
     *     Attestation</a>
668
     */
669
    public RelyingPartyV2Builder<C> attestationConveyancePreference(
670 1 1. attestationConveyancePreference : negated conditional → KILLED
        @NonNull AttestationConveyancePreference attestationConveyancePreference) {
671 1 1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED
      return this.attestationConveyancePreference(Optional.of(attestationConveyancePreference));
672
    }
673
674
    /**
675
     * An {@link AttestationTrustSource} instance to use for looking up trust roots for
676
     * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()}
677
     * is non-empty and not set to {@link AttestationConveyancePreference#NONE}.
678
     *
679
     * <p>By default, this is not set.
680
     *
681
     * @see PublicKeyCredentialCreationOptions#getAttestation()
682
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
683
     *     Attestation</a>
684
     */
685
    public RelyingPartyV2Builder<C> attestationTrustSource(
686 1 1. attestationTrustSource : negated conditional → KILLED
        @NonNull Optional<AttestationTrustSource> attestationTrustSource) {
687
      this.attestationTrustSource = attestationTrustSource;
688 1 1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED
      return this;
689
    }
690
691
    /**
692
     * An {@link AttestationTrustSource} instance to use for looking up trust roots for
693
     * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()}
694
     * is non-empty and not set to {@link AttestationConveyancePreference#NONE}.
695
     *
696
     * <p>By default, this is not set.
697
     *
698
     * @see PublicKeyCredentialCreationOptions#getAttestation()
699
     * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4.
700
     *     Attestation</a>
701
     */
702
    public RelyingPartyV2Builder<C> attestationTrustSource(
703 1 1. attestationTrustSource : negated conditional → KILLED
        @NonNull AttestationTrustSource attestationTrustSource) {
704 1 1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED
      return this.attestationTrustSource(Optional.of(attestationTrustSource));
705
    }
706
  }
707
}

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

457

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

458

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

464

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

478

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

482

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

495

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

498

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

508

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

516

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

518

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

536

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

550

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

555

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

592

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

594

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

623

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

624

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

646

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

648

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

670

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

671

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

686

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

688

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

703

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

704

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