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.PublicKeyCredential;
39
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
40
import com.yubico.webauthn.data.UserIdentity;
41
import java.util.Optional;
42
import lombok.AccessLevel;
43
import lombok.Getter;
44
import lombok.NonNull;
45
import lombok.Value;
46
47
/** The result of a call to {@link RelyingParty#finishAssertion(FinishAssertionOptions)}. */
48
@Value
49
public class AssertionResult {
50
51
  /** <code>true</code> if the assertion was verified successfully. */
52
  private final boolean success;
53
54
  @JsonProperty
55
  @Getter(AccessLevel.NONE)
56
  private final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
57
      credentialResponse;
58
59
  /**
60
   * The {@link RegisteredCredential} that was returned by {@link
61
   * CredentialRepository#lookup(ByteArray, ByteArray)} and whose public key was used to
62
   * successfully verify the assertion signature.
63
   *
64
   * <p>NOTE: The {@link RegisteredCredential#getSignatureCount() signature count}, {@link
65
   * RegisteredCredential#isBackupEligible() backup eligibility} and {@link
66
   * RegisteredCredential#isBackedUp() backup state} properties in this object will reflect the
67
   * state <i>before</i> the assertion operation, not the new state. When updating your database
68
   * state, use the signature counter and backup state from {@link #getSignatureCount()}, {@link
69
   * #isBackupEligible()} and {@link #isBackedUp()} instead.
70
   */
71
  private final RegisteredCredential credential;
72
73
  /**
74
   * The username of the authenticated user.
75
   *
76
   * @see #getUserHandle()
77
   */
78
  @NonNull private final String username;
79
80
  /**
81
   * <code>true</code> if and only if at least one of the following is true:
82
   *
83
   * <ul>
84
   *   <li>The {@link AuthenticatorData#getSignatureCounter() signature counter value} in the
85
   *       assertion was strictly greater than {@link RegisteredCredential#getSignatureCount() the
86
   *       stored one}.
87
   *   <li>The {@link AuthenticatorData#getSignatureCounter() signature counter value} in the
88
   *       assertion and {@link RegisteredCredential#getSignatureCount() the stored one} were both
89
   *       zero.
90
   * </ul>
91
   *
92
   * @see <a
93
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1.
94
   *     Authenticator Data</a>
95
   * @see AuthenticatorData#getSignatureCounter()
96
   * @see RegisteredCredential#getSignatureCount()
97
   * @see com.yubico.webauthn.RelyingParty.RelyingPartyBuilder#validateSignatureCounter(boolean)
98
   */
99
  private final boolean signatureCounterValid;
100
101
  @JsonCreator
102
  AssertionResult(
103
      @JsonProperty("success") boolean success,
104 1 1. <init> : negated conditional → KILLED
      @NonNull @JsonProperty("credentialResponse")
105
          PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>
106
              credentialResponse,
107 1 1. <init> : negated conditional → KILLED
      @NonNull @JsonProperty("credential") RegisteredCredential credential,
108 1 1. <init> : negated conditional → KILLED
      @NonNull @JsonProperty("username") String username,
109
      @JsonProperty("signatureCounterValid") boolean signatureCounterValid) {
110
    this.success = success;
111
    this.credentialResponse = credentialResponse;
112
    this.credential = credential;
113
    this.username = username;
114
    this.signatureCounterValid = signatureCounterValid;
115
  }
116
117
  /**
118
   * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">credential
119
   * ID</a> of the credential used for the assertion.
120
   *
121
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">Credential
122
   *     ID</a>
123
   * @see PublicKeyCredentialRequestOptions#getAllowCredentials()
124
   * @deprecated Use {@link #getCredential()}.{@link RegisteredCredential#getCredentialId()
125
   *     getCredentialId()} instead.
126
   */
127
  @Deprecated
128
  @JsonIgnore
129
  public ByteArray getCredentialId() {
130 1 1. getCredentialId : replaced return value with null for com/yubico/webauthn/AssertionResult::getCredentialId → KILLED
    return credential.getCredentialId();
131
  }
132
133
  /**
134
   * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">user handle</a>
135
   * of the authenticated user.
136
   *
137
   * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User Handle</a>
138
   * @see UserIdentity#getId()
139
   * @see #getUsername()
140
   * @deprecated Use {@link #getCredential()}.{@link RegisteredCredential#getUserHandle()
141
   *     getUserHandle()} instead.
142
   */
143
  @Deprecated
144
  @JsonIgnore
145
  public ByteArray getUserHandle() {
146 1 1. getUserHandle : replaced return value with null for com/yubico/webauthn/AssertionResult::getUserHandle → KILLED
    return credential.getUserHandle();
147
  }
148
149
  /**
150
   * Check whether the <a href="https://www.w3.org/TR/webauthn/#user-verification">user
151
   * verification</a> as performed during the authentication ceremony.
152
   *
153
   * <p>This flag is also available via <code>
154
   * {@link PublicKeyCredential}.{@link PublicKeyCredential#getResponse() getResponse()}.{@link AuthenticatorResponse#getParsedAuthenticatorData() getParsedAuthenticatorData()}.{@link AuthenticatorData#getFlags() getFlags()}.{@link AuthenticatorDataFlags#UV UV}
155
   * </code>.
156
   *
157
   * @return <code>true</code> if and only if the authenticator claims to have performed user
158
   *     verification during the authentication ceremony.
159
   * @see <a href="https://www.w3.org/TR/webauthn/#user-verification">User Verification</a>
160
   * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-uv">UV flag in §6.1. Authenticator
161
   *     Data</a>
162
   */
163
  @JsonIgnore
164
  public boolean isUserVerified() {
165 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;
166
  }
167
168
  /**
169
   * Check whether the asserted credential is <a
170
   * href="https://w3c.github.io/webauthn/#backup-eligible">backup eligible</a>, using the <a
171
   * href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> in the authenticator data.
172
   *
173
   * <p>You SHOULD store this value in your representation of the corresponding {@link
174
   * RegisteredCredential} if no value is stored yet. {@link CredentialRepository} implementations
175
   * SHOULD set this value as the {@link
176
   * RegisteredCredential.RegisteredCredentialBuilder#backupEligible(Boolean)
177
   * backupEligible(Boolean)} value when reconstructing that {@link RegisteredCredential}.
178
   *
179
   * @return <code>true</code> if and only if the created credential is backup eligible. NOTE that
180
   *     this is only a hint and not a guarantee, unless backed by a trusted authenticator
181
   *     attestation.
182
   * @see <a href="https://w3c.github.io/webauthn/#backup-eligible">Backup Eligible in §4.
183
   *     Terminology</a>
184
   * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag in §6.1. Authenticator
185
   *     Data</a>
186
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
187
   *     the standard matures.
188
   */
189
  @Deprecated
190
  @JsonIgnore
191
  public boolean isBackupEligible() {
192 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;
193
  }
194
195
  /**
196
   * Get the current <a href="https://w3c.github.io/webauthn/#backup-state">backup state</a> of the
197
   * asserted credential, using the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS
198
   * flag</a> in the authenticator data.
199
   *
200
   * <p>You SHOULD update this value in your representation of a {@link RegisteredCredential}.
201
   * {@link CredentialRepository} implementations SHOULD set this value as the {@link
202
   * RegisteredCredential.RegisteredCredentialBuilder#backupState(Boolean) backupState(Boolean)}
203
   * value when reconstructing that {@link RegisteredCredential}.
204
   *
205
   * @return <code>true</code> if and only if the created credential is believed to currently be
206
   *     backed up. NOTE that this is only a hint and not a guarantee, unless backed by a trusted
207
   *     authenticator attestation.
208
   * @see <a href="https://w3c.github.io/webauthn/#backup-state">Backup State in §4. Terminology</a>
209
   * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS flag in §6.1. Authenticator
210
   *     Data</a>
211
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
212
   *     the standard matures.
213
   */
214
  @Deprecated
215
  @JsonIgnore
216
  public boolean isBackedUp() {
217 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;
218
  }
219
220
  /**
221
   * The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator
222
   * attachment modality</a> in effect at the time the asserted credential was used.
223
   *
224
   * @see PublicKeyCredential#getAuthenticatorAttachment()
225
   * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
226
   *     the standard matures.
227
   */
228
  @Deprecated
229
  @JsonIgnore
230
  public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() {
231 1 1. getAuthenticatorAttachment : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorAttachment → KILLED
    return credentialResponse.getAuthenticatorAttachment();
232
  }
233
234
  /**
235
   * The new <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature
236
   * count</a> of the credential used for the assertion.
237
   *
238
   * <p>You should update this value in your database.
239
   *
240
   * @see AuthenticatorData#getSignatureCounter()
241
   */
242
  @JsonIgnore
243
  public long getSignatureCount() {
244 1 1. getSignatureCount : replaced long return with 0 for com/yubico/webauthn/AssertionResult::getSignatureCount → KILLED
    return credentialResponse.getResponse().getParsedAuthenticatorData().getSignatureCounter();
245
  }
246
247
  /**
248
   * The <a
249
   * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client
250
   * extension outputs</a>, if any.
251
   *
252
   * <p>This is present if and only if at least one extension output is present in the return value.
253
   *
254
   * @see <a
255
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-client-extension-processing">§9.4.
256
   *     Client Extension Processing</a>
257
   * @see ClientAssertionExtensionOutputs
258
   * @see #getAuthenticatorExtensionOutputs() ()
259
   */
260
  @JsonIgnore
261
  public Optional<ClientAssertionExtensionOutputs> getClientExtensionOutputs() {
262 1 1. getClientExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getClientExtensionOutputs → KILLED
    return Optional.of(credentialResponse.getClientExtensionResults())
263 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());
264
  }
265
266
  /**
267
   * The <a
268
   * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator
269
   * extension outputs</a>, if any.
270
   *
271
   * <p>This is present if and only if at least one extension output is present in the return value.
272
   *
273
   * @see <a
274
   *     href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-extension-processing">§9.5.
275
   *     Authenticator Extension Processing</a>
276
   * @see AuthenticatorAssertionExtensionOutputs
277
   * @see #getClientExtensionOutputs()
278
   */
279
  @JsonIgnore
280
  public Optional<AuthenticatorAssertionExtensionOutputs> getAuthenticatorExtensionOutputs() {
281 1 1. getAuthenticatorExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResult::getAuthenticatorExtensionOutputs → KILLED
    return AuthenticatorAssertionExtensionOutputs.fromAuthenticatorData(
282
        credentialResponse.getResponse().getParsedAuthenticatorData());
283
  }
284
}

Mutations

104

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

107

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

130

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

146

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

165

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

192

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

217

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

231

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

244

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

262

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

263

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

281

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

Active mutators

Tests examined


Report generated by PIT 1.15.0