AssertionResult.java

1
// Copyright (c) 2018, Yubico AB
2
// All rights reserved.
3
//
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are met:
6
//
7
// 1. Redistributions of source code must retain the above copyright notice, this
8
//    list of conditions and the following disclaimer.
9
//
10
// 2. Redistributions in binary form must reproduce the above copyright notice,
11
//    this list of conditions and the following disclaimer in the documentation
12
//    and/or other materials provided with the distribution.
13
//
14
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25
package com.yubico.webauthn;
26
27
import com.fasterxml.jackson.annotation.JsonCreator;
28
import com.fasterxml.jackson.annotation.JsonIgnore;
29
import com.fasterxml.jackson.annotation.JsonProperty;
30
import com.yubico.webauthn.data.AuthenticatorAssertionExtensionOutputs;
31
import com.yubico.webauthn.data.AuthenticatorAssertionResponse;
32
import com.yubico.webauthn.data.AuthenticatorAttachment;
33
import com.yubico.webauthn.data.AuthenticatorData;
34
import com.yubico.webauthn.data.AuthenticatorDataFlags;
35
import com.yubico.webauthn.data.AuthenticatorResponse;
36
import com.yubico.webauthn.data.ByteArray;
37
import com.yubico.webauthn.data.ClientAssertionExtensionOutputs;
38
import com.yubico.webauthn.data.Extensions;
39
import com.yubico.webauthn.data.PublicKeyCredential;
40
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
41
import com.yubico.webauthn.data.UserIdentity;
42
import java.util.Optional;
43
import lombok.AccessLevel;
44
import lombok.Getter;
45
import lombok.NonNull;
46
import lombok.Value;
47
48
/** The result of a call to {@link RelyingParty#finishAssertion(FinishAssertionOptions)}. */
49
@Value
50
public class AssertionResult {
51
52
  /** <code>true</code> if the assertion was verified successfully. */
53
  private final boolean success;
54
55
  @JsonProperty
56
  @Getter(AccessLevel.NONE)
57
  private final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
58
      credentialResponse;
59
60
  /**
61
   * The {@link RegisteredCredential} that was returned by {@link
62
   * CredentialRepository#lookup(ByteArray, ByteArray)} and whose public key was used to
63
   * successfully verify the assertion signature.
64
   *
65
   * <p>NOTE: The {@link RegisteredCredential#getSignatureCount() signature count}, {@link
66
   * RegisteredCredential#isBackupEligible() backup eligibility} and {@link
67
   * RegisteredCredential#isBackedUp() backup state} properties in this object will reflect the
68
   * state <i>before</i> the assertion operation, not the new state. When updating your database
69
   * state, use the signature counter and backup state from {@link #getSignatureCount()}, {@link
70
   * #isBackupEligible()} and {@link #isBackedUp()} instead.
71
   */
72
  private final RegisteredCredential credential;
73
74
  /**
75
   * The username of the authenticated user.
76
   *
77
   * @see #getUserHandle()
78
   */
79
  @NonNull private final String username;
80
81
  /**
82
   * <code>true</code> if and only if at least one of the following is true:
83
   *
84
   * <ul>
85
   *   <li>The {@link AuthenticatorData#getSignatureCounter() signature counter value} in the
86
   *       assertion was strictly greater than {@link RegisteredCredential#getSignatureCount() the
87
   *       stored one}.
88
   *   <li>The {@link AuthenticatorData#getSignatureCounter() signature counter value} in the
89
   *       assertion and {@link RegisteredCredential#getSignatureCount() the stored one} were both
90
   *       zero.
91
   * </ul>
92
   *
93
   * @see <a
94
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1.
95
   *     Authenticator Data</a>
96
   * @see AuthenticatorData#getSignatureCounter()
97
   * @see RegisteredCredential#getSignatureCount()
98
   * @see com.yubico.webauthn.RelyingParty.RelyingPartyBuilder#validateSignatureCounter(boolean)
99
   */
100
  private final boolean signatureCounterValid;
101
102
  @JsonCreator
103
  AssertionResult(
104
      @JsonProperty("success") boolean success,
105 1 1. <init> : negated conditional → KILLED
      @NonNull @JsonProperty("credentialResponse")
106
          PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
107
              credentialResponse,
108 1 1. <init> : negated conditional → KILLED
      @NonNull @JsonProperty("credential") RegisteredCredential credential,
109 1 1. <init> : negated conditional → KILLED
      @NonNull @JsonProperty("username") String username,
110
      @JsonProperty("signatureCounterValid") boolean signatureCounterValid) {
111
    this.success = success;
112
    this.credentialResponse = credentialResponse;
113
    this.credential = credential;
114
    this.username = username;
115
    this.signatureCounterValid = signatureCounterValid;
116
  }
117
118
  /**
119
   * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">credential
120
   * ID</a> of the credential used for the assertion.
121
   *
122
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">Credential
123
   *     ID</a>
124
   * @see PublicKeyCredentialRequestOptions#getAllowCredentials()
125
   * @deprecated Use {@link #getCredential()}.{@link RegisteredCredential#getCredentialId()
126
   *     getCredentialId()} instead.
127
   */
128
  @Deprecated
129
  @JsonIgnore
130
  public ByteArray getCredentialId() {
131 1 1. getCredentialId : replaced return value with null for com/yubico/webauthn/AssertionResult::getCredentialId → KILLED
    return credential.getCredentialId();
132
  }
133
134
  /**
135
   * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">user handle</a>
136
   * of the authenticated user.
137
   *
138
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User Handle</a>
139
   * @see UserIdentity#getId()
140
   * @see #getUsername()
141
   * @deprecated Use {@link #getCredential()}.{@link RegisteredCredential#getUserHandle()
142
   *     getUserHandle()} instead.
143
   */
144
  @Deprecated
145
  @JsonIgnore
146
  public ByteArray getUserHandle() {
147 1 1. getUserHandle : replaced return value with null for com/yubico/webauthn/AssertionResult::getUserHandle → KILLED
    return credential.getUserHandle();
148
  }
149
150
  /**
151
   * Check whether the <a href="https://www.w3.org/TR/webauthn/#user-verification">user
152
   * verification</a> as performed during the authentication ceremony.
153
   *
154
   * <p>This flag is also available via <code>
155
   * {@link PublicKeyCredential}.{@link PublicKeyCredential#getResponse() getResponse()}.{@link AuthenticatorResponse#getParsedAuthenticatorData() getParsedAuthenticatorData()}.{@link AuthenticatorData#getFlags() getFlags()}.{@link AuthenticatorDataFlags#UV UV}
156
   * </code>.
157
   *
158
   * @return <code>true</code> if and only if the authenticator claims to have performed user
159
   *     verification during the authentication ceremony.
160
   * @see <a href="https://www.w3.org/TR/webauthn/#user-verification">User Verification</a>
161
   * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-uv">UV flag in §6.1. Authenticator
162
   *     Data</a>
163
   */
164
  @JsonIgnore
165
  public boolean isUserVerified() {
166 2 1. isUserVerified : replaced boolean return with false for com/yubico/webauthn/AssertionResult::isUserVerified → KILLED
2. isUserVerified : replaced boolean return with true for com/yubico/webauthn/AssertionResult::isUserVerified → KILLED
    return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().UV;
167
  }
168
169
  /**
170
   * Check whether the asserted credential is <a
171
   * href="https://w3c.github.io/webauthn/#backup-eligible">backup eligible</a>, using the <a
172
   * href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> in the authenticator data.
173
   *
174
   * <p>You SHOULD store this value in your representation of the corresponding {@link
175
   * RegisteredCredential} if no value is stored yet. {@link CredentialRepository} implementations
176
   * SHOULD set this value as the {@link
177
   * RegisteredCredential.RegisteredCredentialBuilder#backupEligible(Boolean)
178
   * backupEligible(Boolean)} value when reconstructing that {@link RegisteredCredential}.
179
   *
180
   * @return <code>true</code> if and only if the created credential is backup eligible. NOTE that
181
   *     this is only a hint and not a guarantee, unless backed by a trusted authenticator
182
   *     attestation.
183
   * @see <a href="https://w3c.github.io/webauthn/#backup-eligible">Backup Eligible in §4.
184
   *     Terminology</a>
185
   * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag in §6.1. Authenticator
186
   *     Data</a>
187
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
188
   *     the standard matures.
189
   */
190
  @Deprecated
191
  @JsonIgnore
192
  public boolean isBackupEligible() {
193 2 1. isBackupEligible : replaced boolean return with true for com/yubico/webauthn/AssertionResult::isBackupEligible → KILLED
2. isBackupEligible : replaced boolean return with false for com/yubico/webauthn/AssertionResult::isBackupEligible → KILLED
    return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().BE;
194
  }
195
196
  /**
197
   * Get the current <a href="https://w3c.github.io/webauthn/#backup-state">backup state</a> of the
198
   * asserted credential, using the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS
199
   * flag</a> in the authenticator data.
200
   *
201
   * <p>You SHOULD update this value in your representation of a {@link RegisteredCredential}.
202
   * {@link CredentialRepository} implementations SHOULD set this value as the {@link
203
   * RegisteredCredential.RegisteredCredentialBuilder#backupState(Boolean) backupState(Boolean)}
204
   * value when reconstructing that {@link RegisteredCredential}.
205
   *
206
   * @return <code>true</code> if and only if the created credential is believed to currently be
207
   *     backed up. NOTE that this is only a hint and not a guarantee, unless backed by a trusted
208
   *     authenticator attestation.
209
   * @see <a href="https://w3c.github.io/webauthn/#backup-state">Backup State in §4. Terminology</a>
210
   * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS flag in §6.1. Authenticator
211
   *     Data</a>
212
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
213
   *     the standard matures.
214
   */
215
  @Deprecated
216
  @JsonIgnore
217
  public boolean isBackedUp() {
218 2 1. isBackedUp : replaced boolean return with false for com/yubico/webauthn/AssertionResult::isBackedUp → KILLED
2. isBackedUp : replaced boolean return with true for com/yubico/webauthn/AssertionResult::isBackedUp → KILLED
    return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().BS;
219
  }
220
221
  /**
222
   * The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator
223
   * attachment modality</a> in effect at the time the asserted credential was used.
224
   *
225
   * @see PublicKeyCredential#getAuthenticatorAttachment()
226
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
227
   *     the standard matures.
228
   */
229
  @Deprecated
230
  @JsonIgnore
231
  public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() {
232 1 1. getAuthenticatorAttachment : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorAttachment → KILLED
    return credentialResponse.getAuthenticatorAttachment();
233
  }
234
235
  /**
236
   * The new <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature
237
   * count</a> of the credential used for the assertion.
238
   *
239
   * <p>You should update this value in your database.
240
   *
241
   * @see AuthenticatorData#getSignatureCounter()
242
   */
243
  @JsonIgnore
244
  public long getSignatureCount() {
245 1 1. getSignatureCount : replaced long return with 0 for com/yubico/webauthn/AssertionResult::getSignatureCount → KILLED
    return credentialResponse.getResponse().getParsedAuthenticatorData().getSignatureCounter();
246
  }
247
248
  /**
249
   * The <a
250
   * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client
251
   * extension outputs</a>, if any.
252
   *
253
   * <p>This is present if and only if at least one extension output is present in the return value.
254
   *
255
   * @see <a
256
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-client-extension-processing">§9.4.
257
   *     Client Extension Processing</a>
258
   * @see ClientAssertionExtensionOutputs
259
   * @see #getAuthenticatorExtensionOutputs() ()
260
   */
261
  @JsonIgnore
262
  public Optional<ClientAssertionExtensionOutputs> getClientExtensionOutputs() {
263 1 1. getClientExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getClientExtensionOutputs → KILLED
    return Optional.of(credentialResponse.getClientExtensionResults())
264 2 1. lambda$getClientExtensionOutputs$0 : replaced boolean return with true for com/yubico/webauthn/AssertionResult::lambda$getClientExtensionOutputs$0 → SURVIVED
2. lambda$getClientExtensionOutputs$0 : negated conditional → KILLED
        .filter(ceo -> !ceo.getExtensionIds().isEmpty());
265
  }
266
267
  /**
268
   * The <a
269
   * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator
270
   * extension outputs</a>, if any.
271
   *
272
   * <p>This is present if and only if at least one extension output is present in the return value.
273
   *
274
   * @see <a
275
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-extension-processing">§9.5.
276
   *     Authenticator Extension Processing</a>
277
   * @see AuthenticatorAssertionExtensionOutputs
278
   * @see #getClientExtensionOutputs()
279
   */
280
  @JsonIgnore
281
  public Optional<AuthenticatorAssertionExtensionOutputs> getAuthenticatorExtensionOutputs() {
282 1 1. getAuthenticatorExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorExtensionOutputs → KILLED
    return AuthenticatorAssertionExtensionOutputs.fromAuthenticatorData(
283
        credentialResponse.getResponse().getParsedAuthenticatorData());
284
  }
285
286
  /**
287
   * Retrieve a suitable nickname for this credential, if one is available. This MAY differ from
288
   * {@link RegistrationResult#getAuthenticatorDisplayName() the value returned during
289
   * registration}, if any. In that case the application may want to offer the user to update the
290
   * previously stored value, if any.
291
   *
292
   * <p>This returns the <code>authenticatorDisplayName</code> output from the <a
293
   * href="https://w3c.github.io/webauthn/#sctn-authenticator-credential-properties-extension">
294
   * <code>credProps</code></a> extension.
295
   *
296
   * @return A user-chosen or vendor-default display name for the credential, if available.
297
   *     Otherwise empty.
298
   * @see <a
299
   *     href="https://w3c.github.io/webauthn/#dom-credentialpropertiesoutput-authenticatordisplayname">
300
   *     <code>authenticatorDisplayName</code> in §10.1.3. Credential Properties Extension
301
   *     (credProps)</a>
302
   * @see RegistrationResult#getAuthenticatorDisplayName()
303
   * @see Extensions.CredentialProperties.CredentialPropertiesOutput#getAuthenticatorDisplayName()
304
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
305
   *     the standard matures.
306
   */
307
  @JsonIgnore
308
  @Deprecated
309
  public Optional<String> getAuthenticatorDisplayName() {
310 1 1. getAuthenticatorDisplayName : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorDisplayName → KILLED
    return getClientExtensionOutputs()
311 1 1. lambda$getAuthenticatorDisplayName$1 : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::lambda$getAuthenticatorDisplayName$1 → KILLED
        .flatMap(outputs -> outputs.getCredProps())
312 1 1. lambda$getAuthenticatorDisplayName$2 : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::lambda$getAuthenticatorDisplayName$2 → KILLED
        .flatMap(credProps -> credProps.getAuthenticatorDisplayName());
313
  }
314
}

Mutations

105

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

108

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

109

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

131

1.1
Location : getCredentialId
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with null for com/yubico/webauthn/AssertionResult::getCredentialId → KILLED

147

1.1
Location : getUserHandle
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with null for com/yubico/webauthn/AssertionResult::getUserHandle → KILLED

166

1.1
Location : isUserVerified
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced boolean return with false for com/yubico/webauthn/AssertionResult::isUserVerified → KILLED

2.2
Location : isUserVerified
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced boolean return with true for com/yubico/webauthn/AssertionResult::isUserVerified → KILLED

193

1.1
Location : isBackupEligible
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced boolean return with true for com/yubico/webauthn/AssertionResult::isBackupEligible → KILLED

2.2
Location : isBackupEligible
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced boolean return with false for com/yubico/webauthn/AssertionResult::isBackupEligible → KILLED

218

1.1
Location : isBackedUp
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced boolean return with false for com/yubico/webauthn/AssertionResult::isBackedUp → KILLED

2.2
Location : isBackedUp
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced boolean return with true for com/yubico/webauthn/AssertionResult::isBackedUp → KILLED

232

1.1
Location : getAuthenticatorAttachment
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorAttachment → KILLED

245

1.1
Location : getSignatureCount
Killed by : com.yubico.webauthn.RelyingPartyCeremoniesSpec
replaced long return with 0 for com/yubico/webauthn/AssertionResult::getSignatureCount → KILLED

263

1.1
Location : getClientExtensionOutputs
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getClientExtensionOutputs → KILLED

264

1.1
Location : lambda$getClientExtensionOutputs$0
Killed by : none
replaced boolean return with true for com/yubico/webauthn/AssertionResult::lambda$getClientExtensionOutputs$0 → SURVIVED

2.2
Location : lambda$getClientExtensionOutputs$0
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
negated conditional → KILLED

282

1.1
Location : getAuthenticatorExtensionOutputs
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorExtensionOutputs → KILLED

310

1.1
Location : getAuthenticatorDisplayName
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorDisplayName → KILLED

311

1.1
Location : lambda$getAuthenticatorDisplayName$1
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::lambda$getAuthenticatorDisplayName$1 → KILLED

312

1.1
Location : lambda$getAuthenticatorDisplayName$2
Killed by : com.yubico.webauthn.RelyingPartyAssertionSpec
replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::lambda$getAuthenticatorDisplayName$2 → KILLED

Active mutators

Tests examined


Report generated by PIT 1.15.0