FidoMetadataService.java

1
// Copyright (c) 2015-2021, 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.fido.metadata;
26
27
import com.yubico.fido.metadata.FidoMetadataService.Filters.AuthenticatorToBeFiltered;
28
import com.yubico.internal.util.CertificateParser;
29
import com.yubico.internal.util.OptionalUtil;
30
import com.yubico.webauthn.RegistrationResult;
31
import com.yubico.webauthn.RelyingParty;
32
import com.yubico.webauthn.RelyingParty.RelyingPartyBuilder;
33
import com.yubico.webauthn.attestation.AttestationTrustSource;
34
import com.yubico.webauthn.data.ByteArray;
35
import com.yubico.webauthn.data.exception.Base64UrlException;
36
import java.io.IOException;
37
import java.security.DigestException;
38
import java.security.InvalidAlgorithmParameterException;
39
import java.security.InvalidKeyException;
40
import java.security.NoSuchAlgorithmException;
41
import java.security.SignatureException;
42
import java.security.cert.CertPathValidatorException;
43
import java.security.cert.CertStore;
44
import java.security.cert.CertificateException;
45
import java.security.cert.X509Certificate;
46
import java.util.Arrays;
47
import java.util.Collection;
48
import java.util.Collections;
49
import java.util.HashMap;
50
import java.util.HashSet;
51
import java.util.List;
52
import java.util.Map;
53
import java.util.Optional;
54
import java.util.Set;
55
import java.util.function.Consumer;
56
import java.util.function.Predicate;
57
import java.util.stream.Collectors;
58
import java.util.stream.Stream;
59
import lombok.AccessLevel;
60
import lombok.AllArgsConstructor;
61
import lombok.NonNull;
62
import lombok.RequiredArgsConstructor;
63
import lombok.Value;
64
import lombok.extern.slf4j.Slf4j;
65
66
/**
67
 * Utility for filtering and querying <a
68
 * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">Fido
69
 * Metadata Service BLOB entries</a>.
70
 *
71
 * <p>This class implements {@link AttestationTrustSource}, so it can be configured as the {@link
72
 * RelyingPartyBuilder#attestationTrustSource(AttestationTrustSource) attestationTrustSource}
73
 * setting in {@link RelyingParty}. This implementation always sets {@link
74
 * com.yubico.webauthn.attestation.AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder#enableRevocationChecking(boolean)
75
 * enableRevocationChecking(false)}, because the FIDO MDS has its own revocation procedures and not
76
 * all attestation certificates provide CRLs; and always sets {@link
77
 * com.yubico.webauthn.attestation.AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder#policyTreeValidator(Predicate)
78
 * policyTreeValidator} to accept any policy tree, because a Windows Hello attestation certificate
79
 * is known to include a critical certificate policies extension.
80
 *
81
 * <p>The metadata service may be configured with two stages of filters to select trusted
82
 * authenticators. The first stage is the {@link FidoMetadataServiceBuilder#prefilter(Predicate)
83
 * prefilter} setting, which is executed once when the {@link FidoMetadataService} instance is
84
 * constructed. The second stage is the {@link FidoMetadataServiceBuilder#filter(Predicate) filter}
85
 * setting, which is executed whenever metadata or trust roots are to be looked up for a given
86
 * authenticator. Any metadata entry that satisfies both filters will be considered trusted.
87
 *
88
 * <p>Use the {@link #builder() builder} to configure settings, then use the {@link
89
 * #findEntries(List, AAGUID)} method or its overloads to retrieve metadata entries.
90
 */
91
@Slf4j
92
public final class FidoMetadataService implements AttestationTrustSource {
93
94
  private final HashMap<String, HashSet<MetadataBLOBPayloadEntry>>
95
      prefilteredEntriesByCertificateKeyIdentifier;
96
  private final HashMap<AAGUID, HashSet<MetadataBLOBPayloadEntry>> prefilteredEntriesByAaguid;
97
  private final HashSet<MetadataBLOBPayloadEntry> prefilteredUnindexedEntries;
98
99
  private final Predicate<AuthenticatorToBeFiltered> filter;
100
  private final CertStore certStore;
101
102
  private FidoMetadataService(
103 1 1. <init> : negated conditional → KILLED
      @NonNull MetadataBLOBPayload blob,
104 1 1. <init> : negated conditional → KILLED
      @NonNull Predicate<MetadataBLOBPayloadEntry> prefilter,
105 1 1. <init> : negated conditional → KILLED
      @NonNull Predicate<AuthenticatorToBeFiltered> filter,
106
      CertStore certStore) {
107
    final List<MetadataBLOBPayloadEntry> prefilteredEntries =
108
        blob.getEntries().stream()
109
            .filter(FidoMetadataService::ignoreInvalidUpdateAvailableAuthenticatorVersion)
110
            .filter(prefilter)
111
            .collect(Collectors.toList());
112
113
    this.prefilteredEntriesByCertificateKeyIdentifier = buildCkiMap(prefilteredEntries);
114
    this.prefilteredEntriesByAaguid = buildAaguidMap(prefilteredEntries);
115
116
    this.prefilteredUnindexedEntries = new HashSet<>(prefilteredEntries);
117
    for (HashSet<MetadataBLOBPayloadEntry> byAaguid : prefilteredEntriesByAaguid.values()) {
118
      prefilteredUnindexedEntries.removeAll(byAaguid);
119
    }
120
    for (HashSet<MetadataBLOBPayloadEntry> byCski :
121
        prefilteredEntriesByCertificateKeyIdentifier.values()) {
122
      prefilteredUnindexedEntries.removeAll(byCski);
123
    }
124
125
    this.filter = filter;
126
    this.certStore = certStore;
127
  }
128
129
  private static boolean ignoreInvalidUpdateAvailableAuthenticatorVersion(
130
      MetadataBLOBPayloadEntry metadataBLOBPayloadEntry) {
131 2 1. ignoreInvalidUpdateAvailableAuthenticatorVersion : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::ignoreInvalidUpdateAvailableAuthenticatorVersion → KILLED
2. ignoreInvalidUpdateAvailableAuthenticatorVersion : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::ignoreInvalidUpdateAvailableAuthenticatorVersion → KILLED
    return metadataBLOBPayloadEntry
132
        .getMetadataStatement()
133
        .map(MetadataStatement::getAuthenticatorVersion)
134
        .map(
135
            authenticatorVersion ->
136 2 1. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3 : replaced Boolean return with True for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3 → KILLED
2. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3 : replaced Boolean return with False for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3 → KILLED
                metadataBLOBPayloadEntry.getStatusReports().stream()
137
                    .filter(
138
                        statusReport ->
139 2 1. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0 → SURVIVED
2. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0 → KILLED
                            AuthenticatorStatus.UPDATE_AVAILABLE.equals(statusReport.getStatus()))
140
                    .noneMatch(
141
                        statusReport ->
142
                            statusReport
143
                                .getAuthenticatorVersion()
144 3 1. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1 : changed conditional boundary → KILLED
2. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1 : replaced Boolean return with True for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1 → KILLED
3. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1 : negated conditional → KILLED
                                .map(av -> av > authenticatorVersion)
145 2 1. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2 → KILLED
2. lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2 → KILLED
                                .orElse(false)))
146
        .orElse(true);
147
  }
148
149
  private static HashMap<String, HashSet<MetadataBLOBPayloadEntry>> buildCkiMap(
150 1 1. buildCkiMap : negated conditional → KILLED
      @NonNull List<MetadataBLOBPayloadEntry> entries) {
151
152 1 1. buildCkiMap : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::buildCkiMap → KILLED
    return entries.stream()
153
        .collect(
154
            HashMap::new,
155
            (result, metadataBLOBPayloadEntry) -> {
156
              for (String acki :
157
                  metadataBLOBPayloadEntry.getAttestationCertificateKeyIdentifiers()) {
158 1 1. lambda$buildCkiMap$4 : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildCkiMap$4 → KILLED
                result.computeIfAbsent(acki, o -> new HashSet<>()).add(metadataBLOBPayloadEntry);
159
              }
160
              for (String acki :
161
                  metadataBLOBPayloadEntry
162
                      .getMetadataStatement()
163
                      .map(MetadataStatement::getAttestationCertificateKeyIdentifiers)
164
                      .orElseGet(Collections::emptySet)) {
165 1 1. lambda$buildCkiMap$5 : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildCkiMap$5 → KILLED
                result.computeIfAbsent(acki, o -> new HashSet<>()).add(metadataBLOBPayloadEntry);
166
              }
167
            },
168
            (mapA, mapB) -> {
169
              for (Map.Entry<String, HashSet<MetadataBLOBPayloadEntry>> e : mapB.entrySet()) {
170
                mapA.merge(
171
                    e.getKey(),
172
                    e.getValue(),
173
                    (entriesA, entriesB) -> {
174
                      entriesA.addAll(entriesB);
175 1 1. lambda$buildCkiMap$7 : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildCkiMap$7 → NO_COVERAGE
                      return entriesA;
176
                    });
177
              }
178
            });
179
  }
180
181
  private static HashMap<AAGUID, HashSet<MetadataBLOBPayloadEntry>> buildAaguidMap(
182 1 1. buildAaguidMap : negated conditional → KILLED
      @NonNull List<MetadataBLOBPayloadEntry> entries) {
183
184 1 1. buildAaguidMap : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::buildAaguidMap → KILLED
    return entries.stream()
185
        .collect(
186
            HashMap::new,
187
            (result, metadataBLOBPayloadEntry) -> {
188
              final Consumer<AAGUID> appendToAaguidEntry =
189
                  aaguid ->
190
                      result
191 1 1. lambda$buildAaguidMap$9 : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$9 → KILLED
                          .computeIfAbsent(aaguid, o -> new HashSet<>())
192
                          .add(metadataBLOBPayloadEntry);
193
              metadataBLOBPayloadEntry
194
                  .getAaguid()
195 2 1. lambda$buildAaguidMap$11 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$11 → SURVIVED
2. lambda$buildAaguidMap$11 : negated conditional → KILLED
                  .filter(aaguid -> !aaguid.isZero())
196 1 1. lambda$buildAaguidMap$13 : removed call to java/util/Optional::ifPresent → KILLED
                  .ifPresent(appendToAaguidEntry);
197
              metadataBLOBPayloadEntry
198
                  .getMetadataStatement()
199
                  .flatMap(MetadataStatement::getAaguid)
200 2 1. lambda$buildAaguidMap$12 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$12 → SURVIVED
2. lambda$buildAaguidMap$12 : negated conditional → SURVIVED
                  .filter(aaguid -> !aaguid.isZero())
201 1 1. lambda$buildAaguidMap$13 : removed call to java/util/Optional::ifPresent → SURVIVED
                  .ifPresent(appendToAaguidEntry);
202
            },
203
            (mapA, mapB) -> {
204
              for (Map.Entry<AAGUID, HashSet<MetadataBLOBPayloadEntry>> e : mapB.entrySet()) {
205
                mapA.merge(
206
                    e.getKey(),
207
                    e.getValue(),
208
                    (entriesA, entriesB) -> {
209
                      entriesA.addAll(entriesB);
210 1 1. lambda$buildAaguidMap$14 : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$14 → NO_COVERAGE
                      return entriesA;
211
                    });
212
              }
213
            });
214
  }
215
216
  public static FidoMetadataServiceBuilder.Step1 builder() {
217 1 1. builder : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::builder → KILLED
    return new FidoMetadataServiceBuilder.Step1();
218
  }
219
220
  @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
221
  public static class FidoMetadataServiceBuilder {
222
    @NonNull private final MetadataBLOBPayload blob;
223
224
    private Predicate<MetadataBLOBPayloadEntry> prefilter = Filters.notRevoked();
225
    private Predicate<AuthenticatorToBeFiltered> filter = Filters.noAttestationKeyCompromise();
226
    private CertStore certStore = null;
227
228
    public static class Step1 {
229
      /**
230
       * Use payload of the given <code>blob</code> as the data source.
231
       *
232
       * <p>The {@link FidoMetadataDownloader#loadCachedBlob()} method returns a value suitable for
233
       * use here.
234
       *
235
       * <p>This is an alias of <code>useBlob(blob.getPayload()</code>.
236
       *
237
       * @see FidoMetadataDownloader#loadCachedBlob()
238
       * @see #useBlob(MetadataBLOBPayload)
239
       */
240 1 1. useBlob : negated conditional → KILLED
      public FidoMetadataServiceBuilder useBlob(@NonNull MetadataBLOB blob) {
241 1 1. useBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder$Step1::useBlob → KILLED
        return useBlob(blob.getPayload());
242
      }
243
244
      /**
245
       * Use the given <code>blobPayload</code> as the data source.
246
       *
247
       * <p>The {@link FidoMetadataDownloader#loadCachedBlob()} method returns a value whose {@link
248
       * MetadataBLOB#getPayload() .getPayload()} result is suitable for use here.
249
       *
250
       * @see FidoMetadataDownloader#loadCachedBlob()
251
       * @see #useBlob(MetadataBLOB)
252
       */
253 1 1. useBlob : negated conditional → KILLED
      public FidoMetadataServiceBuilder useBlob(@NonNull MetadataBLOBPayload blobPayload) {
254 1 1. useBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder$Step1::useBlob → KILLED
        return new FidoMetadataServiceBuilder(blobPayload);
255
      }
256
    }
257
258
    /**
259
     * Set a first-stage filter for which metadata entries to include in the data source.
260
     *
261
     * <p>This prefilter is executed once for each metadata entry during initial construction of a
262
     * {@link FidoMetadataService} instance.
263
     *
264
     * <p>The default is {@link Filters#notRevoked() Filters.notRevoked()}. Setting a different
265
     * filter overrides this default; to preserve the "not revoked" condition in addition to the new
266
     * filter, you must explicitly include the condition in the few filter. For example, by using
267
     * {@link Filters#allOf(Predicate[]) Filters.allOf(Predicate...)}.
268
     *
269
     * @param prefilter a {@link Predicate} which returns <code>true</code> for metadata entries to
270
     *     include in the data source.
271
     * @see #filter(Predicate)
272
     * @see Filters#allOf(Predicate[])
273
     */
274
    public FidoMetadataServiceBuilder prefilter(
275 1 1. prefilter : negated conditional → KILLED
        @NonNull Predicate<MetadataBLOBPayloadEntry> prefilter) {
276
      this.prefilter = prefilter;
277 1 1. prefilter : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::prefilter → KILLED
      return this;
278
    }
279
280
    /**
281
     * Set a filter for which metadata entries to allow for a given authenticator during credential
282
     * registration and metadata lookup.
283
     *
284
     * <p>This filter is executed during each execution of {@link #findEntries(List, AAGUID)}, its
285
     * overloads, and {@link #findTrustRoots(List, Optional)}.
286
     *
287
     * <p>The default is {@link Filters#noAttestationKeyCompromise()
288
     * Filters.noAttestationKeyCompromise()}. Setting a different filter overrides this default; to
289
     * preserve this condition in addition to the new filter, you must explicitly include the
290
     * condition in the few filter. For example, by using {@link Filters#allOf(Predicate[])
291
     * Filters.allOf(Predicate...)}.
292
     *
293
     * <p>Note: Returning <code>true</code> in the filter predicate does not automatically make the
294
     * authenticator trusted, as its attestation certificate must also correctly chain to a trusted
295
     * attestation root. Rather, returning <code>true</code> in the filter predicate allows the
296
     * corresponding metadata entry to be used for further trust assessment for that authenticator,
297
     * while returning <code>false</code> eliminates the metadata entry (and thus any associated
298
     * trust roots) for the ongoing query.
299
     *
300
     * @param filter a {@link Predicate} which returns <code>true</code> for metadata entries to
301
     *     allow for the corresponding authenticator during credential registration and metadata
302
     *     lookup.
303
     * @see #prefilter(Predicate)
304
     * @see AuthenticatorToBeFiltered
305
     * @see Filters#allOf(Predicate[])
306
     */
307
    public FidoMetadataServiceBuilder filter(
308 1 1. filter : negated conditional → KILLED
        @NonNull Predicate<FidoMetadataService.Filters.AuthenticatorToBeFiltered> filter) {
309
      this.filter = filter;
310 1 1. filter : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::filter → KILLED
      return this;
311
    }
312
313
    /**
314
     * Set a {@link CertStore} of additional CRLs and/or intermediate certificates to use while
315
     * validating attestation certificate paths.
316
     *
317
     * <p>This setting is most likely useful for tests.
318
     *
319
     * @param certStore a {@link CertStore} of additional CRLs and/or intermediate certificates to
320
     *     use while validating attestation certificate paths.
321
     */
322 1 1. certStore : negated conditional → KILLED
    public FidoMetadataServiceBuilder certStore(@NonNull CertStore certStore) {
323
      this.certStore = certStore;
324 1 1. certStore : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::certStore → KILLED
      return this;
325
    }
326
327
    public FidoMetadataService build()
328
        throws CertPathValidatorException,
329
            InvalidAlgorithmParameterException,
330
            Base64UrlException,
331
            DigestException,
332
            FidoMetadataDownloaderException,
333
            CertificateException,
334
            UnexpectedLegalHeader,
335
            IOException,
336
            NoSuchAlgorithmException,
337
            SignatureException,
338
            InvalidKeyException {
339 1 1. build : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::build → KILLED
      return new FidoMetadataService(blob, prefilter, filter, certStore);
340
    }
341
  }
342
343
  /**
344
   * Preconfigured filters and utilities for combining filters. See the {@link
345
   * FidoMetadataServiceBuilder#prefilter(Predicate) prefilter} and {@link
346
   * FidoMetadataServiceBuilder#filter(Predicate) filter} settings.
347
   *
348
   * @see FidoMetadataServiceBuilder#prefilter(Predicate)
349
   * @see FidoMetadataServiceBuilder#filter(Predicate)
350
   */
351
  public static class Filters {
352
353
    /**
354
     * Combine a set of filters into a filter that requires inputs to satisfy ALL of those filters.
355
     *
356
     * <p>If <code>filters</code> is empty, then all inputs will satisfy the resulting filter.
357
     *
358
     * @param filters A set of filters.
359
     * @return A filter which only accepts inputs that satisfy ALL of the given <code>
360
     *     filters</code>.
361
     */
362
    @SafeVarargs
363
    public static <T> Predicate<T> allOf(Predicate<T>... filters) {
364 5 1. lambda$allOf$0 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$0 → NO_COVERAGE
2. lambda$allOf$0 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$0 → NO_COVERAGE
3. allOf : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$Filters::allOf → NO_COVERAGE
4. lambda$allOf$1 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$1 → NO_COVERAGE
5. lambda$allOf$1 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$1 → NO_COVERAGE
      return (entry) -> Stream.of(filters).allMatch(filter -> filter.test(entry));
365
    }
366
367
    /**
368
     * Include any metadata entry whose {@link MetadataBLOBPayloadEntry#getStatusReports()
369
     * statusReports} array contains no entry with {@link AuthenticatorStatus#REVOKED REVOKED}
370
     * status.
371
     *
372
     * @see AuthenticatorStatus#REVOKED
373
     */
374
    public static Predicate<MetadataBLOBPayloadEntry> notRevoked() {
375 1 1. notRevoked : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$Filters::notRevoked → KILLED
      return (entry) ->
376 2 1. lambda$notRevoked$3 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$3 → KILLED
2. lambda$notRevoked$3 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$3 → KILLED
          entry.getStatusReports().stream()
377
              .noneMatch(
378 2 1. lambda$notRevoked$2 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$2 → KILLED
2. lambda$notRevoked$2 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$2 → KILLED
                  statusReport -> AuthenticatorStatus.REVOKED.equals(statusReport.getStatus()));
379
    }
380
381
    /**
382
     * Accept any authenticator whose matched metadata entry does NOT indicate a compromised
383
     * attestation key.
384
     *
385
     * <p>A metadata entry indicates a compromised attestation key if any of its {@link
386
     * MetadataBLOBPayloadEntry#getStatusReports() statusReports} entries has {@link
387
     * AuthenticatorStatus#ATTESTATION_KEY_COMPROMISE ATTESTATION_KEY_COMPROMISE} status and either
388
     * an empty {@link StatusReport#getCertificate() certificate} field or a {@link
389
     * StatusReport#getCertificate() certificate} whose public key appears in the authenticator's
390
     * {@link AuthenticatorToBeFiltered#getAttestationCertificateChain() attestation certificate
391
     * chain}.
392
     *
393
     * @see AuthenticatorStatus#ATTESTATION_KEY_COMPROMISE
394
     */
395
    public static Predicate<AuthenticatorToBeFiltered> noAttestationKeyCompromise() {
396 1 1. noAttestationKeyCompromise : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$Filters::noAttestationKeyCompromise → KILLED
      return (params) ->
397 2 1. lambda$noAttestationKeyCompromise$7 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$7 → KILLED
2. lambda$noAttestationKeyCompromise$7 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$7 → KILLED
          params.getMetadataEntry().getStatusReports().stream()
398
              .filter(
399
                  statusReport ->
400 2 1. lambda$noAttestationKeyCompromise$4 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$4 → KILLED
2. lambda$noAttestationKeyCompromise$4 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$4 → KILLED
                      AuthenticatorStatus.ATTESTATION_KEY_COMPROMISE.equals(
401
                          statusReport.getStatus()))
402
              .noneMatch(
403
                  statusReport ->
404 2 1. lambda$noAttestationKeyCompromise$6 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$6 → SURVIVED
2. lambda$noAttestationKeyCompromise$6 : negated conditional → KILLED
                      !statusReport.getCertificate().isPresent()
405
                          || (params.getAttestationCertificateChain().stream()
406 1 1. lambda$noAttestationKeyCompromise$6 : negated conditional → KILLED
                              .anyMatch(
407
                                  cert ->
408 2 1. lambda$noAttestationKeyCompromise$5 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$5 → SURVIVED
2. lambda$noAttestationKeyCompromise$5 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$5 → KILLED
                                      Arrays.equals(
409
                                          statusReport
410
                                              .getCertificate()
411
                                              .get()
412
                                              .getPublicKey()
413
                                              .getEncoded(),
414
                                          cert.getPublicKey().getEncoded()))));
415
    }
416
417
    /**
418
     * This class encapsulates parameters for filtering authenticators in the {@link
419
     * FidoMetadataServiceBuilder#filter(Predicate) filter} setting of {@link FidoMetadataService}.
420
     */
421
    @Value
422
    @AllArgsConstructor(access = AccessLevel.PRIVATE)
423
    public static class AuthenticatorToBeFiltered {
424
425
      /**
426
       * The attestation certificate chain from the <a
427
       * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-statement">attestation
428
       * statement</a> from an authenticator about ot be registered.
429
       */
430
      @NonNull List<X509Certificate> attestationCertificateChain;
431
432
      /**
433
       * A metadata BLOB entry that matches the {@link #getAttestationCertificateChain()} and {@link
434
       * #getAaguid()} in this same {@link AuthenticatorToBeFiltered} object.
435
       */
436
      @NonNull MetadataBLOBPayloadEntry metadataEntry;
437
438
      AAGUID aaguid;
439
440
      /**
441
       * The AAGUID from the <a
442
       * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attested-credential-data">attested
443
       * credential data</a> of a credential about ot be registered.
444
       *
445
       * <p>This will not be present if the attested credential data contained an AAGUID of all
446
       * zeroes.
447
       */
448
      public Optional<AAGUID> getAaguid() {
449 1 1. getAaguid : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataService$Filters$AuthenticatorToBeFiltered::getAaguid → SURVIVED
        return Optional.ofNullable(aaguid);
450
      }
451
    }
452
  }
453
454
  /**
455
   * Look up metadata entries matching a given attestation certificate chain or AAGUID.
456
   *
457
   * @param attestationCertificateChain an attestation certificate chain, presumably from a WebAuthn
458
   *     attestation statement.
459
   * @param aaguid the AAGUID of the authenticator to look up, if available.
460
   * @return All metadata entries which satisfy ALL of the following:
461
   *     <ul>
462
   *       <li>It satisfies the {@link FidoMetadataServiceBuilder#prefilter(Predicate) prefilter}.
463
   *       <li>It satisfies AT LEAST ONE of the following:
464
   *           <ul>
465
   *             <li><code>_aaguid</code> is present and equals the {@link
466
   *                 MetadataBLOBPayloadEntry#getAaguid() AAGUID} of the metadata entry.
467
   *             <li><code>_aaguid</code> is present and equals the {@link
468
   *                 MetadataStatement#getAaguid() AAGUID} of the {@link
469
   *                 MetadataBLOBPayloadEntry#getMetadataStatement() metadata statement}, if any, in
470
   *                 the metadata entry.
471
   *             <li>The certificate subject key identifier of any certificate in <code>
472
   *                 attestationCertificateChain</code> matches any element of {@link
473
   *                 MetadataBLOBPayloadEntry#getAttestationCertificateKeyIdentifiers()
474
   *                 attestationCertificateKeyIdentifiers} in the metadata entry.
475
   *             <li>The certificate subject key identifier of any certificate in <code>
476
   *                 attestationCertificateChain</code> matches any element of {@link
477
   *                 MetadataStatement#getAttestationCertificateKeyIdentifiers()
478
   *                 attestationCertificateKeyIdentifiers} in the {@link
479
   *                 MetadataBLOBPayloadEntry#getMetadataStatement() metadata statement}, if any, in
480
   *                 the metadata entry.
481
   *           </ul>
482
   *       <li>It satisfies the {@link FidoMetadataServiceBuilder#filter(Predicate) filter} together
483
   *           with <code>attestationCertificateChain</code> and <code>_aaguid</code>.
484
   *     </ul>
485
   *     In the above, <code>_aaguid</code> is the first of the following that is {@link
486
   *     Optional#isPresent() present} and not {@link AAGUID#isZero() zero}, or empty otherwise:
487
   *     <ul>
488
   *       <li>The <code>aaguid</code> argument.
489
   *       <li>The value of the X.509 extension with OID 1.3.6.1.4.1.45724.1.1.4
490
   *           (id-fido-gen-ce-aaguid), if any, in the first certificate in <code>
491
   *                             attestationCertificateChain</code>, if any.
492
   *     </ul>
493
   *
494
   * @see #findEntries(List)
495
   * @see #findEntries(List, AAGUID)
496
   */
497
  public Set<MetadataBLOBPayloadEntry> findEntries(
498 1 1. findEntries : negated conditional → KILLED
      @NonNull final List<X509Certificate> attestationCertificateChain,
499 1 1. findEntries : negated conditional → KILLED
      @NonNull final Optional<AAGUID> aaguid) {
500
501
    final Set<String> certSubjectKeyIdentifiers =
502
        attestationCertificateChain.stream()
503
            .map(
504
                cert -> {
505
                  try {
506 1 1. lambda$findEntries$16 : replaced return value with "" for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$16 → KILLED
                    return new ByteArray(CertificateParser.computeSubjectKeyIdentifier(cert))
507
                        .getHex();
508
                  } catch (NoSuchAlgorithmException e) {
509
                    throw new RuntimeException(
510
                        "SHA-1 hash algorithm is not available in JCA context.", e);
511
                  }
512
                })
513
            .collect(Collectors.toSet());
514
515
    final Optional<AAGUID> nonzeroAaguid =
516
        OptionalUtil.orElseOptional(
517 2 1. lambda$findEntries$17 : negated conditional → KILLED
2. lambda$findEntries$17 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$17 → KILLED
            aaguid.filter(a -> !a.isZero()),
518
            () -> {
519
              log.debug("findEntries: attempting to look up AAGUID from certificate");
520 1 1. lambda$findEntries$18 : negated conditional → KILLED
              if (attestationCertificateChain.isEmpty()) {
521
                return Optional.empty();
522
              } else {
523 1 1. lambda$findEntries$18 : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$18 → SURVIVED
                return CertificateParser.parseFidoAaguidExtension(
524
                        attestationCertificateChain.get(0))
525
                    .map(ByteArray::new)
526
                    .map(AAGUID::new);
527
              }
528
            });
529
530
    log.debug(
531
        "findEntries(certSubjectKeyIdentifiers = {}, aaguid = {}, nonzeroAaguid= {})",
532
        certSubjectKeyIdentifiers,
533
        aaguid,
534
        nonzeroAaguid);
535
536
    final Set<MetadataBLOBPayloadEntry> result =
537
        Stream.concat(
538
                nonzeroAaguid
539
                    .map(prefilteredEntriesByAaguid::get)
540
                    .map(Collection::stream)
541
                    .orElseGet(Stream::empty),
542
                certSubjectKeyIdentifiers.stream()
543
                    .flatMap(
544
                        cski ->
545
                            Optional.ofNullable(
546
                                    prefilteredEntriesByCertificateKeyIdentifier.get(cski))
547
                                .map(Collection::stream)
548 1 1. lambda$findEntries$19 : replaced return value with Stream.empty for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$19 → KILLED
                                .orElseGet(Stream::empty)))
549
            .filter(
550
                metadataBLOBPayloadEntry ->
551 2 1. lambda$findEntries$20 : replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$20 → KILLED
2. lambda$findEntries$20 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$20 → KILLED
                    this.filter.test(
552
                        new AuthenticatorToBeFiltered(
553
                            attestationCertificateChain,
554
                            metadataBLOBPayloadEntry,
555
                            nonzeroAaguid.orElse(null))))
556
            .collect(Collectors.toSet());
557
558
    log.debug(
559
        "findEntries(certSubjectKeyIdentifiers = {}, aaguid = {}) => {} matches",
560
        certSubjectKeyIdentifiers,
561
        aaguid,
562
        result.size());
563 1 1. findEntries : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → KILLED
    return result;
564
  }
565
566
  /**
567
   * Alias of <code>findEntries(attestationCertificateChain, Optional.empty())</code>.
568
   *
569
   * @see #findEntries(List, Optional)
570
   */
571
  public Set<MetadataBLOBPayloadEntry> findEntries(
572 1 1. findEntries : negated conditional → NO_COVERAGE
      @NonNull List<X509Certificate> attestationCertificateChain) {
573 1 1. findEntries : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → NO_COVERAGE
    return findEntries(attestationCertificateChain, Optional.empty());
574
  }
575
576
  /**
577
   * Alias of <code>findEntries(attestationCertificateChain, Optional.of(aaguid))</code>.
578
   *
579
   * @see #findEntries(List, Optional)
580
   */
581
  public Set<MetadataBLOBPayloadEntry> findEntries(
582 2 1. findEntries : negated conditional → KILLED
2. findEntries : negated conditional → KILLED
      @NonNull List<X509Certificate> attestationCertificateChain, @NonNull AAGUID aaguid) {
583 1 1. findEntries : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → KILLED
    return findEntries(attestationCertificateChain, Optional.of(aaguid));
584
  }
585
586
  /**
587
   * Find metadata entries matching the credential represented by <code>registrationResult</code>.
588
   *
589
   * <p>This is an alias of:
590
   *
591
   * <pre>
592
   * registrationResult.getAttestationTrustPath()
593
   *   .map(atp -&gt; this.findEntries(atp, new AAGUID(registrationResult.getAaguid())))
594
   *   .orElseGet(Collections::emptySet)
595
   * </pre>
596
   *
597
   * @see #findEntries(List, Optional)
598
   */
599 1 1. findEntries : negated conditional → NO_COVERAGE
  public Set<MetadataBLOBPayloadEntry> findEntries(@NonNull RegistrationResult registrationResult) {
600 1 1. findEntries : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → NO_COVERAGE
    return registrationResult
601
        .getAttestationTrustPath()
602 1 1. lambda$findEntries$21 : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$21 → NO_COVERAGE
        .map(atp -> findEntries(atp, new AAGUID(registrationResult.getAaguid())))
603
        .orElseGet(Collections::emptySet);
604
  }
605
606
  /**
607
   * Find metadata entries matching the given AAGUID.
608
   *
609
   * @see #findEntries(List, Optional)
610
   */
611 1 1. findEntries : negated conditional → KILLED
  public Set<MetadataBLOBPayloadEntry> findEntries(@NonNull AAGUID aaguid) {
612 1 1. findEntries : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → SURVIVED
    return findEntries(Collections.emptyList(), aaguid);
613
  }
614
615
  /**
616
   * Retrieve metadata entries matching the given filter.
617
   *
618
   * <p>Note: The result MAY include fewer results than the number of times the <code>filter</code>
619
   * returned <code>true</code>, because of possible duplication in the underlying data store.
620
   *
621
   * @param filter a {@link Predicate} which returns <code>true</code> for metadata entries to
622
   *     include in the result.
623
   * @return All metadata entries which satisfy the {@link
624
   *     FidoMetadataServiceBuilder#prefilter(Predicate) prefilter} AND for which the <code>filter
625
   *     </code> returns <code>true</code>.
626
   * @see #findEntries(List, Optional)
627
   */
628
  public Set<MetadataBLOBPayloadEntry> findEntries(
629 1 1. findEntries : negated conditional → KILLED
      @NonNull Predicate<MetadataBLOBPayloadEntry> filter) {
630 1 1. findEntries : replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → KILLED
    return Stream.concat(
631
            Stream.concat(
632
                prefilteredEntriesByAaguid.values().stream().flatMap(Collection::stream),
633
                prefilteredEntriesByCertificateKeyIdentifier.values().stream()
634
                    .flatMap(Collection::stream)),
635
            prefilteredUnindexedEntries.stream())
636
        .filter(filter)
637
        .collect(Collectors.toSet());
638
  }
639
640
  @Override
641
  public TrustRootsResult findTrustRoots(
642
      List<X509Certificate> attestationCertificateChain, Optional<ByteArray> aaguid) {
643 1 1. findTrustRoots : replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::findTrustRoots → KILLED
    return TrustRootsResult.builder()
644
        .trustRoots(
645
            findEntries(attestationCertificateChain, aaguid.map(AAGUID::new)).stream()
646
                .map(MetadataBLOBPayloadEntry::getMetadataStatement)
647
                .flatMap(OptionalUtil::stream)
648
                .flatMap(
649
                    metadataStatement ->
650 1 1. lambda$findTrustRoots$22 : replaced return value with Stream.empty for com/yubico/fido/metadata/FidoMetadataService::lambda$findTrustRoots$22 → KILLED
                        metadataStatement.getAttestationRootCertificates().stream())
651
                .collect(Collectors.toSet()))
652
        .certStore(certStore)
653
        .enableRevocationChecking(false)
654 1 1. lambda$findTrustRoots$23 : replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$findTrustRoots$23 → KILLED
        .policyTreeValidator(policyNode -> true)
655
        .build();
656
  }
657
}

Mutations

103

1.1
Location : <init>
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

104

1.1
Location : <init>
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

105

1.1
Location : <init>
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

131

1.1
Location : ignoreInvalidUpdateAvailableAuthenticatorVersion
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::ignoreInvalidUpdateAvailableAuthenticatorVersion → KILLED

2.2
Location : ignoreInvalidUpdateAvailableAuthenticatorVersion
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::ignoreInvalidUpdateAvailableAuthenticatorVersion → KILLED

136

1.1
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced Boolean return with True for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3 → KILLED

2.2
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced Boolean return with False for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$3 → KILLED

139

1.1
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0 → SURVIVED

2.2
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$0 → KILLED

144

1.1
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1
Killed by : com.yubico.fido.metadata.FidoMds3Spec
changed conditional boundary → KILLED

2.2
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced Boolean return with True for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1 → KILLED

3.3
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$1
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

145

1.1
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2 → KILLED

2.2
Location : lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$ignoreInvalidUpdateAvailableAuthenticatorVersion$2 → KILLED

150

1.1
Location : buildCkiMap
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

152

1.1
Location : buildCkiMap
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::buildCkiMap → KILLED

158

1.1
Location : lambda$buildCkiMap$4
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildCkiMap$4 → KILLED

165

1.1
Location : lambda$buildCkiMap$5
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildCkiMap$5 → KILLED

175

1.1
Location : lambda$buildCkiMap$7
Killed by : none
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildCkiMap$7 → NO_COVERAGE

182

1.1
Location : buildAaguidMap
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

184

1.1
Location : buildAaguidMap
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::buildAaguidMap → KILLED

191

1.1
Location : lambda$buildAaguidMap$9
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$9 → KILLED

195

1.1
Location : lambda$buildAaguidMap$11
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

2.2
Location : lambda$buildAaguidMap$11
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$11 → SURVIVED

196

1.1
Location : lambda$buildAaguidMap$13
Killed by : com.yubico.fido.metadata.FidoMds3Spec
removed call to java/util/Optional::ifPresent → KILLED

200

1.1
Location : lambda$buildAaguidMap$12
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$12 → SURVIVED

2.2
Location : lambda$buildAaguidMap$12
Killed by : none
negated conditional → SURVIVED

201

1.1
Location : lambda$buildAaguidMap$13
Killed by : none
removed call to java/util/Optional::ifPresent → SURVIVED

210

1.1
Location : lambda$buildAaguidMap$14
Killed by : none
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::lambda$buildAaguidMap$14 → NO_COVERAGE

217

1.1
Location : builder
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::builder → KILLED

240

1.1
Location : useBlob
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

241

1.1
Location : useBlob
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder$Step1::useBlob → KILLED

253

1.1
Location : useBlob
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

254

1.1
Location : useBlob
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder$Step1::useBlob → KILLED

275

1.1
Location : prefilter
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

277

1.1
Location : prefilter
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::prefilter → KILLED

308

1.1
Location : filter
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

310

1.1
Location : filter
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::filter → KILLED

322

1.1
Location : certStore
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

324

1.1
Location : certStore
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::certStore → KILLED

339

1.1
Location : build
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$FidoMetadataServiceBuilder::build → KILLED

364

1.1
Location : lambda$allOf$0
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$0 → NO_COVERAGE

2.2
Location : lambda$allOf$0
Killed by : none
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$0 → NO_COVERAGE

3.3
Location : allOf
Killed by : none
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$Filters::allOf → NO_COVERAGE

4.4
Location : lambda$allOf$1
Killed by : none
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$1 → NO_COVERAGE

5.5
Location : lambda$allOf$1
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$allOf$1 → NO_COVERAGE

375

1.1
Location : notRevoked
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$Filters::notRevoked → KILLED

376

1.1
Location : lambda$notRevoked$3
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$3 → KILLED

2.2
Location : lambda$notRevoked$3
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$3 → KILLED

378

1.1
Location : lambda$notRevoked$2
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$2 → KILLED

2.2
Location : lambda$notRevoked$2
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$notRevoked$2 → KILLED

396

1.1
Location : noAttestationKeyCompromise
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService$Filters::noAttestationKeyCompromise → KILLED

397

1.1
Location : lambda$noAttestationKeyCompromise$7
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$7 → KILLED

2.2
Location : lambda$noAttestationKeyCompromise$7
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$7 → KILLED

400

1.1
Location : lambda$noAttestationKeyCompromise$4
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$4 → KILLED

2.2
Location : lambda$noAttestationKeyCompromise$4
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$4 → KILLED

404

1.1
Location : lambda$noAttestationKeyCompromise$6
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

2.2
Location : lambda$noAttestationKeyCompromise$6
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$6 → SURVIVED

406

1.1
Location : lambda$noAttestationKeyCompromise$6
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

408

1.1
Location : lambda$noAttestationKeyCompromise$5
Killed by : none
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$5 → SURVIVED

2.2
Location : lambda$noAttestationKeyCompromise$5
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService$Filters::lambda$noAttestationKeyCompromise$5 → KILLED

449

1.1
Location : getAaguid
Killed by : none
replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataService$Filters$AuthenticatorToBeFiltered::getAaguid → SURVIVED

498

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

499

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

506

1.1
Location : lambda$findEntries$16
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with "" for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$16 → KILLED

517

1.1
Location : lambda$findEntries$17
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

2.2
Location : lambda$findEntries$17
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$17 → KILLED

520

1.1
Location : lambda$findEntries$18
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

523

1.1
Location : lambda$findEntries$18
Killed by : none
replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$18 → SURVIVED

548

1.1
Location : lambda$findEntries$19
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with Stream.empty for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$19 → KILLED

551

1.1
Location : lambda$findEntries$20
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with true for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$20 → KILLED

2.2
Location : lambda$findEntries$20
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$20 → KILLED

563

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → KILLED

572

1.1
Location : findEntries
Killed by : none
negated conditional → NO_COVERAGE

573

1.1
Location : findEntries
Killed by : none
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → NO_COVERAGE

582

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

2.2
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

583

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → KILLED

599

1.1
Location : findEntries
Killed by : none
negated conditional → NO_COVERAGE

600

1.1
Location : findEntries
Killed by : none
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → NO_COVERAGE

602

1.1
Location : lambda$findEntries$21
Killed by : none
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::lambda$findEntries$21 → NO_COVERAGE

611

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

612

1.1
Location : findEntries
Killed by : none
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → SURVIVED

629

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
negated conditional → KILLED

630

1.1
Location : findEntries
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with Collections.emptySet for com/yubico/fido/metadata/FidoMetadataService::findEntries → KILLED

643

1.1
Location : findTrustRoots
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with null for com/yubico/fido/metadata/FidoMetadataService::findTrustRoots → KILLED

650

1.1
Location : lambda$findTrustRoots$22
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced return value with Stream.empty for com/yubico/fido/metadata/FidoMetadataService::lambda$findTrustRoots$22 → KILLED

654

1.1
Location : lambda$findTrustRoots$23
Killed by : com.yubico.fido.metadata.FidoMds3Spec
replaced boolean return with false for com/yubico/fido/metadata/FidoMetadataService::lambda$findTrustRoots$23 → KILLED

Active mutators

Tests examined


Report generated by PIT 1.15.0