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
   */
61
  TrustRootsResult findTrustRoots(
62
      List<X509Certificate> attestationCertificateChain, Optional<ByteArray> aaguid);
63
64
  /**
65
   * A result of looking up attestation trust roots for a particular attestation statement.
66
   *
67
   * <p>This primarily consists of a set of trust root certificates - see {@link
68
   * TrustRootsResultBuilder#trustRoots(Set) trustRoots(Set)} - but may also:
69
   *
70
   * <ul>
71
   *   <li>include a {@link CertStore} of additional CRLs and/or intermediate certificates to use
72
   *       during certificate path validation - see {@link
73
   *       TrustRootsResultBuilder#certStore(CertStore) certStore(CertStore)};
74
   *   <li>disable certificate revocation checking for the relevant attestation statement - see
75
   *       {@link TrustRootsResultBuilder#enableRevocationChecking(boolean)
76
   *       enableRevocationChecking(boolean)}; and/or
77
   *   <li>define a policy tree validator for the PKIX policy tree result - see {@link
78
   *       TrustRootsResultBuilder#policyTreeValidator(Predicate) policyTreeValidator(Predicate)}.
79
   * </ul>
80
   */
81
  @Value
82
  @Builder(toBuilder = true)
83
  class TrustRootsResult {
84
85
    /**
86
     * A set of attestation root certificates trusted to certify the relevant attestation statement.
87
     * If the attestation statement is not trusted, or if no trust roots were found, this should be
88
     * an empty set.
89
     */
90
    @NonNull private final Set<X509Certificate> trustRoots;
91
92
    /**
93
     * A {@link CertStore} of additional CRLs and/or intermediate certificates to use during
94
     * certificate path validation, if any. This will not be used if {@link
95
     * TrustRootsResultBuilder#trustRoots(Set) trustRoots} is empty.
96
     *
97
     * <p>Any certificates included in this {@link CertStore} are NOT considered trusted; they will
98
     * be trusted only if they chain to any of the {@link TrustRootsResultBuilder#trustRoots(Set)
99
     * trustRoots}.
100
     *
101
     * <p>The default is <code>null</code>.
102
     */
103
    @Builder.Default private final CertStore certStore = null;
104
105
    /**
106
     * Whether certificate revocation should be checked during certificate path validation.
107
     *
108
     * <p>The default is <code>true</code>.
109
     */
110
    @Builder.Default private final boolean enableRevocationChecking = true;
111
112
    /**
113
     * If non-null, the PolicyQualifiersRejected flag will be set to false during certificate path
114
     * validation. See {@link
115
     * java.security.cert.PKIXParameters#setPolicyQualifiersRejected(boolean)}.
116
     *
117
     * <p>The given {@link Predicate} will be used to validate the policy tree. The {@link
118
     * Predicate} should return <code>true</code> if the policy tree is acceptable, and <code>false
119
     * </code> otherwise.
120
     *
121
     * <p>Depending on your <code>"PKIX"</code> JCA provider configuration, this may be required if
122
     * any certificate in the certificate path contains a certificate policies extension marked
123
     * critical. If this is not set, then such a certificate will be rejected by the certificate
124
     * path validator from the default provider.
125
     *
126
     * <p>Consult the <a
127
     * href="https://docs.oracle.com/en/java/javase/17/security/java-pki-programmers-guide.html#GUID-3AD41382-E729-469B-83EE-CB2FE66D71D8">Java
128
     * PKI Programmer's Guide</a> for how to use the {@link PolicyNode} argument of the {@link
129
     * Predicate}.
130
     *
131
     * <p>The default is <code>null</code>.
132
     */
133
    @Builder.Default private final Predicate<PolicyNode> policyTreeValidator = null;
134
135
    private TrustRootsResult(
136 1 1. <init> : negated conditional → KILLED
        @NonNull Set<X509Certificate> trustRoots,
137
        CertStore certStore,
138
        boolean enableRevocationChecking,
139
        Predicate<PolicyNode> policyTreeValidator) {
140
      this.trustRoots = CollectionUtil.immutableSet(trustRoots);
141
      this.certStore = certStore;
142
      this.enableRevocationChecking = enableRevocationChecking;
143
      this.policyTreeValidator = policyTreeValidator;
144
    }
145
146
    /**
147
     * A {@link CertStore} of additional CRLs and/or intermediate certificates to use during
148
     * certificate path validation, if any. This will not be used if {@link
149
     * TrustRootsResultBuilder#trustRoots(Set) trustRoots} is empty.
150
     *
151
     * <p>Any certificates included in this {@link CertStore} are NOT considered trusted; they will
152
     * be trusted only if they chain to any of the {@link TrustRootsResultBuilder#trustRoots(Set)
153
     * trustRoots}.
154
     *
155
     * <p>The default is <code>null</code>.
156
     */
157
    public Optional<CertStore> getCertStore() {
158 1 1. getCertStore : replaced return value with Optional.empty for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getCertStore → KILLED
      return Optional.ofNullable(certStore);
159
    }
160
161
    /**
162
     * If non-null, the PolicyQualifiersRejected flag will be set to false during certificate path
163
     * validation. See {@link
164
     * java.security.cert.PKIXParameters#setPolicyQualifiersRejected(boolean)}.
165
     *
166
     * <p>The given {@link Predicate} will be used to validate the policy tree. The {@link
167
     * Predicate} should return <code>true</code> if the policy tree is acceptable, and <code>false
168
     * </code> otherwise.
169
     *
170
     * <p>Depending on your <code>"PKIX"</code> JCA provider configuration, this may be required if
171
     * any certificate in the certificate path contains a certificate policies extension marked
172
     * critical. If this is not set, then such a certificate will be rejected by the certificate
173
     * path validator from the default provider.
174
     *
175
     * <p>Consult the <a
176
     * href="https://docs.oracle.com/en/java/javase/17/security/java-pki-programmers-guide.html#GUID-3AD41382-E729-469B-83EE-CB2FE66D71D8">Java
177
     * PKI Programmer's Guide</a> for how to use the {@link PolicyNode} argument of the {@link
178
     * Predicate}.
179
     *
180
     * <p>The default is <code>null</code>.
181
     */
182
    public Optional<Predicate<PolicyNode>> getPolicyTreeValidator() {
183 1 1. getPolicyTreeValidator : replaced return value with Optional.empty for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getPolicyTreeValidator → KILLED
      return Optional.ofNullable(policyTreeValidator);
184
    }
185
186
    public static TrustRootsResultBuilder.Step1 builder() {
187 1 1. builder : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::builder → KILLED
      return new TrustRootsResultBuilder.Step1();
188
    }
189
190
    public static class TrustRootsResultBuilder {
191
      public static class Step1 {
192
        /**
193
         * A set of attestation root certificates trusted to certify the relevant attestation
194
         * statement. If the attestation statement is not trusted, or if no trust roots were found,
195
         * this should be an empty set.
196
         */
197 1 1. trustRoots : negated conditional → KILLED
        public TrustRootsResultBuilder trustRoots(@NonNull Set<X509Certificate> trustRoots) {
198 1 1. trustRoots : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder$Step1::trustRoots → KILLED
          return new TrustRootsResultBuilder().trustRoots(trustRoots);
199
        }
200
      }
201
202
      /**
203
       * A set of attestation root certificates trusted to certify the relevant attestation
204
       * statement. If the attestation statement is not trusted, or if no trust roots were found,
205
       * this should be an empty set.
206
       */
207
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
208
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder trustRoots(
209
          @NonNull final Set<X509Certificate> trustRoots) {
210 1 1. trustRoots : negated conditional → KILLED
        if (trustRoots == null) {
211
          throw new java.lang.NullPointerException("trustRoots is marked non-null but is null");
212
        }
213
        this.trustRoots = trustRoots;
214 1 1. trustRoots : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::trustRoots → KILLED
        return this;
215
      }
216
217
      /**
218
       * A {@link CertStore} of additional CRLs and/or intermediate certificates to use during
219
       * certificate path validation, if any. This will not be used if {@link
220
       * TrustRootsResultBuilder#trustRoots(Set) trustRoots} is empty.
221
       *
222
       * <p>Any certificates included in this {@link CertStore} are NOT considered trusted; they
223
       * will be trusted only if they chain to any of the {@link
224
       * TrustRootsResultBuilder#trustRoots(Set) trustRoots}.
225
       *
226
       * <p>The default is <code>null</code>.
227
       */
228
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
229
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder certStore(
230
          final CertStore certStore) {
231
        this.certStore$value = certStore;
232
        certStore$set = true;
233 1 1. certStore : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::certStore → KILLED
        return this;
234
      }
235
236
      /**
237
       * Whether certificate revocation should be checked during certificate path validation.
238
       *
239
       * <p>The default is <code>true</code>.
240
       */
241
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
242
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder
243
          enableRevocationChecking(final boolean enableRevocationChecking) {
244
        this.enableRevocationChecking$value = enableRevocationChecking;
245
        enableRevocationChecking$set = true;
246 1 1. enableRevocationChecking : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::enableRevocationChecking → KILLED
        return this;
247
      }
248
249
      /**
250
       * If non-null, the PolicyQualifiersRejected flag will be set to false during certificate path
251
       * validation. See {@link
252
       * java.security.cert.PKIXParameters#setPolicyQualifiersRejected(boolean)}.
253
       *
254
       * <p>The given {@link Predicate} will be used to validate the policy tree. The {@link
255
       * Predicate} should return <code>true</code> if the policy tree is acceptable, and <code>
256
       * false
257
       * </code> otherwise.
258
       *
259
       * <p>Depending on your <code>"PKIX"</code> JCA provider configuration, this may be required
260
       * if any certificate in the certificate path contains a certificate policies extension marked
261
       * critical. If this is not set, then such a certificate will be rejected by the certificate
262
       * path validator from the default provider.
263
       *
264
       * <p>Consult the <a
265
       * href="https://docs.oracle.com/en/java/javase/17/security/java-pki-programmers-guide.html#GUID-3AD41382-E729-469B-83EE-CB2FE66D71D8">Java
266
       * PKI Programmer's Guide</a> for how to use the {@link PolicyNode} argument of the {@link
267
       * Predicate}.
268
       *
269
       * <p>The default is <code>null</code>.
270
       */
271
      // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
272
      public AttestationTrustSource.TrustRootsResult.TrustRootsResultBuilder policyTreeValidator(
273
          final Predicate<PolicyNode> policyTreeValidator) {
274
        this.policyTreeValidator$value = policyTreeValidator;
275
        policyTreeValidator$set = true;
276 1 1. policyTreeValidator : replaced return value with null for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult$TrustRootsResultBuilder::policyTreeValidator → KILLED
        return this;
277
      }
278
    }
279
280
    /**
281
     * A set of attestation root certificates trusted to certify the relevant attestation statement.
282
     * If the attestation statement is not trusted, or if no trust roots were found, this should be
283
     * an empty set.
284
     */
285
    // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
286
    @NonNull
287
    public Set<X509Certificate> getTrustRoots() {
288 1 1. getTrustRoots : replaced return value with Collections.emptySet for com/yubico/webauthn/attestation/AttestationTrustSource$TrustRootsResult::getTrustRoots → KILLED
      return this.trustRoots;
289
    }
290
291
    /** Whether certificate revocation should be checked during certificate path validation. */
292
    // TODO: Let this auto-generate (investigate why Lombok fails to copy javadoc)
293
    public boolean isEnableRevocationChecking() {
294 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;
295
    }
296
  }
297
}

Mutations

136

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

158

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

183

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

187

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

197

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

198

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

210

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

214

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

233

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

246

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

276

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

288

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

294

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