AttestationTrustSource.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.attestation;
26
27
import com.yubico.internal.util.CollectionUtil;
28
import com.yubico.webauthn.data.ByteArray;
29
import java.security.cert.CertStore;
30
import java.security.cert.PolicyNode;
31
import java.security.cert.X509Certificate;
32
import java.util.List;
33
import java.util.Optional;
34
import java.util.Set;
35
import java.util.function.Predicate;
36
import lombok.Builder;
37
import lombok.NonNull;
38
import lombok.Value;
39
40
/** Abstraction of a repository which can look up trust roots for authenticator attestation. */
41
public interface AttestationTrustSource {
42
43
  /**
44
   * Attempt to look up attestation trust roots for an authenticator.
45
   *
46
   * <p>Note that it is possible for the same trust root to be used for different certificate
47
   * chains. For example, an authenticator vendor may make two different authenticator models, each
48
   * with its own attestation leaf certificate but both signed by the same attestation root
49
   * certificate. If a Relying Party trusts one of those authenticator models but not the other,
50
   * then its implementation of this method MUST return an empty set for the untrusted certificate
51
   * chain.
52
   *
53
   * @param attestationCertificateChain the attestation certificate chain for the authenticator.
54
   * @param aaguid the AAGUID of the authenticator, if available.
55
   * @return A set of attestation root certificates trusted to attest for this authenticator, if any
56
   *     are available. If no trust roots are found, or if this authenticator is not trusted, return
57
   *     an empty result. Implementations MAY reuse the same result object, or parts of it, for
58
   *     multiple calls of this method, even with different arguments, but MUST return an empty set
59
   *     of trust roots for authenticators that should not be trusted.
60
   * @since 2.0.0
61
   */
62
  TrustRootsResult findTrustRoots(
63
      List<X509Certificate> attestationCertificateChain, Optional<ByteArray> aaguid);
64
65
  /**
66
   * A result of looking up attestation trust roots for a particular attestation statement.
67
   *
68
   * <p>This primarily consists of a set of trust root certificates - see {@link
69
   * TrustRootsResultBuilder#trustRoots(Set) trustRoots(Set)} - but may also:
70
   *
71
   * <ul>
72
   *   <li>include a {@link CertStore} of additional CRLs and/or intermediate certificates to use
73
   *       during certificate path validation - see {@link
74
   *       TrustRootsResultBuilder#certStore(CertStore) certStore(CertStore)};
75
   *   <li>disable certificate revocation checking for the relevant attestation statement - see
76
   *       {@link TrustRootsResultBuilder#enableRevocationChecking(boolean)
77
   *       enableRevocationChecking(boolean)}; and/or
78
   *   <li>define a policy tree validator for the PKIX policy tree result - see {@link
79
   *       TrustRootsResultBuilder#policyTreeValidator(Predicate) policyTreeValidator(Predicate)}.
80
   * </ul>
81
   *
82
   * @since 2.0.0
83
   */
84
  @Value
85
  @Builder(toBuilder = true)
86
  class TrustRootsResult {
87
88
    /**
89
     * A set of attestation root certificates trusted to certify the relevant attestation statement.
90
     * If the attestation statement is not trusted, or if no trust roots were found, this should be
91
     * an empty set.
92
     *
93
     * @since 2.0.0
94
     */
95
    @NonNull private final Set<X509Certificate> trustRoots;
96
97
    /**
98
     * A {@link CertStore} of additional CRLs and/or intermediate certificates to use during
99
     * certificate path validation, if any. This will not be used if {@link
100
     * TrustRootsResultBuilder#trustRoots(Set) trustRoots} is empty.
101
     *
102
     * <p>Any certificates included in this {@link CertStore} are NOT considered trusted; they will
103
     * be trusted only if they chain to any of the {@link TrustRootsResultBuilder#trustRoots(Set)
104
     * trustRoots}.
105
     *
106
     * <p>The default is <code>null</code>.
107
     *
108
     * @since 2.0.0
109
     */
110
    @Builder.Default private final CertStore certStore = null;
111
112
    /**
113
     * Whether certificate revocation should be checked during certificate path validation.
114
     *
115
     * <p>The default is <code>true</code>.
116
     *
117
     * @since 2.0.0
118
     */
119
    @Builder.Default private final boolean enableRevocationChecking = true;
120
121
    /**
122
     * If non-null, the PolicyQualifiersRejected flag will be set to false during certificate path
123
     * validation. See {@link
124
     * java.security.cert.PKIXParameters#setPolicyQualifiersRejected(boolean)}.
125
     *
126
     * <p>The given {@link Predicate} will be used to validate the policy tree. The {@link
127
     * Predicate} should return <code>true</code> if the policy tree is acceptable, and <code>false
128
     * </code> otherwise.
129
     *
130
     * <p>Depending on your <code>"PKIX"</code> JCA provider configuration, this may be required if
131
     * any certificate in the certificate path contains a certificate policies extension marked
132
     * critical. If this is not set, then such a certificate will be rejected by the certificate
133
     * path validator from the default provider.
134
     *
135
     * <p>Consult the <a
136
     * href="https://docs.oracle.com/en/java/javase/17/security/java-pki-programmers-guide.html#GUID-3AD41382-E729-469B-83EE-CB2FE66D71D8">Java
137
     * PKI Programmer's Guide</a> for how to use the {@link PolicyNode} argument of the {@link
138
     * Predicate}.
139
     *
140
     * <p>The default is <code>null</code>.
141
     *
142
     * @since 2.1.0
143
     */
144
    @Builder.Default private final Predicate<PolicyNode> policyTreeValidator = null;
145
146
    private TrustRootsResult(
147 1 1. <init> : negated conditional → KILLED
        @NonNull Set<X509Certificate> trustRoots,
148
        CertStore certStore,
149
        boolean enableRevocationChecking,
150
        Predicate<PolicyNode> policyTreeValidator) {
151
      this.trustRoots = CollectionUtil.immutableSet(trustRoots);
152
      this.certStore = certStore;
153
      this.enableRevocationChecking = enableRevocationChecking;
154
      this.policyTreeValidator = policyTreeValidator;
155
    }
156
157
    /**
158
     * A {@link CertStore} of additional CRLs and/or intermediate certificates to use during
159
     * certificate path validation, if any. This will not be used if {@link
160
     * TrustRootsResultBuilder#trustRoots(Set) trustRoots} is empty.
161
     *
162
     * <p>Any certificates included in this {@link CertStore} are NOT considered trusted; they will
163
     * be trusted only if they chain to any of the {@link TrustRootsResultBuilder#trustRoots(Set)
164
     * trustRoots}.
165
     *
166
     * <p>The default is <code>null</code>.
167
     *
168
     * @since 2.0.0
169
     */
170
    public Optional<CertStore> getCertStore() {
171 1 1. getCertStore : replaced return value with Optional.empty for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getCertStore → KILLED
      return Optional.ofNullable(certStore);
172
    }
173
174
    /**
175
     * If non-null, the PolicyQualifiersRejected flag will be set to false during certificate path
176
     * validation. See {@link
177
     * java.security.cert.PKIXParameters#setPolicyQualifiersRejected(boolean)}.
178
     *
179
     * <p>The given {@link Predicate} will be used to validate the policy tree. The {@link
180
     * Predicate} should return <code>true</code> if the policy tree is acceptable, and <code>false
181
     * </code> otherwise.
182
     *
183
     * <p>Depending on your <code>"PKIX"</code> JCA provider configuration, this may be required if
184
     * any certificate in the certificate path contains a certificate policies extension marked
185
     * critical. If this is not set, then such a certificate will be rejected by the certificate
186
     * path validator from the default provider.
187
     *
188
     * <p>Consult the <a
189
     * href="https://docs.oracle.com/en/java/javase/17/security/java-pki-programmers-guide.html#GUID-3AD41382-E729-469B-83EE-CB2FE66D71D8">Java
190
     * PKI Programmer's Guide</a> for how to use the {@link PolicyNode} argument of the {@link
191
     * Predicate}.
192
     *
193
     * <p>The default is <code>null</code>.
194
     *
195
     * @since 2.1.0
196
     */
197
    public Optional<Predicate<PolicyNode>> getPolicyTreeValidator() {
198 1 1. getPolicyTreeValidator : replaced return value with Optional.empty for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getPolicyTreeValidator → KILLED
      return Optional.ofNullable(policyTreeValidator);
199
    }
200
201
    public static TrustRootsResultBuilder.Step1 builder() {
202 1 1. builder : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::builder → KILLED
      return new TrustRootsResultBuilder.Step1();
203
    }
204
205
    public static class TrustRootsResultBuilder {
206
      public static class Step1 {
207
        /**
208
         * A set of attestation root certificates trusted to certify the relevant attestation
209
         * statement. If the attestation statement is not trusted, or if no trust roots were found,
210
         * this should be an empty set.
211
         *
212
         * @since 2.0.0
213
         */
214 1 1. trustRoots : negated conditional → KILLED
        public TrustRootsResultBuilder trustRoots(@NonNull Set<X509Certificate> trustRoots) {
215 1 1. trustRoots : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder$Step1::trustRoots → KILLED
          return new TrustRootsResultBuilder().trustRoots(trustRoots);
216
        }
217
      }
218
219
      /**
220
       * A set of attestation root certificates trusted to certify the relevant attestation
221
       * statement. If the attestation statement is not trusted, or if no trust roots were found,
222
       * this should be an empty set.
223
       *
224
       * @since 2.0.0
225
       */
226
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
227
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder trustRoots(
228
          @NonNull final Set<X509Certificate> trustRoots) {
229 1 1. trustRoots : negated conditional → KILLED
        if (trustRoots == null) {
230
          throw new java.lang.NullPointerException("trustRoots is marked non-null but is null");
231
        }
232
        this.trustRoots = trustRoots;
233 1 1. trustRoots : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::trustRoots → KILLED
        return this;
234
      }
235
236
      /**
237
       * A {@link CertStore} of additional CRLs and/or intermediate certificates to use during
238
       * certificate path validation, if any. This will not be used if {@link
239
       * TrustRootsResultBuilder#trustRoots(Set) trustRoots} is empty.
240
       *
241
       * <p>Any certificates included in this {@link CertStore} are NOT considered trusted; they
242
       * will be trusted only if they chain to any of the {@link
243
       * TrustRootsResultBuilder#trustRoots(Set) trustRoots}.
244
       *
245
       * <p>The default is <code>null</code>.
246
       *
247
       * @since 2.0.0
248
       */
249
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
250
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder certStore(
251
          final CertStore certStore) {
252
        this.certStore$value = certStore;
253
        certStore$set = true;
254 1 1. certStore : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::certStore → KILLED
        return this;
255
      }
256
257
      /**
258
       * Whether certificate revocation should be checked during certificate path validation.
259
       *
260
       * <p>The default is <code>true</code>.
261
       *
262
       * @since 2.0.0
263
       */
264
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
265
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder
266
          enableRevocationChecking(final boolean enableRevocationChecking) {
267
        this.enableRevocationChecking$value = enableRevocationChecking;
268
        enableRevocationChecking$set = true;
269 1 1. enableRevocationChecking : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::enableRevocationChecking → KILLED
        return this;
270
      }
271
272
      /**
273
       * If non-null, the PolicyQualifiersRejected flag will be set to false during certificate path
274
       * validation. See {@link
275
       * java.security.cert.PKIXParameters#setPolicyQualifiersRejected(boolean)}.
276
       *
277
       * <p>The given {@link Predicate} will be used to validate the policy tree. The {@link
278
       * Predicate} should return <code>true</code> if the policy tree is acceptable, and <code>
279
       * false
280
       * </code> otherwise.
281
       *
282
       * <p>Depending on your <code>"PKIX"</code> JCA provider configuration, this may be required
283
       * if any certificate in the certificate path contains a certificate policies extension marked
284
       * critical. If this is not set, then such a certificate will be rejected by the certificate
285
       * path validator from the default provider.
286
       *
287
       * <p>Consult the <a
288
       * href="https://docs.oracle.com/en/java/javase/17/security/java-pki-programmers-guide.html#GUID-3AD41382-E729-469B-83EE-CB2FE66D71D8">Java
289
       * PKI Programmer's Guide</a> for how to use the {@link PolicyNode} argument of the {@link
290
       * Predicate}.
291
       *
292
       * <p>The default is <code>null</code>.
293
       *
294
       * @since 2.1.0
295
       */
296
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
297
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder policyTreeValidator(
298
          final Predicate<PolicyNode> policyTreeValidator) {
299
        this.policyTreeValidator$value = policyTreeValidator;
300
        policyTreeValidator$set = true;
301 1 1. policyTreeValidator : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::policyTreeValidator → KILLED
        return this;
302
      }
303
    }
304
305
    /**
306
     * A set of attestation root certificates trusted to certify the relevant attestation statement.
307
     * If the attestation statement is not trusted, or if no trust roots were found, this should be
308
     * an empty set.
309
     *
310
     * @since 2.0.0
311
     */
312
    // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
313
    @NonNull
314
    public Set<X509Certificate> getTrustRoots() {
315 1 1. getTrustRoots : replaced return value with Collections.emptySet for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getTrustRoots → KILLED
      return this.trustRoots;
316
    }
317
318
    /** Whether certificate revocation should be checked during certificate path validation. */
319
    // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
320
    public boolean isEnableRevocationChecking() {
321 2 1. isEnableRevocationChecking : replaced boolean return with false for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::isEnableRevocationChecking → SURVIVED
2. isEnableRevocationChecking : replaced boolean return with true for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::isEnableRevocationChecking → KILLED
      return this.enableRevocationChecking;
322
    }
323
  }
324
}

Mutations

147

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

171

1.1
Location : getCertStore
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with Optional.empty for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getCertStore → KILLED

198

1.1
Location : getPolicyTreeValidator
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with Optional.empty for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getPolicyTreeValidator → KILLED

202

1.1
Location : builder
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::builder → KILLED

214

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

215

1.1
Location : trustRoots
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder$Step1::trustRoots → KILLED

229

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

233

1.1
Location : trustRoots
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::trustRoots → KILLED

254

1.1
Location : certStore
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::certStore → KILLED

269

1.1
Location : enableRevocationChecking
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::enableRevocationChecking → KILLED

301

1.1
Location : policyTreeValidator
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::policyTreeValidator → KILLED

315

1.1
Location : getTrustRoots
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced return value with Collections.emptySet for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getTrustRoots → KILLED

321

1.1
Location : isEnableRevocationChecking
Killed by : com.yubico.webauthn.RelyingPartyV2RegistrationSpec
replaced boolean return with true for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::isEnableRevocationChecking → KILLED

2.2
Location : isEnableRevocationChecking
Killed by : none
replaced boolean return with false for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::isEnableRevocationChecking → SURVIVED

Active mutators

Tests examined


Report generated by PIT 1.15.0