diff --git a/coverage-badge.json b/coverage-badge.json new file mode 100644 index 000000000..b5fc2b240 --- /dev/null +++ b/coverage-badge.json @@ -0,0 +1,7 @@ +{ + "schemaVersion": 1, + "label": "mutation coverage", + "message": "82 %", + "color": "hsl(98, 100%, 40%)", + "cacheSeconds": 3600 +} diff --git a/index.html b/index.html new file mode 100644 index 000000000..b52f132b4 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + +
+ ++ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +5 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +6 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +7 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.HexException; |
+ +8 + | ++ + + + + + | +import java.util.regex.Matcher; |
+ +9 + | ++ + + + + + | +import java.util.regex.Pattern; |
+ +10 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +11 + | ++ + + + + + | +import lombok.Getter; |
+ +12 + | ++ + + + + + | +import lombok.ToString; |
+ +13 + | ++ + + + + + | +import lombok.Value; |
+ +14 + | ++ + + + + + | +|
+ +15 + | ++ + + + + + | +/** |
+ +16 + | ++ + + + + + | + * Some authenticators have an AAGUID, which is a 128-bit identifier that indicates the type (e.g. |
+ +17 + | ++ + + + + + | + * make and model) of the authenticator. The AAGUID MUST be chosen by the manufacturer to be |
+ +18 + | ++ + + + + + | + * identical across all substantially identical authenticators made by that manufacturer, and |
+ +19 + | ++ + + + + + | + * different (with probability 1-2-128 or greater) from the AAGUIDs of all other types of |
+ +20 + | ++ + + + + + | + * authenticators. |
+ +21 + | ++ + + + + + | + * |
+ +22 + | ++ + + + + + | + * <p>The AAGUID is represented as a string (e.g. "7a98c250-6808-11cf-b73b-00aa00b677a7") consisting |
+ +23 + | ++ + + + + + | + * of 5 hex strings separated by a dash ("-"), see [RFC4122]. |
+ +24 + | ++ + + + + + | + * |
+ +25 + | ++ + + + + + | + * @see <a |
+ +26 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#typedefdef-aaguid">FIDO |
+ +27 + | ++ + + + + + | + * Metadata Statement §3.1. Authenticator Attestation GUID (AAGUID) typedef</a> |
+ +28 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc4122">RFC 4122: A Universally Unique IDentifier |
+ +29 + | ++ + + + + + | + * (UUID) URN Namespace</a> |
+ +30 + | ++ + + + + + | + */ |
+ +31 + | ++ + + + + + | +@Value |
+ +32 + | ++ + + + + + | +@Getter(AccessLevel.NONE) |
+ +33 + | ++ + + + + + | +@ToString(includeFieldNames = false, onlyExplicitlyIncluded = true) |
+ +34 + | ++ + + + + + | +public class AAGUID { |
+ +35 + | ++ + + + + + | +|
+ +36 + | ++ + + + + + | + private static final Pattern AAGUID_PATTERN = |
+ +37 + | ++ + + + + + | + Pattern.compile( |
+ +38 + | ++ + + + + + | + "^([0-9a-fA-F]{8})-?([0-9a-fA-F]{4})-?([0-9a-fA-F]{4})-?([0-9a-fA-F]{4})-?([0-9a-fA-F]{12})$"); |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | + private static final ByteArray ZERO = |
+ +41 + | ++ + + + + + | + new ByteArray(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + ByteArray value; |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** |
+ +46 + | ++ + + + + + | + * Construct an AAGUID from its raw binary representation. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * <p>This is the inverse of {@link #asBytes()}. |
+ +49 + | ++ + + + + + | + * |
+ +50 + | ++ + + + + + | + * @param value a {@link ByteArray} of length exactly 16. |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | + public AAGUID(ByteArray value) { |
+ +53 + | +
+
+1
+
+1. <init> : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ ExceptionUtil.assertTrue( |
+ +54 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ value.size() == 16, |
+ +55 + | ++ + + + + + | + "AAGUID as bytes must be exactly 16 bytes long, was %d: %s", |
+ +56 + | ++ + + + + + | + value.size(), |
+ +57 + | ++ + + + + + | + value); |
+ +58 + | ++ + + + + + | + this.value = value; |
+ +59 + | ++ + + + + + | + } |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * The 16-byte binary representation of this AAGUID, for example <code> |
+ +63 + | ++ + + + + + | + * 7a98c250680811cfb73b00aa00b677a7</code> when hex-encoded. |
+ +64 + | ++ + + + + + | + * |
+ +65 + | ++ + + + + + | + * <p>This is the inverse of {@link #AAGUID(ByteArray)}. |
+ +66 + | ++ + + + + + | + */ |
+ +67 + | ++ + + + + + | + public ByteArray asBytes() { |
+ +68 + | +
+
+1
+
+1. asBytes : replaced return value with null for com/yubico/fido/metadata/AAGUID::asBytes → KILLED + + + + |
+ return value; |
+ +69 + | ++ + + + + + | + } |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | + /** |
+ +72 + | ++ + + + + + | + * The 32-character hexadecimal representation of this AAGUID, for example <code> |
+ +73 + | ++ + + + + + | + * "7a98c250680811cfb73b00aa00b677a7"</code>. |
+ +74 + | ++ + + + + + | + */ |
+ +75 + | ++ + + + + + | + public String asHexString() { |
+ +76 + | +
+
+1
+
+1. asHexString : replaced return value with "" for com/yubico/fido/metadata/AAGUID::asHexString → KILLED + + + + |
+ return value.getHex(); |
+ +77 + | ++ + + + + + | + } |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + /** |
+ +80 + | ++ + + + + + | + * The 36-character string representation of this AAGUID, for example <code> |
+ +81 + | ++ + + + + + | + * "7a98c250-6808-11cf-b73b-00aa00b677a7"</code>. |
+ +82 + | ++ + + + + + | + */ |
+ +83 + | ++ + + + + + | + @JsonValue |
+ +84 + | ++ + + + + + | + @ToString.Include |
+ +85 + | ++ + + + + + | + public String asGuidString() { |
+ +86 + | ++ + + + + + | + final String hex = value.getHex(); |
+ +87 + | +
+
+1
+
+1. asGuidString : replaced return value with "" for com/yubico/fido/metadata/AAGUID::asGuidString → KILLED + + + + |
+ return String.format( |
+ +88 + | ++ + + + + + | + "%s-%s-%s-%s-%s", |
+ +89 + | ++ + + + + + | + hex.substring(0, 8), |
+ +90 + | ++ + + + + + | + hex.substring(8, 8 + 4), |
+ +91 + | ++ + + + + + | + hex.substring(8 + 4, 8 + 4 + 4), |
+ +92 + | ++ + + + + + | + hex.substring(8 + 4 + 4, 8 + 4 + 4 + 4), |
+ +93 + | ++ + + + + + | + hex.substring(8 + 4 + 4 + 4, 8 + 4 + 4 + 4 + 12)); |
+ +94 + | ++ + + + + + | + } |
+ +95 + | ++ + + + + + | +|
+ +96 + | ++ + + + + + | + /** |
+ +97 + | ++ + + + + + | + * <code>true</code> if and only if this {@link AAGUID} consists of all zeroes. This typically |
+ +98 + | ++ + + + + + | + * indicates that an authenticator has no AAGUID, or that the AAGUID has been redacted. |
+ +99 + | ++ + + + + + | + */ |
+ +100 + | ++ + + + + + | + public boolean isZero() { |
+ +101 + | +
+
+2
+
+1. isZero : replaced boolean return with true for com/yubico/fido/metadata/AAGUID::isZero → KILLED +2. isZero : replaced boolean return with false for com/yubico/fido/metadata/AAGUID::isZero → KILLED + + + + |
+ return ZERO.equals(value); |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + private static ByteArray parse(String value) { |
+ +105 + | ++ + + + + + | + Matcher matcher = AAGUID_PATTERN.matcher(value); |
+ +106 + | +
+
+1
+
+1. parse : negated conditional → KILLED + + + + |
+ if (matcher.find()) { |
+ +107 + | ++ + + + + + | + try { |
+ +108 + | +
+
+1
+
+1. parse : replaced return value with null for com/yubico/fido/metadata/AAGUID::parse → KILLED + + + + |
+ return ByteArray.fromHex(matcher.group(1)) |
+ +109 + | ++ + + + + + | + .concat(ByteArray.fromHex(matcher.group(2))) |
+ +110 + | ++ + + + + + | + .concat(ByteArray.fromHex(matcher.group(3))) |
+ +111 + | ++ + + + + + | + .concat(ByteArray.fromHex(matcher.group(4))) |
+ +112 + | ++ + + + + + | + .concat(ByteArray.fromHex(matcher.group(5))); |
+ +113 + | ++ + + + + + | + } catch (HexException e) { |
+ +114 + | ++ + + + + + | + throw new RuntimeException( |
+ +115 + | ++ + + + + + | + "This exception should be impossible, please file a bug report.", e); |
+ +116 + | ++ + + + + + | + } |
+ +117 + | ++ + + + + + | + } else { |
+ +118 + | ++ + + + + + | + throw new IllegalArgumentException("Value does not match AAGUID pattern: " + value); |
+ +119 + | ++ + + + + + | + } |
+ +120 + | ++ + + + + + | + } |
+ +121 + | ++ + + + + + | +|
+ +122 + | ++ + + + + + | + @JsonCreator |
+ +123 + | ++ + + + + + | + private static AAGUID fromString(String aaguid) { |
+ +124 + | +
+
+1
+
+1. fromString : replaced return value with null for com/yubico/fido/metadata/AAGUID::fromString → KILLED + + + + |
+ return new AAGUID(parse(aaguid)); |
+ +125 + | ++ + + + + + | + } |
+ +126 + | ++ + + + + + | +} |
Mutations | ||
53 | ++ |
+
+
+
+ 1.1 |
+
54 | ++ |
+
+
+
+ 1.1 |
+
68 | ++ |
+
+
+
+ 1.1 |
+
76 | ++ |
+
+
+
+ 1.1 |
+
87 | ++ |
+
+
+
+ 1.1 |
+
101 | ++ |
+
+
+
+ 1.1 2.2 |
+
106 | ++ |
+
+
+
+ 1.1 |
+
108 | ++ |
+
+
+
+ 1.1 |
+
124 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +5 + | ++ + + + + + | +import java.util.regex.Pattern; |
+ +6 + | ++ + + + + + | +import lombok.Value; |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | +/** |
+ +9 + | ++ + + + + + | + * Each UAF authenticator MUST have an AAID to identify UAF enabled authenticator models globally. |
+ +10 + | ++ + + + + + | + * The AAID MUST uniquely identify a specific authenticator model within the range of all |
+ +11 + | ++ + + + + + | + * UAF-enabled authenticator models made by all authenticator vendors, where authenticators of a |
+ +12 + | ++ + + + + + | + * specific model must share identical security characteristics within the model (see Security |
+ +13 + | ++ + + + + + | + * Considerations). |
+ +14 + | ++ + + + + + | + * |
+ +15 + | ++ + + + + + | + * <p>The AAID is a string with format <code>"V#M"</code>, where |
+ +16 + | ++ + + + + + | + * |
+ +17 + | ++ + + + + + | + * <ul> |
+ +18 + | ++ + + + + + | + * <li><code>#</code> is a separator |
+ +19 + | ++ + + + + + | + * <li><code>V</code> indicates the authenticator Vendor Code. This code consists of 4 hexadecimal |
+ +20 + | ++ + + + + + | + * digits. |
+ +21 + | ++ + + + + + | + * <li><code>M</code> indicates the authenticator Model Code. This code consists of 4 hexadecimal |
+ +22 + | ++ + + + + + | + * digits. |
+ +23 + | ++ + + + + + | + * </ul> |
+ +24 + | ++ + + + + + | + * |
+ +25 + | ++ + + + + + | + * @see <a |
+ +26 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-uaf-v1.2-ps-20201020/fido-uaf-protocol-v1.2-ps-20201020.html#authenticator-attestation-id-aaid-typedef">FIDO |
+ +27 + | ++ + + + + + | + * UAF Protocol Specification §3.1.4 Authenticator Attestation ID (AAID) typedef</a> |
+ +28 + | ++ + + + + + | + */ |
+ +29 + | ++ + + + + + | +@Value |
+ +30 + | ++ + + + + + | +public class AAID { |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | + private static final Pattern AAID_PATTERN = Pattern.compile("^[0-9a-fA-F]{4}#[0-9a-fA-F]{4}$"); |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | + /** |
+ +35 + | ++ + + + + + | + * The underlying string value of this AAID. |
+ +36 + | ++ + + + + + | + * |
+ +37 + | ++ + + + + + | + * <p>The AAID is a string with format <code>"V#M"</code>, where |
+ +38 + | ++ + + + + + | + * |
+ +39 + | ++ + + + + + | + * <ul> |
+ +40 + | ++ + + + + + | + * <li><code>#</code> is a separator |
+ +41 + | ++ + + + + + | + * <li><code>V</code> indicates the authenticator Vendor Code. This code consists of 4 |
+ +42 + | ++ + + + + + | + * hexadecimal digits. |
+ +43 + | ++ + + + + + | + * <li><code>M</code> indicates the authenticator Model Code. This code consists of 4 |
+ +44 + | ++ + + + + + | + * hexadecimal digits. |
+ +45 + | ++ + + + + + | + * </ul> |
+ +46 + | ++ + + + + + | + * |
+ +47 + | ++ + + + + + | + * @see <a |
+ +48 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-uaf-v1.2-ps-20201020/fido-uaf-protocol-v1.2-ps-20201020.html#authenticator-attestation-id-aaid-typedef">Authenticator |
+ +49 + | ++ + + + + + | + * Attestation ID (AAID) typedef</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | + @JsonValue String value; |
+ +52 + | ++ + + + + + | +|
+ +53 + | ++ + + + + + | + /** |
+ +54 + | ++ + + + + + | + * Construct an {@link AAID} from its String representation. |
+ +55 + | ++ + + + + + | + * |
+ +56 + | ++ + + + + + | + * <p>This is the inverse of {@link #getValue()}. |
+ +57 + | ++ + + + + + | + * |
+ +58 + | ++ + + + + + | + * @param value a {@link String} conforming to the rules specified in the {@link AAID} type. |
+ +59 + | ++ + + + + + | + */ |
+ +60 + | ++ + + + + + | + @JsonCreator |
+ +61 + | ++ + + + + + | + public AAID(String value) { |
+ +62 + | ++ + + + + + | + this.value = validate(value); |
+ +63 + | ++ + + + + + | + } |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + private String validate(String value) { |
+ +66 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ if (AAID_PATTERN.matcher(value).matches()) { |
+ +67 + | +
+
+1
+
+1. validate : replaced return value with "" for com/yubico/fido/metadata/AAID::validate → KILLED + + + + |
+ return value; |
+ +68 + | ++ + + + + + | + } else { |
+ +69 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +70 + | ++ + + + + + | + String.format("Value does not satisfy AAID format: %s", value)); |
+ +71 + | ++ + + + + + | + } |
+ +72 + | ++ + + + + + | + } |
+ +73 + | ++ + + + + + | +} |
Mutations | ||
66 | ++ |
+
+
+
+ 1.1 |
+
67 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +5 + | ++ + + + + + | +import java.util.Map; |
+ +6 + | ++ + + + + + | +import java.util.Optional; |
+ +7 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +8 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +9 + | ++ + + + + + | +import lombok.Getter; |
+ +10 + | ++ + + + + + | +import lombok.Value; |
+ +11 + | ++ + + + + + | +|
+ +12 + | ++ + + + + + | +/** |
+ +13 + | ++ + + + + + | + * See: |
+ +14 + | ++ + + + + + | + * https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#alternativedescriptions-dictionary |
+ +15 + | ++ + + + + + | + * |
+ +16 + | ++ + + + + + | + * @see <a |
+ +17 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#alternativedescriptions-dictionary">FIDO |
+ +18 + | ++ + + + + + | + * Metadata Statement §3.11. AlternativeDescriptions dictionary</a> |
+ +19 + | ++ + + + + + | + */ |
+ +20 + | ++ + + + + + | +@Value |
+ +21 + | ++ + + + + + | +@AllArgsConstructor(onConstructor_ = {@JsonCreator}) |
+ +22 + | ++ + + + + + | +public class AlternativeDescriptions { |
+ +23 + | ++ + + + + + | +|
+ +24 + | ++ + + + + + | + @JsonValue |
+ +25 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +26 + | ++ + + + + + | + Map<String, String> values; |
+ +27 + | ++ + + + + + | +|
+ +28 + | ++ + + + + + | + /** |
+ +29 + | ++ + + + + + | + * Get a map entry in accordance with the rules defined in <a |
+ +30 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#alternativedescriptions-dictionary">AlternativeDescriptions |
+ +31 + | ++ + + + + + | + * dictionary</a>. |
+ +32 + | ++ + + + + + | + * |
+ +33 + | ++ + + + + + | + * @see <a |
+ +34 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#alternativedescriptions-dictionary">AlternativeDescriptions |
+ +35 + | ++ + + + + + | + * dictionary</a>. |
+ +36 + | ++ + + + + + | + */ |
+ +37 + | ++ + + + + + | + public Optional<String> get(String languageCode) { |
+ +38 + | +
+
+1
+
+1. get : negated conditional → NO_COVERAGE + + + + |
+ if (values.containsKey(languageCode)) { |
+ +39 + | +
+
+1
+
+1. get : replaced return value with Optional.empty for com/yubico/fido/metadata/AlternativeDescriptions::get → NO_COVERAGE + + + + |
+ return Optional.of(values.get(languageCode)); |
+ +40 + | ++ + + + + + | + } else { |
+ +41 + | ++ + + + + + | + final String[] splits = languageCode.split("-"); |
+ +42 + | +
+
+3
+
+1. get : changed conditional boundary → NO_COVERAGE +2. get : negated conditional → NO_COVERAGE +3. get : negated conditional → NO_COVERAGE + + + + |
+ if (splits.length > 1 && values.containsKey(splits[0])) { |
+ +43 + | +
+
+1
+
+1. get : replaced return value with Optional.empty for com/yubico/fido/metadata/AlternativeDescriptions::get → NO_COVERAGE + + + + |
+ return Optional.of(values.get(splits[0])); |
+ +44 + | ++ + + + + + | + } else { |
+ +45 + | ++ + + + + + | + return Optional.empty(); |
+ +46 + | ++ + + + + + | + } |
+ +47 + | ++ + + + + + | + } |
+ +48 + | ++ + + + + + | + } |
+ +49 + | ++ + + + + + | +} |
Mutations | ||
38 | ++ |
+
+
+
+ 1.1 |
+
39 | ++ |
+
+
+
+ 1.1 |
+
42 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
43 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonGenerator; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonParser; |
+ +6 + | ++ + + + + + | +import com.fasterxml.jackson.databind.DeserializationContext; |
+ +7 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonDeserializer; |
+ +8 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonSerializer; |
+ +9 + | ++ + + + + + | +import com.fasterxml.jackson.databind.SerializerProvider; |
+ +10 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
+ +11 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +12 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorTransport; |
+ +13 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialParameters; |
+ +14 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.UserVerificationMethod; |
+ +15 + | ++ + + + + + | +import java.io.IOException; |
+ +16 + | ++ + + + + + | +import java.util.Arrays; |
+ +17 + | ++ + + + + + | +import java.util.List; |
+ +18 + | ++ + + + + + | +import java.util.Map; |
+ +19 + | ++ + + + + + | +import java.util.Optional; |
+ +20 + | ++ + + + + + | +import java.util.Set; |
+ +21 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +22 + | ++ + + + + + | +import lombok.Builder; |
+ +23 + | ++ + + + + + | +import lombok.NonNull; |
+ +24 + | ++ + + + + + | +import lombok.Value; |
+ +25 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +/** |
+ +28 + | ++ + + + + + | + * This dictionary describes supported versions, extensions, AAGUID of the device and its |
+ +29 + | ++ + + + + + | + * capabilities. |
+ +30 + | ++ + + + + + | + * |
+ +31 + | ++ + + + + + | + * <p>See: <a |
+ +32 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +33 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +34 + | ++ + + + + + | + * |
+ +35 + | ++ + + + + + | + * @see <a |
+ +36 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#authenticatorgetinfo-dictionary">FIDO |
+ +37 + | ++ + + + + + | + * Metadata Statement §3.12. AuthenticatorGetInfo dictionary</a> |
+ +38 + | ++ + + + + + | + * @see <a |
+ +39 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +40 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | +@Value |
+ +43 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +44 + | ++ + + + + + | +@Jacksonized |
+ +45 + | ++ + + + + + | +@JsonIgnoreProperties({ |
+ +46 + | ++ + + + + + | + "maxAuthenticatorConfigLength", |
+ +47 + | ++ + + + + + | + "defaultCredProtect" |
+ +48 + | ++ + + + + + | +}) // Present in example but not defined |
+ +49 + | ++ + + + + + | +public class AuthenticatorGetInfo { |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + /** |
+ +52 + | ++ + + + + + | + * @see <a |
+ +53 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +54 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | + @NonNull Set<CtapVersion> versions; |
+ +57 + | ++ + + + + + | +|
+ +58 + | ++ + + + + + | + /** |
+ +59 + | ++ + + + + + | + * @see <a |
+ +60 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +61 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +62 + | ++ + + + + + | + */ |
+ +63 + | ++ + + + + + | + Set<String> extensions; |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + /** |
+ +66 + | ++ + + + + + | + * @see <a |
+ +67 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +68 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +69 + | ++ + + + + + | + */ |
+ +70 + | ++ + + + + + | + AAGUID aaguid; |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + /** |
+ +73 + | ++ + + + + + | + * @see <a |
+ +74 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +75 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + SupportedCtapOptions options; |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + /** |
+ +80 + | ++ + + + + + | + * @see <a |
+ +81 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +82 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +83 + | ++ + + + + + | + */ |
+ +84 + | ++ + + + + + | + Integer maxMsgSize; |
+ +85 + | ++ + + + + + | +|
+ +86 + | ++ + + + + + | + /** |
+ +87 + | ++ + + + + + | + * @see <a |
+ +88 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +89 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +90 + | ++ + + + + + | + */ |
+ +91 + | ++ + + + + + | + Set<CtapPinUvAuthProtocolVersion> pinUvAuthProtocols; |
+ +92 + | ++ + + + + + | +|
+ +93 + | ++ + + + + + | + /** |
+ +94 + | ++ + + + + + | + * @see <a |
+ +95 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +96 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +97 + | ++ + + + + + | + */ |
+ +98 + | ++ + + + + + | + Integer maxCredentialCountInList; |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + /** |
+ +101 + | ++ + + + + + | + * @see <a |
+ +102 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +103 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +104 + | ++ + + + + + | + */ |
+ +105 + | ++ + + + + + | + Integer maxCredentialIdLength; |
+ +106 + | ++ + + + + + | +|
+ +107 + | ++ + + + + + | + /** |
+ +108 + | ++ + + + + + | + * @see <a |
+ +109 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +110 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +111 + | ++ + + + + + | + */ |
+ +112 + | ++ + + + + + | + Set<AuthenticatorTransport> transports; |
+ +113 + | ++ + + + + + | +|
+ +114 + | ++ + + + + + | + /** |
+ +115 + | ++ + + + + + | + * @see <a |
+ +116 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +117 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +118 + | ++ + + + + + | + */ |
+ +119 + | ++ + + + + + | + List<PublicKeyCredentialParameters> algorithms; |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + /** |
+ +122 + | ++ + + + + + | + * @see <a |
+ +123 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +124 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +125 + | ++ + + + + + | + */ |
+ +126 + | ++ + + + + + | + Integer maxSerializedLargeBlobArray; |
+ +127 + | ++ + + + + + | +|
+ +128 + | ++ + + + + + | + /** |
+ +129 + | ++ + + + + + | + * @see <a |
+ +130 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +131 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +132 + | ++ + + + + + | + */ |
+ +133 + | ++ + + + + + | + Boolean forcePINChange; |
+ +134 + | ++ + + + + + | +|
+ +135 + | ++ + + + + + | + /** |
+ +136 + | ++ + + + + + | + * @see <a |
+ +137 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +138 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +139 + | ++ + + + + + | + */ |
+ +140 + | ++ + + + + + | + Integer minPINLength; |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + /** |
+ +143 + | ++ + + + + + | + * @see <a |
+ +144 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +145 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +146 + | ++ + + + + + | + */ |
+ +147 + | ++ + + + + + | + Integer firmwareVersion; |
+ +148 + | ++ + + + + + | +|
+ +149 + | ++ + + + + + | + /** |
+ +150 + | ++ + + + + + | + * @see <a |
+ +151 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +152 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +153 + | ++ + + + + + | + */ |
+ +154 + | ++ + + + + + | + Integer maxCredBlobLength; |
+ +155 + | ++ + + + + + | +|
+ +156 + | ++ + + + + + | + /** |
+ +157 + | ++ + + + + + | + * @see <a |
+ +158 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +159 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +160 + | ++ + + + + + | + */ |
+ +161 + | ++ + + + + + | + Integer maxRPIDsForSetMinPINLength; |
+ +162 + | ++ + + + + + | +|
+ +163 + | ++ + + + + + | + /** |
+ +164 + | ++ + + + + + | + * @see <a |
+ +165 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +166 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +167 + | ++ + + + + + | + */ |
+ +168 + | ++ + + + + + | + Integer preferredPlatformUvAttempts; |
+ +169 + | ++ + + + + + | +|
+ +170 + | ++ + + + + + | + @JsonDeserialize(using = SetFromIntJsonDeserializer.class) |
+ +171 + | ++ + + + + + | + @JsonSerialize(contentUsing = IntFromSetJsonSerializer.class) |
+ +172 + | ++ + + + + + | + Set<UserVerificationMethod> uvModality; |
+ +173 + | ++ + + + + + | +|
+ +174 + | ++ + + + + + | + Map<CtapCertificationId, Integer> certifications; |
+ +175 + | ++ + + + + + | + Integer remainingDiscoverableCredentials; |
+ +176 + | ++ + + + + + | + Set<Integer> vendorPrototypeConfigCommands; |
+ +177 + | ++ + + + + + | +|
+ +178 + | ++ + + + + + | + /** |
+ +179 + | ++ + + + + + | + * @see <a |
+ +180 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +181 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +182 + | ++ + + + + + | + */ |
+ +183 + | ++ + + + + + | + public Optional<Set<String>> getExtensions() { |
+ +184 + | +
+
+1
+
+1. getExtensions : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getExtensions → SURVIVED + + + + |
+ return Optional.ofNullable(extensions); |
+ +185 + | ++ + + + + + | + } |
+ +186 + | ++ + + + + + | +|
+ +187 + | ++ + + + + + | + /** |
+ +188 + | ++ + + + + + | + * @see <a |
+ +189 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +190 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +191 + | ++ + + + + + | + */ |
+ +192 + | ++ + + + + + | + public Optional<AAGUID> getAaguid() { |
+ +193 + | +
+
+1
+
+1. getAaguid : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getAaguid → SURVIVED + + + + |
+ return Optional.ofNullable(aaguid); |
+ +194 + | ++ + + + + + | + } |
+ +195 + | ++ + + + + + | +|
+ +196 + | ++ + + + + + | + /** |
+ +197 + | ++ + + + + + | + * @see <a |
+ +198 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +199 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +200 + | ++ + + + + + | + */ |
+ +201 + | ++ + + + + + | + public Optional<SupportedCtapOptions> getOptions() { |
+ +202 + | +
+
+1
+
+1. getOptions : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getOptions → SURVIVED + + + + |
+ return Optional.ofNullable(options); |
+ +203 + | ++ + + + + + | + } |
+ +204 + | ++ + + + + + | +|
+ +205 + | ++ + + + + + | + /** |
+ +206 + | ++ + + + + + | + * @see <a |
+ +207 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +208 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +209 + | ++ + + + + + | + */ |
+ +210 + | ++ + + + + + | + public Optional<Integer> getMaxMsgSize() { |
+ +211 + | +
+
+1
+
+1. getMaxMsgSize : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMaxMsgSize → SURVIVED + + + + |
+ return Optional.ofNullable(maxMsgSize); |
+ +212 + | ++ + + + + + | + } |
+ +213 + | ++ + + + + + | +|
+ +214 + | ++ + + + + + | + /** |
+ +215 + | ++ + + + + + | + * @see <a |
+ +216 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +217 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +218 + | ++ + + + + + | + */ |
+ +219 + | ++ + + + + + | + public Optional<Set<CtapPinUvAuthProtocolVersion>> getPinUvAuthProtocols() { |
+ +220 + | +
+
+1
+
+1. getPinUvAuthProtocols : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getPinUvAuthProtocols → SURVIVED + + + + |
+ return Optional.ofNullable(pinUvAuthProtocols); |
+ +221 + | ++ + + + + + | + } |
+ +222 + | ++ + + + + + | +|
+ +223 + | ++ + + + + + | + /** |
+ +224 + | ++ + + + + + | + * @see <a |
+ +225 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +226 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +227 + | ++ + + + + + | + */ |
+ +228 + | ++ + + + + + | + public Optional<Integer> getMaxCredentialCountInList() { |
+ +229 + | +
+
+1
+
+1. getMaxCredentialCountInList : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMaxCredentialCountInList → SURVIVED + + + + |
+ return Optional.ofNullable(maxCredentialCountInList); |
+ +230 + | ++ + + + + + | + } |
+ +231 + | ++ + + + + + | +|
+ +232 + | ++ + + + + + | + /** |
+ +233 + | ++ + + + + + | + * @see <a |
+ +234 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +235 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +236 + | ++ + + + + + | + */ |
+ +237 + | ++ + + + + + | + public Optional<Integer> getMaxCredentialIdLength() { |
+ +238 + | +
+
+1
+
+1. getMaxCredentialIdLength : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMaxCredentialIdLength → SURVIVED + + + + |
+ return Optional.ofNullable(maxCredentialIdLength); |
+ +239 + | ++ + + + + + | + } |
+ +240 + | ++ + + + + + | +|
+ +241 + | ++ + + + + + | + /** |
+ +242 + | ++ + + + + + | + * @see <a |
+ +243 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +244 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +245 + | ++ + + + + + | + */ |
+ +246 + | ++ + + + + + | + public Optional<Set<AuthenticatorTransport>> getTransports() { |
+ +247 + | +
+
+1
+
+1. getTransports : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getTransports → SURVIVED + + + + |
+ return Optional.ofNullable(transports); |
+ +248 + | ++ + + + + + | + } |
+ +249 + | ++ + + + + + | +|
+ +250 + | ++ + + + + + | + /** |
+ +251 + | ++ + + + + + | + * @see <a |
+ +252 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +253 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +254 + | ++ + + + + + | + */ |
+ +255 + | ++ + + + + + | + public Optional<List<PublicKeyCredentialParameters>> getAlgorithms() { |
+ +256 + | +
+
+1
+
+1. getAlgorithms : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getAlgorithms → SURVIVED + + + + |
+ return Optional.ofNullable(algorithms); |
+ +257 + | ++ + + + + + | + } |
+ +258 + | ++ + + + + + | +|
+ +259 + | ++ + + + + + | + /** |
+ +260 + | ++ + + + + + | + * @see <a |
+ +261 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +262 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +263 + | ++ + + + + + | + */ |
+ +264 + | ++ + + + + + | + public Optional<Integer> getMaxSerializedLargeBlobArray() { |
+ +265 + | +
+
+1
+
+1. getMaxSerializedLargeBlobArray : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMaxSerializedLargeBlobArray → SURVIVED + + + + |
+ return Optional.ofNullable(maxSerializedLargeBlobArray); |
+ +266 + | ++ + + + + + | + } |
+ +267 + | ++ + + + + + | +|
+ +268 + | ++ + + + + + | + /** |
+ +269 + | ++ + + + + + | + * @see <a |
+ +270 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +271 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +272 + | ++ + + + + + | + */ |
+ +273 + | ++ + + + + + | + public Optional<Boolean> getForcePINChange() { |
+ +274 + | +
+
+1
+
+1. getForcePINChange : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getForcePINChange → SURVIVED + + + + |
+ return Optional.ofNullable(forcePINChange); |
+ +275 + | ++ + + + + + | + } |
+ +276 + | ++ + + + + + | +|
+ +277 + | ++ + + + + + | + /** |
+ +278 + | ++ + + + + + | + * @see <a |
+ +279 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +280 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +281 + | ++ + + + + + | + */ |
+ +282 + | ++ + + + + + | + public Optional<Integer> getMinPINLength() { |
+ +283 + | +
+
+1
+
+1. getMinPINLength : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMinPINLength → SURVIVED + + + + |
+ return Optional.ofNullable(minPINLength); |
+ +284 + | ++ + + + + + | + } |
+ +285 + | ++ + + + + + | +|
+ +286 + | ++ + + + + + | + /** |
+ +287 + | ++ + + + + + | + * @see <a |
+ +288 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +289 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +290 + | ++ + + + + + | + */ |
+ +291 + | ++ + + + + + | + public Optional<Integer> getFirmwareVersion() { |
+ +292 + | +
+
+1
+
+1. getFirmwareVersion : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getFirmwareVersion → SURVIVED + + + + |
+ return Optional.ofNullable(firmwareVersion); |
+ +293 + | ++ + + + + + | + } |
+ +294 + | ++ + + + + + | +|
+ +295 + | ++ + + + + + | + /** |
+ +296 + | ++ + + + + + | + * @see <a |
+ +297 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +298 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +299 + | ++ + + + + + | + */ |
+ +300 + | ++ + + + + + | + public Optional<Integer> getMaxCredBlobLength() { |
+ +301 + | +
+
+1
+
+1. getMaxCredBlobLength : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMaxCredBlobLength → SURVIVED + + + + |
+ return Optional.ofNullable(maxCredBlobLength); |
+ +302 + | ++ + + + + + | + } |
+ +303 + | ++ + + + + + | +|
+ +304 + | ++ + + + + + | + /** |
+ +305 + | ++ + + + + + | + * @see <a |
+ +306 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +307 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +308 + | ++ + + + + + | + */ |
+ +309 + | ++ + + + + + | + public Optional<Integer> getMaxRPIDsForSetMinPINLength() { |
+ +310 + | +
+
+1
+
+1. getMaxRPIDsForSetMinPINLength : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getMaxRPIDsForSetMinPINLength → SURVIVED + + + + |
+ return Optional.ofNullable(maxRPIDsForSetMinPINLength); |
+ +311 + | ++ + + + + + | + } |
+ +312 + | ++ + + + + + | +|
+ +313 + | ++ + + + + + | + /** |
+ +314 + | ++ + + + + + | + * @see <a |
+ +315 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +316 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +317 + | ++ + + + + + | + */ |
+ +318 + | ++ + + + + + | + public Optional<Integer> getPreferredPlatformUvAttempts() { |
+ +319 + | +
+
+1
+
+1. getPreferredPlatformUvAttempts : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getPreferredPlatformUvAttempts → SURVIVED + + + + |
+ return Optional.ofNullable(preferredPlatformUvAttempts); |
+ +320 + | ++ + + + + + | + } |
+ +321 + | ++ + + + + + | +|
+ +322 + | ++ + + + + + | + /** |
+ +323 + | ++ + + + + + | + * @see <a |
+ +324 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +325 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +326 + | ++ + + + + + | + */ |
+ +327 + | ++ + + + + + | + public Optional<Set<UserVerificationMethod>> getUvModality() { |
+ +328 + | +
+
+1
+
+1. getUvModality : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getUvModality → SURVIVED + + + + |
+ return Optional.ofNullable(uvModality); |
+ +329 + | ++ + + + + + | + } |
+ +330 + | ++ + + + + + | +|
+ +331 + | ++ + + + + + | + /** |
+ +332 + | ++ + + + + + | + * @see <a |
+ +333 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +334 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +335 + | ++ + + + + + | + */ |
+ +336 + | ++ + + + + + | + public Optional<Map<CtapCertificationId, Integer>> getCertifications() { |
+ +337 + | +
+
+1
+
+1. getCertifications : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getCertifications → SURVIVED + + + + |
+ return Optional.ofNullable(certifications); |
+ +338 + | ++ + + + + + | + } |
+ +339 + | ++ + + + + + | +|
+ +340 + | ++ + + + + + | + /** |
+ +341 + | ++ + + + + + | + * @see <a |
+ +342 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +343 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +344 + | ++ + + + + + | + */ |
+ +345 + | ++ + + + + + | + public Optional<Integer> getRemainingDiscoverableCredentials() { |
+ +346 + | +
+
+1
+
+1. getRemainingDiscoverableCredentials : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getRemainingDiscoverableCredentials → SURVIVED + + + + |
+ return Optional.ofNullable(remainingDiscoverableCredentials); |
+ +347 + | ++ + + + + + | + } |
+ +348 + | ++ + + + + + | +|
+ +349 + | ++ + + + + + | + /** |
+ +350 + | ++ + + + + + | + * @see <a |
+ +351 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#authenticatorGetInfo">Client |
+ +352 + | ++ + + + + + | + * to Authenticator Protocol (CTAP) §6.4. authenticatorGetInfo (0x04)</a> |
+ +353 + | ++ + + + + + | + */ |
+ +354 + | ++ + + + + + | + public Optional<Set<Integer>> getVendorPrototypeConfigCommands() { |
+ +355 + | +
+
+1
+
+1. getVendorPrototypeConfigCommands : replaced return value with Optional.empty for com/yubico/fido/metadata/AuthenticatorGetInfo::getVendorPrototypeConfigCommands → SURVIVED + + + + |
+ return Optional.ofNullable(vendorPrototypeConfigCommands); |
+ +356 + | ++ + + + + + | + } |
+ +357 + | ++ + + + + + | +|
+ +358 + | ++ + + + + + | + private static class SetFromIntJsonDeserializer |
+ +359 + | ++ + + + + + | + extends JsonDeserializer<Set<UserVerificationMethod>> { |
+ +360 + | ++ + + + + + | + @Override |
+ +361 + | ++ + + + + + | + public Set<UserVerificationMethod> deserialize(JsonParser p, DeserializationContext ctxt) |
+ +362 + | ++ + + + + + | + throws IOException { |
+ +363 + | ++ + + + + + | + final int bitset = p.getNumberValue().intValue(); |
+ +364 + | +
+
+1
+
+1. deserialize : replaced return value with Collections.emptySet for com/yubico/fido/metadata/AuthenticatorGetInfo$SetFromIntJsonDeserializer::deserialize → KILLED + + + + |
+ return Arrays.stream(UserVerificationMethod.values()) |
+ +365 + | +
+
+3
+
+1. lambda$deserialize$0 : Replaced bitwise AND with OR → KILLED +2. lambda$deserialize$0 : negated conditional → KILLED +3. lambda$deserialize$0 : replaced boolean return with true for com/yubico/fido/metadata/AuthenticatorGetInfo$SetFromIntJsonDeserializer::lambda$deserialize$0 → KILLED + + + + |
+ .filter(uvm -> (uvm.getValue() & bitset) != 0) |
+ +366 + | ++ + + + + + | + .collect(Collectors.toSet()); |
+ +367 + | ++ + + + + + | + } |
+ +368 + | ++ + + + + + | + } |
+ +369 + | ++ + + + + + | +|
+ +370 + | ++ + + + + + | + private static class IntFromSetJsonSerializer |
+ +371 + | ++ + + + + + | + extends JsonSerializer<Set<UserVerificationMethod>> { |
+ +372 + | ++ + + + + + | + @Override |
+ +373 + | ++ + + + + + | + public void serialize( |
+ +374 + | ++ + + + + + | + Set<UserVerificationMethod> value, JsonGenerator gen, SerializerProvider serializers) |
+ +375 + | ++ + + + + + | + throws IOException { |
+ +376 + | +
+
+1
+
+1. serialize : removed call to com/fasterxml/jackson/core/JsonGenerator::writeNumber → KILLED + + + + |
+ gen.writeNumber( |
+ +377 + | +
+
+4
+
+1. lambda$serialize$1 : Replaced bitwise OR with AND → NO_COVERAGE +2. lambda$serialize$1 : replaced Integer return value with 0 for com/yubico/fido/metadata/AuthenticatorGetInfo$IntFromSetJsonSerializer::lambda$serialize$1 → NO_COVERAGE +3. lambda$serialize$0 : replaced Integer return value with 0 for com/yubico/fido/metadata/AuthenticatorGetInfo$IntFromSetJsonSerializer::lambda$serialize$0 → KILLED +4. lambda$serialize$0 : Replaced bitwise OR with AND → KILLED + + + + |
+ value.stream().reduce(0, (acc, next) -> acc | next.getValue(), (a, b) -> a | b)); |
+ +378 + | ++ + + + + + | + } |
+ +379 + | ++ + + + + + | + } |
+ +380 + | ++ + + + + + | +} |
Mutations | ||
184 | ++ |
+
+
+
+ 1.1 |
+
193 | ++ |
+
+
+
+ 1.1 |
+
202 | ++ |
+
+
+
+ 1.1 |
+
211 | ++ |
+
+
+
+ 1.1 |
+
220 | ++ |
+
+
+
+ 1.1 |
+
229 | ++ |
+
+
+
+ 1.1 |
+
238 | ++ |
+
+
+
+ 1.1 |
+
247 | ++ |
+
+
+
+ 1.1 |
+
256 | ++ |
+
+
+
+ 1.1 |
+
265 | ++ |
+
+
+
+ 1.1 |
+
274 | ++ |
+
+
+
+ 1.1 |
+
283 | ++ |
+
+
+
+ 1.1 |
+
292 | ++ |
+
+
+
+ 1.1 |
+
301 | ++ |
+
+
+
+ 1.1 |
+
310 | ++ |
+
+
+
+ 1.1 |
+
319 | ++ |
+
+
+
+ 1.1 |
+
328 | ++ |
+
+
+
+ 1.1 |
+
337 | ++ |
+
+
+
+ 1.1 |
+
346 | ++ |
+
+
+
+ 1.1 |
+
355 | ++ |
+
+
+
+ 1.1 |
+
364 | ++ |
+
+
+
+ 1.1 |
+
365 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
376 | ++ |
+
+
+
+ 1.1 |
+
377 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.util.Optional; |
+ +4 + | ++ + + + + + | +import lombok.Builder; |
+ +5 + | ++ + + + + + | +import lombok.Value; |
+ +6 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | +/** |
+ +9 + | ++ + + + + + | + * The BiometricAccuracyDescriptor describes relevant accuracy/complexity aspects in the case of a |
+ +10 + | ++ + + + + + | + * biometric user verification method, see [<a |
+ +11 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/biometric/requirements/Biometrics-Requirements-v2.0-fd-20201006.html">FIDOBiometricsRequirements</a>]. |
+ +12 + | ++ + + + + + | + * |
+ +13 + | ++ + + + + + | + * <p>At least one of the values MUST be set. If the vendor doesn’t want to specify such values, |
+ +14 + | ++ + + + + + | + * then {@link VerificationMethodDescriptor#getBaDesc()} MUST be omitted. |
+ +15 + | ++ + + + + + | + * |
+ +16 + | ++ + + + + + | + * @see <a |
+ +17 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary">FIDO |
+ +18 + | ++ + + + + + | + * Metadata Statement §3.3. BiometricAccuracyDescriptor dictionary</a> |
+ +19 + | ++ + + + + + | + */ |
+ +20 + | ++ + + + + + | +@Value |
+ +21 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +22 + | ++ + + + + + | +@Jacksonized |
+ +23 + | ++ + + + + + | +public class BiometricAccuracyDescriptor { |
+ +24 + | ++ + + + + + | +|
+ +25 + | ++ + + + + + | + Double selfAttestedFRR; |
+ +26 + | ++ + + + + + | + Double selfAttestedFAR; |
+ +27 + | ++ + + + + + | + Integer maxTemplates; |
+ +28 + | ++ + + + + + | + Integer maxRetries; |
+ +29 + | ++ + + + + + | + Integer blockSlowdown; |
+ +30 + | ++ + + + + + | +|
+ +31 + | ++ + + + + + | + /** |
+ +32 + | ++ + + + + + | + * @see <a |
+ +33 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary">FIDO |
+ +34 + | ++ + + + + + | + * Metadata Statement §3.3. BiometricAccuracyDescriptor dictionary</a> |
+ +35 + | ++ + + + + + | + */ |
+ +36 + | ++ + + + + + | + public Optional<Double> getSelfAttestedFRR() { |
+ +37 + | +
+
+1
+
+1. getSelfAttestedFRR : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricAccuracyDescriptor::getSelfAttestedFRR → SURVIVED + + + + |
+ return Optional.ofNullable(selfAttestedFRR); |
+ +38 + | ++ + + + + + | + } |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | + /** |
+ +41 + | ++ + + + + + | + * @see <a |
+ +42 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary">FIDO |
+ +43 + | ++ + + + + + | + * Metadata Statement §3.3. BiometricAccuracyDescriptor dictionary</a> |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | + public Optional<Double> getSelfAttestedFAR() { |
+ +46 + | +
+
+1
+
+1. getSelfAttestedFAR : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricAccuracyDescriptor::getSelfAttestedFAR → SURVIVED + + + + |
+ return Optional.ofNullable(selfAttestedFAR); |
+ +47 + | ++ + + + + + | + } |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** |
+ +50 + | ++ + + + + + | + * @see <a |
+ +51 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary">FIDO |
+ +52 + | ++ + + + + + | + * Metadata Statement §3.3. BiometricAccuracyDescriptor dictionary</a> |
+ +53 + | ++ + + + + + | + */ |
+ +54 + | ++ + + + + + | + public Optional<Integer> getMaxTemplates() { |
+ +55 + | +
+
+1
+
+1. getMaxTemplates : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricAccuracyDescriptor::getMaxTemplates → SURVIVED + + + + |
+ return Optional.ofNullable(maxTemplates); |
+ +56 + | ++ + + + + + | + } |
+ +57 + | ++ + + + + + | +|
+ +58 + | ++ + + + + + | + /** |
+ +59 + | ++ + + + + + | + * @see <a |
+ +60 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary">FIDO |
+ +61 + | ++ + + + + + | + * Metadata Statement §3.3. BiometricAccuracyDescriptor dictionary</a> |
+ +62 + | ++ + + + + + | + */ |
+ +63 + | ++ + + + + + | + public Optional<Integer> getMaxRetries() { |
+ +64 + | +
+
+1
+
+1. getMaxRetries : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricAccuracyDescriptor::getMaxRetries → SURVIVED + + + + |
+ return Optional.ofNullable(maxRetries); |
+ +65 + | ++ + + + + + | + } |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + /** |
+ +68 + | ++ + + + + + | + * @see <a |
+ +69 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biometricaccuracydescriptor-dictionary">FIDO |
+ +70 + | ++ + + + + + | + * Metadata Statement §3.3. BiometricAccuracyDescriptor dictionary</a> |
+ +71 + | ++ + + + + + | + */ |
+ +72 + | ++ + + + + + | + public Optional<Integer> getBlockSlowdown() { |
+ +73 + | +
+
+1
+
+1. getBlockSlowdown : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricAccuracyDescriptor::getBlockSlowdown → SURVIVED + + + + |
+ return Optional.ofNullable(blockSlowdown); |
+ +74 + | ++ + + + + + | + } |
+ +75 + | ++ + + + + + | +} |
Mutations | ||
37 | ++ |
+
+
+
+ 1.1 |
+
46 | ++ |
+
+
+
+ 1.1 |
+
55 | ++ |
+
+
+
+ 1.1 |
+
64 | ++ |
+
+
+
+ 1.1 |
+
73 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.UserVerificationMethod; |
+ +4 + | ++ + + + + + | +import java.time.LocalDate; |
+ +5 + | ++ + + + + + | +import java.util.Optional; |
+ +6 + | ++ + + + + + | +import lombok.Builder; |
+ +7 + | ++ + + + + + | +import lombok.NonNull; |
+ +8 + | ++ + + + + + | +import lombok.Value; |
+ +9 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +10 + | ++ + + + + + | +|
+ +11 + | ++ + + + + + | +/** |
+ +12 + | ++ + + + + + | + * Contains the current BiometricStatusReport of one of the authenticator’s biometric component. |
+ +13 + | ++ + + + + + | + * |
+ +14 + | ++ + + + + + | + * @see <a |
+ +15 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +16 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +17 + | ++ + + + + + | + */ |
+ +18 + | ++ + + + + + | +@Value |
+ +19 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +20 + | ++ + + + + + | +@Jacksonized |
+ +21 + | ++ + + + + + | +public class BiometricStatusReport { |
+ +22 + | ++ + + + + + | +|
+ +23 + | ++ + + + + + | + /** |
+ +24 + | ++ + + + + + | + * @see <a |
+ +25 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +26 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +27 + | ++ + + + + + | + */ |
+ +28 + | ++ + + + + + | + int certLevel; |
+ +29 + | ++ + + + + + | +|
+ +30 + | ++ + + + + + | + /** |
+ +31 + | ++ + + + + + | + * @see <a |
+ +32 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +33 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +34 + | ++ + + + + + | + */ |
+ +35 + | ++ + + + + + | + @NonNull UserVerificationMethod modality; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | + LocalDate effectiveDate; |
+ +38 + | ++ + + + + + | + String certificationDescriptor; |
+ +39 + | ++ + + + + + | + String certificateNumber; |
+ +40 + | ++ + + + + + | + String certificationPolicyVersion; |
+ +41 + | ++ + + + + + | + String certificationRequirementsVersion; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + /** |
+ +44 + | ++ + + + + + | + * @see <a |
+ +45 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +46 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +47 + | ++ + + + + + | + */ |
+ +48 + | ++ + + + + + | + public Optional<LocalDate> getEffectiveDate() { |
+ +49 + | +
+
+1
+
+1. getEffectiveDate : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricStatusReport::getEffectiveDate → SURVIVED + + + + |
+ return Optional.ofNullable(effectiveDate); |
+ +50 + | ++ + + + + + | + } |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * @see <a |
+ +54 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +55 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +56 + | ++ + + + + + | + */ |
+ +57 + | ++ + + + + + | + public Optional<String> getCertificationDescriptor() { |
+ +58 + | +
+
+1
+
+1. getCertificationDescriptor : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricStatusReport::getCertificationDescriptor → SURVIVED + + + + |
+ return Optional.ofNullable(certificationDescriptor); |
+ +59 + | ++ + + + + + | + } |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * @see <a |
+ +63 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +64 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +65 + | ++ + + + + + | + */ |
+ +66 + | ++ + + + + + | + public Optional<String> getCertificateNumber() { |
+ +67 + | +
+
+1
+
+1. getCertificateNumber : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricStatusReport::getCertificateNumber → SURVIVED + + + + |
+ return Optional.ofNullable(certificateNumber); |
+ +68 + | ++ + + + + + | + } |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * @see <a |
+ +72 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +73 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +74 + | ++ + + + + + | + */ |
+ +75 + | ++ + + + + + | + public Optional<String> getCertificationPolicyVersion() { |
+ +76 + | +
+
+1
+
+1. getCertificationPolicyVersion : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricStatusReport::getCertificationPolicyVersion → SURVIVED + + + + |
+ return Optional.ofNullable(certificationPolicyVersion); |
+ +77 + | ++ + + + + + | + } |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + /** |
+ +80 + | ++ + + + + + | + * @see <a |
+ +81 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biometricstatusreport-dictionary">FIDO |
+ +82 + | ++ + + + + + | + * Metadata Service §3.1.2. BiometricStatusReport dictionary</a> |
+ +83 + | ++ + + + + + | + */ |
+ +84 + | ++ + + + + + | + public Optional<String> getCertificationRequirementsVersion() { |
+ +85 + | +
+
+1
+
+1. getCertificationRequirementsVersion : replaced return value with Optional.empty for com/yubico/fido/metadata/BiometricStatusReport::getCertificationRequirementsVersion → SURVIVED + + + + |
+ return Optional.ofNullable(certificationRequirementsVersion); |
+ +86 + | ++ + + + + + | + } |
+ +87 + | ++ + + + + + | +} |
Mutations | ||
49 | ++ |
+
+
+
+ 1.1 |
+
58 | ++ |
+
+
+
+ 1.1 |
+
67 | ++ |
+
+
+
+ 1.1 |
+
76 | ++ |
+
+
+
+ 1.1 |
+
85 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JavaType; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.databind.type.TypeFactory; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.databind.util.Converter; |
+ +6 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +7 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +8 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +9 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +10 + | ++ + + + + + | +|
+ +11 + | ++ + + + + + | +class CertFromBase64Converter implements Converter<String, X509Certificate> { |
+ +12 + | ++ + + + + + | + @Override |
+ +13 + | ++ + + + + + | + public X509Certificate convert(String value) { |
+ +14 + | ++ + + + + + | + try { |
+ +15 + | +
+
+1
+
+1. convert : replaced return value with null for com/yubico/fido/metadata/CertFromBase64Converter::convert → KILLED + + + + |
+ return CertificateParser.parseDer( |
+ +16 + | ++ + + + + + | + ByteArray.fromBase64(value.replaceAll("\\s+", "")).getBytes()); |
+ +17 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +18 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +19 + | ++ + + + + + | + } |
+ +20 + | ++ + + + + + | + } |
+ +21 + | ++ + + + + + | +|
+ +22 + | ++ + + + + + | + @Override |
+ +23 + | ++ + + + + + | + public JavaType getInputType(TypeFactory typeFactory) { |
+ +24 + | +
+
+1
+
+1. getInputType : replaced return value with null for com/yubico/fido/metadata/CertFromBase64Converter::getInputType → KILLED + + + + |
+ return typeFactory.constructType(String.class); |
+ +25 + | ++ + + + + + | + } |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | + @Override |
+ +28 + | ++ + + + + + | + public JavaType getOutputType(TypeFactory typeFactory) { |
+ +29 + | +
+
+1
+
+1. getOutputType : replaced return value with null for com/yubico/fido/metadata/CertFromBase64Converter::getOutputType → NO_COVERAGE + + + + |
+ return typeFactory.constructType(X509Certificate.class); |
+ +30 + | ++ + + + + + | + } |
+ +31 + | ++ + + + + + | +} |
Mutations | ||
15 | ++ |
+
+
+
+ 1.1 |
+
24 | ++ |
+
+
+
+ 1.1 |
+
29 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JavaType; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.databind.type.TypeFactory; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.databind.util.Converter; |
+ +6 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +7 + | ++ + + + + + | +import java.security.cert.CertificateEncodingException; |
+ +8 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +9 + | ++ + + + + + | +|
+ +10 + | ++ + + + + + | +class CertToBase64Converter implements Converter<X509Certificate, String> { |
+ +11 + | ++ + + + + + | + @Override |
+ +12 + | ++ + + + + + | + public String convert(X509Certificate value) { |
+ +13 + | ++ + + + + + | + try { |
+ +14 + | +
+
+1
+
+1. convert : replaced return value with "" for com/yubico/fido/metadata/CertToBase64Converter::convert → KILLED + + + + |
+ return new ByteArray(value.getEncoded()).getBase64(); |
+ +15 + | ++ + + + + + | + } catch (CertificateEncodingException e) { |
+ +16 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +17 + | ++ + + + + + | + } |
+ +18 + | ++ + + + + + | + } |
+ +19 + | ++ + + + + + | +|
+ +20 + | ++ + + + + + | + @Override |
+ +21 + | ++ + + + + + | + public JavaType getInputType(TypeFactory typeFactory) { |
+ +22 + | +
+
+1
+
+1. getInputType : replaced return value with null for com/yubico/fido/metadata/CertToBase64Converter::getInputType → NO_COVERAGE + + + + |
+ return typeFactory.constructType(X509Certificate.class); |
+ +23 + | ++ + + + + + | + } |
+ +24 + | ++ + + + + + | +|
+ +25 + | ++ + + + + + | + @Override |
+ +26 + | ++ + + + + + | + public JavaType getOutputType(TypeFactory typeFactory) { |
+ +27 + | +
+
+1
+
+1. getOutputType : replaced return value with null for com/yubico/fido/metadata/CertToBase64Converter::getOutputType → KILLED + + + + |
+ return typeFactory.constructType(String.class); |
+ +28 + | ++ + + + + + | + } |
+ +29 + | ++ + + + + + | +} |
Mutations | ||
14 | ++ |
+
+
+
+ 1.1 |
+
22 | ++ |
+
+
+
+ 1.1 |
+
27 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.util.Optional; |
+ +4 + | ++ + + + + + | +import lombok.Builder; |
+ +5 + | ++ + + + + + | +import lombok.Value; |
+ +6 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | +/** |
+ +9 + | ++ + + + + + | + * The CodeAccuracyDescriptor describes the relevant accuracy/complexity aspects of passcode user |
+ +10 + | ++ + + + + + | + * verification methods. |
+ +11 + | ++ + + + + + | + * |
+ +12 + | ++ + + + + + | + * @see <a |
+ +13 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#codeaccuracydescriptor-dictionary">FIDO |
+ +14 + | ++ + + + + + | + * Metadata Statement §3.2. CodeAccuracyDescriptor dictionary</a> |
+ +15 + | ++ + + + + + | + */ |
+ +16 + | ++ + + + + + | +@Value |
+ +17 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +18 + | ++ + + + + + | +@Jacksonized |
+ +19 + | ++ + + + + + | +public class CodeAccuracyDescriptor { |
+ +20 + | ++ + + + + + | +|
+ +21 + | ++ + + + + + | + /** |
+ +22 + | ++ + + + + + | + * @see <a |
+ +23 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#codeaccuracydescriptor-dictionary">FIDO |
+ +24 + | ++ + + + + + | + * Metadata Statement §3.2. CodeAccuracyDescriptor dictionary</a> |
+ +25 + | ++ + + + + + | + */ |
+ +26 + | ++ + + + + + | + int base; |
+ +27 + | ++ + + + + + | +|
+ +28 + | ++ + + + + + | + /** |
+ +29 + | ++ + + + + + | + * @see <a |
+ +30 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#codeaccuracydescriptor-dictionary">FIDO |
+ +31 + | ++ + + + + + | + * Metadata Statement §3.2. CodeAccuracyDescriptor dictionary</a> |
+ +32 + | ++ + + + + + | + */ |
+ +33 + | ++ + + + + + | + int minLength; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + Integer maxRetries; |
+ +36 + | ++ + + + + + | + Integer blockSlowdown; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + /** |
+ +39 + | ++ + + + + + | + * @see <a |
+ +40 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#codeaccuracydescriptor-dictionary">FIDO |
+ +41 + | ++ + + + + + | + * Metadata Statement §3.2. CodeAccuracyDescriptor dictionary</a> |
+ +42 + | ++ + + + + + | + */ |
+ +43 + | ++ + + + + + | + public Optional<Integer> getMaxRetries() { |
+ +44 + | +
+
+1
+
+1. getMaxRetries : replaced return value with Optional.empty for com/yubico/fido/metadata/CodeAccuracyDescriptor::getMaxRetries → SURVIVED + + + + |
+ return Optional.ofNullable(maxRetries); |
+ +45 + | ++ + + + + + | + } |
+ +46 + | ++ + + + + + | +|
+ +47 + | ++ + + + + + | + /** |
+ +48 + | ++ + + + + + | + * @see <a |
+ +49 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#codeaccuracydescriptor-dictionary">FIDO |
+ +50 + | ++ + + + + + | + * Metadata Statement §3.2. CodeAccuracyDescriptor dictionary</a> |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | + public Optional<Integer> getBlockSlowdown() { |
+ +53 + | +
+
+1
+
+1. getBlockSlowdown : replaced return value with Optional.empty for com/yubico/fido/metadata/CodeAccuracyDescriptor::getBlockSlowdown → SURVIVED + + + + |
+ return Optional.ofNullable(blockSlowdown); |
+ +54 + | ++ + + + + + | + } |
+ +55 + | ++ + + + + + | +} |
Mutations | ||
44 | ++ |
+
+
+
+ 1.1 |
+
53 | ++ |
+
+
+
+ 1.1 |
+
+ +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.fasterxml.jackson.core.Base64Variants; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.databind.DeserializationFeature; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.databind.ObjectMapper; |
+ +30 + | ++ + + + + + | +import com.yubico.fido.metadata.FidoMetadataDownloaderException.Reason; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.BinaryUtil; |
+ +32 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.Base64UrlException; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.HexException; |
+ +36 + | ++ + + + + + | +import java.io.ByteArrayInputStream; |
+ +37 + | ++ + + + + + | +import java.io.File; |
+ +38 + | ++ + + + + + | +import java.io.FileInputStream; |
+ +39 + | ++ + + + + + | +import java.io.FileNotFoundException; |
+ +40 + | ++ + + + + + | +import java.io.FileOutputStream; |
+ +41 + | ++ + + + + + | +import java.io.IOException; |
+ +42 + | ++ + + + + + | +import java.io.InputStream; |
+ +43 + | ++ + + + + + | +import java.net.MalformedURLException; |
+ +44 + | ++ + + + + + | +import java.net.URL; |
+ +45 + | ++ + + + + + | +import java.net.URLConnection; |
+ +46 + | ++ + + + + + | +import java.nio.charset.StandardCharsets; |
+ +47 + | ++ + + + + + | +import java.security.DigestException; |
+ +48 + | ++ + + + + + | +import java.security.InvalidAlgorithmParameterException; |
+ +49 + | ++ + + + + + | +import java.security.InvalidKeyException; |
+ +50 + | ++ + + + + + | +import java.security.KeyManagementException; |
+ +51 + | ++ + + + + + | +import java.security.KeyStore; |
+ +52 + | ++ + + + + + | +import java.security.KeyStoreException; |
+ +53 + | ++ + + + + + | +import java.security.MessageDigest; |
+ +54 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +55 + | ++ + + + + + | +import java.security.Signature; |
+ +56 + | ++ + + + + + | +import java.security.SignatureException; |
+ +57 + | ++ + + + + + | +import java.security.cert.CRL; |
+ +58 + | ++ + + + + + | +import java.security.cert.CertPath; |
+ +59 + | ++ + + + + + | +import java.security.cert.CertPathValidator; |
+ +60 + | ++ + + + + + | +import java.security.cert.CertPathValidatorException; |
+ +61 + | ++ + + + + + | +import java.security.cert.CertStore; |
+ +62 + | ++ + + + + + | +import java.security.cert.CertStoreParameters; |
+ +63 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +64 + | ++ + + + + + | +import java.security.cert.CertificateFactory; |
+ +65 + | ++ + + + + + | +import java.security.cert.CollectionCertStoreParameters; |
+ +66 + | ++ + + + + + | +import java.security.cert.PKIXParameters; |
+ +67 + | ++ + + + + + | +import java.security.cert.TrustAnchor; |
+ +68 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +69 + | ++ + + + + + | +import java.time.Clock; |
+ +70 + | ++ + + + + + | +import java.util.ArrayList; |
+ +71 + | ++ + + + + + | +import java.util.Collection; |
+ +72 + | ++ + + + + + | +import java.util.Collections; |
+ +73 + | ++ + + + + + | +import java.util.Date; |
+ +74 + | ++ + + + + + | +import java.util.List; |
+ +75 + | ++ + + + + + | +import java.util.Optional; |
+ +76 + | ++ + + + + + | +import java.util.Scanner; |
+ +77 + | ++ + + + + + | +import java.util.Set; |
+ +78 + | ++ + + + + + | +import java.util.UUID; |
+ +79 + | ++ + + + + + | +import java.util.function.Consumer; |
+ +80 + | ++ + + + + + | +import java.util.function.Supplier; |
+ +81 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +82 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +83 + | ++ + + + + + | +import javax.net.ssl.HttpsURLConnection; |
+ +84 + | ++ + + + + + | +import javax.net.ssl.SSLContext; |
+ +85 + | ++ + + + + + | +import javax.net.ssl.TrustManagerFactory; |
+ +86 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +87 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +88 + | ++ + + + + + | +import lombok.NonNull; |
+ +89 + | ++ + + + + + | +import lombok.RequiredArgsConstructor; |
+ +90 + | ++ + + + + + | +import lombok.Value; |
+ +91 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +92 + | ++ + + + + + | +|
+ +93 + | ++ + + + + + | +/** |
+ +94 + | ++ + + + + + | + * Utility for downloading, caching and verifying Fido Metadata Service BLOBs and associated |
+ +95 + | ++ + + + + + | + * certificates. |
+ +96 + | ++ + + + + + | + * |
+ +97 + | ++ + + + + + | + * <p>This class is NOT THREAD SAFE since it reads and writes caches. However, it has no internal |
+ +98 + | ++ + + + + + | + * mutable state, so instances MAY be reused in single-threaded or externally synchronized contexts. |
+ +99 + | ++ + + + + + | + * See also the {@link #loadCachedBlob()} and {@link #refreshBlob()} methods. |
+ +100 + | ++ + + + + + | + * |
+ +101 + | ++ + + + + + | + * <p>Use the {@link #builder() builder} to configure settings, then use the {@link |
+ +102 + | ++ + + + + + | + * #loadCachedBlob()} and {@link #refreshBlob()} methods to load the metadata BLOB. |
+ +103 + | ++ + + + + + | + */ |
+ +104 + | ++ + + + + + | +@Slf4j |
+ +105 + | ++ + + + + + | +@AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +106 + | ++ + + + + + | +public final class FidoMetadataDownloader { |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + @NonNull private final Set<String> expectedLegalHeaders; |
+ +109 + | ++ + + + + + | + private final X509Certificate trustRootCertificate; |
+ +110 + | ++ + + + + + | + private final URL trustRootUrl; |
+ +111 + | ++ + + + + + | + private final Set<ByteArray> trustRootSha256; |
+ +112 + | ++ + + + + + | + private final File trustRootCacheFile; |
+ +113 + | ++ + + + + + | + private final Supplier<Optional<ByteArray>> trustRootCacheSupplier; |
+ +114 + | ++ + + + + + | + private final Consumer<ByteArray> trustRootCacheConsumer; |
+ +115 + | ++ + + + + + | + private final String blobJwt; |
+ +116 + | ++ + + + + + | + private final URL blobUrl; |
+ +117 + | ++ + + + + + | + private final File blobCacheFile; |
+ +118 + | ++ + + + + + | + private final Supplier<Optional<ByteArray>> blobCacheSupplier; |
+ +119 + | ++ + + + + + | + private final Consumer<ByteArray> blobCacheConsumer; |
+ +120 + | ++ + + + + + | + private final CertStore certStore; |
+ +121 + | ++ + + + + + | + @NonNull private final Clock clock; |
+ +122 + | ++ + + + + + | + private final KeyStore httpsTrustStore; |
+ +123 + | ++ + + + + + | + private final boolean verifyDownloadsOnly; |
+ +124 + | ++ + + + + + | +|
+ +125 + | ++ + + + + + | + /** |
+ +126 + | ++ + + + + + | + * Begin configuring a {@link FidoMetadataDownloader} instance. See the {@link |
+ +127 + | ++ + + + + + | + * FidoMetadataDownloaderBuilder.Step1 Step1} type. |
+ +128 + | ++ + + + + + | + * |
+ +129 + | ++ + + + + + | + * @see FidoMetadataDownloaderBuilder.Step1 |
+ +130 + | ++ + + + + + | + */ |
+ +131 + | ++ + + + + + | + public static FidoMetadataDownloaderBuilder.Step1 builder() { |
+ +132 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::builder → KILLED + + + + |
+ return new FidoMetadataDownloaderBuilder.Step1(); |
+ +133 + | ++ + + + + + | + } |
+ +134 + | ++ + + + + + | +|
+ +135 + | ++ + + + + + | + @RequiredArgsConstructor(access = AccessLevel.PRIVATE) |
+ +136 + | ++ + + + + + | + public static class FidoMetadataDownloaderBuilder { |
+ +137 + | ++ + + + + + | + @NonNull private final Set<String> expectedLegalHeaders; |
+ +138 + | ++ + + + + + | + private final X509Certificate trustRootCertificate; |
+ +139 + | ++ + + + + + | + private final URL trustRootUrl; |
+ +140 + | ++ + + + + + | + private final Set<ByteArray> trustRootSha256; |
+ +141 + | ++ + + + + + | + private final File trustRootCacheFile; |
+ +142 + | ++ + + + + + | + private final Supplier<Optional<ByteArray>> trustRootCacheSupplier; |
+ +143 + | ++ + + + + + | + private final Consumer<ByteArray> trustRootCacheConsumer; |
+ +144 + | ++ + + + + + | + private final String blobJwt; |
+ +145 + | ++ + + + + + | + private final URL blobUrl; |
+ +146 + | ++ + + + + + | + private final File blobCacheFile; |
+ +147 + | ++ + + + + + | + private final Supplier<Optional<ByteArray>> blobCacheSupplier; |
+ +148 + | ++ + + + + + | + private final Consumer<ByteArray> blobCacheConsumer; |
+ +149 + | ++ + + + + + | +|
+ +150 + | ++ + + + + + | + private CertStore certStore = null; |
+ +151 + | ++ + + + + + | + @NonNull private Clock clock = Clock.systemUTC(); |
+ +152 + | ++ + + + + + | + private KeyStore httpsTrustStore = null; |
+ +153 + | ++ + + + + + | + private boolean verifyDownloadsOnly = false; |
+ +154 + | ++ + + + + + | +|
+ +155 + | ++ + + + + + | + public FidoMetadataDownloader build() { |
+ +156 + | +
+
+1
+
+1. build : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::build → KILLED + + + + |
+ return new FidoMetadataDownloader( |
+ +157 + | ++ + + + + + | + expectedLegalHeaders, |
+ +158 + | ++ + + + + + | + trustRootCertificate, |
+ +159 + | ++ + + + + + | + trustRootUrl, |
+ +160 + | ++ + + + + + | + trustRootSha256, |
+ +161 + | ++ + + + + + | + trustRootCacheFile, |
+ +162 + | ++ + + + + + | + trustRootCacheSupplier, |
+ +163 + | ++ + + + + + | + trustRootCacheConsumer, |
+ +164 + | ++ + + + + + | + blobJwt, |
+ +165 + | ++ + + + + + | + blobUrl, |
+ +166 + | ++ + + + + + | + blobCacheFile, |
+ +167 + | ++ + + + + + | + blobCacheSupplier, |
+ +168 + | ++ + + + + + | + blobCacheConsumer, |
+ +169 + | ++ + + + + + | + certStore, |
+ +170 + | ++ + + + + + | + clock, |
+ +171 + | ++ + + + + + | + httpsTrustStore, |
+ +172 + | ++ + + + + + | + verifyDownloadsOnly); |
+ +173 + | ++ + + + + + | + } |
+ +174 + | ++ + + + + + | +|
+ +175 + | ++ + + + + + | + /** |
+ +176 + | ++ + + + + + | + * Step 1: Set the legal header to expect from the FIDO Metadata Service. |
+ +177 + | ++ + + + + + | + * |
+ +178 + | ++ + + + + + | + * <p>By using the FIDO Metadata Service, you will be subject to its terms of service. This step |
+ +179 + | ++ + + + + + | + * serves two purposes: |
+ +180 + | ++ + + + + + | + * |
+ +181 + | ++ + + + + + | + * <ol> |
+ +182 + | ++ + + + + + | + * <li>To remind you and any code reviewers that you need to read those terms of service |
+ +183 + | ++ + + + + + | + * before using this feature. |
+ +184 + | ++ + + + + + | + * <li>To help you detect if the legal header changes, so you can take appropriate action. |
+ +185 + | ++ + + + + + | + * </ol> |
+ +186 + | ++ + + + + + | + * |
+ +187 + | ++ + + + + + | + * <p>See {@link Step1#expectLegalHeader(String...)}. |
+ +188 + | ++ + + + + + | + * |
+ +189 + | ++ + + + + + | + * @see Step1#expectLegalHeader(String...) |
+ +190 + | ++ + + + + + | + */ |
+ +191 + | ++ + + + + + | + @AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +192 + | ++ + + + + + | + public static class Step1 { |
+ +193 + | ++ + + + + + | +|
+ +194 + | ++ + + + + + | + /** |
+ +195 + | ++ + + + + + | + * Set legal headers expected in the metadata BLOB. |
+ +196 + | ++ + + + + + | + * |
+ +197 + | ++ + + + + + | + * <p>By using the FIDO Metadata Service, you will be subject to its terms of service. This |
+ +198 + | ++ + + + + + | + * builder step serves two purposes: |
+ +199 + | ++ + + + + + | + * |
+ +200 + | ++ + + + + + | + * <ol> |
+ +201 + | ++ + + + + + | + * <li>To remind you and any code reviewers that you need to read those terms of service |
+ +202 + | ++ + + + + + | + * before using this feature. |
+ +203 + | ++ + + + + + | + * <li>To help you detect if the legal header changes, so you can take appropriate action. |
+ +204 + | ++ + + + + + | + * </ol> |
+ +205 + | ++ + + + + + | + * |
+ +206 + | ++ + + + + + | + * <p>If the legal header in the downloaded BLOB does not equal any of the <code> |
+ +207 + | ++ + + + + + | + * expectedLegalHeaders</code>, an {@link UnexpectedLegalHeader} exception will be thrown in |
+ +208 + | ++ + + + + + | + * the finalizing builder step. |
+ +209 + | ++ + + + + + | + * |
+ +210 + | ++ + + + + + | + * <p>Note that this library makes no guarantee that a change to the FIDO Metadata Service |
+ +211 + | ++ + + + + + | + * terms of service will also cause a change to the legal header in the BLOB. |
+ +212 + | ++ + + + + + | + * |
+ +213 + | ++ + + + + + | + * <p>At the time of this library release, the current legal header is <code> |
+ +214 + | ++ + + + + + | + * "Retrieval and use of this BLOB indicates acceptance of the appropriate agreement located at https://fidoalliance.org/metadata/metadata-legal-terms/" |
+ +215 + | ++ + + + + + | + * </code>. |
+ +216 + | ++ + + + + + | + * |
+ +217 + | ++ + + + + + | + * @param expectedLegalHeaders the set of BLOB legal headers you expect in the metadata BLOB |
+ +218 + | ++ + + + + + | + * payload. |
+ +219 + | ++ + + + + + | + */ |
+ +220 + | +
+
+1
+
+1. expectLegalHeader : negated conditional → KILLED + + + + |
+ public Step2 expectLegalHeader(@NonNull String... expectedLegalHeaders) { |
+ +221 + | +
+
+1
+
+1. expectLegalHeader : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step1::expectLegalHeader → KILLED + + + + |
+ return new Step2(Stream.of(expectedLegalHeaders).collect(Collectors.toSet())); |
+ +222 + | ++ + + + + + | + } |
+ +223 + | ++ + + + + + | + } |
+ +224 + | ++ + + + + + | +|
+ +225 + | ++ + + + + + | + /** |
+ +226 + | ++ + + + + + | + * Step 2: Configure how to retrieve the FIDO Metadata Service trust root certificate when |
+ +227 + | ++ + + + + + | + * necessary. |
+ +228 + | ++ + + + + + | + * |
+ +229 + | ++ + + + + + | + * <p>This step offers three mutually exclusive options: |
+ +230 + | ++ + + + + + | + * |
+ +231 + | ++ + + + + + | + * <ol> |
+ +232 + | ++ + + + + + | + * <li>Use the default download URL and certificate hash. This is the main intended use case. |
+ +233 + | ++ + + + + + | + * See {@link #useDefaultTrustRoot()}. |
+ +234 + | ++ + + + + + | + * <li>Use a custom download URL and certificate hash. This is for future-proofing in case the |
+ +235 + | ++ + + + + + | + * trust root certificate changes and there is no new release of this library. See {@link |
+ +236 + | ++ + + + + + | + * #downloadTrustRoot(URL, Set)}. |
+ +237 + | ++ + + + + + | + * <li>Use a pre-retrieved trust root certificate. It is up to you to perform any integrity |
+ +238 + | ++ + + + + + | + * checks and cache it as desired. See {@link #useTrustRoot(X509Certificate)}. |
+ +239 + | ++ + + + + + | + * </ol> |
+ +240 + | ++ + + + + + | + */ |
+ +241 + | ++ + + + + + | + @AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +242 + | ++ + + + + + | + public static class Step2 { |
+ +243 + | ++ + + + + + | +|
+ +244 + | ++ + + + + + | + @NonNull private final Set<String> expectedLegalHeaders; |
+ +245 + | ++ + + + + + | +|
+ +246 + | ++ + + + + + | + /** |
+ +247 + | ++ + + + + + | + * Download the trust root certificate from a hard-coded URL and verify it against a |
+ +248 + | ++ + + + + + | + * hard-coded SHA-256 hash. |
+ +249 + | ++ + + + + + | + * |
+ +250 + | ++ + + + + + | + * <p>This is an alias of: |
+ +251 + | ++ + + + + + | + * |
+ +252 + | ++ + + + + + | + * <pre> |
+ +253 + | ++ + + + + + | + * downloadTrustRoot( |
+ +254 + | ++ + + + + + | + * new URL("https://secure.globalsign.com/cacert/root-r3.crt"), |
+ +255 + | ++ + + + + + | + * Collections.singleton(ByteArray.fromHex("cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b")) |
+ +256 + | ++ + + + + + | + * ) |
+ +257 + | ++ + + + + + | + * </pre> |
+ +258 + | ++ + + + + + | + * |
+ +259 + | ++ + + + + + | + * This is the current FIDO Metadata Service trust root certificate at the time of this |
+ +260 + | ++ + + + + + | + * library release. |
+ +261 + | ++ + + + + + | + * |
+ +262 + | ++ + + + + + | + * @see #downloadTrustRoot(URL, Set) |
+ +263 + | ++ + + + + + | + */ |
+ +264 + | ++ + + + + + | + public Step3 useDefaultTrustRoot() { |
+ +265 + | ++ + + + + + | + try { |
+ +266 + | +
+
+1
+
+1. useDefaultTrustRoot : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step2::useDefaultTrustRoot → NO_COVERAGE + + + + |
+ return downloadTrustRoot( |
+ +267 + | ++ + + + + + | + new URL("https://secure.globalsign.com/cacert/root-r3.crt"), |
+ +268 + | ++ + + + + + | + Collections.singleton( |
+ +269 + | ++ + + + + + | + ByteArray.fromHex( |
+ +270 + | ++ + + + + + | + "cbb522d7b7f127ad6a0113865bdf1cd4102e7d0759af635a7cf4720dc963c53b"))); |
+ +271 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +272 + | ++ + + + + + | + throw new RuntimeException( |
+ +273 + | ++ + + + + + | + "Bad hard-coded trust root certificate URL. Please file a bug report.", e); |
+ +274 + | ++ + + + + + | + } catch (HexException e) { |
+ +275 + | ++ + + + + + | + throw new RuntimeException( |
+ +276 + | ++ + + + + + | + "Bad hard-coded trust root certificate hash. Please file a bug report.", e); |
+ +277 + | ++ + + + + + | + } |
+ +278 + | ++ + + + + + | + } |
+ +279 + | ++ + + + + + | +|
+ +280 + | ++ + + + + + | + /** |
+ +281 + | ++ + + + + + | + * Download the trust root certificate from the given HTTPS <code>url</code> and verify its |
+ +282 + | ++ + + + + + | + * SHA-256 hash against <code>acceptedCertSha256</code>. |
+ +283 + | ++ + + + + + | + * |
+ +284 + | ++ + + + + + | + * <p>The certificate will be downloaded if it does not exist in the cache, or if the cached |
+ +285 + | ++ + + + + + | + * certificate is not currently valid. |
+ +286 + | ++ + + + + + | + * |
+ +287 + | ++ + + + + + | + * <p>If the cert is downloaded, it is also written to the cache {@link File} or {@link |
+ +288 + | ++ + + + + + | + * Consumer} configured in the {@link Step3 next step}. |
+ +289 + | ++ + + + + + | + * |
+ +290 + | ++ + + + + + | + * @param url the HTTP URL to download. It MUST use the <code>https:</code> scheme. |
+ +291 + | ++ + + + + + | + * @param acceptedCertSha256 a set of SHA-256 hashes to verify the downloaded certificate |
+ +292 + | ++ + + + + + | + * against. The downloaded certificate MUST match at least one of these hashes. |
+ +293 + | ++ + + + + + | + * @throws IllegalArgumentException if <code>url</code> is not a HTTPS URL. |
+ +294 + | ++ + + + + + | + */ |
+ +295 + | +
+
+2
+
+1. downloadTrustRoot : negated conditional → KILLED +2. downloadTrustRoot : negated conditional → KILLED + + + + |
+ public Step3 downloadTrustRoot(@NonNull URL url, @NonNull Set<ByteArray> acceptedCertSha256) { |
+ +296 + | +
+
+1
+
+1. downloadTrustRoot : negated conditional → KILLED + + + + |
+ if (!"https".equals(url.getProtocol())) { |
+ +297 + | ++ + + + + + | + throw new IllegalArgumentException("Trust certificate download URL must be a HTTPS URL."); |
+ +298 + | ++ + + + + + | + } |
+ +299 + | +
+
+1
+
+1. downloadTrustRoot : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step2::downloadTrustRoot → KILLED + + + + |
+ return new Step3(this, null, url, acceptedCertSha256); |
+ +300 + | ++ + + + + + | + } |
+ +301 + | ++ + + + + + | +|
+ +302 + | ++ + + + + + | + /** |
+ +303 + | ++ + + + + + | + * Use the given trust root certificate. It is the caller's responsibility to perform any |
+ +304 + | ++ + + + + + | + * integrity checks and/or caching logic. |
+ +305 + | ++ + + + + + | + * |
+ +306 + | ++ + + + + + | + * @param trustRootCertificate the certificate to use as the FIDO Metadata Service trust root. |
+ +307 + | ++ + + + + + | + */ |
+ +308 + | +
+
+1
+
+1. useTrustRoot : negated conditional → KILLED + + + + |
+ public Step4 useTrustRoot(@NonNull X509Certificate trustRootCertificate) { |
+ +309 + | +
+
+1
+
+1. useTrustRoot : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step2::useTrustRoot → KILLED + + + + |
+ return new Step4(new Step3(this, trustRootCertificate, null, null), null, null, null); |
+ +310 + | ++ + + + + + | + } |
+ +311 + | ++ + + + + + | + } |
+ +312 + | ++ + + + + + | +|
+ +313 + | ++ + + + + + | + /** |
+ +314 + | ++ + + + + + | + * Step 3: Configure how to cache the trust root certificate. |
+ +315 + | ++ + + + + + | + * |
+ +316 + | ++ + + + + + | + * <p>This step offers two mutually exclusive options: |
+ +317 + | ++ + + + + + | + * |
+ +318 + | ++ + + + + + | + * <ol> |
+ +319 + | ++ + + + + + | + * <li>Cache the trust root certificate in a {@link File}. See {@link |
+ +320 + | ++ + + + + + | + * Step3#useTrustRootCacheFile(File)}. |
+ +321 + | ++ + + + + + | + * <li>Cache the trust root certificate using a {@link Supplier} to read the cache and a |
+ +322 + | ++ + + + + + | + * {@link Consumer} to write the cache. See {@link Step3#useTrustRootCache(Supplier, |
+ +323 + | ++ + + + + + | + * Consumer)}. |
+ +324 + | ++ + + + + + | + * </ol> |
+ +325 + | ++ + + + + + | + */ |
+ +326 + | ++ + + + + + | + @AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +327 + | ++ + + + + + | + public static class Step3 { |
+ +328 + | ++ + + + + + | + @NonNull private final Step2 step2; |
+ +329 + | ++ + + + + + | + private final X509Certificate trustRootCertificate; |
+ +330 + | ++ + + + + + | + private final URL trustRootUrl; |
+ +331 + | ++ + + + + + | + private final Set<ByteArray> trustRootSha256; |
+ +332 + | ++ + + + + + | +|
+ +333 + | ++ + + + + + | + /** |
+ +334 + | ++ + + + + + | + * Cache the trust root certificate in the file <code>cacheFile</code>. |
+ +335 + | ++ + + + + + | + * |
+ +336 + | ++ + + + + + | + * <p>If <code>cacheFile</code> exists, is a normal file, is readable, matches one of the |
+ +337 + | ++ + + + + + | + * SHA-256 hashes configured in the previous step, and contains a currently valid X.509 |
+ +338 + | ++ + + + + + | + * certificate, then it will be used as the trust root for the FIDO Metadata Service blob. |
+ +339 + | ++ + + + + + | + * |
+ +340 + | ++ + + + + + | + * <p>Otherwise, the trust root certificate will be downloaded and written to this file. |
+ +341 + | ++ + + + + + | + */ |
+ +342 + | +
+
+1
+
+1. useTrustRootCacheFile : negated conditional → KILLED + + + + |
+ public Step4 useTrustRootCacheFile(@NonNull File cacheFile) { |
+ +343 + | +
+
+1
+
+1. useTrustRootCacheFile : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step3::useTrustRootCacheFile → KILLED + + + + |
+ return new Step4(this, cacheFile, null, null); |
+ +344 + | ++ + + + + + | + } |
+ +345 + | ++ + + + + + | +|
+ +346 + | ++ + + + + + | + /** |
+ +347 + | ++ + + + + + | + * Cache the trust root certificate using a {@link Supplier} to read the cache, and using a |
+ +348 + | ++ + + + + + | + * {@link Consumer} to write the cache. |
+ +349 + | ++ + + + + + | + * |
+ +350 + | ++ + + + + + | + * <p>If <code>getCachedTrustRootCert</code> returns non-empty, the value matches one of the |
+ +351 + | ++ + + + + + | + * SHA-256 hashes configured in the previous step, and is a currently valid X.509 certificate, |
+ +352 + | ++ + + + + + | + * then it will be used as the trust root for the FIDO Metadata Service blob. |
+ +353 + | ++ + + + + + | + * |
+ +354 + | ++ + + + + + | + * <p>Otherwise, the trust root certificate will be downloaded and written to <code> |
+ +355 + | ++ + + + + + | + * writeCachedTrustRootCert</code>. |
+ +356 + | ++ + + + + + | + * |
+ +357 + | ++ + + + + + | + * @param getCachedTrustRootCert a {@link Supplier} that fetches the cached trust root |
+ +358 + | ++ + + + + + | + * certificate if it exists. MUST NOT return <code>null</code>. The returned value, if |
+ +359 + | ++ + + + + + | + * present, MUST be the trust root certificate in X.509 DER format. |
+ +360 + | ++ + + + + + | + * @param writeCachedTrustRootCert a {@link Consumer} that accepts the trust root certificate |
+ +361 + | ++ + + + + + | + * in X.509 DER format and writes it to the cache. Its argument will never be <code>null |
+ +362 + | ++ + + + + + | + * </code>. |
+ +363 + | ++ + + + + + | + */ |
+ +364 + | ++ + + + + + | + public Step4 useTrustRootCache( |
+ +365 + | +
+
+1
+
+1. useTrustRootCache : negated conditional → KILLED + + + + |
+ @NonNull Supplier<Optional<ByteArray>> getCachedTrustRootCert, |
+ +366 + | +
+
+1
+
+1. useTrustRootCache : negated conditional → KILLED + + + + |
+ @NonNull Consumer<ByteArray> writeCachedTrustRootCert) { |
+ +367 + | +
+
+1
+
+1. useTrustRootCache : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step3::useTrustRootCache → KILLED + + + + |
+ return new Step4(this, null, getCachedTrustRootCert, writeCachedTrustRootCert); |
+ +368 + | ++ + + + + + | + } |
+ +369 + | ++ + + + + + | + } |
+ +370 + | ++ + + + + + | +|
+ +371 + | ++ + + + + + | + /** |
+ +372 + | ++ + + + + + | + * Step 4: Configure how to fetch the FIDO Metadata Service metadata BLOB. |
+ +373 + | ++ + + + + + | + * |
+ +374 + | ++ + + + + + | + * <p>This step offers three mutually exclusive options: |
+ +375 + | ++ + + + + + | + * |
+ +376 + | ++ + + + + + | + * <ol> |
+ +377 + | ++ + + + + + | + * <li>Use the default download URL. This is the main intended use case. See {@link |
+ +378 + | ++ + + + + + | + * #useDefaultBlob()}. |
+ +379 + | ++ + + + + + | + * <li>Use a custom download URL. This is for future-proofing in case the BLOB download URL |
+ +380 + | ++ + + + + + | + * changes and there is no new release of this library. See {@link #downloadBlob(URL)}. |
+ +381 + | ++ + + + + + | + * <li>Use a pre-retrieved BLOB. The signature will still be verified, but it is up to you to |
+ +382 + | ++ + + + + + | + * renew it when appropriate and perform any caching as desired. See {@link |
+ +383 + | ++ + + + + + | + * #useBlob(String)}. |
+ +384 + | ++ + + + + + | + * </ol> |
+ +385 + | ++ + + + + + | + */ |
+ +386 + | ++ + + + + + | + @AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +387 + | ++ + + + + + | + public static class Step4 { |
+ +388 + | ++ + + + + + | + @NonNull private final Step3 step3; |
+ +389 + | ++ + + + + + | + private final File trustRootCacheFile; |
+ +390 + | ++ + + + + + | + private final Supplier<Optional<ByteArray>> trustRootCacheSupplier; |
+ +391 + | ++ + + + + + | + private final Consumer<ByteArray> trustRootCacheConsumer; |
+ +392 + | ++ + + + + + | +|
+ +393 + | ++ + + + + + | + /** |
+ +394 + | ++ + + + + + | + * Download the metadata BLOB from a hard-coded URL. |
+ +395 + | ++ + + + + + | + * |
+ +396 + | ++ + + + + + | + * <p>This is an alias of <code>downloadBlob(new URL("https://mds.fidoalliance.org/"))</code>. |
+ +397 + | ++ + + + + + | + * |
+ +398 + | ++ + + + + + | + * <p>This is the current FIDO Metadata Service BLOB download URL at the time of this library |
+ +399 + | ++ + + + + + | + * release. |
+ +400 + | ++ + + + + + | + * |
+ +401 + | ++ + + + + + | + * @see #downloadBlob(URL) |
+ +402 + | ++ + + + + + | + */ |
+ +403 + | ++ + + + + + | + public Step5 useDefaultBlob() { |
+ +404 + | ++ + + + + + | + try { |
+ +405 + | +
+
+1
+
+1. useDefaultBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step4::useDefaultBlob → NO_COVERAGE + + + + |
+ return downloadBlob(new URL("https://mds.fidoalliance.org/")); |
+ +406 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +407 + | ++ + + + + + | + throw new RuntimeException( |
+ +408 + | ++ + + + + + | + "Bad hard-coded trust root certificate URL. Please file a bug report.", e); |
+ +409 + | ++ + + + + + | + } |
+ +410 + | ++ + + + + + | + } |
+ +411 + | ++ + + + + + | +|
+ +412 + | ++ + + + + + | + /** |
+ +413 + | ++ + + + + + | + * Download the metadata BLOB from the given HTTPS <code>url</code>. |
+ +414 + | ++ + + + + + | + * |
+ +415 + | ++ + + + + + | + * <p>The BLOB will be downloaded if it does not exist in the cache, or if the <code> |
+ +416 + | ++ + + + + + | + * nextUpdate</code> property of the cached BLOB is the current date or earlier. |
+ +417 + | ++ + + + + + | + * |
+ +418 + | ++ + + + + + | + * <p>If the BLOB is downloaded, it is also written to the cache {@link File} or {@link |
+ +419 + | ++ + + + + + | + * Consumer} configured in the next step. |
+ +420 + | ++ + + + + + | + * |
+ +421 + | ++ + + + + + | + * @param url the HTTP URL to download. It MUST use the <code>https:</code> scheme. |
+ +422 + | ++ + + + + + | + */ |
+ +423 + | +
+
+1
+
+1. downloadBlob : negated conditional → KILLED + + + + |
+ public Step5 downloadBlob(@NonNull URL url) { |
+ +424 + | +
+
+1
+
+1. downloadBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step4::downloadBlob → KILLED + + + + |
+ return new Step5(this, null, url); |
+ +425 + | ++ + + + + + | + } |
+ +426 + | ++ + + + + + | +|
+ +427 + | ++ + + + + + | + /** |
+ +428 + | ++ + + + + + | + * Use the given metadata BLOB; never download it. |
+ +429 + | ++ + + + + + | + * |
+ +430 + | ++ + + + + + | + * <p>The blob signature and trust chain will still be verified, but it is the caller's |
+ +431 + | ++ + + + + + | + * responsibility to renew the metadata BLOB according to the <a |
+ +432 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-object-processing-rules">FIDO |
+ +433 + | ++ + + + + + | + * Metadata Service specification</a>. |
+ +434 + | ++ + + + + + | + * |
+ +435 + | ++ + + + + + | + * @param blobJwt the Metadata BLOB in JWT format as defined in <a |
+ +436 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob">FIDO |
+ +437 + | ++ + + + + + | + * Metadata Service §3.1.7. Metadata BLOB</a>. The byte array MUST NOT be Base64-decoded. |
+ +438 + | ++ + + + + + | + * @see <a |
+ +439 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob">FIDO |
+ +440 + | ++ + + + + + | + * Metadata Service §3.1.7. Metadata BLOB</a> |
+ +441 + | ++ + + + + + | + * @see <a |
+ +442 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-object-processing-rules">FIDO |
+ +443 + | ++ + + + + + | + * Metadata Service §3.2. Metadata BLOB object processing rules</a> |
+ +444 + | ++ + + + + + | + */ |
+ +445 + | +
+
+1
+
+1. useBlob : negated conditional → KILLED + + + + |
+ public FidoMetadataDownloaderBuilder useBlob(@NonNull String blobJwt) { |
+ +446 + | +
+
+1
+
+1. useBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step4::useBlob → KILLED + + + + |
+ return finishRequiredSteps(new Step5(this, blobJwt, null), null, null, null); |
+ +447 + | ++ + + + + + | + } |
+ +448 + | ++ + + + + + | + } |
+ +449 + | ++ + + + + + | +|
+ +450 + | ++ + + + + + | + /** |
+ +451 + | ++ + + + + + | + * Step 5: Configure how to cache the metadata BLOB. |
+ +452 + | ++ + + + + + | + * |
+ +453 + | ++ + + + + + | + * <p>This step offers two mutually exclusive options: |
+ +454 + | ++ + + + + + | + * |
+ +455 + | ++ + + + + + | + * <ol> |
+ +456 + | ++ + + + + + | + * <li>Cache the metadata BLOB in a {@link File}. See {@link Step5#useBlobCacheFile(File)}. |
+ +457 + | ++ + + + + + | + * <li>Cache the metadata BLOB using a {@link Supplier} to read the cache and a {@link |
+ +458 + | ++ + + + + + | + * Consumer} to write the cache. See {@link Step5#useBlobCache(Supplier, Consumer)}. |
+ +459 + | ++ + + + + + | + * </ol> |
+ +460 + | ++ + + + + + | + */ |
+ +461 + | ++ + + + + + | + @AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +462 + | ++ + + + + + | + public static class Step5 { |
+ +463 + | ++ + + + + + | + @NonNull private final Step4 step4; |
+ +464 + | ++ + + + + + | + private final String blobJwt; |
+ +465 + | ++ + + + + + | + private final URL blobUrl; |
+ +466 + | ++ + + + + + | +|
+ +467 + | ++ + + + + + | + /** |
+ +468 + | ++ + + + + + | + * Cache metadata BLOB in the file <code>cacheFile</code>. |
+ +469 + | ++ + + + + + | + * |
+ +470 + | ++ + + + + + | + * <p>If <code>cacheFile</code> exists, is a normal file, is readable, and is not out of date, |
+ +471 + | ++ + + + + + | + * then it will be used as the FIDO Metadata Service BLOB. |
+ +472 + | ++ + + + + + | + * |
+ +473 + | ++ + + + + + | + * <p>Otherwise, the metadata BLOB will be downloaded and written to this file. |
+ +474 + | ++ + + + + + | + * |
+ +475 + | ++ + + + + + | + * @param cacheFile a {@link File} which may or may not exist. If it exists, it MUST contain |
+ +476 + | ++ + + + + + | + * the metadata BLOB in JWS compact serialization format <a |
+ +477 + | ++ + + + + + | + * href="https://datatracker.ietf.org/doc/html/rfc7515#section-3.1">[RFC7515]</a>. |
+ +478 + | ++ + + + + + | + */ |
+ +479 + | +
+
+1
+
+1. useBlobCacheFile : negated conditional → KILLED + + + + |
+ public FidoMetadataDownloaderBuilder useBlobCacheFile(@NonNull File cacheFile) { |
+ +480 + | +
+
+1
+
+1. useBlobCacheFile : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step5::useBlobCacheFile → KILLED + + + + |
+ return finishRequiredSteps(this, cacheFile, null, null); |
+ +481 + | ++ + + + + + | + } |
+ +482 + | ++ + + + + + | +|
+ +483 + | ++ + + + + + | + /** |
+ +484 + | ++ + + + + + | + * Cache the metadata BLOB using a {@link Supplier} to read the cache, and using a {@link |
+ +485 + | ++ + + + + + | + * Consumer} to write the cache. |
+ +486 + | ++ + + + + + | + * |
+ +487 + | ++ + + + + + | + * <p>If <code>getCachedBlob</code> returns non-empty and the content is not out of date, then |
+ +488 + | ++ + + + + + | + * it will be used as the FIDO Metadata Service BLOB. |
+ +489 + | ++ + + + + + | + * |
+ +490 + | ++ + + + + + | + * <p>Otherwise, the metadata BLOB will be downloaded and written to <code>writeCachedBlob |
+ +491 + | ++ + + + + + | + * </code>. |
+ +492 + | ++ + + + + + | + * |
+ +493 + | ++ + + + + + | + * @param getCachedBlob a {@link Supplier} that fetches the cached metadata BLOB if it exists. |
+ +494 + | ++ + + + + + | + * MUST NOT return <code>null</code>. The returned value, if present, MUST be in JWS |
+ +495 + | ++ + + + + + | + * compact serialization format <a |
+ +496 + | ++ + + + + + | + * href="https://datatracker.ietf.org/doc/html/rfc7515#section-3.1">[RFC7515]</a>. |
+ +497 + | ++ + + + + + | + * @param writeCachedBlob a {@link Consumer} that accepts the metadata BLOB in JWS compact |
+ +498 + | ++ + + + + + | + * serialization format <a |
+ +499 + | ++ + + + + + | + * href="https://datatracker.ietf.org/doc/html/rfc7515#section-3.1">[RFC7515]</a> and |
+ +500 + | ++ + + + + + | + * writes it to the cache. Its argument will never be <code>null</code>. |
+ +501 + | ++ + + + + + | + */ |
+ +502 + | ++ + + + + + | + public FidoMetadataDownloaderBuilder useBlobCache( |
+ +503 + | +
+
+1
+
+1. useBlobCache : negated conditional → KILLED + + + + |
+ @NonNull Supplier<Optional<ByteArray>> getCachedBlob, |
+ +504 + | +
+
+1
+
+1. useBlobCache : negated conditional → KILLED + + + + |
+ @NonNull Consumer<ByteArray> writeCachedBlob) { |
+ +505 + | +
+
+1
+
+1. useBlobCache : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder$Step5::useBlobCache → KILLED + + + + |
+ return finishRequiredSteps(this, null, getCachedBlob, writeCachedBlob); |
+ +506 + | ++ + + + + + | + } |
+ +507 + | ++ + + + + + | + } |
+ +508 + | ++ + + + + + | +|
+ +509 + | ++ + + + + + | + private static FidoMetadataDownloaderBuilder finishRequiredSteps( |
+ +510 + | ++ + + + + + | + FidoMetadataDownloaderBuilder.Step5 step5, |
+ +511 + | ++ + + + + + | + File blobCacheFile, |
+ +512 + | ++ + + + + + | + Supplier<Optional<ByteArray>> blobCacheSupplier, |
+ +513 + | ++ + + + + + | + Consumer<ByteArray> blobCacheConsumer) { |
+ +514 + | +
+
+1
+
+1. finishRequiredSteps : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::finishRequiredSteps → KILLED + + + + |
+ return new FidoMetadataDownloaderBuilder( |
+ +515 + | ++ + + + + + | + step5.step4.step3.step2.expectedLegalHeaders, |
+ +516 + | ++ + + + + + | + step5.step4.step3.trustRootCertificate, |
+ +517 + | ++ + + + + + | + step5.step4.step3.trustRootUrl, |
+ +518 + | ++ + + + + + | + step5.step4.step3.trustRootSha256, |
+ +519 + | ++ + + + + + | + step5.step4.trustRootCacheFile, |
+ +520 + | ++ + + + + + | + step5.step4.trustRootCacheSupplier, |
+ +521 + | ++ + + + + + | + step5.step4.trustRootCacheConsumer, |
+ +522 + | ++ + + + + + | + step5.blobJwt, |
+ +523 + | ++ + + + + + | + step5.blobUrl, |
+ +524 + | ++ + + + + + | + blobCacheFile, |
+ +525 + | ++ + + + + + | + blobCacheSupplier, |
+ +526 + | ++ + + + + + | + blobCacheConsumer); |
+ +527 + | ++ + + + + + | + } |
+ +528 + | ++ + + + + + | +|
+ +529 + | ++ + + + + + | + /** |
+ +530 + | ++ + + + + + | + * Use <code>clock</code> as the source of the current time for some application-level logic. |
+ +531 + | ++ + + + + + | + * |
+ +532 + | ++ + + + + + | + * <p>This is primarily intended for testing. |
+ +533 + | ++ + + + + + | + * |
+ +534 + | ++ + + + + + | + * <p>The default is {@link Clock#systemUTC()}. |
+ +535 + | ++ + + + + + | + * |
+ +536 + | ++ + + + + + | + * @param clock a {@link Clock} which the finished {@link FidoMetadataDownloader} will use to |
+ +537 + | ++ + + + + + | + * tell the time. |
+ +538 + | ++ + + + + + | + */ |
+ +539 + | +
+
+1
+
+1. clock : negated conditional → KILLED + + + + |
+ public FidoMetadataDownloaderBuilder clock(@NonNull Clock clock) { |
+ +540 + | ++ + + + + + | + this.clock = clock; |
+ +541 + | +
+
+1
+
+1. clock : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::clock → KILLED + + + + |
+ return this; |
+ +542 + | ++ + + + + + | + } |
+ +543 + | ++ + + + + + | +|
+ +544 + | ++ + + + + + | + /** |
+ +545 + | ++ + + + + + | + * Use the provided CRLs. |
+ +546 + | ++ + + + + + | + * |
+ +547 + | ++ + + + + + | + * <p>CRLs will also be downloaded from distribution points if the <code> |
+ +548 + | ++ + + + + + | + * com.sun.security.enableCRLDP</code> system property is set to <code>true</code> (assuming the |
+ +549 + | ++ + + + + + | + * use of the {@link CertPathValidator} implementation from the SUN provider). |
+ +550 + | ++ + + + + + | + * |
+ +551 + | ++ + + + + + | + * @throws InvalidAlgorithmParameterException if {@link CertStore#getInstance(String, |
+ +552 + | ++ + + + + + | + * CertStoreParameters)} does. |
+ +553 + | ++ + + + + + | + * @throws NoSuchAlgorithmException if a <code>"Collection"</code> type {@link CertStore} |
+ +554 + | ++ + + + + + | + * provider is not available. |
+ +555 + | ++ + + + + + | + * @see #useCrls(CertStore) |
+ +556 + | ++ + + + + + | + */ |
+ +557 + | +
+
+1
+
+1. useCrls : negated conditional → KILLED + + + + |
+ public FidoMetadataDownloaderBuilder useCrls(@NonNull Collection<CRL> crls) |
+ +558 + | ++ + + + + + | + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { |
+ +559 + | +
+
+1
+
+1. useCrls : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::useCrls → KILLED + + + + |
+ return useCrls(CertStore.getInstance("Collection", new CollectionCertStoreParameters(crls))); |
+ +560 + | ++ + + + + + | + } |
+ +561 + | ++ + + + + + | +|
+ +562 + | ++ + + + + + | + /** |
+ +563 + | ++ + + + + + | + * Use CRLs in the provided {@link CertStore}. |
+ +564 + | ++ + + + + + | + * |
+ +565 + | ++ + + + + + | + * <p>CRLs will also be downloaded from distribution points if the <code> |
+ +566 + | ++ + + + + + | + * com.sun.security.enableCRLDP</code> system property is set to <code>true</code> (assuming the |
+ +567 + | ++ + + + + + | + * use of the {@link CertPathValidator} implementation from the SUN provider). |
+ +568 + | ++ + + + + + | + * |
+ +569 + | ++ + + + + + | + * @see #useCrls(Collection) |
+ +570 + | ++ + + + + + | + */ |
+ +571 + | ++ + + + + + | + public FidoMetadataDownloaderBuilder useCrls(CertStore certStore) { |
+ +572 + | ++ + + + + + | + this.certStore = certStore; |
+ +573 + | +
+
+1
+
+1. useCrls : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::useCrls → KILLED + + + + |
+ return this; |
+ +574 + | ++ + + + + + | + } |
+ +575 + | ++ + + + + + | +|
+ +576 + | ++ + + + + + | + /** |
+ +577 + | ++ + + + + + | + * Use the provided {@link X509Certificate}s as trust roots for HTTPS downloads. |
+ +578 + | ++ + + + + + | + * |
+ +579 + | ++ + + + + + | + * <p>This is primarily useful when setting {@link Step2#downloadTrustRoot(URL, Set) |
+ +580 + | ++ + + + + + | + * downloadTrustRoot} and/or {@link Step4#downloadBlob(URL) downloadBlob} to download from |
+ +581 + | ++ + + + + + | + * custom servers instead of the defaults. |
+ +582 + | ++ + + + + + | + * |
+ +583 + | ++ + + + + + | + * <p>If provided, these will be used for downloading |
+ +584 + | ++ + + + + + | + * |
+ +585 + | ++ + + + + + | + * <ul> |
+ +586 + | ++ + + + + + | + * <li>the trust root certificate for the BLOB signature chain, and |
+ +587 + | ++ + + + + + | + * <li>the metadata BLOB. |
+ +588 + | ++ + + + + + | + * </ul> |
+ +589 + | ++ + + + + + | + * |
+ +590 + | ++ + + + + + | + * If not set, the system default certificate store will be used. |
+ +591 + | ++ + + + + + | + */ |
+ +592 + | +
+
+1
+
+1. trustHttpsCerts : negated conditional → KILLED + + + + |
+ public FidoMetadataDownloaderBuilder trustHttpsCerts(@NonNull X509Certificate... certificates) { |
+ +593 + | ++ + + + + + | + final KeyStore trustStore; |
+ +594 + | ++ + + + + + | + try { |
+ +595 + | ++ + + + + + | + trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); |
+ +596 + | +
+
+1
+
+1. trustHttpsCerts : removed call to java/security/KeyStore::load → KILLED + + + + |
+ trustStore.load(null); |
+ +597 + | ++ + + + + + | + } catch (KeyStoreException |
+ +598 + | ++ + + + + + | + | IOException |
+ +599 + | ++ + + + + + | + | NoSuchAlgorithmException |
+ +600 + | ++ + + + + + | + | CertificateException e) { |
+ +601 + | ++ + + + + + | + throw new RuntimeException( |
+ +602 + | ++ + + + + + | + "Failed to instantiate or initialize KeyStore. This should not be possible, please file a bug report.", |
+ +603 + | ++ + + + + + | + e); |
+ +604 + | ++ + + + + + | + } |
+ +605 + | ++ + + + + + | + for (X509Certificate cert : certificates) { |
+ +606 + | ++ + + + + + | + try { |
+ +607 + | +
+
+1
+
+1. trustHttpsCerts : removed call to java/security/KeyStore::setCertificateEntry → KILLED + + + + |
+ trustStore.setCertificateEntry(UUID.randomUUID().toString(), cert); |
+ +608 + | ++ + + + + + | + } catch (KeyStoreException e) { |
+ +609 + | ++ + + + + + | + throw new RuntimeException( |
+ +610 + | ++ + + + + + | + "Failed to import HTTPS cert into KeyStore. This should not be possible, please file a bug report.", |
+ +611 + | ++ + + + + + | + e); |
+ +612 + | ++ + + + + + | + } |
+ +613 + | ++ + + + + + | + } |
+ +614 + | ++ + + + + + | + this.httpsTrustStore = trustStore; |
+ +615 + | ++ + + + + + | +|
+ +616 + | +
+
+1
+
+1. trustHttpsCerts : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::trustHttpsCerts → KILLED + + + + |
+ return this; |
+ +617 + | ++ + + + + + | + } |
+ +618 + | ++ + + + + + | +|
+ +619 + | ++ + + + + + | + /** |
+ +620 + | ++ + + + + + | + * If set to <code>true</code>, the BLOB signature will not be verified when loading the BLOB |
+ +621 + | ++ + + + + + | + * from cache or when explicitly set via {@link Step4#useBlob(String)}. This means that if a |
+ +622 + | ++ + + + + + | + * BLOB was successfully verified once and written to cache, that cached value will be |
+ +623 + | ++ + + + + + | + * implicitly trusted when loaded in the future. |
+ +624 + | ++ + + + + + | + * |
+ +625 + | ++ + + + + + | + * <p>If set to <code>false</code>, the BLOB signature will always be verified no matter where |
+ +626 + | ++ + + + + + | + * the BLOB came from. This means that a cached BLOB may become invalid if the BLOB certificate |
+ +627 + | ++ + + + + + | + * expires, even if the BLOB was successfully verified at the time it was downloaded. |
+ +628 + | ++ + + + + + | + * |
+ +629 + | ++ + + + + + | + * <p>The default setting is <code>false</code>. |
+ +630 + | ++ + + + + + | + * |
+ +631 + | ++ + + + + + | + * @param verifyDownloadsOnly <code>true</code> if the BLOB signature should be ignored when |
+ +632 + | ++ + + + + + | + * loading the BLOB from cache or when explicitly set via {@link Step4#useBlob(String)}. |
+ +633 + | ++ + + + + + | + */ |
+ +634 + | ++ + + + + + | + public FidoMetadataDownloaderBuilder verifyDownloadsOnly(final boolean verifyDownloadsOnly) { |
+ +635 + | ++ + + + + + | + this.verifyDownloadsOnly = verifyDownloadsOnly; |
+ +636 + | +
+
+1
+
+1. verifyDownloadsOnly : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader$FidoMetadataDownloaderBuilder::verifyDownloadsOnly → KILLED + + + + |
+ return this; |
+ +637 + | ++ + + + + + | + } |
+ +638 + | ++ + + + + + | + } |
+ +639 + | ++ + + + + + | +|
+ +640 + | ++ + + + + + | + /** |
+ +641 + | ++ + + + + + | + * Load the metadata BLOB from cache, or download a fresh one if necessary. |
+ +642 + | ++ + + + + + | + * |
+ +643 + | ++ + + + + + | + * <p>This method is NOT THREAD SAFE since it reads and writes caches. |
+ +644 + | ++ + + + + + | + * |
+ +645 + | ++ + + + + + | + * <p>On each execution this will, in order: |
+ +646 + | ++ + + + + + | + * |
+ +647 + | ++ + + + + + | + * <ol> |
+ +648 + | ++ + + + + + | + * <li>Download the trust root certificate, if necessary: if the cache is empty, the cache fails |
+ +649 + | ++ + + + + + | + * to load, or the cached cert is not valid at the current time (as determined by the {@link |
+ +650 + | ++ + + + + + | + * FidoMetadataDownloaderBuilder#clock(Clock) clock} setting). |
+ +651 + | ++ + + + + + | + * <li>If downloaded, cache the trust root certificate using the configured {@link File} or |
+ +652 + | ++ + + + + + | + * {@link Consumer} (see {@link FidoMetadataDownloaderBuilder.Step3}) |
+ +653 + | ++ + + + + + | + * <li>Download the metadata BLOB, if necessary: if the cache is empty, the cache fails to load, |
+ +654 + | ++ + + + + + | + * or the <code>"nextUpdate"</code> property in the cached BLOB is the current date (as |
+ +655 + | ++ + + + + + | + * determined by the {@link FidoMetadataDownloaderBuilder#clock(Clock) clock} setting) or |
+ +656 + | ++ + + + + + | + * earlier. |
+ +657 + | ++ + + + + + | + * <li>Check the <code>"no"</code> property of the downloaded BLOB, if any, and compare it with |
+ +658 + | ++ + + + + + | + * the <code>"no"</code> of the cached BLOB, if any. The one with a greater <code>"no" |
+ +659 + | ++ + + + + + | + * </code> overrides the other, even if its <code>"nextUpdate"</code> is in the past. |
+ +660 + | ++ + + + + + | + * <li>If a BLOB with a newer <code>"no"</code> was downloaded, verify that the value of its |
+ +661 + | ++ + + + + + | + * <code>"legalHeader"</code> appears in the configured {@link |
+ +662 + | ++ + + + + + | + * FidoMetadataDownloaderBuilder.Step1#expectLegalHeader(String...) expectLegalHeader} |
+ +663 + | ++ + + + + + | + * setting. If not, throw an {@link UnexpectedLegalHeader} exception containing the cached |
+ +664 + | ++ + + + + + | + * BLOB, if any, and the downloaded BLOB. |
+ +665 + | ++ + + + + + | + * <li>If a BLOB with a newer <code>"no"</code> was downloaded and had an expected <code> |
+ +666 + | ++ + + + + + | + * "legalHeader"</code>, cache the new BLOB using the configured {@link File} or {@link |
+ +667 + | ++ + + + + + | + * Consumer} (see {@link FidoMetadataDownloaderBuilder.Step5}). |
+ +668 + | ++ + + + + + | + * </ol> |
+ +669 + | ++ + + + + + | + * |
+ +670 + | ++ + + + + + | + * No internal mutable state is maintained between invocations of this method; each invocation |
+ +671 + | ++ + + + + + | + * will reload/rewrite caches, perform downloads and check the <code>"legalHeader" |
+ +672 + | ++ + + + + + | + * </code> as necessary. You may therefore reuse a {@link FidoMetadataDownloader} instance and, |
+ +673 + | ++ + + + + + | + * for example, call this method periodically to refresh the BLOB when appropriate. Each call will |
+ +674 + | ++ + + + + + | + * return a new {@link MetadataBLOB} instance; ones already returned will not be updated by |
+ +675 + | ++ + + + + + | + * subsequent calls. |
+ +676 + | ++ + + + + + | + * |
+ +677 + | ++ + + + + + | + * @return the successfully retrieved and validated metadata BLOB. |
+ +678 + | ++ + + + + + | + * @throws Base64UrlException if the explicitly configured or newly downloaded BLOB is not a |
+ +679 + | ++ + + + + + | + * well-formed JWT in compact serialization. |
+ +680 + | ++ + + + + + | + * @throws CertPathValidatorException if the explicitly configured or newly downloaded BLOB fails |
+ +681 + | ++ + + + + + | + * certificate path validation. |
+ +682 + | ++ + + + + + | + * @throws CertificateException if the trust root certificate was downloaded and passed the |
+ +683 + | ++ + + + + + | + * SHA-256 integrity check, but does not contain a currently valid X.509 DER certificate; or |
+ +684 + | ++ + + + + + | + * if the BLOB signing certificate chain fails to parse. |
+ +685 + | ++ + + + + + | + * @throws DigestException if the trust root certificate was downloaded but failed the SHA-256 |
+ +686 + | ++ + + + + + | + * integrity check. |
+ +687 + | ++ + + + + + | + * @throws FidoMetadataDownloaderException if the explicitly configured or newly downloaded BLOB |
+ +688 + | ++ + + + + + | + * (if any) has a bad signature and there is no cached BLOB to fall back to. |
+ +689 + | ++ + + + + + | + * @throws IOException if any of the following fails: downloading the trust root certificate, |
+ +690 + | ++ + + + + + | + * downloading the BLOB, reading or writing any cache file (if any), or parsing the BLOB |
+ +691 + | ++ + + + + + | + * contents. |
+ +692 + | ++ + + + + + | + * @throws InvalidAlgorithmParameterException if certificate path validation fails. |
+ +693 + | ++ + + + + + | + * @throws InvalidKeyException if signature verification fails. |
+ +694 + | ++ + + + + + | + * @throws NoSuchAlgorithmException if signature verification fails, or if the SHA-256 algorithm |
+ +695 + | ++ + + + + + | + * is not available. |
+ +696 + | ++ + + + + + | + * @throws SignatureException if signature verification fails. |
+ +697 + | ++ + + + + + | + * @throws UnexpectedLegalHeader if the downloaded BLOB (if any) contains a <code>"legalHeader" |
+ +698 + | ++ + + + + + | + * </code> value not configured in {@link |
+ +699 + | ++ + + + + + | + * FidoMetadataDownloaderBuilder.Step1#expectLegalHeader(String...) |
+ +700 + | ++ + + + + + | + * expectLegalHeader(String...)} but is otherwise valid. The downloaded BLOB will not be |
+ +701 + | ++ + + + + + | + * written to cache in this case. |
+ +702 + | ++ + + + + + | + */ |
+ +703 + | ++ + + + + + | + public MetadataBLOB loadCachedBlob() |
+ +704 + | ++ + + + + + | + throws CertPathValidatorException, |
+ +705 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +706 + | ++ + + + + + | + Base64UrlException, |
+ +707 + | ++ + + + + + | + CertificateException, |
+ +708 + | ++ + + + + + | + IOException, |
+ +709 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +710 + | ++ + + + + + | + SignatureException, |
+ +711 + | ++ + + + + + | + InvalidKeyException, |
+ +712 + | ++ + + + + + | + UnexpectedLegalHeader, |
+ +713 + | ++ + + + + + | + DigestException, |
+ +714 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +715 + | ++ + + + + + | + final X509Certificate trustRoot = retrieveTrustRootCert(); |
+ +716 + | ++ + + + + + | +|
+ +717 + | ++ + + + + + | + final Optional<MetadataBLOB> explicit = loadExplicitBlobOnly(trustRoot); |
+ +718 + | +
+
+1
+
+1. loadCachedBlob : negated conditional → KILLED + + + + |
+ if (explicit.isPresent()) { |
+ +719 + | ++ + + + + + | + log.debug("Explicit BLOB is set - disregarding cache and download."); |
+ +720 + | +
+
+1
+
+1. loadCachedBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::loadCachedBlob → KILLED + + + + |
+ return explicit.get(); |
+ +721 + | ++ + + + + + | + } |
+ +722 + | ++ + + + + + | +|
+ +723 + | ++ + + + + + | + final Optional<MetadataBLOB> cached = loadCachedBlobOnly(trustRoot); |
+ +724 + | +
+
+1
+
+1. loadCachedBlob : negated conditional → MEMORY_ERROR + + + + |
+ if (cached.isPresent()) { |
+ +725 + | ++ + + + + + | + log.debug("Cached BLOB exists, checking expiry date..."); |
+ +726 + | ++ + + + + + | + if (cached |
+ +727 + | ++ + + + + + | + .get() |
+ +728 + | ++ + + + + + | + .getPayload() |
+ +729 + | ++ + + + + + | + .getNextUpdate() |
+ +730 + | ++ + + + + + | + .atStartOfDay() |
+ +731 + | ++ + + + + + | + .atZone(clock.getZone()) |
+ +732 + | +
+
+1
+
+1. loadCachedBlob : negated conditional → KILLED + + + + |
+ .isAfter(clock.instant().atZone(clock.getZone()))) { |
+ +733 + | ++ + + + + + | + log.debug("Cached BLOB has not yet expired - using cached BLOB."); |
+ +734 + | +
+
+1
+
+1. loadCachedBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::loadCachedBlob → KILLED + + + + |
+ return cached.get(); |
+ +735 + | ++ + + + + + | + } else { |
+ +736 + | ++ + + + + + | + log.debug("Cached BLOB has expired."); |
+ +737 + | ++ + + + + + | + } |
+ +738 + | ++ + + + + + | +|
+ +739 + | ++ + + + + + | + } else { |
+ +740 + | ++ + + + + + | + log.debug("Cached BLOB does not exist or is invalid."); |
+ +741 + | ++ + + + + + | + } |
+ +742 + | ++ + + + + + | +|
+ +743 + | +
+
+1
+
+1. loadCachedBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::loadCachedBlob → KILLED + + + + |
+ return refreshBlobInternal(trustRoot, cached).get(); |
+ +744 + | ++ + + + + + | + } |
+ +745 + | ++ + + + + + | +|
+ +746 + | ++ + + + + + | + /** |
+ +747 + | ++ + + + + + | + * Download and cache a fresh metadata BLOB, or read it from cache if the downloaded BLOB is not |
+ +748 + | ++ + + + + + | + * up to date. |
+ +749 + | ++ + + + + + | + * |
+ +750 + | ++ + + + + + | + * <p>This method is NOT THREAD SAFE since it reads and writes caches. |
+ +751 + | ++ + + + + + | + * |
+ +752 + | ++ + + + + + | + * <p>On each execution this will, in order: |
+ +753 + | ++ + + + + + | + * |
+ +754 + | ++ + + + + + | + * <ol> |
+ +755 + | ++ + + + + + | + * <li>Download the trust root certificate, if necessary: if the cache is empty, the cache fails |
+ +756 + | ++ + + + + + | + * to load, or the cached cert is not valid at the current time (as determined by the {@link |
+ +757 + | ++ + + + + + | + * FidoMetadataDownloaderBuilder#clock(Clock) clock} setting). |
+ +758 + | ++ + + + + + | + * <li>If downloaded, cache the trust root certificate using the configured {@link File} or |
+ +759 + | ++ + + + + + | + * {@link Consumer} (see {@link FidoMetadataDownloaderBuilder.Step3}) |
+ +760 + | ++ + + + + + | + * <li>Download the metadata BLOB. |
+ +761 + | ++ + + + + + | + * <li>Check the <code>"no"</code> property of the downloaded BLOB and compare it with the |
+ +762 + | ++ + + + + + | + * <code>"no"</code> of the cached BLOB, if any. The one with a greater <code>"no" |
+ +763 + | ++ + + + + + | + * </code> overrides the other, even if its <code>"nextUpdate"</code> is in the past. |
+ +764 + | ++ + + + + + | + * <li>If the downloaded BLOB has a newer <code>"no"</code>, or if no BLOB was cached, verify |
+ +765 + | ++ + + + + + | + * that the value of the downloaded BLOB's <code>"legalHeader"</code> appears in the |
+ +766 + | ++ + + + + + | + * configured {@link FidoMetadataDownloaderBuilder.Step1#expectLegalHeader(String...) |
+ +767 + | ++ + + + + + | + * expectLegalHeader} setting. If not, throw an {@link UnexpectedLegalHeader} exception |
+ +768 + | ++ + + + + + | + * containing the cached BLOB, if any, and the downloaded BLOB. |
+ +769 + | ++ + + + + + | + * <li>If the downloaded BLOB has an expected <code> |
+ +770 + | ++ + + + + + | + * "legalHeader"</code>, cache it using the configured {@link File} or {@link Consumer} (see |
+ +771 + | ++ + + + + + | + * {@link FidoMetadataDownloaderBuilder.Step5}). |
+ +772 + | ++ + + + + + | + * </ol> |
+ +773 + | ++ + + + + + | + * |
+ +774 + | ++ + + + + + | + * No internal mutable state is maintained between invocations of this method; each invocation |
+ +775 + | ++ + + + + + | + * will reload/rewrite caches, perform downloads and check the <code>"legalHeader" |
+ +776 + | ++ + + + + + | + * </code> as necessary. You may therefore reuse a {@link FidoMetadataDownloader} instance and, |
+ +777 + | ++ + + + + + | + * for example, call this method periodically to refresh the BLOB. Each call will return a new |
+ +778 + | ++ + + + + + | + * {@link MetadataBLOB} instance; ones already returned will not be updated by subsequent calls. |
+ +779 + | ++ + + + + + | + * |
+ +780 + | ++ + + + + + | + * @return the successfully retrieved and validated metadata BLOB. |
+ +781 + | ++ + + + + + | + * @throws Base64UrlException if the explicitly configured or newly downloaded BLOB is not a |
+ +782 + | ++ + + + + + | + * well-formed JWT in compact serialization. |
+ +783 + | ++ + + + + + | + * @throws CertPathValidatorException if the explicitly configured or newly downloaded BLOB fails |
+ +784 + | ++ + + + + + | + * certificate path validation. |
+ +785 + | ++ + + + + + | + * @throws CertificateException if the trust root certificate was downloaded and passed the |
+ +786 + | ++ + + + + + | + * SHA-256 integrity check, but does not contain a currently valid X.509 DER certificate; or |
+ +787 + | ++ + + + + + | + * if the BLOB signing certificate chain fails to parse. |
+ +788 + | ++ + + + + + | + * @throws DigestException if the trust root certificate was downloaded but failed the SHA-256 |
+ +789 + | ++ + + + + + | + * integrity check. |
+ +790 + | ++ + + + + + | + * @throws FidoMetadataDownloaderException if the explicitly configured or newly downloaded BLOB |
+ +791 + | ++ + + + + + | + * (if any) has a bad signature and there is no cached BLOB to fall back to. |
+ +792 + | ++ + + + + + | + * @throws IOException if any of the following fails: downloading the trust root certificate, |
+ +793 + | ++ + + + + + | + * downloading the BLOB, reading or writing any cache file (if any), or parsing the BLOB |
+ +794 + | ++ + + + + + | + * contents. |
+ +795 + | ++ + + + + + | + * @throws InvalidAlgorithmParameterException if certificate path validation fails. |
+ +796 + | ++ + + + + + | + * @throws InvalidKeyException if signature verification fails. |
+ +797 + | ++ + + + + + | + * @throws NoSuchAlgorithmException if signature verification fails, or if the SHA-256 algorithm |
+ +798 + | ++ + + + + + | + * is not available. |
+ +799 + | ++ + + + + + | + * @throws SignatureException if signature verification fails. |
+ +800 + | ++ + + + + + | + * @throws UnexpectedLegalHeader if the downloaded BLOB (if any) contains a <code>"legalHeader" |
+ +801 + | ++ + + + + + | + * </code> value not configured in {@link |
+ +802 + | ++ + + + + + | + * FidoMetadataDownloaderBuilder.Step1#expectLegalHeader(String...) |
+ +803 + | ++ + + + + + | + * expectLegalHeader(String...)} but is otherwise valid. The downloaded BLOB will not be |
+ +804 + | ++ + + + + + | + * written to cache in this case. |
+ +805 + | ++ + + + + + | + */ |
+ +806 + | ++ + + + + + | + public MetadataBLOB refreshBlob() |
+ +807 + | ++ + + + + + | + throws CertPathValidatorException, |
+ +808 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +809 + | ++ + + + + + | + Base64UrlException, |
+ +810 + | ++ + + + + + | + CertificateException, |
+ +811 + | ++ + + + + + | + IOException, |
+ +812 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +813 + | ++ + + + + + | + SignatureException, |
+ +814 + | ++ + + + + + | + InvalidKeyException, |
+ +815 + | ++ + + + + + | + UnexpectedLegalHeader, |
+ +816 + | ++ + + + + + | + DigestException, |
+ +817 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +818 + | ++ + + + + + | + final X509Certificate trustRoot = retrieveTrustRootCert(); |
+ +819 + | ++ + + + + + | +|
+ +820 + | ++ + + + + + | + final Optional<MetadataBLOB> explicit = loadExplicitBlobOnly(trustRoot); |
+ +821 + | +
+
+1
+
+1. refreshBlob : negated conditional → KILLED + + + + |
+ if (explicit.isPresent()) { |
+ +822 + | ++ + + + + + | + log.debug("Explicit BLOB is set - disregarding cache and download."); |
+ +823 + | +
+
+1
+
+1. refreshBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::refreshBlob → KILLED + + + + |
+ return explicit.get(); |
+ +824 + | ++ + + + + + | + } |
+ +825 + | ++ + + + + + | +|
+ +826 + | ++ + + + + + | + final Optional<MetadataBLOB> cached = loadCachedBlobOnly(trustRoot); |
+ +827 + | +
+
+1
+
+1. refreshBlob : negated conditional → MEMORY_ERROR + + + + |
+ if (cached.isPresent()) { |
+ +828 + | ++ + + + + + | + log.debug("Cached BLOB exists, proceeding to compare against fresh BLOB..."); |
+ +829 + | ++ + + + + + | + } else { |
+ +830 + | ++ + + + + + | + log.debug("Cached BLOB does not exist or is invalid."); |
+ +831 + | ++ + + + + + | + } |
+ +832 + | ++ + + + + + | +|
+ +833 + | +
+
+1
+
+1. refreshBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::refreshBlob → KILLED + + + + |
+ return refreshBlobInternal(trustRoot, cached).get(); |
+ +834 + | ++ + + + + + | + } |
+ +835 + | ++ + + + + + | +|
+ +836 + | ++ + + + + + | + private Optional<MetadataBLOB> refreshBlobInternal( |
+ +837 + | +
+
+2
+
+1. refreshBlobInternal : negated conditional → KILLED +2. refreshBlobInternal : negated conditional → KILLED + + + + |
+ @NonNull X509Certificate trustRoot, @NonNull Optional<MetadataBLOB> cached) |
+ +838 + | ++ + + + + + | + throws CertPathValidatorException, |
+ +839 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +840 + | ++ + + + + + | + Base64UrlException, |
+ +841 + | ++ + + + + + | + CertificateException, |
+ +842 + | ++ + + + + + | + IOException, |
+ +843 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +844 + | ++ + + + + + | + SignatureException, |
+ +845 + | ++ + + + + + | + InvalidKeyException, |
+ +846 + | ++ + + + + + | + UnexpectedLegalHeader, |
+ +847 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +848 + | ++ + + + + + | +|
+ +849 + | ++ + + + + + | + try { |
+ +850 + | ++ + + + + + | + log.debug("Attempting to download new BLOB..."); |
+ +851 + | ++ + + + + + | + final ByteArray downloadedBytes = download(blobUrl); |
+ +852 + | ++ + + + + + | + final MetadataBLOB downloadedBlob = parseAndVerifyBlob(downloadedBytes, trustRoot); |
+ +853 + | ++ + + + + + | + log.debug("New BLOB downloaded."); |
+ +854 + | ++ + + + + + | +|
+ +855 + | +
+
+1
+
+1. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (cached.isPresent()) { |
+ +856 + | ++ + + + + + | + log.debug("Cached BLOB exists - checking if new BLOB has a higher \"no\"..."); |
+ +857 + | +
+
+2
+
+1. refreshBlobInternal : changed conditional boundary → SURVIVED +2. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (downloadedBlob.getPayload().getNo() <= cached.get().getPayload().getNo()) { |
+ +858 + | ++ + + + + + | + log.debug("New BLOB does not have a higher \"no\" - using cached BLOB instead."); |
+ +859 + | +
+
+1
+
+1. refreshBlobInternal : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataDownloader::refreshBlobInternal → KILLED + + + + |
+ return cached; |
+ +860 + | ++ + + + + + | + } |
+ +861 + | ++ + + + + + | + log.debug("New BLOB has a higher \"no\" - proceeding with new BLOB."); |
+ +862 + | ++ + + + + + | + } |
+ +863 + | ++ + + + + + | +|
+ +864 + | ++ + + + + + | + log.debug("Checking legalHeader in new BLOB..."); |
+ +865 + | +
+
+1
+
+1. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (!expectedLegalHeaders.contains(downloadedBlob.getPayload().getLegalHeader())) { |
+ +866 + | ++ + + + + + | + throw new UnexpectedLegalHeader(cached.orElse(null), downloadedBlob); |
+ +867 + | ++ + + + + + | + } |
+ +868 + | ++ + + + + + | +|
+ +869 + | ++ + + + + + | + log.debug("Writing new BLOB to cache..."); |
+ +870 + | +
+
+1
+
+1. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (blobCacheFile != null) { |
+ +871 + | ++ + + + + + | + try (FileOutputStream f = new FileOutputStream(blobCacheFile)) { |
+ +872 + | +
+
+1
+
+1. refreshBlobInternal : removed call to java/io/FileOutputStream::write → MEMORY_ERROR + + + + |
+ f.write(downloadedBytes.getBytes()); |
+ +873 + | ++ + + + + + | + } |
+ +874 + | ++ + + + + + | + } |
+ +875 + | ++ + + + + + | +|
+ +876 + | +
+
+1
+
+1. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (blobCacheConsumer != null) { |
+ +877 + | +
+
+1
+
+1. refreshBlobInternal : removed call to java/util/function/Consumer::accept → KILLED + + + + |
+ blobCacheConsumer.accept(downloadedBytes); |
+ +878 + | ++ + + + + + | + } |
+ +879 + | ++ + + + + + | +|
+ +880 + | +
+
+1
+
+1. refreshBlobInternal : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataDownloader::refreshBlobInternal → KILLED + + + + |
+ return Optional.of(downloadedBlob); |
+ +881 + | ++ + + + + + | + } catch (FidoMetadataDownloaderException e) { |
+ +882 + | +
+
+2
+
+1. refreshBlobInternal : negated conditional → KILLED +2. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (e.getReason() == Reason.BAD_SIGNATURE && cached.isPresent()) { |
+ +883 + | ++ + + + + + | + log.warn("New BLOB has bad signature - falling back to cached BLOB."); |
+ +884 + | +
+
+1
+
+1. refreshBlobInternal : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataDownloader::refreshBlobInternal → KILLED + + + + |
+ return cached; |
+ +885 + | ++ + + + + + | + } else { |
+ +886 + | ++ + + + + + | + throw e; |
+ +887 + | ++ + + + + + | + } |
+ +888 + | ++ + + + + + | + } catch (Exception e) { |
+ +889 + | +
+
+1
+
+1. refreshBlobInternal : negated conditional → KILLED + + + + |
+ if (cached.isPresent()) { |
+ +890 + | ++ + + + + + | + log.warn("Failed to download new BLOB - falling back to cached BLOB.", e); |
+ +891 + | +
+
+1
+
+1. refreshBlobInternal : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataDownloader::refreshBlobInternal → MEMORY_ERROR + + + + |
+ return cached; |
+ +892 + | ++ + + + + + | + } else { |
+ +893 + | ++ + + + + + | + throw e; |
+ +894 + | ++ + + + + + | + } |
+ +895 + | ++ + + + + + | + } |
+ +896 + | ++ + + + + + | + } |
+ +897 + | ++ + + + + + | +|
+ +898 + | ++ + + + + + | + /** |
+ +899 + | ++ + + + + + | + * @throws CertificateException if the trust root certificate was downloaded and passed the |
+ +900 + | ++ + + + + + | + * SHA-256 integrity check, but does not contain a currently valid X.509 DER certificate. |
+ +901 + | ++ + + + + + | + * @throws DigestException if the trust root certificate was downloaded but failed the SHA-256 |
+ +902 + | ++ + + + + + | + * integrity check. |
+ +903 + | ++ + + + + + | + * @throws IOException if the trust root certificate download failed, or if reading or writing the |
+ +904 + | ++ + + + + + | + * cache file (if any) failed. |
+ +905 + | ++ + + + + + | + * @throws NoSuchAlgorithmException if the SHA-256 algorithm is not available. |
+ +906 + | ++ + + + + + | + */ |
+ +907 + | ++ + + + + + | + private X509Certificate retrieveTrustRootCert() |
+ +908 + | ++ + + + + + | + throws CertificateException, DigestException, IOException, NoSuchAlgorithmException { |
+ +909 + | ++ + + + + + | +|
+ +910 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (trustRootCertificate != null) { |
+ +911 + | +
+
+1
+
+1. retrieveTrustRootCert : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::retrieveTrustRootCert → KILLED + + + + |
+ return trustRootCertificate; |
+ +912 + | ++ + + + + + | +|
+ +913 + | ++ + + + + + | + } else { |
+ +914 + | ++ + + + + + | + final Optional<ByteArray> cachedContents; |
+ +915 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (trustRootCacheFile != null) { |
+ +916 + | ++ + + + + + | + cachedContents = readCacheFile(trustRootCacheFile); |
+ +917 + | ++ + + + + + | + } else { |
+ +918 + | ++ + + + + + | + cachedContents = trustRootCacheSupplier.get(); |
+ +919 + | ++ + + + + + | + } |
+ +920 + | ++ + + + + + | +|
+ +921 + | ++ + + + + + | + X509Certificate cert = null; |
+ +922 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (cachedContents.isPresent()) { |
+ +923 + | ++ + + + + + | + final ByteArray verifiedCachedContents = verifyHash(cachedContents.get(), trustRootSha256); |
+ +924 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (verifiedCachedContents != null) { |
+ +925 + | ++ + + + + + | + try { |
+ +926 + | ++ + + + + + | + final X509Certificate cachedCert = |
+ +927 + | ++ + + + + + | + CertificateParser.parseDer(verifiedCachedContents.getBytes()); |
+ +928 + | +
+
+1
+
+1. retrieveTrustRootCert : removed call to java/security/cert/X509Certificate::checkValidity → MEMORY_ERROR + + + + |
+ cachedCert.checkValidity(Date.from(clock.instant())); |
+ +929 + | ++ + + + + + | + cert = cachedCert; |
+ +930 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +931 + | ++ + + + + + | + // Fall through |
+ +932 + | ++ + + + + + | + } |
+ +933 + | ++ + + + + + | + } |
+ +934 + | ++ + + + + + | + } |
+ +935 + | ++ + + + + + | +|
+ +936 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (cert == null) { |
+ +937 + | ++ + + + + + | + final ByteArray downloaded = verifyHash(download(trustRootUrl), trustRootSha256); |
+ +938 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (downloaded == null) { |
+ +939 + | ++ + + + + + | + throw new DigestException( |
+ +940 + | ++ + + + + + | + "Downloaded trust root certificate matches none of the acceptable hashes."); |
+ +941 + | ++ + + + + + | + } |
+ +942 + | ++ + + + + + | +|
+ +943 + | ++ + + + + + | + cert = CertificateParser.parseDer(downloaded.getBytes()); |
+ +944 + | +
+
+1
+
+1. retrieveTrustRootCert : removed call to java/security/cert/X509Certificate::checkValidity → SURVIVED + + + + |
+ cert.checkValidity(Date.from(clock.instant())); |
+ +945 + | ++ + + + + + | +|
+ +946 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (trustRootCacheFile != null) { |
+ +947 + | ++ + + + + + | + try (FileOutputStream f = new FileOutputStream(trustRootCacheFile)) { |
+ +948 + | +
+
+1
+
+1. retrieveTrustRootCert : removed call to java/io/FileOutputStream::write → KILLED + + + + |
+ f.write(downloaded.getBytes()); |
+ +949 + | ++ + + + + + | + } |
+ +950 + | ++ + + + + + | + } |
+ +951 + | ++ + + + + + | +|
+ +952 + | +
+
+1
+
+1. retrieveTrustRootCert : negated conditional → KILLED + + + + |
+ if (trustRootCacheConsumer != null) { |
+ +953 + | +
+
+1
+
+1. retrieveTrustRootCert : removed call to java/util/function/Consumer::accept → KILLED + + + + |
+ trustRootCacheConsumer.accept(downloaded); |
+ +954 + | ++ + + + + + | + } |
+ +955 + | ++ + + + + + | + } |
+ +956 + | ++ + + + + + | +|
+ +957 + | +
+
+1
+
+1. retrieveTrustRootCert : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::retrieveTrustRootCert → KILLED + + + + |
+ return cert; |
+ +958 + | ++ + + + + + | + } |
+ +959 + | ++ + + + + + | + } |
+ +960 + | ++ + + + + + | +|
+ +961 + | ++ + + + + + | + /** |
+ +962 + | ++ + + + + + | + * @throws Base64UrlException if the metadata BLOB is not a well-formed JWT in compact |
+ +963 + | ++ + + + + + | + * serialization. |
+ +964 + | ++ + + + + + | + * @throws CertPathValidatorException if the explicitly configured BLOB fails certificate path |
+ +965 + | ++ + + + + + | + * validation. |
+ +966 + | ++ + + + + + | + * @throws CertificateException if the BLOB signing certificate chain fails to parse. |
+ +967 + | ++ + + + + + | + * @throws IOException on failure to parse the BLOB contents. |
+ +968 + | ++ + + + + + | + * @throws InvalidAlgorithmParameterException if certificate path validation fails. |
+ +969 + | ++ + + + + + | + * @throws InvalidKeyException if signature verification fails. |
+ +970 + | ++ + + + + + | + * @throws NoSuchAlgorithmException if signature verification fails. |
+ +971 + | ++ + + + + + | + * @throws SignatureException if signature verification fails. |
+ +972 + | ++ + + + + + | + * @throws FidoMetadataDownloaderException if the explicitly configured BLOB (if any) has a bad |
+ +973 + | ++ + + + + + | + * signature. |
+ +974 + | ++ + + + + + | + */ |
+ +975 + | ++ + + + + + | + private Optional<MetadataBLOB> loadExplicitBlobOnly(X509Certificate trustRootCertificate) |
+ +976 + | ++ + + + + + | + throws Base64UrlException, |
+ +977 + | ++ + + + + + | + CertPathValidatorException, |
+ +978 + | ++ + + + + + | + CertificateException, |
+ +979 + | ++ + + + + + | + IOException, |
+ +980 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +981 + | ++ + + + + + | + InvalidKeyException, |
+ +982 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +983 + | ++ + + + + + | + SignatureException, |
+ +984 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +985 + | +
+
+1
+
+1. loadExplicitBlobOnly : negated conditional → KILLED + + + + |
+ if (blobJwt != null) { |
+ +986 + | +
+
+1
+
+1. loadExplicitBlobOnly : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataDownloader::loadExplicitBlobOnly → KILLED + + + + |
+ return Optional.of( |
+ +987 + | ++ + + + + + | + parseAndMaybeVerifyBlob( |
+ +988 + | ++ + + + + + | + new ByteArray(blobJwt.getBytes(StandardCharsets.UTF_8)), trustRootCertificate)); |
+ +989 + | ++ + + + + + | +|
+ +990 + | ++ + + + + + | + } else { |
+ +991 + | ++ + + + + + | + return Optional.empty(); |
+ +992 + | ++ + + + + + | + } |
+ +993 + | ++ + + + + + | + } |
+ +994 + | ++ + + + + + | +|
+ +995 + | ++ + + + + + | + private Optional<MetadataBLOB> loadCachedBlobOnly(X509Certificate trustRootCertificate) { |
+ +996 + | ++ + + + + + | +|
+ +997 + | ++ + + + + + | + final Optional<ByteArray> cachedContents; |
+ +998 + | +
+
+1
+
+1. loadCachedBlobOnly : negated conditional → KILLED + + + + |
+ if (blobCacheFile != null) { |
+ +999 + | ++ + + + + + | + log.debug("Attempting to read BLOB from cache file..."); |
+ +1000 + | ++ + + + + + | +|
+ +1001 + | ++ + + + + + | + try { |
+ +1002 + | ++ + + + + + | + cachedContents = readCacheFile(blobCacheFile); |
+ +1003 + | ++ + + + + + | + } catch (IOException e) { |
+ +1004 + | ++ + + + + + | + return Optional.empty(); |
+ +1005 + | ++ + + + + + | + } |
+ +1006 + | ++ + + + + + | + } else { |
+ +1007 + | ++ + + + + + | + log.debug("Attempting to read BLOB from cache Supplier..."); |
+ +1008 + | ++ + + + + + | + cachedContents = blobCacheSupplier.get(); |
+ +1009 + | ++ + + + + + | + } |
+ +1010 + | ++ + + + + + | +|
+ +1011 + | +
+
+1
+
+1. loadCachedBlobOnly : replaced return value with Optional.empty for com/yubico/fido/metadata/FidoMetadataDownloader::loadCachedBlobOnly → KILLED + + + + |
+ return cachedContents.map( |
+ +1012 + | ++ + + + + + | + cached -> { |
+ +1013 + | ++ + + + + + | + try { |
+ +1014 + | +
+
+1
+
+1. lambda$loadCachedBlobOnly$0 : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::lambda$loadCachedBlobOnly$0 → KILLED + + + + |
+ return parseAndMaybeVerifyBlob(cached, trustRootCertificate); |
+ +1015 + | ++ + + + + + | + } catch (Exception e) { |
+ +1016 + | ++ + + + + + | + log.warn("Failed to read or parse cached BLOB.", e); |
+ +1017 + | ++ + + + + + | + return null; |
+ +1018 + | ++ + + + + + | + } |
+ +1019 + | ++ + + + + + | + }); |
+ +1020 + | ++ + + + + + | + } |
+ +1021 + | ++ + + + + + | +|
+ +1022 + | ++ + + + + + | + private Optional<ByteArray> readCacheFile(File cacheFile) throws IOException { |
+ +1023 + | +
+
+3
+
+1. readCacheFile : negated conditional → KILLED +2. readCacheFile : negated conditional → KILLED +3. readCacheFile : negated conditional → KILLED + + + + |
+ if (cacheFile.exists() && cacheFile.canRead() && cacheFile.isFile()) { |
+ +1024 + | ++ + + + + + | + try (FileInputStream f = new FileInputStream(cacheFile)) { |
+ +1025 + | ++ + + + + + | + return Optional.of(readAll(f)); |
+ +1026 + | ++ + + + + + | + } catch (FileNotFoundException e) { |
+ +1027 + | ++ + + + + + | + throw new RuntimeException( |
+ +1028 + | ++ + + + + + | + "This exception should be impossible, please file a bug report.", e); |
+ +1029 + | ++ + + + + + | + } |
+ +1030 + | ++ + + + + + | + } else { |
+ +1031 + | ++ + + + + + | + return Optional.empty(); |
+ +1032 + | ++ + + + + + | + } |
+ +1033 + | ++ + + + + + | + } |
+ +1034 + | ++ + + + + + | +|
+ +1035 + | ++ + + + + + | + private ByteArray download(URL url) throws IOException { |
+ +1036 + | ++ + + + + + | + URLConnection conn = url.openConnection(); |
+ +1037 + | ++ + + + + + | +|
+ +1038 + | +
+
+1
+
+1. download : negated conditional → KILLED + + + + |
+ if (conn instanceof HttpsURLConnection) { |
+ +1039 + | ++ + + + + + | + HttpsURLConnection httpsConn = (HttpsURLConnection) conn; |
+ +1040 + | +
+
+1
+
+1. download : negated conditional → KILLED + + + + |
+ if (httpsTrustStore != null) { |
+ +1041 + | ++ + + + + + | + try { |
+ +1042 + | ++ + + + + + | + TrustManagerFactory trustMan = |
+ +1043 + | ++ + + + + + | + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); |
+ +1044 + | +
+
+1
+
+1. download : removed call to javax/net/ssl/TrustManagerFactory::init → KILLED + + + + |
+ trustMan.init(httpsTrustStore); |
+ +1045 + | ++ + + + + + | + SSLContext sslContext = SSLContext.getInstance("TLS"); |
+ +1046 + | +
+
+1
+
+1. download : removed call to javax/net/ssl/SSLContext::init → KILLED + + + + |
+ sslContext.init(null, trustMan.getTrustManagers(), null); |
+ +1047 + | ++ + + + + + | +|
+ +1048 + | +
+
+1
+
+1. download : removed call to javax/net/ssl/HttpsURLConnection::setSSLSocketFactory → KILLED + + + + |
+ httpsConn.setSSLSocketFactory(sslContext.getSocketFactory()); |
+ +1049 + | ++ + + + + + | + } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { |
+ +1050 + | ++ + + + + + | + throw new RuntimeException( |
+ +1051 + | ++ + + + + + | + "Failed to initialize HTTPS trust store. This should be impossible, please file a bug report.", |
+ +1052 + | ++ + + + + + | + e); |
+ +1053 + | ++ + + + + + | + } |
+ +1054 + | ++ + + + + + | + } |
+ +1055 + | +
+
+1
+
+1. download : removed call to javax/net/ssl/HttpsURLConnection::setRequestMethod → SURVIVED + + + + |
+ httpsConn.setRequestMethod("GET"); |
+ +1056 + | ++ + + + + + | + } |
+ +1057 + | ++ + + + + + | +|
+ +1058 + | +
+
+1
+
+1. download : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::download → KILLED + + + + |
+ return readAll(conn.getInputStream()); |
+ +1059 + | ++ + + + + + | + } |
+ +1060 + | ++ + + + + + | +|
+ +1061 + | ++ + + + + + | + private MetadataBLOB parseAndVerifyBlob(ByteArray jwt, X509Certificate trustRootCertificate) |
+ +1062 + | ++ + + + + + | + throws CertPathValidatorException, |
+ +1063 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +1064 + | ++ + + + + + | + CertificateException, |
+ +1065 + | ++ + + + + + | + IOException, |
+ +1066 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +1067 + | ++ + + + + + | + SignatureException, |
+ +1068 + | ++ + + + + + | + InvalidKeyException, |
+ +1069 + | ++ + + + + + | + Base64UrlException, |
+ +1070 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +1071 + | +
+
+1
+
+1. parseAndVerifyBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::parseAndVerifyBlob → KILLED + + + + |
+ return verifyBlob(parseBlob(jwt), trustRootCertificate); |
+ +1072 + | ++ + + + + + | + } |
+ +1073 + | ++ + + + + + | +|
+ +1074 + | ++ + + + + + | + private MetadataBLOB parseAndMaybeVerifyBlob(ByteArray jwt, X509Certificate trustRootCertificate) |
+ +1075 + | ++ + + + + + | + throws CertPathValidatorException, |
+ +1076 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +1077 + | ++ + + + + + | + CertificateException, |
+ +1078 + | ++ + + + + + | + IOException, |
+ +1079 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +1080 + | ++ + + + + + | + SignatureException, |
+ +1081 + | ++ + + + + + | + InvalidKeyException, |
+ +1082 + | ++ + + + + + | + Base64UrlException, |
+ +1083 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +1084 + | +
+
+1
+
+1. parseAndMaybeVerifyBlob : negated conditional → KILLED + + + + |
+ if (verifyDownloadsOnly) { |
+ +1085 + | +
+
+1
+
+1. parseAndMaybeVerifyBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::parseAndMaybeVerifyBlob → KILLED + + + + |
+ return parseBlob(jwt).blob; |
+ +1086 + | ++ + + + + + | + } else { |
+ +1087 + | +
+
+1
+
+1. parseAndMaybeVerifyBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::parseAndMaybeVerifyBlob → KILLED + + + + |
+ return verifyBlob(parseBlob(jwt), trustRootCertificate); |
+ +1088 + | ++ + + + + + | + } |
+ +1089 + | ++ + + + + + | + } |
+ +1090 + | ++ + + + + + | +|
+ +1091 + | ++ + + + + + | + private MetadataBLOB verifyBlob(ParseResult parseResult, X509Certificate trustRootCertificate) |
+ +1092 + | ++ + + + + + | + throws IOException, |
+ +1093 + | ++ + + + + + | + CertificateException, |
+ +1094 + | ++ + + + + + | + NoSuchAlgorithmException, |
+ +1095 + | ++ + + + + + | + InvalidKeyException, |
+ +1096 + | ++ + + + + + | + SignatureException, |
+ +1097 + | ++ + + + + + | + CertPathValidatorException, |
+ +1098 + | ++ + + + + + | + InvalidAlgorithmParameterException, |
+ +1099 + | ++ + + + + + | + FidoMetadataDownloaderException { |
+ +1100 + | ++ + + + + + | + final MetadataBLOBHeader header = parseResult.blob.getHeader(); |
+ +1101 + | ++ + + + + + | +|
+ +1102 + | ++ + + + + + | + final List<X509Certificate> certChain; |
+ +1103 + | +
+
+1
+
+1. verifyBlob : negated conditional → KILLED + + + + |
+ if (header.getX5u().isPresent()) { |
+ +1104 + | ++ + + + + + | + final URL x5u = header.getX5u().get(); |
+ +1105 + | +
+
+1
+
+1. verifyBlob : negated conditional → KILLED + + + + |
+ if (blobUrl != null |
+ +1106 + | +
+
+1
+
+1. verifyBlob : negated conditional → SURVIVED + + + + |
+ && (!(x5u.getHost().equals(blobUrl.getHost()) |
+ +1107 + | +
+
+1
+
+1. verifyBlob : negated conditional → SURVIVED + + + + |
+ && x5u.getProtocol().equals(blobUrl.getProtocol()) |
+ +1108 + | +
+
+1
+
+1. verifyBlob : negated conditional → KILLED + + + + |
+ && x5u.getPort() == blobUrl.getPort()))) { |
+ +1109 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +1110 + | ++ + + + + + | + String.format( |
+ +1111 + | ++ + + + + + | + "x5u in BLOB header must have same origin as the URL the BLOB was downloaded from. Expected origin of: %s ; found: %s", |
+ +1112 + | ++ + + + + + | + blobUrl, x5u)); |
+ +1113 + | ++ + + + + + | + } |
+ +1114 + | ++ + + + + + | + List<X509Certificate> certs = new ArrayList<>(); |
+ +1115 + | ++ + + + + + | + for (String pem : |
+ +1116 + | ++ + + + + + | + new String(download(x5u).getBytes(), StandardCharsets.UTF_8) |
+ +1117 + | ++ + + + + + | + .trim() |
+ +1118 + | ++ + + + + + | + .split("\\n+-----END CERTIFICATE-----\\n+-----BEGIN CERTIFICATE-----\\n+")) { |
+ +1119 + | ++ + + + + + | + X509Certificate x509Certificate = CertificateParser.parsePem(pem); |
+ +1120 + | ++ + + + + + | + certs.add(x509Certificate); |
+ +1121 + | ++ + + + + + | + } |
+ +1122 + | ++ + + + + + | + certChain = certs; |
+ +1123 + | +
+
+1
+
+1. verifyBlob : negated conditional → KILLED + + + + |
+ } else if (header.getX5c().isPresent()) { |
+ +1124 + | ++ + + + + + | + certChain = header.getX5c().get(); |
+ +1125 + | ++ + + + + + | + } else { |
+ +1126 + | ++ + + + + + | + certChain = Collections.singletonList(trustRootCertificate); |
+ +1127 + | ++ + + + + + | + } |
+ +1128 + | ++ + + + + + | +|
+ +1129 + | ++ + + + + + | + final X509Certificate leafCert = certChain.get(0); |
+ +1130 + | ++ + + + + + | +|
+ +1131 + | ++ + + + + + | + final Signature signature; |
+ +1132 + | ++ + + + + + | + switch (header.getAlg()) { |
+ +1133 + | ++ + + + + + | + case "RS256": |
+ +1134 + | ++ + + + + + | + signature = Signature.getInstance("SHA256withRSA"); |
+ +1135 + | ++ + + + + + | + break; |
+ +1136 + | ++ + + + + + | +|
+ +1137 + | ++ + + + + + | + case "ES256": |
+ +1138 + | ++ + + + + + | + signature = Signature.getInstance("SHA256withECDSA"); |
+ +1139 + | ++ + + + + + | + break; |
+ +1140 + | ++ + + + + + | +|
+ +1141 + | ++ + + + + + | + default: |
+ +1142 + | ++ + + + + + | + throw new UnsupportedOperationException( |
+ +1143 + | ++ + + + + + | + "Unimplemented JWT verification algorithm: " + header.getAlg()); |
+ +1144 + | ++ + + + + + | + } |
+ +1145 + | ++ + + + + + | +|
+ +1146 + | +
+
+1
+
+1. verifyBlob : removed call to java/security/Signature::initVerify → KILLED + + + + |
+ signature.initVerify(leafCert.getPublicKey()); |
+ +1147 + | +
+
+1
+
+1. verifyBlob : removed call to java/security/Signature::update → KILLED + + + + |
+ signature.update( |
+ +1148 + | ++ + + + + + | + (parseResult.jwtHeader.getBase64Url() + "." + parseResult.jwtPayload.getBase64Url()) |
+ +1149 + | ++ + + + + + | + .getBytes(StandardCharsets.UTF_8)); |
+ +1150 + | +
+
+1
+
+1. verifyBlob : negated conditional → KILLED + + + + |
+ if (!signature.verify(parseResult.jwtSignature.getBytes())) { |
+ +1151 + | ++ + + + + + | + throw new FidoMetadataDownloaderException(Reason.BAD_SIGNATURE); |
+ +1152 + | ++ + + + + + | + } |
+ +1153 + | ++ + + + + + | +|
+ +1154 + | ++ + + + + + | + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); |
+ +1155 + | ++ + + + + + | + final CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); |
+ +1156 + | ++ + + + + + | + final CertPath blobCertPath = certFactory.generateCertPath(certChain); |
+ +1157 + | ++ + + + + + | + final PKIXParameters pathParams = |
+ +1158 + | ++ + + + + + | + new PKIXParameters(Collections.singleton(new TrustAnchor(trustRootCertificate, null))); |
+ +1159 + | +
+
+1
+
+1. verifyBlob : negated conditional → KILLED + + + + |
+ if (certStore != null) { |
+ +1160 + | +
+
+1
+
+1. verifyBlob : removed call to java/security/cert/PKIXParameters::addCertStore → KILLED + + + + |
+ pathParams.addCertStore(certStore); |
+ +1161 + | ++ + + + + + | + } |
+ +1162 + | +
+
+1
+
+1. verifyBlob : removed call to java/security/cert/PKIXParameters::setDate → KILLED + + + + |
+ pathParams.setDate(Date.from(clock.instant())); |
+ +1163 + | ++ + + + + + | + cpv.validate(blobCertPath, pathParams); |
+ +1164 + | ++ + + + + + | +|
+ +1165 + | +
+
+1
+
+1. verifyBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::verifyBlob → KILLED + + + + |
+ return parseResult.blob; |
+ +1166 + | ++ + + + + + | + } |
+ +1167 + | ++ + + + + + | +|
+ +1168 + | ++ + + + + + | + private static ParseResult parseBlob(ByteArray jwt) throws IOException, Base64UrlException { |
+ +1169 + | ++ + + + + + | + Scanner s = new Scanner(new ByteArrayInputStream(jwt.getBytes())).useDelimiter("\\."); |
+ +1170 + | ++ + + + + + | + final ByteArray jwtHeader = ByteArray.fromBase64Url(s.next()); |
+ +1171 + | ++ + + + + + | + final ByteArray jwtPayload = ByteArray.fromBase64Url(s.next()); |
+ +1172 + | ++ + + + + + | + final ByteArray jwtSignature = ByteArray.fromBase64Url(s.next()); |
+ +1173 + | ++ + + + + + | +|
+ +1174 + | ++ + + + + + | + final ObjectMapper headerJsonMapper = |
+ +1175 + | ++ + + + + + | + com.yubico.internal.util.JacksonCodecs.json() |
+ +1176 + | ++ + + + + + | + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) |
+ +1177 + | ++ + + + + + | + .setBase64Variant(Base64Variants.MIME_NO_LINEFEEDS); |
+ +1178 + | ++ + + + + + | +|
+ +1179 + | +
+
+1
+
+1. parseBlob : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::parseBlob → KILLED + + + + |
+ return new ParseResult( |
+ +1180 + | ++ + + + + + | + new MetadataBLOB( |
+ +1181 + | ++ + + + + + | + headerJsonMapper.readValue(jwtHeader.getBytes(), MetadataBLOBHeader.class), |
+ +1182 + | ++ + + + + + | + JacksonCodecs.jsonWithDefaultEnums() |
+ +1183 + | ++ + + + + + | + .readValue(jwtPayload.getBytes(), MetadataBLOBPayload.class)), |
+ +1184 + | ++ + + + + + | + jwtHeader, |
+ +1185 + | ++ + + + + + | + jwtPayload, |
+ +1186 + | ++ + + + + + | + jwtSignature); |
+ +1187 + | ++ + + + + + | + } |
+ +1188 + | ++ + + + + + | +|
+ +1189 + | ++ + + + + + | + private static ByteArray readAll(InputStream is) throws IOException { |
+ +1190 + | +
+
+1
+
+1. readAll : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::readAll → KILLED + + + + |
+ return new ByteArray(BinaryUtil.readAll(is)); |
+ +1191 + | ++ + + + + + | + } |
+ +1192 + | ++ + + + + + | +|
+ +1193 + | ++ + + + + + | + /** |
+ +1194 + | ++ + + + + + | + * @return <code>contents</code> if its SHA-256 hash matches any element of <code> |
+ +1195 + | ++ + + + + + | + * acceptedCertSha256</code>, otherwise <code>null</code>. |
+ +1196 + | ++ + + + + + | + */ |
+ +1197 + | ++ + + + + + | + private static ByteArray verifyHash(ByteArray contents, Set<ByteArray> acceptedCertSha256) |
+ +1198 + | ++ + + + + + | + throws NoSuchAlgorithmException { |
+ +1199 + | ++ + + + + + | + MessageDigest digest = MessageDigest.getInstance("SHA-256"); |
+ +1200 + | ++ + + + + + | + final ByteArray hash = new ByteArray(digest.digest(contents.getBytes())); |
+ +1201 + | +
+
+1
+
+1. verifyHash : negated conditional → KILLED + + + + |
+ if (acceptedCertSha256.stream().anyMatch(hash::equals)) { |
+ +1202 + | +
+
+1
+
+1. verifyHash : replaced return value with null for com/yubico/fido/metadata/FidoMetadataDownloader::verifyHash → KILLED + + + + |
+ return contents; |
+ +1203 + | ++ + + + + + | + } else { |
+ +1204 + | ++ + + + + + | + return null; |
+ +1205 + | ++ + + + + + | + } |
+ +1206 + | ++ + + + + + | + } |
+ +1207 + | ++ + + + + + | +|
+ +1208 + | ++ + + + + + | + @Value |
+ +1209 + | ++ + + + + + | + private static class ParseResult { |
+ +1210 + | ++ + + + + + | + private MetadataBLOB blob; |
+ +1211 + | ++ + + + + + | + private ByteArray jwtHeader; |
+ +1212 + | ++ + + + + + | + private ByteArray jwtPayload; |
+ +1213 + | ++ + + + + + | + private ByteArray jwtSignature; |
+ +1214 + | ++ + + + + + | + } |
+ +1215 + | ++ + + + + + | +} |
Mutations | ||
132 | ++ |
+
+
+
+ 1.1 |
+
156 | ++ |
+
+
+
+ 1.1 |
+
220 | ++ |
+
+
+
+ 1.1 |
+
221 | ++ |
+
+
+
+ 1.1 |
+
266 | ++ |
+
+
+
+ 1.1 |
+
295 | ++ |
+
+
+
+ 1.1 2.2 |
+
296 | ++ |
+
+
+
+ 1.1 |
+
299 | ++ |
+
+
+
+ 1.1 |
+
308 | ++ |
+
+
+
+ 1.1 |
+
309 | ++ |
+
+
+
+ 1.1 |
+
342 | ++ |
+
+
+
+ 1.1 |
+
343 | ++ |
+
+
+
+ 1.1 |
+
365 | ++ |
+
+
+
+ 1.1 |
+
366 | ++ |
+
+
+
+ 1.1 |
+
367 | ++ |
+
+
+
+ 1.1 |
+
405 | ++ |
+
+
+
+ 1.1 |
+
423 | ++ |
+
+
+
+ 1.1 |
+
424 | ++ |
+
+
+
+ 1.1 |
+
445 | ++ |
+
+
+
+ 1.1 |
+
446 | ++ |
+
+
+
+ 1.1 |
+
479 | ++ |
+
+
+
+ 1.1 |
+
480 | ++ |
+
+
+
+ 1.1 |
+
503 | ++ |
+
+
+
+ 1.1 |
+
504 | ++ |
+
+
+
+ 1.1 |
+
505 | ++ |
+
+
+
+ 1.1 |
+
514 | ++ |
+
+
+
+ 1.1 |
+
539 | ++ |
+
+
+
+ 1.1 |
+
541 | ++ |
+
+
+
+ 1.1 |
+
557 | ++ |
+
+
+
+ 1.1 |
+
559 | ++ |
+
+
+
+ 1.1 |
+
573 | ++ |
+
+
+
+ 1.1 |
+
592 | ++ |
+
+
+
+ 1.1 |
+
596 | ++ |
+
+
+
+ 1.1 |
+
607 | ++ |
+
+
+
+ 1.1 |
+
616 | ++ |
+
+
+
+ 1.1 |
+
636 | ++ |
+
+
+
+ 1.1 |
+
718 | ++ |
+
+
+
+ 1.1 |
+
720 | ++ |
+
+
+
+ 1.1 |
+
724 | ++ |
+
+
+
+ 1.1 |
+
732 | ++ |
+
+
+
+ 1.1 |
+
734 | ++ |
+
+
+
+ 1.1 |
+
743 | ++ |
+
+
+
+ 1.1 |
+
821 | ++ |
+
+
+
+ 1.1 |
+
823 | ++ |
+
+
+
+ 1.1 |
+
827 | ++ |
+
+
+
+ 1.1 |
+
833 | ++ |
+
+
+
+ 1.1 |
+
837 | ++ |
+
+
+
+ 1.1 2.2 |
+
855 | ++ |
+
+
+
+ 1.1 |
+
857 | ++ |
+
+
+
+ 1.1 2.2 |
+
859 | ++ |
+
+
+
+ 1.1 |
+
865 | ++ |
+
+
+
+ 1.1 |
+
870 | ++ |
+
+
+
+ 1.1 |
+
872 | ++ |
+
+
+
+ 1.1 |
+
876 | ++ |
+
+
+
+ 1.1 |
+
877 | ++ |
+
+
+
+ 1.1 |
+
880 | ++ |
+
+
+
+ 1.1 |
+
882 | ++ |
+
+
+
+ 1.1 2.2 |
+
884 | ++ |
+
+
+
+ 1.1 |
+
889 | ++ |
+
+
+
+ 1.1 |
+
891 | ++ |
+
+
+
+ 1.1 |
+
910 | ++ |
+
+
+
+ 1.1 |
+
911 | ++ |
+
+
+
+ 1.1 |
+
915 | ++ |
+
+
+
+ 1.1 |
+
922 | ++ |
+
+
+
+ 1.1 |
+
924 | ++ |
+
+
+
+ 1.1 |
+
928 | ++ |
+
+
+
+ 1.1 |
+
936 | ++ |
+
+
+
+ 1.1 |
+
938 | ++ |
+
+
+
+ 1.1 |
+
944 | ++ |
+
+
+
+ 1.1 |
+
946 | ++ |
+
+
+
+ 1.1 |
+
948 | ++ |
+
+
+
+ 1.1 |
+
952 | ++ |
+
+
+
+ 1.1 |
+
953 | ++ |
+
+
+
+ 1.1 |
+
957 | ++ |
+
+
+
+ 1.1 |
+
985 | ++ |
+
+
+
+ 1.1 |
+
986 | ++ |
+
+
+
+ 1.1 |
+
998 | ++ |
+
+
+
+ 1.1 |
+
1011 | ++ |
+
+
+
+ 1.1 |
+
1014 | ++ |
+
+
+
+ 1.1 |
+
1023 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
1038 | ++ |
+
+
+
+ 1.1 |
+
1040 | ++ |
+
+
+
+ 1.1 |
+
1044 | ++ |
+
+
+
+ 1.1 |
+
1046 | ++ |
+
+
+
+ 1.1 |
+
1048 | ++ |
+
+
+
+ 1.1 |
+
1055 | ++ |
+
+
+
+ 1.1 |
+
1058 | ++ |
+
+
+
+ 1.1 |
+
1071 | ++ |
+
+
+
+ 1.1 |
+
1084 | ++ |
+
+
+
+ 1.1 |
+
1085 | ++ |
+
+
+
+ 1.1 |
+
1087 | ++ |
+
+
+
+ 1.1 |
+
1103 | ++ |
+
+
+
+ 1.1 |
+
1105 | ++ |
+
+
+
+ 1.1 |
+
1106 | ++ |
+
+
+
+ 1.1 |
+
1107 | ++ |
+
+
+
+ 1.1 |
+
1108 | ++ |
+
+
+
+ 1.1 |
+
1123 | ++ |
+
+
+
+ 1.1 |
+
1146 | ++ |
+
+
+
+ 1.1 |
+
1147 | ++ |
+
+
+
+ 1.1 |
+
1150 | ++ |
+
+
+
+ 1.1 |
+
1159 | ++ |
+
+
+
+ 1.1 |
+
1160 | ++ |
+
+
+
+ 1.1 |
+
1162 | ++ |
+
+
+
+ 1.1 |
+
1165 | ++ |
+
+
+
+ 1.1 |
+
1179 | ++ |
+
+
+
+ 1.1 |
+
1190 | ++ |
+
+
+
+ 1.1 |
+
1201 | ++ |
+
+
+
+ 1.1 |
+
1202 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import lombok.Getter; |
+ +4 + | ++ + + + + + | +import lombok.NonNull; |
+ +5 + | ++ + + + + + | +|
+ +6 + | ++ + + + + + | +public class FidoMetadataDownloaderException extends Exception { |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | + public enum Reason { |
+ +9 + | ++ + + + + + | + BAD_SIGNATURE("Bad JWT signature."); |
+ +10 + | ++ + + + + + | +|
+ +11 + | ++ + + + + + | + private final String message; |
+ +12 + | ++ + + + + + | +|
+ +13 + | ++ + + + + + | + Reason(String message) { |
+ +14 + | ++ + + + + + | + this.message = message; |
+ +15 + | ++ + + + + + | + } |
+ +16 + | ++ + + + + + | + } |
+ +17 + | ++ + + + + + | +|
+ +18 + | ++ + + + + + | + @NonNull @Getter |
+ +19 + | ++ + + + + + | + /** The reason why this exception was thrown. */ |
+ +20 + | ++ + + + + + | + private final Reason reason; |
+ +21 + | ++ + + + + + | +|
+ +22 + | ++ + + + + + | + /** A {@link Throwable} that caused this exception. May be null. */ |
+ +23 + | ++ + + + + + | + @Getter private final Throwable cause; |
+ +24 + | ++ + + + + + | +|
+ +25 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ FidoMetadataDownloaderException(@NonNull Reason reason, Throwable cause) { |
+ +26 + | ++ + + + + + | + super(cause); |
+ +27 + | ++ + + + + + | + this.reason = reason; |
+ +28 + | ++ + + + + + | + this.cause = cause; |
+ +29 + | ++ + + + + + | + } |
+ +30 + | ++ + + + + + | +|
+ +31 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ FidoMetadataDownloaderException(@NonNull Reason reason) { |
+ +32 + | ++ + + + + + | + this(reason, null); |
+ +33 + | ++ + + + + + | + } |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + @Override |
+ +36 + | ++ + + + + + | + public String getMessage() { |
+ +37 + | +
+
+1
+
+1. getMessage : replaced return value with "" for com/yubico/fido/metadata/FidoMetadataDownloaderException::getMessage → NO_COVERAGE + + + + |
+ return reason.message; |
+ +38 + | ++ + + + + + | + } |
+ +39 + | ++ + + + + + | +} |
Mutations | ||
25 | ++ |
+
+
+
+ 1.1 |
+
31 | ++ |
+
+
+
+ 1.1 |
+
37 | ++ |
+
+
+
+ 1.1 |
+
+ +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 -> 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 |
+
104 | ++ |
+
+
+
+ 1.1 |
+
105 | ++ |
+
+
+
+ 1.1 |
+
131 | ++ |
+
+
+
+ 1.1 2.2 |
+
136 | ++ |
+
+
+
+ 1.1 2.2 |
+
139 | ++ |
+
+
+
+ 1.1 2.2 |
+
144 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
145 | ++ |
+
+
+
+ 1.1 2.2 |
+
150 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 |
+
158 | ++ |
+
+
+
+ 1.1 |
+
165 | ++ |
+
+
+
+ 1.1 |
+
175 | ++ |
+
+
+
+ 1.1 |
+
182 | ++ |
+
+
+
+ 1.1 |
+
184 | ++ |
+
+
+
+ 1.1 |
+
191 | ++ |
+
+
+
+ 1.1 |
+
195 | ++ |
+
+
+
+ 1.1 2.2 |
+
196 | ++ |
+
+
+
+ 1.1 |
+
200 | ++ |
+
+
+
+ 1.1 2.2 |
+
201 | ++ |
+
+
+
+ 1.1 |
+
210 | ++ |
+
+
+
+ 1.1 |
+
217 | ++ |
+
+
+
+ 1.1 |
+
240 | ++ |
+
+
+
+ 1.1 |
+
241 | ++ |
+
+
+
+ 1.1 |
+
253 | ++ |
+
+
+
+ 1.1 |
+
254 | ++ |
+
+
+
+ 1.1 |
+
275 | ++ |
+
+
+
+ 1.1 |
+
277 | ++ |
+
+
+
+ 1.1 |
+
308 | ++ |
+
+
+
+ 1.1 |
+
310 | ++ |
+
+
+
+ 1.1 |
+
322 | ++ |
+
+
+
+ 1.1 |
+
324 | ++ |
+
+
+
+ 1.1 |
+
339 | ++ |
+
+
+
+ 1.1 |
+
364 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 5.5 |
+
375 | ++ |
+
+
+
+ 1.1 |
+
376 | ++ |
+
+
+
+ 1.1 2.2 |
+
378 | ++ |
+
+
+
+ 1.1 2.2 |
+
396 | ++ |
+
+
+
+ 1.1 |
+
397 | ++ |
+
+
+
+ 1.1 2.2 |
+
400 | ++ |
+
+
+
+ 1.1 2.2 |
+
404 | ++ |
+
+
+
+ 1.1 2.2 |
+
406 | ++ |
+
+
+
+ 1.1 |
+
408 | ++ |
+
+
+
+ 1.1 2.2 |
+
449 | ++ |
+
+
+
+ 1.1 |
+
498 | ++ |
+
+
+
+ 1.1 |
+
499 | ++ |
+
+
+
+ 1.1 |
+
506 | ++ |
+
+
+
+ 1.1 |
+
517 | ++ |
+
+
+
+ 1.1 2.2 |
+
520 | ++ |
+
+
+
+ 1.1 |
+
523 | ++ |
+
+
+
+ 1.1 |
+
548 | ++ |
+
+
+
+ 1.1 |
+
551 | ++ |
+
+
+
+ 1.1 2.2 |
+
563 | ++ |
+
+
+
+ 1.1 |
+
572 | ++ |
+
+
+
+ 1.1 |
+
573 | ++ |
+
+
+
+ 1.1 |
+
582 | ++ |
+
+
+
+ 1.1 2.2 |
+
583 | ++ |
+
+
+
+ 1.1 |
+
599 | ++ |
+
+
+
+ 1.1 |
+
600 | ++ |
+
+
+
+ 1.1 |
+
602 | ++ |
+
+
+
+ 1.1 |
+
611 | ++ |
+
+
+
+ 1.1 |
+
612 | ++ |
+
+
+
+ 1.1 |
+
629 | ++ |
+
+
+
+ 1.1 |
+
630 | ++ |
+
+
+
+ 1.1 |
+
643 | ++ |
+
+
+
+ 1.1 |
+
650 | ++ |
+
+
+
+ 1.1 |
+
654 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.databind.DeserializationFeature; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.databind.ObjectMapper; |
+ +5 + | ++ + + + + + | +|
+ +6 + | ++ + + + + + | +class JacksonCodecs { |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | + static ObjectMapper jsonWithDefaultEnums() { |
+ +9 + | +
+
+1
+
+1. jsonWithDefaultEnums : replaced return value with null for com/yubico/fido/metadata/JacksonCodecs::jsonWithDefaultEnums → KILLED + + + + |
+ return com.yubico.internal.util.JacksonCodecs.json() |
+ +10 + | ++ + + + + + | + .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true); |
+ +11 + | ++ + + + + + | + } |
+ +12 + | ++ + + + + + | +} |
Mutations | ||
9 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +5 + | ++ + + + + + | +import java.net.URL; |
+ +6 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +7 + | ++ + + + + + | +import java.util.List; |
+ +8 + | ++ + + + + + | +import java.util.Optional; |
+ +9 + | ++ + + + + + | +import lombok.Builder; |
+ +10 + | ++ + + + + + | +import lombok.NonNull; |
+ +11 + | ++ + + + + + | +import lombok.Value; |
+ +12 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +13 + | ++ + + + + + | +|
+ +14 + | ++ + + + + + | +/** |
+ +15 + | ++ + + + + + | + * The metadata BLOB is a JSON Web Token (see [<a |
+ +16 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biblio-jwt">JWT</a>] |
+ +17 + | ++ + + + + + | + * and [<a |
+ +18 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#biblio-jws">JWS</a>]). |
+ +19 + | ++ + + + + + | + * |
+ +20 + | ++ + + + + + | + * <p>This type represents the contents of the JWT header. |
+ +21 + | ++ + + + + + | + * |
+ +22 + | ++ + + + + + | + * @see <a |
+ +23 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob">FIDO |
+ +24 + | ++ + + + + + | + * Metadata Service §3.1.7. Metadata BLOB</a> |
+ +25 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7519">RFC 7519: JSON Web Token (JWT)</a> |
+ +26 + | ++ + + + + + | + */ |
+ +27 + | ++ + + + + + | +@Value |
+ +28 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +29 + | ++ + + + + + | +@Jacksonized |
+ +30 + | ++ + + + + + | +public class MetadataBLOBHeader { |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | + /** |
+ +33 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7519#section-5.1">RFC 7519 §5.1. "typ" |
+ +34 + | ++ + + + + + | + * (Type) Header Parameter</a> |
+ +35 + | ++ + + + + + | + */ |
+ +36 + | ++ + + + + + | + String typ; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + /** |
+ +39 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.1">RFC 7515 §4.1.1. |
+ +40 + | ++ + + + + + | + * "alg" (Algorithm) Header Parameter</a> |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | + @NonNull String alg; |
+ +43 + | ++ + + + + + | +|
+ +44 + | ++ + + + + + | + /** |
+ +45 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.5">RFC 7515 §4.1.5. |
+ +46 + | ++ + + + + + | + * "x5u" (X.509 URL) Header Parameter</a> |
+ +47 + | ++ + + + + + | + */ |
+ +48 + | ++ + + + + + | + URL x5u; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + /** |
+ +51 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6">RFC 7515 §4.1.6. |
+ +52 + | ++ + + + + + | + * "x5c" (X.509 Certificate Chain) Header Parameter</a> |
+ +53 + | ++ + + + + + | + */ |
+ +54 + | ++ + + + + + | + @JsonDeserialize(contentConverter = CertFromBase64Converter.class) |
+ +55 + | ++ + + + + + | + @JsonSerialize(contentConverter = CertToBase64Converter.class) |
+ +56 + | ++ + + + + + | + List<X509Certificate> x5c; |
+ +57 + | ++ + + + + + | +|
+ +58 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ private MetadataBLOBHeader(String typ, @NonNull String alg, URL x5u, List<X509Certificate> x5c) { |
+ +59 + | ++ + + + + + | + this.typ = typ; |
+ +60 + | ++ + + + + + | + this.alg = alg; |
+ +61 + | ++ + + + + + | + this.x5u = x5u; |
+ +62 + | ++ + + + + + | + this.x5c = x5c; |
+ +63 + | ++ + + + + + | +|
+ +64 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ if (typ != null && !typ.equals("JWT")) { |
+ +65 + | ++ + + + + + | + throw new IllegalArgumentException("Unsupported JWT type: " + typ); |
+ +66 + | ++ + + + + + | + } |
+ +67 + | ++ + + + + + | + } |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + /** |
+ +70 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7519#section-5.1">RFC 7519 §5.1. "typ" |
+ +71 + | ++ + + + + + | + * (Type) Header Parameter</a> |
+ +72 + | ++ + + + + + | + */ |
+ +73 + | ++ + + + + + | + public Optional<String> getTyp() { |
+ +74 + | +
+
+1
+
+1. getTyp : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBHeader::getTyp → SURVIVED + + + + |
+ return Optional.ofNullable(typ); |
+ +75 + | ++ + + + + + | + } |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.5">RFC 7515 §4.1.5. |
+ +79 + | ++ + + + + + | + * "x5u" (X.509 URL) Header Parameter</a> |
+ +80 + | ++ + + + + + | + */ |
+ +81 + | ++ + + + + + | + public Optional<URL> getX5u() { |
+ +82 + | +
+
+1
+
+1. getX5u : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBHeader::getX5u → KILLED + + + + |
+ return Optional.ofNullable(x5u); |
+ +83 + | ++ + + + + + | + } |
+ +84 + | ++ + + + + + | +|
+ +85 + | ++ + + + + + | + /** |
+ +86 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6">RFC 7515 §4.1.6. |
+ +87 + | ++ + + + + + | + * "x5c" (X.509 Certificate Chain) Header Parameter</a> |
+ +88 + | ++ + + + + + | + */ |
+ +89 + | ++ + + + + + | + public Optional<List<X509Certificate>> getX5c() { |
+ +90 + | +
+
+1
+
+1. getX5c : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBHeader::getX5c → KILLED + + + + |
+ return Optional.ofNullable(x5c); |
+ +91 + | ++ + + + + + | + } |
+ +92 + | ++ + + + + + | +} |
Mutations | ||
58 | ++ |
+
+
+
+ 1.1 |
+
64 | ++ |
+
+
+
+ 1.1 2.2 |
+
74 | ++ |
+
+
+
+ 1.1 |
+
82 | ++ |
+
+
+
+ 1.1 |
+
90 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +4 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +5 + | ++ + + + + + | +import java.net.URL; |
+ +6 + | ++ + + + + + | +import java.time.LocalDate; |
+ +7 + | ++ + + + + + | +import java.util.Collections; |
+ +8 + | ++ + + + + + | +import java.util.List; |
+ +9 + | ++ + + + + + | +import java.util.Optional; |
+ +10 + | ++ + + + + + | +import java.util.Set; |
+ +11 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +12 + | ++ + + + + + | +import lombok.Builder; |
+ +13 + | ++ + + + + + | +import lombok.NonNull; |
+ +14 + | ++ + + + + + | +import lombok.Value; |
+ +15 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +16 + | ++ + + + + + | +|
+ +17 + | ++ + + + + + | +/** |
+ +18 + | ++ + + + + + | + * An element of {@link MetadataBLOBPayload#getEntries() entries} in a {@link MetadataBLOBPayload}. |
+ +19 + | ++ + + + + + | + * |
+ +20 + | ++ + + + + + | + * @see <a |
+ +21 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +22 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +23 + | ++ + + + + + | + */ |
+ +24 + | ++ + + + + + | +@Value |
+ +25 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +26 + | ++ + + + + + | +@Jacksonized |
+ +27 + | ++ + + + + + | +public class MetadataBLOBPayloadEntry { |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | + /** |
+ +30 + | ++ + + + + + | + * @see <a |
+ +31 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +32 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +33 + | ++ + + + + + | + */ |
+ +34 + | ++ + + + + + | + AAID aaid; |
+ +35 + | ++ + + + + + | +|
+ +36 + | ++ + + + + + | + /** |
+ +37 + | ++ + + + + + | + * @see <a |
+ +38 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +39 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +40 + | ++ + + + + + | + */ |
+ +41 + | ++ + + + + + | + AAGUID aaguid; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + /** |
+ +44 + | ++ + + + + + | + * @see <a |
+ +45 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +46 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +47 + | ++ + + + + + | + */ |
+ +48 + | ++ + + + + + | + Set<String> attestationCertificateKeyIdentifiers; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + /** |
+ +51 + | ++ + + + + + | + * @see <a |
+ +52 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +53 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +54 + | ++ + + + + + | + */ |
+ +55 + | ++ + + + + + | + MetadataStatement metadataStatement; |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + /** |
+ +58 + | ++ + + + + + | + * @see <a |
+ +59 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +60 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +61 + | ++ + + + + + | + */ |
+ +62 + | ++ + + + + + | + List<BiometricStatusReport> biometricStatusReports; |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + /** |
+ +65 + | ++ + + + + + | + * @see <a |
+ +66 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +67 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +68 + | ++ + + + + + | + */ |
+ +69 + | ++ + + + + + | + @NonNull List<StatusReport> statusReports; |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | + /** |
+ +72 + | ++ + + + + + | + * @see <a |
+ +73 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +74 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +75 + | ++ + + + + + | + */ |
+ +76 + | ++ + + + + + | + @NonNull LocalDate timeOfLastStatusChange; |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + /** |
+ +79 + | ++ + + + + + | + * @see <a |
+ +80 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +81 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +82 + | ++ + + + + + | + */ |
+ +83 + | ++ + + + + + | + URL rogueListURL; |
+ +84 + | ++ + + + + + | +|
+ +85 + | ++ + + + + + | + /** |
+ +86 + | ++ + + + + + | + * @see <a |
+ +87 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +88 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +89 + | ++ + + + + + | + */ |
+ +90 + | ++ + + + + + | + ByteArray rogueListHash; |
+ +91 + | ++ + + + + + | +|
+ +92 + | ++ + + + + + | + private MetadataBLOBPayloadEntry( |
+ +93 + | ++ + + + + + | + AAID aaid, |
+ +94 + | ++ + + + + + | + AAGUID aaguid, |
+ +95 + | ++ + + + + + | + Set<String> attestationCertificateKeyIdentifiers, |
+ +96 + | ++ + + + + + | + MetadataStatement metadataStatement, |
+ +97 + | ++ + + + + + | + List<BiometricStatusReport> biometricStatusReports, |
+ +98 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull List<StatusReport> statusReports, |
+ +99 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull LocalDate timeOfLastStatusChange, |
+ +100 + | ++ + + + + + | + URL rogueListURL, |
+ +101 + | ++ + + + + + | + ByteArray rogueListHash) { |
+ +102 + | ++ + + + + + | + this.aaid = aaid; |
+ +103 + | ++ + + + + + | + this.aaguid = aaguid; |
+ +104 + | ++ + + + + + | + this.attestationCertificateKeyIdentifiers = |
+ +105 + | ++ + + + + + | + CollectionUtil.immutableSetOrEmpty(attestationCertificateKeyIdentifiers); |
+ +106 + | ++ + + + + + | + this.metadataStatement = metadataStatement; |
+ +107 + | ++ + + + + + | + this.biometricStatusReports = CollectionUtil.immutableListOrEmpty(biometricStatusReports); |
+ +108 + | ++ + + + + + | + this.statusReports = |
+ +109 + | ++ + + + + + | + Collections.unmodifiableList( |
+ +110 + | ++ + + + + + | + statusReports.stream() |
+ +111 + | ++ + + + + + | + .filter( |
+ +112 + | +
+
+2
+
+1. lambda$new$0 : replaced boolean return with true for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::lambda$new$0 → KILLED +2. lambda$new$0 : negated conditional → KILLED + + + + |
+ statusReport -> !statusReport.getStatus().equals(AuthenticatorStatus.UNKNOWN)) |
+ +113 + | ++ + + + + + | + .collect(Collectors.toList())); |
+ +114 + | ++ + + + + + | + this.timeOfLastStatusChange = timeOfLastStatusChange; |
+ +115 + | ++ + + + + + | + this.rogueListURL = rogueListURL; |
+ +116 + | ++ + + + + + | + this.rogueListHash = rogueListHash; |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * @see <a |
+ +121 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +122 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +123 + | ++ + + + + + | + */ |
+ +124 + | ++ + + + + + | + public Optional<AAID> getAaid() { |
+ +125 + | +
+
+1
+
+1. getAaid : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::getAaid → KILLED + + + + |
+ return Optional.ofNullable(this.aaid); |
+ +126 + | ++ + + + + + | + } |
+ +127 + | ++ + + + + + | +|
+ +128 + | ++ + + + + + | + /** |
+ +129 + | ++ + + + + + | + * @see <a |
+ +130 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +131 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +132 + | ++ + + + + + | + */ |
+ +133 + | ++ + + + + + | + public Optional<AAGUID> getAaguid() { |
+ +134 + | +
+
+1
+
+1. getAaguid : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::getAaguid → KILLED + + + + |
+ return Optional.ofNullable(this.aaguid); |
+ +135 + | ++ + + + + + | + } |
+ +136 + | ++ + + + + + | +|
+ +137 + | ++ + + + + + | + /** |
+ +138 + | ++ + + + + + | + * @see <a |
+ +139 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +140 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +141 + | ++ + + + + + | + */ |
+ +142 + | ++ + + + + + | + public Optional<MetadataStatement> getMetadataStatement() { |
+ +143 + | +
+
+1
+
+1. getMetadataStatement : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::getMetadataStatement → KILLED + + + + |
+ return Optional.ofNullable(this.metadataStatement); |
+ +144 + | ++ + + + + + | + } |
+ +145 + | ++ + + + + + | +|
+ +146 + | ++ + + + + + | + /** |
+ +147 + | ++ + + + + + | + * @see <a |
+ +148 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +149 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +150 + | ++ + + + + + | + */ |
+ +151 + | ++ + + + + + | + public Optional<LocalDate> getTimeOfLastStatusChange() { |
+ +152 + | +
+
+1
+
+1. getTimeOfLastStatusChange : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::getTimeOfLastStatusChange → KILLED + + + + |
+ return Optional.of(this.timeOfLastStatusChange); |
+ +153 + | ++ + + + + + | + } |
+ +154 + | ++ + + + + + | +|
+ +155 + | ++ + + + + + | + /** |
+ +156 + | ++ + + + + + | + * @see <a |
+ +157 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +158 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +159 + | ++ + + + + + | + */ |
+ +160 + | ++ + + + + + | + public Optional<URL> getRogueListURL() { |
+ +161 + | +
+
+1
+
+1. getRogueListURL : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::getRogueListURL → SURVIVED + + + + |
+ return Optional.ofNullable(this.rogueListURL); |
+ +162 + | ++ + + + + + | + } |
+ +163 + | ++ + + + + + | +|
+ +164 + | ++ + + + + + | + /** |
+ +165 + | ++ + + + + + | + * @see <a |
+ +166 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#metadata-blob-payload-entry-dictionary">FIDO |
+ +167 + | ++ + + + + + | + * Metadata Service §3.1.1. Metadata BLOB Payload Entry dictionary</a> |
+ +168 + | ++ + + + + + | + */ |
+ +169 + | ++ + + + + + | + public Optional<ByteArray> getRogueListHash() { |
+ +170 + | +
+
+1
+
+1. getRogueListHash : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataBLOBPayloadEntry::getRogueListHash → SURVIVED + + + + |
+ return Optional.ofNullable(this.rogueListHash); |
+ +171 + | ++ + + + + + | + } |
+ +172 + | ++ + + + + + | +} |
Mutations | ||
98 | ++ |
+
+
+
+ 1.1 |
+
99 | ++ |
+
+
+
+ 1.1 |
+
112 | ++ |
+
+
+
+ 1.1 2.2 |
+
125 | ++ |
+
+
+
+ 1.1 |
+
134 | ++ |
+
+
+
+ 1.1 |
+
143 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 |
+
161 | ++ |
+
+
+
+ 1.1 |
+
170 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +5 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +6 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.KeyProtectionType; |
+ +7 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.MatcherProtectionType; |
+ +8 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +9 + | ++ + + + + + | +import java.util.List; |
+ +10 + | ++ + + + + + | +import java.util.Optional; |
+ +11 + | ++ + + + + + | +import java.util.Set; |
+ +12 + | ++ + + + + + | +import lombok.Builder; |
+ +13 + | ++ + + + + + | +import lombok.NonNull; |
+ +14 + | ++ + + + + + | +import lombok.Value; |
+ +15 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +16 + | ++ + + + + + | +|
+ +17 + | ++ + + + + + | +/** |
+ +18 + | ++ + + + + + | + * Relying Parties can learn a subset of verifiable information for authenticators certified by the |
+ +19 + | ++ + + + + + | + * FIDO Alliance with an Authenticator Metadata statement. The Metadata statement can be acquired |
+ +20 + | ++ + + + + + | + * from the Metadata BLOB that is hosted on the Metadata Service [<a |
+ +21 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#biblio-fidometadataservice">FIDOMetadataService</a>]. |
+ +22 + | ++ + + + + + | + * |
+ +23 + | ++ + + + + + | + * <p>This class does not include the field <code>ecdaaTrustAnchors</code> since ECDAA is deprecated |
+ +24 + | ++ + + + + + | + * in WebAuthn Level 2. |
+ +25 + | ++ + + + + + | + * |
+ +26 + | ++ + + + + + | + * @see <a |
+ +27 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +28 + | ++ + + + + + | + * Metadata Statement</a> |
+ +29 + | ++ + + + + + | + */ |
+ +30 + | ++ + + + + + | +@Value |
+ +31 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +32 + | ++ + + + + + | +@Jacksonized |
+ +33 + | ++ + + + + + | +public class MetadataStatement { |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + /** |
+ +36 + | ++ + + + + + | + * @see <a |
+ +37 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +38 + | ++ + + + + + | + * Metadata Statement</a> |
+ +39 + | ++ + + + + + | + */ |
+ +40 + | ++ + + + + + | + String legalHeader; |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | + /** |
+ +43 + | ++ + + + + + | + * @see <a |
+ +44 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +45 + | ++ + + + + + | + * Metadata Statement</a> |
+ +46 + | ++ + + + + + | + */ |
+ +47 + | ++ + + + + + | + AAID aaid; |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** |
+ +50 + | ++ + + + + + | + * @see <a |
+ +51 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +52 + | ++ + + + + + | + * Metadata Statement</a> |
+ +53 + | ++ + + + + + | + */ |
+ +54 + | ++ + + + + + | + AAGUID aaguid; |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** |
+ +57 + | ++ + + + + + | + * @see <a |
+ +58 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +59 + | ++ + + + + + | + * Metadata Statement</a> |
+ +60 + | ++ + + + + + | + */ |
+ +61 + | ++ + + + + + | + Set<String> attestationCertificateKeyIdentifiers; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + /** |
+ +64 + | ++ + + + + + | + * @see <a |
+ +65 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +66 + | ++ + + + + + | + * Metadata Statement</a> |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + String description; |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * @see <a |
+ +72 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +73 + | ++ + + + + + | + * Metadata Statement</a> |
+ +74 + | ++ + + + + + | + */ |
+ +75 + | ++ + + + + + | + AlternativeDescriptions alternativeDescriptions; |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * @see <a |
+ +79 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +80 + | ++ + + + + + | + * Metadata Statement</a> |
+ +81 + | ++ + + + + + | + */ |
+ +82 + | ++ + + + + + | + long authenticatorVersion; |
+ +83 + | ++ + + + + + | +|
+ +84 + | ++ + + + + + | + /** |
+ +85 + | ++ + + + + + | + * @see <a |
+ +86 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +87 + | ++ + + + + + | + * Metadata Statement</a> |
+ +88 + | ++ + + + + + | + */ |
+ +89 + | ++ + + + + + | + @NonNull ProtocolFamily protocolFamily; |
+ +90 + | ++ + + + + + | +|
+ +91 + | ++ + + + + + | + /** |
+ +92 + | ++ + + + + + | + * @see <a |
+ +93 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +94 + | ++ + + + + + | + * Metadata Statement</a> |
+ +95 + | ++ + + + + + | + */ |
+ +96 + | ++ + + + + + | + int schema; |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + /** |
+ +99 + | ++ + + + + + | + * @see <a |
+ +100 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +101 + | ++ + + + + + | + * Metadata Statement</a> |
+ +102 + | ++ + + + + + | + */ |
+ +103 + | ++ + + + + + | + @NonNull Set<Version> upv; |
+ +104 + | ++ + + + + + | +|
+ +105 + | ++ + + + + + | + /** |
+ +106 + | ++ + + + + + | + * @see <a |
+ +107 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +108 + | ++ + + + + + | + * Metadata Statement</a> |
+ +109 + | ++ + + + + + | + */ |
+ +110 + | ++ + + + + + | + @NonNull Set<AuthenticationAlgorithm> authenticationAlgorithms; |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + /** |
+ +113 + | ++ + + + + + | + * @see <a |
+ +114 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +115 + | ++ + + + + + | + * Metadata Statement</a> |
+ +116 + | ++ + + + + + | + */ |
+ +117 + | ++ + + + + + | + @NonNull Set<PublicKeyRepresentationFormat> publicKeyAlgAndEncodings; |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * @see <a |
+ +121 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +122 + | ++ + + + + + | + * Metadata Statement</a> |
+ +123 + | ++ + + + + + | + */ |
+ +124 + | ++ + + + + + | + @NonNull Set<AuthenticatorAttestationType> attestationTypes; |
+ +125 + | ++ + + + + + | +|
+ +126 + | ++ + + + + + | + /** |
+ +127 + | ++ + + + + + | + * @see <a |
+ +128 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +129 + | ++ + + + + + | + * Metadata Statement</a> |
+ +130 + | ++ + + + + + | + */ |
+ +131 + | ++ + + + + + | + @NonNull Set<Set<VerificationMethodDescriptor>> userVerificationDetails; |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + /** |
+ +134 + | ++ + + + + + | + * @see <a |
+ +135 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +136 + | ++ + + + + + | + * Metadata Statement</a> |
+ +137 + | ++ + + + + + | + */ |
+ +138 + | ++ + + + + + | + @NonNull Set<KeyProtectionType> keyProtection; |
+ +139 + | ++ + + + + + | +|
+ +140 + | ++ + + + + + | + /** |
+ +141 + | ++ + + + + + | + * @see <a |
+ +142 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +143 + | ++ + + + + + | + * Metadata Statement</a> |
+ +144 + | ++ + + + + + | + */ |
+ +145 + | ++ + + + + + | + Boolean isKeyRestricted; |
+ +146 + | ++ + + + + + | +|
+ +147 + | ++ + + + + + | + /** |
+ +148 + | ++ + + + + + | + * @see <a |
+ +149 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +150 + | ++ + + + + + | + * Metadata Statement</a> |
+ +151 + | ++ + + + + + | + */ |
+ +152 + | ++ + + + + + | + Boolean isFreshUserVerificationRequired; |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + /** |
+ +155 + | ++ + + + + + | + * @see <a |
+ +156 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +157 + | ++ + + + + + | + * Metadata Statement</a> |
+ +158 + | ++ + + + + + | + */ |
+ +159 + | ++ + + + + + | + @NonNull Set<MatcherProtectionType> matcherProtection; |
+ +160 + | ++ + + + + + | +|
+ +161 + | ++ + + + + + | + /** |
+ +162 + | ++ + + + + + | + * @see <a |
+ +163 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +164 + | ++ + + + + + | + * Metadata Statement</a> |
+ +165 + | ++ + + + + + | + */ |
+ +166 + | ++ + + + + + | + Integer cryptoStrength; |
+ +167 + | ++ + + + + + | +|
+ +168 + | ++ + + + + + | + /** |
+ +169 + | ++ + + + + + | + * @see <a |
+ +170 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +171 + | ++ + + + + + | + * Metadata Statement</a> |
+ +172 + | ++ + + + + + | + */ |
+ +173 + | ++ + + + + + | + Set<AttachmentHint> attachmentHint; |
+ +174 + | ++ + + + + + | +|
+ +175 + | ++ + + + + + | + /** |
+ +176 + | ++ + + + + + | + * @see <a |
+ +177 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +178 + | ++ + + + + + | + * Metadata Statement</a> |
+ +179 + | ++ + + + + + | + */ |
+ +180 + | ++ + + + + + | + @NonNull Set<TransactionConfirmationDisplayType> tcDisplay; |
+ +181 + | ++ + + + + + | +|
+ +182 + | ++ + + + + + | + /** |
+ +183 + | ++ + + + + + | + * @see <a |
+ +184 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +185 + | ++ + + + + + | + * Metadata Statement</a> |
+ +186 + | ++ + + + + + | + */ |
+ +187 + | ++ + + + + + | + String tcDisplayContentType; |
+ +188 + | ++ + + + + + | +|
+ +189 + | ++ + + + + + | + /** |
+ +190 + | ++ + + + + + | + * @see <a |
+ +191 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +192 + | ++ + + + + + | + * Metadata Statement</a> |
+ +193 + | ++ + + + + + | + */ |
+ +194 + | ++ + + + + + | + List<DisplayPNGCharacteristicsDescriptor> tcDisplayPNGCharacteristics; |
+ +195 + | ++ + + + + + | +|
+ +196 + | ++ + + + + + | + /** |
+ +197 + | ++ + + + + + | + * @see <a |
+ +198 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +199 + | ++ + + + + + | + * Metadata Statement</a> |
+ +200 + | ++ + + + + + | + */ |
+ +201 + | ++ + + + + + | + @NonNull |
+ +202 + | ++ + + + + + | + @JsonDeserialize(contentConverter = CertFromBase64Converter.class) |
+ +203 + | ++ + + + + + | + @JsonSerialize(contentConverter = CertToBase64Converter.class) |
+ +204 + | ++ + + + + + | + Set<X509Certificate> attestationRootCertificates; |
+ +205 + | ++ + + + + + | +|
+ +206 + | ++ + + + + + | + /** |
+ +207 + | ++ + + + + + | + * @see <a |
+ +208 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +209 + | ++ + + + + + | + * Metadata Statement</a> |
+ +210 + | ++ + + + + + | + */ |
+ +211 + | ++ + + + + + | + String icon; |
+ +212 + | ++ + + + + + | +|
+ +213 + | ++ + + + + + | + /** |
+ +214 + | ++ + + + + + | + * @see <a |
+ +215 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +216 + | ++ + + + + + | + * Metadata Statement</a> |
+ +217 + | ++ + + + + + | + */ |
+ +218 + | ++ + + + + + | + Set<ExtensionDescriptor> supportedExtensions; |
+ +219 + | ++ + + + + + | +|
+ +220 + | ++ + + + + + | + /** |
+ +221 + | ++ + + + + + | + * @see <a |
+ +222 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +223 + | ++ + + + + + | + * Metadata Statement</a> |
+ +224 + | ++ + + + + + | + */ |
+ +225 + | ++ + + + + + | + AuthenticatorGetInfo authenticatorGetInfo; |
+ +226 + | ++ + + + + + | +|
+ +227 + | ++ + + + + + | + public MetadataStatement( |
+ +228 + | ++ + + + + + | + String legalHeader, |
+ +229 + | ++ + + + + + | + AAID aaid, |
+ +230 + | ++ + + + + + | + AAGUID aaguid, |
+ +231 + | ++ + + + + + | + Set<String> attestationCertificateKeyIdentifiers, |
+ +232 + | ++ + + + + + | + String description, |
+ +233 + | ++ + + + + + | + AlternativeDescriptions alternativeDescriptions, |
+ +234 + | ++ + + + + + | + long authenticatorVersion, |
+ +235 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull ProtocolFamily protocolFamily, |
+ +236 + | ++ + + + + + | + int schema, |
+ +237 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<Version> upv, |
+ +238 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<AuthenticationAlgorithm> authenticationAlgorithms, |
+ +239 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<PublicKeyRepresentationFormat> publicKeyAlgAndEncodings, |
+ +240 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<AuthenticatorAttestationType> attestationTypes, |
+ +241 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<Set<VerificationMethodDescriptor>> userVerificationDetails, |
+ +242 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<KeyProtectionType> keyProtection, |
+ +243 + | ++ + + + + + | + Boolean isKeyRestricted, |
+ +244 + | ++ + + + + + | + Boolean isFreshUserVerificationRequired, |
+ +245 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<MatcherProtectionType> matcherProtection, |
+ +246 + | ++ + + + + + | + Integer cryptoStrength, |
+ +247 + | ++ + + + + + | + Set<AttachmentHint> attachmentHint, |
+ +248 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<TransactionConfirmationDisplayType> tcDisplay, |
+ +249 + | ++ + + + + + | + String tcDisplayContentType, |
+ +250 + | ++ + + + + + | + List<DisplayPNGCharacteristicsDescriptor> tcDisplayPNGCharacteristics, |
+ +251 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Set<X509Certificate> attestationRootCertificates, |
+ +252 + | ++ + + + + + | + String icon, |
+ +253 + | ++ + + + + + | + Set<ExtensionDescriptor> supportedExtensions, |
+ +254 + | ++ + + + + + | + AuthenticatorGetInfo authenticatorGetInfo) { |
+ +255 + | ++ + + + + + | + this.legalHeader = legalHeader; |
+ +256 + | ++ + + + + + | + this.aaid = aaid; |
+ +257 + | ++ + + + + + | + this.aaguid = aaguid; |
+ +258 + | ++ + + + + + | + this.attestationCertificateKeyIdentifiers = |
+ +259 + | ++ + + + + + | + CollectionUtil.immutableSetOrEmpty(attestationCertificateKeyIdentifiers); |
+ +260 + | ++ + + + + + | + this.description = description; |
+ +261 + | ++ + + + + + | + this.alternativeDescriptions = alternativeDescriptions; |
+ +262 + | ++ + + + + + | + this.authenticatorVersion = authenticatorVersion; |
+ +263 + | ++ + + + + + | + this.protocolFamily = protocolFamily; |
+ +264 + | ++ + + + + + | + this.schema = schema; |
+ +265 + | ++ + + + + + | + this.upv = upv; |
+ +266 + | ++ + + + + + | + this.authenticationAlgorithms = authenticationAlgorithms; |
+ +267 + | ++ + + + + + | + this.publicKeyAlgAndEncodings = publicKeyAlgAndEncodings; |
+ +268 + | ++ + + + + + | + this.attestationTypes = attestationTypes; |
+ +269 + | ++ + + + + + | + this.userVerificationDetails = userVerificationDetails; |
+ +270 + | ++ + + + + + | + this.keyProtection = keyProtection; |
+ +271 + | ++ + + + + + | + this.isKeyRestricted = isKeyRestricted; |
+ +272 + | ++ + + + + + | + this.isFreshUserVerificationRequired = isFreshUserVerificationRequired; |
+ +273 + | ++ + + + + + | + this.matcherProtection = matcherProtection; |
+ +274 + | ++ + + + + + | + this.cryptoStrength = cryptoStrength; |
+ +275 + | ++ + + + + + | + this.attachmentHint = attachmentHint; |
+ +276 + | ++ + + + + + | + this.tcDisplay = tcDisplay; |
+ +277 + | ++ + + + + + | + this.tcDisplayContentType = tcDisplayContentType; |
+ +278 + | ++ + + + + + | + this.tcDisplayPNGCharacteristics = tcDisplayPNGCharacteristics; |
+ +279 + | ++ + + + + + | + this.attestationRootCertificates = attestationRootCertificates; |
+ +280 + | ++ + + + + + | + this.icon = icon; |
+ +281 + | ++ + + + + + | + this.supportedExtensions = supportedExtensions; |
+ +282 + | ++ + + + + + | + this.authenticatorGetInfo = authenticatorGetInfo; |
+ +283 + | ++ + + + + + | + } |
+ +284 + | ++ + + + + + | +|
+ +285 + | ++ + + + + + | + /** |
+ +286 + | ++ + + + + + | + * @see <a |
+ +287 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +288 + | ++ + + + + + | + * Metadata Statement</a> |
+ +289 + | ++ + + + + + | + */ |
+ +290 + | ++ + + + + + | + public Optional<String> getLegalHeader() { |
+ +291 + | +
+
+1
+
+1. getLegalHeader : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getLegalHeader → SURVIVED + + + + |
+ return Optional.ofNullable(this.legalHeader); |
+ +292 + | ++ + + + + + | + } |
+ +293 + | ++ + + + + + | +|
+ +294 + | ++ + + + + + | + /** |
+ +295 + | ++ + + + + + | + * @see <a |
+ +296 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +297 + | ++ + + + + + | + * Metadata Statement</a> |
+ +298 + | ++ + + + + + | + */ |
+ +299 + | ++ + + + + + | + public Optional<AAID> getAaid() { |
+ +300 + | +
+
+1
+
+1. getAaid : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getAaid → SURVIVED + + + + |
+ return Optional.ofNullable(this.aaid); |
+ +301 + | ++ + + + + + | + } |
+ +302 + | ++ + + + + + | +|
+ +303 + | ++ + + + + + | + /** |
+ +304 + | ++ + + + + + | + * @see <a |
+ +305 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +306 + | ++ + + + + + | + * Metadata Statement</a> |
+ +307 + | ++ + + + + + | + */ |
+ +308 + | ++ + + + + + | + public Optional<AAGUID> getAaguid() { |
+ +309 + | +
+
+1
+
+1. getAaguid : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getAaguid → SURVIVED + + + + |
+ return Optional.ofNullable(this.aaguid); |
+ +310 + | ++ + + + + + | + } |
+ +311 + | ++ + + + + + | +|
+ +312 + | ++ + + + + + | + /** |
+ +313 + | ++ + + + + + | + * @see <a |
+ +314 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +315 + | ++ + + + + + | + * Metadata Statement</a> |
+ +316 + | ++ + + + + + | + */ |
+ +317 + | ++ + + + + + | + public Optional<String> getDescription() { |
+ +318 + | +
+
+1
+
+1. getDescription : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getDescription → SURVIVED + + + + |
+ return Optional.ofNullable(this.description); |
+ +319 + | ++ + + + + + | + } |
+ +320 + | ++ + + + + + | +|
+ +321 + | ++ + + + + + | + /** |
+ +322 + | ++ + + + + + | + * @see <a |
+ +323 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +324 + | ++ + + + + + | + * Metadata Statement</a> |
+ +325 + | ++ + + + + + | + */ |
+ +326 + | ++ + + + + + | + public Optional<AlternativeDescriptions> getAlternativeDescriptions() { |
+ +327 + | +
+
+1
+
+1. getAlternativeDescriptions : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getAlternativeDescriptions → SURVIVED + + + + |
+ return Optional.ofNullable(this.alternativeDescriptions); |
+ +328 + | ++ + + + + + | + } |
+ +329 + | ++ + + + + + | +|
+ +330 + | ++ + + + + + | + /** |
+ +331 + | ++ + + + + + | + * @see <a |
+ +332 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +333 + | ++ + + + + + | + * Metadata Statement</a> |
+ +334 + | ++ + + + + + | + */ |
+ +335 + | ++ + + + + + | + public Optional<Boolean> getIsKeyRestricted() { |
+ +336 + | +
+
+1
+
+1. getIsKeyRestricted : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getIsKeyRestricted → SURVIVED + + + + |
+ return Optional.ofNullable(this.isKeyRestricted); |
+ +337 + | ++ + + + + + | + } |
+ +338 + | ++ + + + + + | +|
+ +339 + | ++ + + + + + | + /** |
+ +340 + | ++ + + + + + | + * @see <a |
+ +341 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +342 + | ++ + + + + + | + * Metadata Statement</a> |
+ +343 + | ++ + + + + + | + */ |
+ +344 + | ++ + + + + + | + public Optional<Boolean> getIsFreshUserVerificationRequired() { |
+ +345 + | +
+
+1
+
+1. getIsFreshUserVerificationRequired : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getIsFreshUserVerificationRequired → SURVIVED + + + + |
+ return Optional.ofNullable(this.isFreshUserVerificationRequired); |
+ +346 + | ++ + + + + + | + } |
+ +347 + | ++ + + + + + | +|
+ +348 + | ++ + + + + + | + /** |
+ +349 + | ++ + + + + + | + * @see <a |
+ +350 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +351 + | ++ + + + + + | + * Metadata Statement</a> |
+ +352 + | ++ + + + + + | + */ |
+ +353 + | ++ + + + + + | + public Optional<Integer> getCryptoStrength() { |
+ +354 + | +
+
+1
+
+1. getCryptoStrength : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getCryptoStrength → SURVIVED + + + + |
+ return Optional.ofNullable(this.cryptoStrength); |
+ +355 + | ++ + + + + + | + } |
+ +356 + | ++ + + + + + | +|
+ +357 + | ++ + + + + + | + /** |
+ +358 + | ++ + + + + + | + * @see <a |
+ +359 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +360 + | ++ + + + + + | + * Metadata Statement</a> |
+ +361 + | ++ + + + + + | + */ |
+ +362 + | ++ + + + + + | + public Optional<Set<AttachmentHint>> getAttachmentHint() { |
+ +363 + | +
+
+1
+
+1. getAttachmentHint : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getAttachmentHint → SURVIVED + + + + |
+ return Optional.ofNullable(this.attachmentHint); |
+ +364 + | ++ + + + + + | + } |
+ +365 + | ++ + + + + + | +|
+ +366 + | ++ + + + + + | + /** |
+ +367 + | ++ + + + + + | + * @see <a |
+ +368 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +369 + | ++ + + + + + | + * Metadata Statement</a> |
+ +370 + | ++ + + + + + | + */ |
+ +371 + | ++ + + + + + | + public Optional<String> getTcDisplayContentType() { |
+ +372 + | +
+
+1
+
+1. getTcDisplayContentType : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getTcDisplayContentType → SURVIVED + + + + |
+ return Optional.ofNullable(this.tcDisplayContentType); |
+ +373 + | ++ + + + + + | + } |
+ +374 + | ++ + + + + + | +|
+ +375 + | ++ + + + + + | + /** |
+ +376 + | ++ + + + + + | + * @see <a |
+ +377 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +378 + | ++ + + + + + | + * Metadata Statement</a> |
+ +379 + | ++ + + + + + | + */ |
+ +380 + | ++ + + + + + | + public Optional<List<DisplayPNGCharacteristicsDescriptor>> getTcDisplayPNGCharacteristics() { |
+ +381 + | +
+
+1
+
+1. getTcDisplayPNGCharacteristics : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getTcDisplayPNGCharacteristics → SURVIVED + + + + |
+ return Optional.ofNullable(this.tcDisplayPNGCharacteristics); |
+ +382 + | ++ + + + + + | + } |
+ +383 + | ++ + + + + + | +|
+ +384 + | ++ + + + + + | + /** |
+ +385 + | ++ + + + + + | + * @see <a |
+ +386 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +387 + | ++ + + + + + | + * Metadata Statement</a> |
+ +388 + | ++ + + + + + | + */ |
+ +389 + | ++ + + + + + | + public Optional<String> getIcon() { |
+ +390 + | +
+
+1
+
+1. getIcon : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getIcon → SURVIVED + + + + |
+ return Optional.ofNullable(this.icon); |
+ +391 + | ++ + + + + + | + } |
+ +392 + | ++ + + + + + | +|
+ +393 + | ++ + + + + + | + /** |
+ +394 + | ++ + + + + + | + * @see <a |
+ +395 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +396 + | ++ + + + + + | + * Metadata Statement</a> |
+ +397 + | ++ + + + + + | + */ |
+ +398 + | ++ + + + + + | + public Optional<Set<ExtensionDescriptor>> getSupportedExtensions() { |
+ +399 + | +
+
+1
+
+1. getSupportedExtensions : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getSupportedExtensions → SURVIVED + + + + |
+ return Optional.ofNullable(this.supportedExtensions); |
+ +400 + | ++ + + + + + | + } |
+ +401 + | ++ + + + + + | +|
+ +402 + | ++ + + + + + | + /** |
+ +403 + | ++ + + + + + | + * @see <a |
+ +404 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#metadata-statement-format">FIDO |
+ +405 + | ++ + + + + + | + * Metadata Statement</a> |
+ +406 + | ++ + + + + + | + */ |
+ +407 + | ++ + + + + + | + public Optional<AuthenticatorGetInfo> getAuthenticatorGetInfo() { |
+ +408 + | +
+
+1
+
+1. getAuthenticatorGetInfo : replaced return value with Optional.empty for com/yubico/fido/metadata/MetadataStatement::getAuthenticatorGetInfo → SURVIVED + + + + |
+ return Optional.ofNullable(this.authenticatorGetInfo); |
+ +409 + | ++ + + + + + | + } |
+ +410 + | ++ + + + + + | +} |
Mutations | ||
235 | ++ |
+
+
+
+ 1.1 |
+
237 | ++ |
+
+
+
+ 1.1 |
+
238 | ++ |
+
+
+
+ 1.1 |
+
239 | ++ |
+
+
+
+ 1.1 |
+
240 | ++ |
+
+
+
+ 1.1 |
+
241 | ++ |
+
+
+
+ 1.1 |
+
242 | ++ |
+
+
+
+ 1.1 |
+
245 | ++ |
+
+
+
+ 1.1 |
+
248 | ++ |
+
+
+
+ 1.1 |
+
251 | ++ |
+
+
+
+ 1.1 |
+
291 | ++ |
+
+
+
+ 1.1 |
+
300 | ++ |
+
+
+
+ 1.1 |
+
309 | ++ |
+
+
+
+ 1.1 |
+
318 | ++ |
+
+
+
+ 1.1 |
+
327 | ++ |
+
+
+
+ 1.1 |
+
336 | ++ |
+
+
+
+ 1.1 |
+
345 | ++ |
+
+
+
+ 1.1 |
+
354 | ++ |
+
+
+
+ 1.1 |
+
363 | ++ |
+
+
+
+ 1.1 |
+
372 | ++ |
+
+
+
+ 1.1 |
+
381 | ++ |
+
+
+
+ 1.1 |
+
390 | ++ |
+
+
+
+ 1.1 |
+
399 | ++ |
+
+
+
+ 1.1 |
+
408 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.util.Optional; |
+ +4 + | ++ + + + + + | +import lombok.Builder; |
+ +5 + | ++ + + + + + | +import lombok.Value; |
+ +6 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | +/** |
+ +9 + | ++ + + + + + | + * The {@link PatternAccuracyDescriptor} describes relevant accuracy/complexity aspects in the case |
+ +10 + | ++ + + + + + | + * that a pattern is used as the user verification method. |
+ +11 + | ++ + + + + + | + * |
+ +12 + | ++ + + + + + | + * @see <a |
+ +13 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary">FIDO |
+ +14 + | ++ + + + + + | + * Metadata Statement §3.4. PatternAccuracyDescriptor dictionary</a> |
+ +15 + | ++ + + + + + | + */ |
+ +16 + | ++ + + + + + | +@Value |
+ +17 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +18 + | ++ + + + + + | +@Jacksonized |
+ +19 + | ++ + + + + + | +public class PatternAccuracyDescriptor { |
+ +20 + | ++ + + + + + | +|
+ +21 + | ++ + + + + + | + /** |
+ +22 + | ++ + + + + + | + * @see <a |
+ +23 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary">FIDO |
+ +24 + | ++ + + + + + | + * Metadata Statement §3.4. PatternAccuracyDescriptor dictionary</a> |
+ +25 + | ++ + + + + + | + */ |
+ +26 + | ++ + + + + + | + long minComplexity; |
+ +27 + | ++ + + + + + | +|
+ +28 + | ++ + + + + + | + /** |
+ +29 + | ++ + + + + + | + * @see <a |
+ +30 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary">FIDO |
+ +31 + | ++ + + + + + | + * Metadata Statement §3.4. PatternAccuracyDescriptor dictionary</a> |
+ +32 + | ++ + + + + + | + */ |
+ +33 + | ++ + + + + + | + Integer maxRetries; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + /** |
+ +36 + | ++ + + + + + | + * @see <a |
+ +37 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary">FIDO |
+ +38 + | ++ + + + + + | + * Metadata Statement §3.4. PatternAccuracyDescriptor dictionary</a> |
+ +39 + | ++ + + + + + | + */ |
+ +40 + | ++ + + + + + | + Integer blockSlowdown; |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | + /** |
+ +43 + | ++ + + + + + | + * @see <a |
+ +44 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary">FIDO |
+ +45 + | ++ + + + + + | + * Metadata Statement §3.4. PatternAccuracyDescriptor dictionary</a> |
+ +46 + | ++ + + + + + | + */ |
+ +47 + | ++ + + + + + | + public Optional<Integer> getMaxRetries() { |
+ +48 + | +
+
+1
+
+1. getMaxRetries : replaced return value with Optional.empty for com/yubico/fido/metadata/PatternAccuracyDescriptor::getMaxRetries → SURVIVED + + + + |
+ return Optional.ofNullable(maxRetries); |
+ +49 + | ++ + + + + + | + } |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + /** |
+ +52 + | ++ + + + + + | + * @see <a |
+ +53 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#patternaccuracydescriptor-dictionary">FIDO |
+ +54 + | ++ + + + + + | + * Metadata Statement §3.4. PatternAccuracyDescriptor dictionary</a> |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | + public Optional<Integer> getBlockSlowdown() { |
+ +57 + | +
+
+1
+
+1. getBlockSlowdown : replaced return value with Optional.empty for com/yubico/fido/metadata/PatternAccuracyDescriptor::getBlockSlowdown → SURVIVED + + + + |
+ return Optional.ofNullable(blockSlowdown); |
+ +58 + | ++ + + + + + | + } |
+ +59 + | ++ + + + + + | +} |
Mutations | ||
48 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnore; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
+ +6 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +7 + | ++ + + + + + | +import java.net.MalformedURLException; |
+ +8 + | ++ + + + + + | +import java.net.URL; |
+ +9 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +10 + | ++ + + + + + | +import java.time.LocalDate; |
+ +11 + | ++ + + + + + | +import java.util.Optional; |
+ +12 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +13 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +14 + | ++ + + + + + | +import lombok.Builder; |
+ +15 + | ++ + + + + + | +import lombok.Getter; |
+ +16 + | ++ + + + + + | +import lombok.NonNull; |
+ +17 + | ++ + + + + + | +import lombok.Value; |
+ +18 + | ++ + + + + + | +import lombok.extern.jackson.Jacksonized; |
+ +19 + | ++ + + + + + | +|
+ +20 + | ++ + + + + + | +/** |
+ +21 + | ++ + + + + + | + * Contains an {@link AuthenticatorStatus} and additional data associated with it, if any. |
+ +22 + | ++ + + + + + | + * |
+ +23 + | ++ + + + + + | + * @see <a |
+ +24 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +25 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +26 + | ++ + + + + + | + */ |
+ +27 + | ++ + + + + + | +@Value |
+ +28 + | ++ + + + + + | +@Builder |
+ +29 + | ++ + + + + + | +@Jacksonized |
+ +30 + | ++ + + + + + | +@AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +31 + | ++ + + + + + | +public class StatusReport { |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | + /** |
+ +34 + | ++ + + + + + | + * @see <a |
+ +35 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +36 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +37 + | ++ + + + + + | + */ |
+ +38 + | ++ + + + + + | + @NonNull AuthenticatorStatus status; |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | + /** |
+ +41 + | ++ + + + + + | + * @see <a |
+ +42 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +43 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | + LocalDate effectiveDate; |
+ +46 + | ++ + + + + + | +|
+ +47 + | ++ + + + + + | + /** |
+ +48 + | ++ + + + + + | + * @see <a |
+ +49 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +50 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | + Long authenticatorVersion; |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + /** |
+ +55 + | ++ + + + + + | + * @see <a |
+ +56 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +57 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +58 + | ++ + + + + + | + */ |
+ +59 + | ++ + + + + + | + @JsonDeserialize(converter = CertFromBase64Converter.class) |
+ +60 + | ++ + + + + + | + @JsonSerialize(converter = CertToBase64Converter.class) |
+ +61 + | ++ + + + + + | + X509Certificate certificate; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + /** |
+ +64 + | ++ + + + + + | + * @see <a |
+ +65 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +66 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + @JsonProperty("url") |
+ +69 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +70 + | ++ + + + + + | + String url; |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + /** |
+ +73 + | ++ + + + + + | + * @see <a |
+ +74 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +75 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + String certificationDescriptor; |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + /** |
+ +80 + | ++ + + + + + | + * @see <a |
+ +81 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +82 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +83 + | ++ + + + + + | + */ |
+ +84 + | ++ + + + + + | + String certificateNumber; |
+ +85 + | ++ + + + + + | +|
+ +86 + | ++ + + + + + | + /** |
+ +87 + | ++ + + + + + | + * @see <a |
+ +88 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +89 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +90 + | ++ + + + + + | + */ |
+ +91 + | ++ + + + + + | + String certificationPolicyVersion; |
+ +92 + | ++ + + + + + | +|
+ +93 + | ++ + + + + + | + /** |
+ +94 + | ++ + + + + + | + * @see <a |
+ +95 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +96 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +97 + | ++ + + + + + | + */ |
+ +98 + | ++ + + + + + | + String certificationRequirementsVersion; |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + /** |
+ +101 + | ++ + + + + + | + * @see <a |
+ +102 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +103 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +104 + | ++ + + + + + | + */ |
+ +105 + | ++ + + + + + | + public Optional<LocalDate> getEffectiveDate() { |
+ +106 + | +
+
+1
+
+1. getEffectiveDate : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getEffectiveDate → SURVIVED + + + + |
+ return Optional.ofNullable(effectiveDate); |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | +|
+ +109 + | ++ + + + + + | + /** |
+ +110 + | ++ + + + + + | + * @see <a |
+ +111 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +112 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +113 + | ++ + + + + + | + */ |
+ +114 + | ++ + + + + + | + public Optional<Long> getAuthenticatorVersion() { |
+ +115 + | +
+
+1
+
+1. getAuthenticatorVersion : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getAuthenticatorVersion → KILLED + + + + |
+ return Optional.ofNullable(authenticatorVersion); |
+ +116 + | ++ + + + + + | + } |
+ +117 + | ++ + + + + + | +|
+ +118 + | ++ + + + + + | + /** |
+ +119 + | ++ + + + + + | + * @see <a |
+ +120 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +121 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +122 + | ++ + + + + + | + */ |
+ +123 + | ++ + + + + + | + @JsonIgnore |
+ +124 + | ++ + + + + + | + public Optional<X509Certificate> getCertificate() { |
+ +125 + | +
+
+1
+
+1. getCertificate : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getCertificate → SURVIVED + + + + |
+ return Optional.ofNullable(this.certificate); |
+ +126 + | ++ + + + + + | + } |
+ +127 + | ++ + + + + + | +|
+ +128 + | ++ + + + + + | + /** |
+ +129 + | ++ + + + + + | + * Attempt to parse the {@link #getUrlAsString() url} property, if any, as a {@link URL}. |
+ +130 + | ++ + + + + + | + * |
+ +131 + | ++ + + + + + | + * @return A present value if and only if {@link #getUrlAsString()} is present and a valid URL. |
+ +132 + | ++ + + + + + | + */ |
+ +133 + | ++ + + + + + | + public Optional<URL> getUrl() { |
+ +134 + | ++ + + + + + | + try { |
+ +135 + | +
+
+1
+
+1. getUrl : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getUrl → NO_COVERAGE + + + + |
+ return Optional.of(new URL(url)); |
+ +136 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +137 + | ++ + + + + + | + return Optional.empty(); |
+ +138 + | ++ + + + + + | + } |
+ +139 + | ++ + + + + + | + } |
+ +140 + | ++ + + + + + | +|
+ +141 + | ++ + + + + + | + /** |
+ +142 + | ++ + + + + + | + * Get the raw <code>url</code> property of this {@link StatusReport} object. This may or may not |
+ +143 + | ++ + + + + + | + * be a valid URL. |
+ +144 + | ++ + + + + + | + * |
+ +145 + | ++ + + + + + | + * @see <a |
+ +146 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +147 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +148 + | ++ + + + + + | + */ |
+ +149 + | ++ + + + + + | + @JsonIgnore |
+ +150 + | ++ + + + + + | + public Optional<String> getUrlAsString() { |
+ +151 + | +
+
+1
+
+1. getUrlAsString : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getUrlAsString → NO_COVERAGE + + + + |
+ return Optional.ofNullable(this.url); |
+ +152 + | ++ + + + + + | + } |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + /** |
+ +155 + | ++ + + + + + | + * @see <a |
+ +156 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +157 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +158 + | ++ + + + + + | + */ |
+ +159 + | ++ + + + + + | + public Optional<String> getCertificationDescriptor() { |
+ +160 + | +
+
+1
+
+1. getCertificationDescriptor : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getCertificationDescriptor → SURVIVED + + + + |
+ return Optional.ofNullable(this.certificationDescriptor); |
+ +161 + | ++ + + + + + | + } |
+ +162 + | ++ + + + + + | +|
+ +163 + | ++ + + + + + | + /** |
+ +164 + | ++ + + + + + | + * @see <a |
+ +165 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +166 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +167 + | ++ + + + + + | + */ |
+ +168 + | ++ + + + + + | + public Optional<String> getCertificateNumber() { |
+ +169 + | +
+
+1
+
+1. getCertificateNumber : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getCertificateNumber → SURVIVED + + + + |
+ return Optional.ofNullable(this.certificateNumber); |
+ +170 + | ++ + + + + + | + } |
+ +171 + | ++ + + + + + | +|
+ +172 + | ++ + + + + + | + /** |
+ +173 + | ++ + + + + + | + * @see <a |
+ +174 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +175 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +176 + | ++ + + + + + | + */ |
+ +177 + | ++ + + + + + | + public Optional<String> getCertificationPolicyVersion() { |
+ +178 + | +
+
+1
+
+1. getCertificationPolicyVersion : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getCertificationPolicyVersion → SURVIVED + + + + |
+ return Optional.ofNullable(this.certificationPolicyVersion); |
+ +179 + | ++ + + + + + | + } |
+ +180 + | ++ + + + + + | +|
+ +181 + | ++ + + + + + | + /** |
+ +182 + | ++ + + + + + | + * @see <a |
+ +183 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#statusreport-dictionary">FIDO |
+ +184 + | ++ + + + + + | + * Metadata Service §3.1.3. StatusReport dictionary</a> |
+ +185 + | ++ + + + + + | + */ |
+ +186 + | ++ + + + + + | + public Optional<String> getCertificationRequirementsVersion() { |
+ +187 + | +
+
+1
+
+1. getCertificationRequirementsVersion : replaced return value with Optional.empty for com/yubico/fido/metadata/StatusReport::getCertificationRequirementsVersion → SURVIVED + + + + |
+ return Optional.ofNullable(this.certificationRequirementsVersion); |
+ +188 + | ++ + + + + + | + } |
+ +189 + | ++ + + + + + | +} |
Mutations | ||
106 | ++ |
+
+
+
+ 1.1 |
+
115 | ++ |
+
+
+
+ 1.1 |
+
125 | ++ |
+
+
+
+ 1.1 |
+
135 | ++ |
+
+
+
+ 1.1 |
+
151 | ++ |
+
+
+
+ 1.1 |
+
160 | ++ |
+
+
+
+ 1.1 |
+
169 | ++ |
+
+
+
+ 1.1 |
+
178 | ++ |
+
+
+
+ 1.1 |
+
187 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.fido.metadata; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.util.Optional; |
+ +4 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +5 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +6 + | ++ + + + + + | +import lombok.Getter; |
+ +7 + | ++ + + + + + | +import lombok.NonNull; |
+ +8 + | ++ + + + + + | +|
+ +9 + | ++ + + + + + | +/** |
+ +10 + | ++ + + + + + | + * A FIDO Metadata Service metadata BLOB was successfully downloaded and validated, but contained an |
+ +11 + | ++ + + + + + | + * unexpected legal header. |
+ +12 + | ++ + + + + + | + * |
+ +13 + | ++ + + + + + | + * <p>This exception contains the offending downloaded metadata BLOB as well as the cached metadata |
+ +14 + | ++ + + + + + | + * BLOB, if any (see {@link #getCachedBlob()}). This enables applications to gracefully fall back to |
+ +15 + | ++ + + + + + | + * the cached blob when possible, while notifying maintainers that action is required for the new |
+ +16 + | ++ + + + + + | + * legal header. |
+ +17 + | ++ + + + + + | + */ |
+ +18 + | ++ + + + + + | +@AllArgsConstructor(access = AccessLevel.PACKAGE) |
+ +19 + | ++ + + + + + | +public class UnexpectedLegalHeader extends Exception { |
+ +20 + | ++ + + + + + | +|
+ +21 + | ++ + + + + + | + /** The cached metadata BLOB, if any, which is assumed to have an expected legal header. */ |
+ +22 + | ++ + + + + + | + private final MetadataBLOB cachedBlob; |
+ +23 + | ++ + + + + + | +|
+ +24 + | ++ + + + + + | + /** |
+ +25 + | ++ + + + + + | + * The newly downloaded metadata BLOB, which has an unexpected legal header. |
+ +26 + | ++ + + + + + | + * |
+ +27 + | ++ + + + + + | + * <p>The unexpected legal header can be retrieved via the {@link MetadataBLOB#getPayload() |
+ +28 + | ++ + + + + + | + * getPayload()}.{@link MetadataBLOBPayload#getLegalHeader() getLegalHeader()} methods. |
+ +29 + | ++ + + + + + | + * |
+ +30 + | ++ + + + + + | + * @see MetadataBLOB#getPayload() |
+ +31 + | ++ + + + + + | + * @see MetadataBLOBPayload#getLegalHeader() |
+ +32 + | ++ + + + + + | + */ |
+ +33 + | ++ + + + + + | + @Getter @NonNull private final MetadataBLOB downloadedBlob; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + /** The cached metadata BLOB, if any. */ |
+ +36 + | ++ + + + + + | + public Optional<MetadataBLOB> getCachedBlob() { |
+ +37 + | +
+
+1
+
+1. getCachedBlob : replaced return value with Optional.empty for com/yubico/fido/metadata/UnexpectedLegalHeader::getCachedBlob → NO_COVERAGE + + + + |
+ return Optional.ofNullable(cachedBlob); |
+ +38 + | ++ + + + + + | + } |
+ +39 + | ++ + + + + + | +} |
Mutations | ||
37 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
19 | +89% | +69% | +75% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
AAGUID.java | +91% |
+ 90% |
+ 90% |
+
AAID.java | +80% |
+ 100% |
+ 100% |
+
AlternativeDescriptions.java | +25% |
+ 0% |
+ 100% |
+
AuthenticatorGetInfo.java | +100% |
+ 24% |
+ 26% |
+
BiometricAccuracyDescriptor.java | +100% |
+ 0% |
+ 0% |
+
BiometricStatusReport.java | +100% |
+ 0% |
+ 0% |
+
CertFromBase64Converter.java | +57% |
+ 67% |
+ 100% |
+
CertToBase64Converter.java | +50% |
+ 67% |
+ 100% |
+
CodeAccuracyDescriptor.java | +100% |
+ 0% |
+ 0% |
+
FidoMetadataDownloader.java | +90% |
+ 94% |
+ 96% |
+
FidoMetadataDownloaderException.java | +82% |
+ 67% |
+ 100% |
+
FidoMetadataService.java | +86% |
+ 75% |
+ 87% |
+
JacksonCodecs.java | +67% |
+ 100% |
+ 100% |
+
MetadataBLOBHeader.java | +93% |
+ 83% |
+ 83% |
+
MetadataBLOBPayloadEntry.java | +100% |
+ 80% |
+ 80% |
+
MetadataStatement.java | +100% |
+ 42% |
+ 42% |
+
PatternAccuracyDescriptor.java | +100% |
+ 0% |
+ 0% |
+
StatusReport.java | +93% |
+ 11% |
+ 14% |
+
UnexpectedLegalHeader.java | +0% |
+ 0% |
+ 100% |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
19 | +89% | +69% | +75% | +
Name | +Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|---|
com.yubico.fido.metadata | +19 | +89% |
+ 69% |
+ 75% |
+
+ +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 |
+
158 | ++ |
+
+
+
+ 1.1 |
+
183 | ++ |
+
+
+
+ 1.1 |
+
187 | ++ |
+
+
+
+ 1.1 |
+
197 | ++ |
+
+
+
+ 1.1 |
+
198 | ++ |
+
+
+
+ 1.1 |
+
210 | ++ |
+
+
+
+ 1.1 |
+
214 | ++ |
+
+
+
+ 1.1 |
+
233 | ++ |
+
+
+
+ 1.1 |
+
246 | ++ |
+
+
+
+ 1.1 |
+
276 | ++ |
+
+
+
+ 1.1 |
+
288 | ++ |
+
+
+
+ 1.1 |
+
294 | ++ |
+
+
+
+ 1.1 2.2 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
1 | +94% | +93% | +93% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
AttestationTrustSource.java | +94% |
+ 93% |
+ 93% |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.RelyingParty; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.StartAssertionOptions; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.extension.appid.AppId; |
+ +33 + | ++ + + + + + | +import java.util.HashSet; |
+ +34 + | ++ + + + + + | +import java.util.Optional; |
+ +35 + | ++ + + + + + | +import java.util.Set; |
+ +36 + | ++ + + + + + | +import lombok.Builder; |
+ +37 + | ++ + + + + + | +import lombok.NonNull; |
+ +38 + | ++ + + + + + | +import lombok.Value; |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | +/** |
+ +41 + | ++ + + + + + | + * Contains <a |
+ +42 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-input">client |
+ +43 + | ++ + + + + + | + * extension inputs</a> to a <code>navigator.credentials.get()</code> operation. All members are |
+ +44 + | ++ + + + + + | + * optional. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * <p>The authenticator extension inputs are derived from these client extension inputs. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9. WebAuthn |
+ +49 + | ++ + + + + + | + * Extensions</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | +@Value |
+ +52 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +53 + | ++ + + + + + | +@JsonIgnoreProperties(ignoreUnknown = true) |
+ +54 + | ++ + + + + + | +public class AssertionExtensionInputs implements ExtensionInputs { |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + private final AppId appid; |
+ +57 + | ++ + + + + + | + private final Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob; |
+ +58 + | ++ + + + + + | + private final Boolean uvm; |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + @JsonCreator |
+ +61 + | ++ + + + + + | + private AssertionExtensionInputs( |
+ +62 + | ++ + + + + + | + @JsonProperty("appid") AppId appid, |
+ +63 + | ++ + + + + + | + @JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob, |
+ +64 + | ++ + + + + + | + @JsonProperty("uvm") Boolean uvm) { |
+ +65 + | ++ + + + + + | + this.appid = appid; |
+ +66 + | ++ + + + + + | + this.largeBlob = largeBlob; |
+ +67 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ this.uvm = (uvm != null && uvm) ? true : null; |
+ +68 + | ++ + + + + + | + } |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * Merge <code>other</code> into <code>this</code>. Non-null field values from <code>this</code> |
+ +72 + | ++ + + + + + | + * take precedence. |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * @return a new {@link AssertionExtensionInputs} instance with the settings from both <code>this |
+ +75 + | ++ + + + + + | + * </code> and <code>other</code>. |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + public AssertionExtensionInputs merge(AssertionExtensionInputs other) { |
+ +78 + | +
+
+1
+
+1. merge : replaced return value with null for com/yubico/webauthn/data/AssertionExtensionInputs::merge → KILLED + + + + |
+ return new AssertionExtensionInputs( |
+ +79 + | +
+
+1
+
+1. merge : negated conditional → KILLED + + + + |
+ this.appid != null ? this.appid : other.appid, |
+ +80 + | +
+
+1
+
+1. merge : negated conditional → SURVIVED + + + + |
+ this.largeBlob != null ? this.largeBlob : other.largeBlob, |
+ +81 + | +
+
+1
+
+1. merge : negated conditional → SURVIVED + + + + |
+ this.uvm != null ? this.uvm : other.uvm); |
+ +82 + | ++ + + + + + | + } |
+ +83 + | ++ + + + + + | +|
+ +84 + | ++ + + + + + | + /** |
+ +85 + | ++ + + + + + | + * @return The extension identifiers of all extensions configured. |
+ +86 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extension-id">§9.1. |
+ +87 + | ++ + + + + + | + * Extension Identifiers</a> |
+ +88 + | ++ + + + + + | + */ |
+ +89 + | ++ + + + + + | + @Override |
+ +90 + | ++ + + + + + | + public Set<String> getExtensionIds() { |
+ +91 + | ++ + + + + + | + Set<String> ids = new HashSet<>(); |
+ +92 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (appid != null) { |
+ +93 + | ++ + + + + + | + ids.add(Extensions.Appid.EXTENSION_ID); |
+ +94 + | ++ + + + + + | + } |
+ +95 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (largeBlob != null) { |
+ +96 + | ++ + + + + + | + ids.add(Extensions.LargeBlob.EXTENSION_ID); |
+ +97 + | ++ + + + + + | + } |
+ +98 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (getUvm()) { |
+ +99 + | ++ + + + + + | + ids.add(Extensions.Uvm.EXTENSION_ID); |
+ +100 + | ++ + + + + + | + } |
+ +101 + | +
+
+1
+
+1. getExtensionIds : replaced return value with Collections.emptySet for com/yubico/webauthn/data/AssertionExtensionInputs::getExtensionIds → KILLED + + + + |
+ return ids; |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + public static class AssertionExtensionInputsBuilder { |
+ +105 + | ++ + + + + + | + /** |
+ +106 + | ++ + + + + + | + * The input to the FIDO AppID Extension (<code>appid</code>). |
+ +107 + | ++ + + + + + | + * |
+ +108 + | ++ + + + + + | + * <p>You usually do not need to call this method explicitly; if {@link RelyingParty#getAppId()} |
+ +109 + | ++ + + + + + | + * is present, then {@link RelyingParty#startAssertion(StartAssertionOptions)} will enable this |
+ +110 + | ++ + + + + + | + * extension automatically. |
+ +111 + | ++ + + + + + | + * |
+ +112 + | ++ + + + + + | + * <p>This extension allows WebAuthn Relying Parties that have previously registered a |
+ +113 + | ++ + + + + + | + * credential using the legacy FIDO JavaScript APIs to request an assertion. The FIDO APIs use |
+ +114 + | ++ + + + + + | + * an alternative identifier for Relying Parties called an <a |
+ +115 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html">AppID</a>, |
+ +116 + | ++ + + + + + | + * and any credentials created using those APIs will be scoped to that identifier. Without this |
+ +117 + | ++ + + + + + | + * extension, they would need to be re-registered in order to be scoped to an RP ID. |
+ +118 + | ++ + + + + + | + * |
+ +119 + | ++ + + + + + | + * <p>This extension does not allow FIDO-compatible credentials to be created. Thus, credentials |
+ +120 + | ++ + + + + + | + * created with WebAuthn are not backwards compatible with the FIDO JavaScript APIs. |
+ +121 + | ++ + + + + + | + * |
+ +122 + | ++ + + + + + | + * @see <a |
+ +123 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +124 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +125 + | ++ + + + + + | + */ |
+ +126 + | +
+
+1
+
+1. appid : negated conditional → KILLED + + + + |
+ public AssertionExtensionInputsBuilder appid(@NonNull Optional<AppId> appid) { |
+ +127 + | +
+
+1
+
+1. appid : replaced return value with null for com/yubico/webauthn/data/AssertionExtensionInputs$AssertionExtensionInputsBuilder::appid → KILLED + + + + |
+ return this.appid(appid.orElse(null)); |
+ +128 + | ++ + + + + + | + } |
+ +129 + | ++ + + + + + | +|
+ +130 + | ++ + + + + + | + /** |
+ +131 + | ++ + + + + + | + * The input to the FIDO AppID Extension (<code>appid</code>). |
+ +132 + | ++ + + + + + | + * |
+ +133 + | ++ + + + + + | + * <p>You usually do not need to call this method explicitly; if {@link RelyingParty#getAppId()} |
+ +134 + | ++ + + + + + | + * is present, then {@link RelyingParty#startAssertion(StartAssertionOptions)} will enable this |
+ +135 + | ++ + + + + + | + * extension automatically. |
+ +136 + | ++ + + + + + | + * |
+ +137 + | ++ + + + + + | + * <p>This extension allows WebAuthn Relying Parties that have previously registered a |
+ +138 + | ++ + + + + + | + * credential using the legacy FIDO JavaScript APIs to request an assertion. The FIDO APIs use |
+ +139 + | ++ + + + + + | + * an alternative identifier for Relying Parties called an <a |
+ +140 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html">AppID</a>, |
+ +141 + | ++ + + + + + | + * and any credentials created using those APIs will be scoped to that identifier. Without this |
+ +142 + | ++ + + + + + | + * extension, they would need to be re-registered in order to be scoped to an RP ID. |
+ +143 + | ++ + + + + + | + * |
+ +144 + | ++ + + + + + | + * <p>This extension does not allow FIDO-compatible credentials to be created. Thus, credentials |
+ +145 + | ++ + + + + + | + * created with WebAuthn are not backwards compatible with the FIDO JavaScript APIs. |
+ +146 + | ++ + + + + + | + * |
+ +147 + | ++ + + + + + | + * @see <a |
+ +148 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +149 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +150 + | ++ + + + + + | + */ |
+ +151 + | ++ + + + + + | + public AssertionExtensionInputsBuilder appid(AppId appid) { |
+ +152 + | ++ + + + + + | + this.appid = appid; |
+ +153 + | +
+
+1
+
+1. appid : replaced return value with null for com/yubico/webauthn/data/AssertionExtensionInputs$AssertionExtensionInputsBuilder::appid → KILLED + + + + |
+ return this; |
+ +154 + | ++ + + + + + | + } |
+ +155 + | ++ + + + + + | +|
+ +156 + | ++ + + + + + | + /** |
+ +157 + | ++ + + + + + | + * Enable the Large blob storage extension (<code>largeBlob</code>). |
+ +158 + | ++ + + + + + | + * |
+ +159 + | ++ + + + + + | + * <p>Suitable arguments can be obtained using {@link |
+ +160 + | ++ + + + + + | + * Extensions.LargeBlob.LargeBlobAuthenticationInput#read()} or {@link |
+ +161 + | ++ + + + + + | + * Extensions.LargeBlob.LargeBlobAuthenticationInput#write(ByteArray)}. |
+ +162 + | ++ + + + + + | + * |
+ +163 + | ++ + + + + + | + * @see Extensions.LargeBlob.LargeBlobAuthenticationInput#read() |
+ +164 + | ++ + + + + + | + * @see Extensions.LargeBlob.LargeBlobAuthenticationInput#write(ByteArray) |
+ +165 + | ++ + + + + + | + * @see <a |
+ +166 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +167 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +168 + | ++ + + + + + | + */ |
+ +169 + | ++ + + + + + | + public AssertionExtensionInputsBuilder largeBlob( |
+ +170 + | ++ + + + + + | + Extensions.LargeBlob.LargeBlobAuthenticationInput largeBlob) { |
+ +171 + | ++ + + + + + | + this.largeBlob = largeBlob; |
+ +172 + | +
+
+1
+
+1. largeBlob : replaced return value with null for com/yubico/webauthn/data/AssertionExtensionInputs$AssertionExtensionInputsBuilder::largeBlob → KILLED + + + + |
+ return this; |
+ +173 + | ++ + + + + + | + } |
+ +174 + | ++ + + + + + | +|
+ +175 + | ++ + + + + + | + /** |
+ +176 + | ++ + + + + + | + * Enable the User Verification Method Extension (<code>uvm</code>). |
+ +177 + | ++ + + + + + | + * |
+ +178 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +179 + | ++ + + + + + | + * User Verification Method Extension (uvm)</a> |
+ +180 + | ++ + + + + + | + */ |
+ +181 + | ++ + + + + + | + public AssertionExtensionInputsBuilder uvm() { |
+ +182 + | ++ + + + + + | + this.uvm = true; |
+ +183 + | +
+
+1
+
+1. uvm : replaced return value with null for com/yubico/webauthn/data/AssertionExtensionInputs$AssertionExtensionInputsBuilder::uvm → KILLED + + + + |
+ return this; |
+ +184 + | ++ + + + + + | + } |
+ +185 + | ++ + + + + + | +|
+ +186 + | ++ + + + + + | + /** For compatibility with {@link Builder}(toBuilder = true) */ |
+ +187 + | ++ + + + + + | + private AssertionExtensionInputsBuilder uvm(Boolean uvm) { |
+ +188 + | ++ + + + + + | + this.uvm = uvm; |
+ +189 + | +
+
+1
+
+1. uvm : replaced return value with null for com/yubico/webauthn/data/AssertionExtensionInputs$AssertionExtensionInputsBuilder::uvm → KILLED + + + + |
+ return this; |
+ +190 + | ++ + + + + + | + } |
+ +191 + | ++ + + + + + | + } |
+ +192 + | ++ + + + + + | +|
+ +193 + | ++ + + + + + | + /** |
+ +194 + | ++ + + + + + | + * The input to the FIDO AppID Extension (<code>appid</code>). |
+ +195 + | ++ + + + + + | + * |
+ +196 + | ++ + + + + + | + * <p>This extension allows WebAuthn Relying Parties that have previously registered a credential |
+ +197 + | ++ + + + + + | + * using the legacy FIDO JavaScript APIs to request an assertion. The FIDO APIs use an alternative |
+ +198 + | ++ + + + + + | + * identifier for Relying Parties called an <a |
+ +199 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html">AppID</a>, |
+ +200 + | ++ + + + + + | + * and any credentials created using those APIs will be scoped to that identifier. Without this |
+ +201 + | ++ + + + + + | + * extension, they would need to be re-registered in order to be scoped to an RP ID. |
+ +202 + | ++ + + + + + | + * |
+ +203 + | ++ + + + + + | + * <p>This extension does not allow FIDO-compatible credentials to be created. Thus, credentials |
+ +204 + | ++ + + + + + | + * created with WebAuthn are not backwards compatible with the FIDO JavaScript APIs. |
+ +205 + | ++ + + + + + | + * |
+ +206 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +207 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +208 + | ++ + + + + + | + */ |
+ +209 + | ++ + + + + + | + public Optional<AppId> getAppid() { |
+ +210 + | +
+
+1
+
+1. getAppid : replaced return value with Optional.empty for com/yubico/webauthn/data/AssertionExtensionInputs::getAppid → KILLED + + + + |
+ return Optional.ofNullable(appid); |
+ +211 + | ++ + + + + + | + } |
+ +212 + | ++ + + + + + | +|
+ +213 + | ++ + + + + + | + /** |
+ +214 + | ++ + + + + + | + * The input to the Large blob storage extension (<code>largeBlob</code>). |
+ +215 + | ++ + + + + + | + * |
+ +216 + | ++ + + + + + | + * <p>This extension allows a Relying Party to store opaque data associated with a credential. |
+ +217 + | ++ + + + + + | + * |
+ +218 + | ++ + + + + + | + * @see Extensions.LargeBlob.LargeBlobAuthenticationInput#read() |
+ +219 + | ++ + + + + + | + * @see Extensions.LargeBlob.LargeBlobAuthenticationInput#write(ByteArray) |
+ +220 + | ++ + + + + + | + * @see <a |
+ +221 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +222 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +223 + | ++ + + + + + | + */ |
+ +224 + | ++ + + + + + | + public Optional<Extensions.LargeBlob.LargeBlobAuthenticationInput> getLargeBlob() { |
+ +225 + | +
+
+1
+
+1. getLargeBlob : replaced return value with Optional.empty for com/yubico/webauthn/data/AssertionExtensionInputs::getLargeBlob → KILLED + + + + |
+ return Optional.ofNullable(largeBlob); |
+ +226 + | ++ + + + + + | + } |
+ +227 + | ++ + + + + + | +|
+ +228 + | ++ + + + + + | + /** For JSON serialization, to omit false and null values. */ |
+ +229 + | ++ + + + + + | + @JsonProperty("largeBlob") |
+ +230 + | ++ + + + + + | + private Extensions.LargeBlob.LargeBlobAuthenticationInput getLargeBlobJson() { |
+ +231 + | +
+
+3
+
+1. getLargeBlobJson : negated conditional → KILLED +2. getLargeBlobJson : negated conditional → KILLED +3. getLargeBlobJson : negated conditional → KILLED + + + + |
+ return largeBlob != null && (largeBlob.getRead() || largeBlob.getWrite().isPresent()) |
+ +232 + | ++ + + + + + | + ? largeBlob |
+ +233 + | ++ + + + + + | + : null; |
+ +234 + | ++ + + + + + | + } |
+ +235 + | ++ + + + + + | +|
+ +236 + | ++ + + + + + | + /** |
+ +237 + | ++ + + + + + | + * @return <code>true</code> if the User Verification Method Extension (<code>uvm</code>) is |
+ +238 + | ++ + + + + + | + * enabled, <code>false</code> otherwise. |
+ +239 + | ++ + + + + + | + * @see AssertionExtensionInputsBuilder#uvm() |
+ +240 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +241 + | ++ + + + + + | + * User Verification Method Extension (uvm)</a> |
+ +242 + | ++ + + + + + | + */ |
+ +243 + | ++ + + + + + | + public boolean getUvm() { |
+ +244 + | +
+
+3
+
+1. getUvm : replaced boolean return with true for com/yubico/webauthn/data/AssertionExtensionInputs::getUvm → KILLED +2. getUvm : negated conditional → KILLED +3. getUvm : negated conditional → KILLED + + + + |
+ return uvm != null && uvm; |
+ +245 + | ++ + + + + + | + } |
+ +246 + | ++ + + + + + | +|
+ +247 + | ++ + + + + + | + /** For JSON serialization, to omit false values. */ |
+ +248 + | ++ + + + + + | + @JsonProperty("uvm") |
+ +249 + | ++ + + + + + | + private Boolean getUvmJson() { |
+ +250 + | +
+
+3
+
+1. getUvmJson : replaced Boolean return with True for com/yubico/webauthn/data/AssertionExtensionInputs::getUvmJson → KILLED +2. getUvmJson : replaced Boolean return with False for com/yubico/webauthn/data/AssertionExtensionInputs::getUvmJson → KILLED +3. getUvmJson : negated conditional → KILLED + + + + |
+ return getUvm() ? true : null; |
+ +251 + | ++ + + + + + | + } |
+ +252 + | ++ + + + + + | +} |
Mutations | ||
67 | ++ |
+
+
+
+ 1.1 2.2 |
+
78 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
80 | ++ |
+
+
+
+ 1.1 |
+
81 | ++ |
+
+
+
+ 1.1 |
+
92 | ++ |
+
+
+
+ 1.1 |
+
95 | ++ |
+
+
+
+ 1.1 |
+
98 | ++ |
+
+
+
+ 1.1 |
+
101 | ++ |
+
+
+
+ 1.1 |
+
126 | ++ |
+
+
+
+ 1.1 |
+
127 | ++ |
+
+
+
+ 1.1 |
+
153 | ++ |
+
+
+
+ 1.1 |
+
172 | ++ |
+
+
+
+ 1.1 |
+
183 | ++ |
+
+
+
+ 1.1 |
+
189 | ++ |
+
+
+
+ 1.1 |
+
210 | ++ |
+
+
+
+ 1.1 |
+
225 | ++ |
+
+
+
+ 1.1 |
+
231 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
244 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
250 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +31 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +32 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +33 + | ++ + + + + + | +import lombok.Getter; |
+ +34 + | ++ + + + + + | +import lombok.NonNull; |
+ +35 + | ++ + + + + + | +|
+ +36 + | ++ + + + + + | +/** |
+ +37 + | ++ + + + + + | + * Relying Parties may use this to specify their preference regarding attestation conveyance during |
+ +38 + | ++ + + + + + | + * credential generation. |
+ +39 + | ++ + + + + + | + * |
+ +40 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-conveyance">§5.4.6. |
+ +41 + | ++ + + + + + | + * Attestation Conveyance Preference Enumeration (enum AttestationConveyancePreference) </a> |
+ +42 + | ++ + + + + + | + */ |
+ +43 + | ++ + + + + + | +@AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +44 + | ++ + + + + + | +public enum AttestationConveyancePreference { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** |
+ +47 + | ++ + + + + + | + * Indicates that the Relying Party is not interested in authenticator attestation. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * <p>For example, in order to potentially avoid having to obtain user consent to relay |
+ +50 + | ++ + + + + + | + * identifying information to the Relying Party, or to save a roundtrip to an <a |
+ +51 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-ca">Attestation CA</a>. |
+ +52 + | ++ + + + + + | + * |
+ +53 + | ++ + + + + + | + * <p>This is the default value. |
+ +54 + | ++ + + + + + | + */ |
+ +55 + | ++ + + + + + | + NONE("none"), |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + /** |
+ +58 + | ++ + + + + + | + * Indicates that the Relying Party prefers an attestation conveyance yielding verifiable |
+ +59 + | ++ + + + + + | + * attestation statements, but allows the client to decide how to obtain such attestation |
+ +60 + | ++ + + + + + | + * statements. The client MAY replace the authenticator-generated attestation statements with |
+ +61 + | ++ + + + + + | + * attestation statements generated by an <a |
+ +62 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#anonymization-ca">Anonymization |
+ +63 + | ++ + + + + + | + * CA</a>, in order to protect the user’s privacy, or to assist Relying Parties with attestation |
+ +64 + | ++ + + + + + | + * verification in a heterogeneous ecosystem. |
+ +65 + | ++ + + + + + | + * |
+ +66 + | ++ + + + + + | + * <p>Note: There is no guarantee that the Relying Party will obtain a verifiable attestation |
+ +67 + | ++ + + + + + | + * statement in this case. For example, in the case that the authenticator employs self |
+ +68 + | ++ + + + + + | + * attestation. |
+ +69 + | ++ + + + + + | + */ |
+ +70 + | ++ + + + + + | + INDIRECT("indirect"), |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + /** |
+ +73 + | ++ + + + + + | + * Indicates that the Relying Party wants to receive the attestation statement as generated by the |
+ +74 + | ++ + + + + + | + * authenticator. |
+ +75 + | ++ + + + + + | + */ |
+ +76 + | ++ + + + + + | + DIRECT("direct"), |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + /** |
+ +79 + | ++ + + + + + | + * This value indicates that the Relying Party wants to receive an attestation statement that may |
+ +80 + | ++ + + + + + | + * include uniquely identifying information. This is intended for controlled deployments within an |
+ +81 + | ++ + + + + + | + * enterprise where the organization wishes to tie registrations to specific authenticators. User |
+ +82 + | ++ + + + + + | + * agents MUST NOT provide such an attestation unless the user agent or authenticator |
+ +83 + | ++ + + + + + | + * configuration permits it for the requested RP ID. |
+ +84 + | ++ + + + + + | + * |
+ +85 + | ++ + + + + + | + * <p>If permitted, the user agent SHOULD signal to the authenticator (at invocation time) that |
+ +86 + | ++ + + + + + | + * enterprise attestation is requested, and convey the resulting AAGUID and attestation statement, |
+ +87 + | ++ + + + + + | + * unaltered, to the Relying Party. |
+ +88 + | ++ + + + + + | + */ |
+ +89 + | ++ + + + + + | + ENTERPRISE("enterprise"); |
+ +90 + | ++ + + + + + | +|
+ +91 + | ++ + + + + + | + @JsonValue @Getter @NonNull private final String value; |
+ +92 + | ++ + + + + + | +|
+ +93 + | +
+
+1
+
+1. fromString : negated conditional → KILLED + + + + |
+ private static Optional<AttestationConveyancePreference> fromString(@NonNull String value) { |
+ +94 + | +
+
+3
+
+1. lambda$fromString$0 : replaced boolean return with false for com/yubico/webauthn/data/AttestationConveyancePreference::lambda$fromString$0 → KILLED +2. lambda$fromString$0 : replaced boolean return with true for com/yubico/webauthn/data/AttestationConveyancePreference::lambda$fromString$0 → KILLED +3. fromString : replaced return value with Optional.empty for com/yubico/webauthn/data/AttestationConveyancePreference::fromString → KILLED + + + + |
+ return Stream.of(values()).filter(v -> v.value.equals(value)).findAny(); |
+ +95 + | ++ + + + + + | + } |
+ +96 + | ++ + + + + + | +|
+ +97 + | ++ + + + + + | + @JsonCreator |
+ +98 + | +
+
+1
+
+1. fromJsonString : negated conditional → KILLED + + + + |
+ private static AttestationConveyancePreference fromJsonString(@NonNull String value) { |
+ +99 + | +
+
+1
+
+1. fromJsonString : replaced return value with null for com/yubico/webauthn/data/AttestationConveyancePreference::fromJsonString → KILLED + + + + |
+ return fromString(value) |
+ +100 + | ++ + + + + + | + .orElseThrow( |
+ +101 + | ++ + + + + + | + () -> |
+ +102 + | +
+
+1
+
+1. lambda$fromJsonString$1 : replaced return value with null for com/yubico/webauthn/data/AttestationConveyancePreference::lambda$fromJsonString$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +103 + | ++ + + + + + | + String.format( |
+ +104 + | ++ + + + + + | + "Unknown %s value: %s", |
+ +105 + | ++ + + + + + | + AttestationConveyancePreference.class.getSimpleName(), value))); |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +} |
Mutations | ||
93 | ++ |
+
+
+
+ 1.1 |
+
94 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
98 | ++ |
+
+
+
+ 1.1 |
+
99 | ++ |
+
+
+
+ 1.1 |
+
102 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonGenerator; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonNode; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.databind.SerializerProvider; |
+ +31 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +32 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ObjectNode; |
+ +33 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +34 + | ++ + + + + + | +import java.io.IOException; |
+ +35 + | ++ + + + + + | +import lombok.NonNull; |
+ +36 + | ++ + + + + + | +import lombok.Value; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | +/** |
+ +39 + | ++ + + + + + | + * Authenticators MUST provide some form of attestation. The basic requirement is that the |
+ +40 + | ++ + + + + + | + * authenticator can produce, for each credential public key, an attestation statement verifiable by |
+ +41 + | ++ + + + + + | + * the WebAuthn Relying Party. Typically, this attestation statement contains a signature by an |
+ +42 + | ++ + + + + + | + * attestation private key over the attested credential public key and a challenge, as well as a |
+ +43 + | ++ + + + + + | + * certificate or similar data providing provenance information for the attestation public key, |
+ +44 + | ++ + + + + + | + * enabling the Relying Party to make a trust decision. However, if an attestation key pair is not |
+ +45 + | ++ + + + + + | + * available, then the authenticator MUST perform <a |
+ +46 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#self-attestation">self attestation</a> |
+ +47 + | ++ + + + + + | + * of the credential public key with the corresponding credential private key. All this information |
+ +48 + | ++ + + + + + | + * is returned by authenticators any time a new public key credential is generated, in the overall |
+ +49 + | ++ + + + + + | + * form of an attestation object. The relationship of the attestation object with authenticator data |
+ +50 + | ++ + + + + + | + * (containing attested credential data) and the attestation statement is illustrated in <a |
+ +51 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#fig-attStructs">figure 5</a>. |
+ +52 + | ++ + + + + + | + * |
+ +53 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +54 + | ++ + + + + + | + * Attestation</a> |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | +@Value |
+ +57 + | ++ + + + + + | +@JsonSerialize(using = AttestationObject.JsonSerializer.class) |
+ +58 + | ++ + + + + + | +public class AttestationObject { |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + /** |
+ +61 + | ++ + + + + + | + * The original raw byte array that this object is decoded from. |
+ +62 + | ++ + + + + + | + * |
+ +63 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +64 + | ++ + + + + + | + * Attestation</a> |
+ +65 + | ++ + + + + + | + */ |
+ +66 + | ++ + + + + + | + @NonNull private final ByteArray bytes; |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + /** |
+ +69 + | ++ + + + + + | + * The authenticator data embedded inside this attestation object. This is one part of the signed |
+ +70 + | ++ + + + + + | + * data that the signature in the attestation statement (if any) is computed over. |
+ +71 + | ++ + + + + + | + */ |
+ +72 + | ++ + + + + + | + @NonNull private final transient AuthenticatorData authenticatorData; |
+ +73 + | ++ + + + + + | +|
+ +74 + | ++ + + + + + | + /** |
+ +75 + | ++ + + + + + | + * The attestation statement format identifier of this attestation object. |
+ +76 + | ++ + + + + + | + * |
+ +77 + | ++ + + + + + | + * @see <a |
+ +78 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-defined-attestation-formats">§8. |
+ +79 + | ++ + + + + + | + * Defined Attestation Statement Formats</a> |
+ +80 + | ++ + + + + + | + * <p>Users of this library should not need to access this value directly. |
+ +81 + | ++ + + + + + | + */ |
+ +82 + | ++ + + + + + | + @NonNull private final transient String format; |
+ +83 + | ++ + + + + + | +|
+ +84 + | ++ + + + + + | + /** |
+ +85 + | ++ + + + + + | + * An important component of the attestation object is the attestation statement. This is a |
+ +86 + | ++ + + + + + | + * specific type of signed data object, containing statements about a public key credential itself |
+ +87 + | ++ + + + + + | + * and the authenticator that created it. It contains an attestation signature created using the |
+ +88 + | ++ + + + + + | + * key of the attesting authority (except for the case of self attestation, when it is created |
+ +89 + | ++ + + + + + | + * using the credential private key). |
+ +90 + | ++ + + + + + | + * |
+ +91 + | ++ + + + + + | + * <p>Users of this library should not need to access this value directly. |
+ +92 + | ++ + + + + + | + */ |
+ +93 + | ++ + + + + + | + @NonNull private final transient ObjectNode attestationStatement; |
+ +94 + | ++ + + + + + | +|
+ +95 + | ++ + + + + + | + /** |
+ +96 + | ++ + + + + + | + * Decode an {@link AttestationObject} object from a raw attestation object byte array. |
+ +97 + | ++ + + + + + | + * |
+ +98 + | ++ + + + + + | + * @throws IOException if <code>bytes</code> cannot be parsed as a CBOR map. |
+ +99 + | ++ + + + + + | + */ |
+ +100 + | ++ + + + + + | + @JsonCreator |
+ +101 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ public AttestationObject(@NonNull ByteArray bytes) throws IOException { |
+ +102 + | ++ + + + + + | + this.bytes = bytes; |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + final JsonNode decoded = JacksonCodecs.cbor().readTree(bytes.getBytes()); |
+ +105 + | ++ + + + + + | + final ByteArray authDataBytes; |
+ +106 + | ++ + + + + + | +|
+ +107 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (!decoded.isObject()) { |
+ +108 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +109 + | ++ + + + + + | + String.format("Attestation object must be a CBOR map, was: %s", decoded.getNodeType())); |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + final JsonNode authData = decoded.get("authData"); |
+ +113 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (authData == null) { |
+ +114 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +115 + | ++ + + + + + | + "Required property \"authData\" missing from attestation object: " |
+ +116 + | ++ + + + + + | + + bytes.getBase64Url()); |
+ +117 + | ++ + + + + + | + } else { |
+ +118 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (authData.isBinary()) { |
+ +119 + | ++ + + + + + | + authDataBytes = new ByteArray(authData.binaryValue()); |
+ +120 + | ++ + + + + + | + } else { |
+ +121 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +122 + | ++ + + + + + | + String.format( |
+ +123 + | ++ + + + + + | + "Property \"authData\" of attestation object must be a CBOR byte array, was: %s. Attestation object: %s", |
+ +124 + | ++ + + + + + | + authData.getNodeType(), bytes.getBase64Url())); |
+ +125 + | ++ + + + + + | + } |
+ +126 + | ++ + + + + + | + } |
+ +127 + | ++ + + + + + | +|
+ +128 + | ++ + + + + + | + final JsonNode format = decoded.get("fmt"); |
+ +129 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (format == null) { |
+ +130 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +131 + | ++ + + + + + | + "Required property \"fmt\" missing from attestation object: " + bytes.getBase64Url()); |
+ +132 + | ++ + + + + + | + } else { |
+ +133 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (format.isTextual()) { |
+ +134 + | ++ + + + + + | + this.format = decoded.get("fmt").textValue(); |
+ +135 + | ++ + + + + + | + } else { |
+ +136 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +137 + | ++ + + + + + | + String.format( |
+ +138 + | ++ + + + + + | + "Property \"fmt\" of attestation object must be a CBOR text value, was: %s. Attestation object: %s", |
+ +139 + | ++ + + + + + | + format.getNodeType(), bytes.getBase64Url())); |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | + } |
+ +142 + | ++ + + + + + | +|
+ +143 + | ++ + + + + + | + final JsonNode attStmt = decoded.get("attStmt"); |
+ +144 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (attStmt == null) { |
+ +145 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +146 + | ++ + + + + + | + "Required property \"attStmt\" missing from attestation object: " + bytes.getBase64Url()); |
+ +147 + | ++ + + + + + | + } else { |
+ +148 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (attStmt.isObject()) { |
+ +149 + | ++ + + + + + | + this.attestationStatement = (ObjectNode) attStmt; |
+ +150 + | ++ + + + + + | + } else { |
+ +151 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +152 + | ++ + + + + + | + String.format( |
+ +153 + | ++ + + + + + | + "Property \"attStmt\" of attestation object must be a CBOR map, was: %s. Attestation object: %s", |
+ +154 + | ++ + + + + + | + attStmt.getNodeType(), bytes.getBase64Url())); |
+ +155 + | ++ + + + + + | + } |
+ +156 + | ++ + + + + + | + } |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + authenticatorData = new AuthenticatorData(authDataBytes); |
+ +159 + | ++ + + + + + | + } |
+ +160 + | ++ + + + + + | +|
+ +161 + | ++ + + + + + | + static class JsonSerializer |
+ +162 + | ++ + + + + + | + extends com.fasterxml.jackson.databind.JsonSerializer<AttestationObject> { |
+ +163 + | ++ + + + + + | + @Override |
+ +164 + | ++ + + + + + | + public void serialize( |
+ +165 + | ++ + + + + + | + AttestationObject value, JsonGenerator gen, SerializerProvider serializers) |
+ +166 + | ++ + + + + + | + throws IOException { |
+ +167 + | +
+
+1
+
+1. serialize : removed call to com/fasterxml/jackson/core/JsonGenerator::writeString → KILLED + + + + |
+ gen.writeString(value.getBytes().getBase64Url()); |
+ +168 + | ++ + + + + + | + } |
+ +169 + | ++ + + + + + | + } |
+ +170 + | ++ + + + + + | +} |
Mutations | ||
101 | ++ |
+
+
+
+ 1.1 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
113 | ++ |
+
+
+
+ 1.1 |
+
118 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 |
+
133 | ++ |
+
+
+
+ 1.1 |
+
144 | ++ |
+
+
+
+ 1.1 |
+
148 | ++ |
+
+
+
+ 1.1 |
+
167 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import lombok.Builder; |
+ +30 + | ++ + + + + + | +import lombok.NonNull; |
+ +31 + | ++ + + + + + | +import lombok.Value; |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | +/** |
+ +34 + | ++ + + + + + | + * Attested credential data is a variable-length byte array added to the authenticator data when |
+ +35 + | ++ + + + + + | + * generating an attestation object for a given credential. This class provides access to the three |
+ +36 + | ++ + + + + + | + * data segments of that byte array. |
+ +37 + | ++ + + + + + | + * |
+ +38 + | ++ + + + + + | + * @see <a |
+ +39 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attested-credential-data">6.4.1. |
+ +40 + | ++ + + + + + | + * Attested Credential Data</a> |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | +@Value |
+ +43 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +44 + | ++ + + + + + | +public class AttestedCredentialData { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** The AAGUID of the authenticator. */ |
+ +47 + | ++ + + + + + | + @NonNull private final ByteArray aaguid; |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** The credential ID of the attested credential. */ |
+ +50 + | ++ + + + + + | + @NonNull private final ByteArray credentialId; |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * The credential public key encoded in COSE_Key format, as defined in Section 7 of <a |
+ +54 + | ++ + + + + + | + * href="https://tools.ietf.org/html/rfc8152">RFC 8152</a>. |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | + @NonNull private final ByteArray credentialPublicKey; |
+ +57 + | ++ + + + + + | +|
+ +58 + | ++ + + + + + | + @JsonCreator |
+ +59 + | ++ + + + + + | + private AttestedCredentialData( |
+ +60 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("aaguid") ByteArray aaguid, |
+ +61 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("credentialId") ByteArray credentialId, |
+ +62 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("credentialPublicKey") ByteArray credentialPublicKey) { |
+ +63 + | ++ + + + + + | + this.aaguid = aaguid; |
+ +64 + | ++ + + + + + | + this.credentialId = credentialId; |
+ +65 + | ++ + + + + + | + this.credentialPublicKey = credentialPublicKey; |
+ +66 + | ++ + + + + + | + } |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + static AttestedCredentialDataBuilder builder() { |
+ +69 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/AttestedCredentialData::builder → KILLED + + + + |
+ return new AttestedCredentialDataBuilder(); |
+ +70 + | ++ + + + + + | + } |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + static class AttestedCredentialDataBuilder {} |
+ +73 + | ++ + + + + + | +} |
Mutations | ||
60 | ++ |
+
+
+
+ 1.1 |
+
61 | ++ |
+
+
+
+ 1.1 |
+
62 | ++ |
+
+
+
+ 1.1 |
+
69 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +30 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +32 + | ++ + + + + + | +import java.util.HashSet; |
+ +33 + | ++ + + + + + | +import java.util.List; |
+ +34 + | ++ + + + + + | +import java.util.Optional; |
+ +35 + | ++ + + + + + | +import java.util.Set; |
+ +36 + | ++ + + + + + | +import lombok.Builder; |
+ +37 + | ++ + + + + + | +import lombok.EqualsAndHashCode; |
+ +38 + | ++ + + + + + | +import lombok.Value; |
+ +39 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | +/** |
+ +42 + | ++ + + + + + | + * Contains <a |
+ +43 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +44 + | ++ + + + + + | + * extension outputs</a> from a <code>navigator.credentials.create()</code> operation. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * <p>Note that there is no guarantee that any extension input present in {@link |
+ +47 + | ++ + + + + + | + * RegistrationExtensionInputs} will have a corresponding output present here. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * <p>The values contained here are parsed from the {@link AuthenticatorData} structure. |
+ +50 + | ++ + + + + + | + * |
+ +51 + | ++ + + + + + | + * <p>The client extension outputs are represented by the {@link ClientRegistrationExtensionOutputs} |
+ +52 + | ++ + + + + + | + * type. |
+ +53 + | ++ + + + + + | + * |
+ +54 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9. WebAuthn |
+ +55 + | ++ + + + + + | + * Extensions</a> |
+ +56 + | ++ + + + + + | + */ |
+ +57 + | ++ + + + + + | +@Value |
+ +58 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +59 + | ++ + + + + + | +@Slf4j |
+ +60 + | ++ + + + + + | +@JsonIgnoreProperties(ignoreUnknown = true) |
+ +61 + | ++ + + + + + | +public final class AuthenticatorAssertionExtensionOutputs implements AuthenticatorExtensionOutputs { |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + private final List<Extensions.Uvm.UvmEntry> uvm; |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + @JsonCreator |
+ +66 + | ++ + + + + + | + private AuthenticatorAssertionExtensionOutputs( |
+ +67 + | ++ + + + + + | + @JsonProperty("uvm") List<Extensions.Uvm.UvmEntry> uvm) { |
+ +68 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ this.uvm = uvm == null ? null : CollectionUtil.immutableList(uvm); |
+ +69 + | ++ + + + + + | + } |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | + /** |
+ +72 + | ++ + + + + + | + * Parse <a |
+ +73 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authentication-extension">authentication</a> |
+ +74 + | ++ + + + + + | + * <a |
+ +75 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +76 + | ++ + + + + + | + * extension outputs</a> from the given authenticator data. |
+ +77 + | ++ + + + + + | + * |
+ +78 + | ++ + + + + + | + * <p>If the <code>authData</code> does not contain authenticator extension outputs, this returns |
+ +79 + | ++ + + + + + | + * an empty {@link Optional}. |
+ +80 + | ++ + + + + + | + * |
+ +81 + | ++ + + + + + | + * <p>Otherwise, this returns a present {@link Optional} containing an {@link |
+ +82 + | ++ + + + + + | + * AuthenticatorAssertionExtensionOutputs} value with all validly-formatted <a |
+ +83 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authentication-extension">authentication</a> |
+ +84 + | ++ + + + + + | + * <a |
+ +85 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">extension |
+ +86 + | ++ + + + + + | + * outputs</a> supported by this library. This silently ignores <a |
+ +87 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#registration-extension">registration</a> |
+ +88 + | ++ + + + + + | + * extension outputs, malformed extension outputs, and unsupported extensions. The raw set of |
+ +89 + | ++ + + + + + | + * extension outputs can instead be obtained via {@link AuthenticatorData#getExtensions()}. |
+ +90 + | ++ + + + + + | + * |
+ +91 + | ++ + + + + + | + * <p>Note that a present {@link AuthenticatorAssertionExtensionOutputs} may contain zero |
+ +92 + | ++ + + + + + | + * extension outputs. |
+ +93 + | ++ + + + + + | + * |
+ +94 + | ++ + + + + + | + * @param authData the <a |
+ +95 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-data">authenticator |
+ +96 + | ++ + + + + + | + * data</a> to parse extension outputs from |
+ +97 + | ++ + + + + + | + * @return an empty {@link Optional} if the <code>authData</code> does not contain authenticator |
+ +98 + | ++ + + + + + | + * extension outputs. Otherwise a present {@link Optional} containing parsed extension output |
+ +99 + | ++ + + + + + | + * values. |
+ +100 + | ++ + + + + + | + */ |
+ +101 + | ++ + + + + + | + public static Optional<AuthenticatorAssertionExtensionOutputs> fromAuthenticatorData( |
+ +102 + | ++ + + + + + | + AuthenticatorData authData) { |
+ +103 + | +
+
+1
+
+1. fromAuthenticatorData : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorAssertionExtensionOutputs::fromAuthenticatorData → KILLED + + + + |
+ return authData.getExtensions().flatMap(AuthenticatorAssertionExtensionOutputs::fromCbor); |
+ +104 + | ++ + + + + + | + } |
+ +105 + | ++ + + + + + | +|
+ +106 + | ++ + + + + + | + static Optional<AuthenticatorAssertionExtensionOutputs> fromCbor(CBORObject cbor) { |
+ +107 + | ++ + + + + + | + AuthenticatorAssertionExtensionOutputs.AuthenticatorAssertionExtensionOutputsBuilder b = |
+ +108 + | ++ + + + + + | + builder(); |
+ +109 + | ++ + + + + + | +|
+ +110 + | +
+
+1
+
+1. fromCbor : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ Extensions.Uvm.parseAuthenticatorExtensionOutput(cbor).ifPresent(b::uvm); |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + AuthenticatorAssertionExtensionOutputs result = b.build(); |
+ +113 + | ++ + + + + + | +|
+ +114 + | +
+
+1
+
+1. fromCbor : negated conditional → KILLED + + + + |
+ if (result.getExtensionIds().isEmpty()) { |
+ +115 + | ++ + + + + + | + return Optional.empty(); |
+ +116 + | ++ + + + + + | + } else { |
+ +117 + | +
+
+1
+
+1. fromCbor : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorAssertionExtensionOutputs::fromCbor → KILLED + + + + |
+ return Optional.of(result); |
+ +118 + | ++ + + + + + | + } |
+ +119 + | ++ + + + + + | + } |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + @Override |
+ +122 + | ++ + + + + + | + @EqualsAndHashCode.Include |
+ +123 + | ++ + + + + + | + public Set<String> getExtensionIds() { |
+ +124 + | ++ + + + + + | + HashSet<String> ids = new HashSet<>(); |
+ +125 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (uvm != null) { |
+ +126 + | ++ + + + + + | + ids.add(Extensions.Uvm.EXTENSION_ID); |
+ +127 + | ++ + + + + + | + } |
+ +128 + | +
+
+1
+
+1. getExtensionIds : replaced return value with Collections.emptySet for com/yubico/webauthn/data/AuthenticatorAssertionExtensionOutputs::getExtensionIds → KILLED + + + + |
+ return ids; |
+ +129 + | ++ + + + + + | + } |
+ +130 + | ++ + + + + + | +|
+ +131 + | ++ + + + + + | + /** |
+ +132 + | ++ + + + + + | + * @return The <a |
+ +133 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +134 + | ++ + + + + + | + * extension output</a> for the <a |
+ +135 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">User |
+ +136 + | ++ + + + + + | + * Verification Method (<code>uvm</code>) extension</a>, if any. |
+ +137 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +138 + | ++ + + + + + | + * User Verification Method extension (uvm)</a> |
+ +139 + | ++ + + + + + | + */ |
+ +140 + | ++ + + + + + | + public Optional<List<Extensions.Uvm.UvmEntry>> getUvm() { |
+ +141 + | +
+
+1
+
+1. getUvm : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorAssertionExtensionOutputs::getUvm → KILLED + + + + |
+ return Optional.ofNullable(uvm); |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | +} |
Mutations | ||
68 | ++ |
+
+
+
+ 1.1 |
+
103 | ++ |
+
+
+
+ 1.1 |
+
110 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
117 | ++ |
+
+
+
+ 1.1 |
+
125 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.Base64UrlException; |
+ +30 + | ++ + + + + + | +import java.io.IOException; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import lombok.Builder; |
+ +33 + | ++ + + + + + | +import lombok.Getter; |
+ +34 + | ++ + + + + + | +import lombok.NonNull; |
+ +35 + | ++ + + + + + | +import lombok.Value; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | +/** |
+ +38 + | ++ + + + + + | + * Represents an authenticator's response to a client’s request for generation of a new |
+ +39 + | ++ + + + + + | + * authentication assertion given the WebAuthn Relying Party's {@linkplain |
+ +40 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getChallenge()} challenge} and OPTIONAL {@linkplain |
+ +41 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} list of credentials} it is aware of. |
+ +42 + | ++ + + + + + | + * This response contains a cryptographic {@linkplain #signature} proving possession of the |
+ +43 + | ++ + + + + + | + * credential private key, and optionally evidence of user consent to a specific transaction. |
+ +44 + | ++ + + + + + | + * |
+ +45 + | ++ + + + + + | + * @see <a |
+ +46 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticatorassertionresponse">§5.2.2. |
+ +47 + | ++ + + + + + | + * Web Authentication Assertion (interface AuthenticatorAssertionResponse) </a> |
+ +48 + | ++ + + + + + | + */ |
+ +49 + | ++ + + + + + | +@Value |
+ +50 + | ++ + + + + + | +public class AuthenticatorAssertionResponse implements AuthenticatorResponse { |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + @NonNull |
+ +53 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +54 + | ++ + + + + + | + private final ByteArray authenticatorData; |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + @NonNull |
+ +57 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +58 + | ++ + + + + + | + private final ByteArray clientDataJSON; |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + /** |
+ +61 + | ++ + + + + + | + * The raw signature returned from the authenticator. See <a |
+ +62 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-op-get-assertion">§6.3.3 The |
+ +63 + | ++ + + + + + | + * authenticatorGetAssertion Operation</a>. |
+ +64 + | ++ + + + + + | + */ |
+ +65 + | ++ + + + + + | + @NonNull private final ByteArray signature; |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + /** |
+ +68 + | ++ + + + + + | + * The user handle returned from the authenticator, or empty if the authenticator did not return a |
+ +69 + | ++ + + + + + | + * user handle. See <a |
+ +70 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-op-get-assertion">§6.3.3 The |
+ +71 + | ++ + + + + + | + * authenticatorGetAssertion Operation</a>. |
+ +72 + | ++ + + + + + | + */ |
+ +73 + | ++ + + + + + | + private final ByteArray userHandle; |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + // This overrides the default getter in AuthenticatorResponse which re-parses the authenticator |
+ +76 + | ++ + + + + + | + // data on every invocation. This "optimization" has no measurable impact on performance, but it |
+ +77 + | ++ + + + + + | + // seems rude to obviously waste cycles for nothing. |
+ +78 + | ++ + + + + + | + private final transient AuthenticatorData parsedAuthenticatorData; |
+ +79 + | ++ + + + + + | +|
+ +80 + | ++ + + + + + | + @NonNull |
+ +81 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +82 + | ++ + + + + + | + private final transient CollectedClientData clientData; |
+ +83 + | ++ + + + + + | +|
+ +84 + | ++ + + + + + | + @JsonCreator |
+ +85 + | ++ + + + + + | + @Builder(toBuilder = true) |
+ +86 + | ++ + + + + + | + private AuthenticatorAssertionResponse( |
+ +87 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("authenticatorData") final ByteArray authenticatorData, |
+ +88 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("clientDataJSON") final ByteArray clientDataJSON, |
+ +89 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("signature") final ByteArray signature, |
+ +90 + | ++ + + + + + | + @JsonProperty("userHandle") final ByteArray userHandle) |
+ +91 + | ++ + + + + + | + throws IOException, Base64UrlException { |
+ +92 + | ++ + + + + + | + this.authenticatorData = authenticatorData; |
+ +93 + | ++ + + + + + | + this.parsedAuthenticatorData = new AuthenticatorData(authenticatorData); |
+ +94 + | ++ + + + + + | + this.clientDataJSON = clientDataJSON; |
+ +95 + | ++ + + + + + | + this.signature = signature; |
+ +96 + | ++ + + + + + | + this.userHandle = userHandle; |
+ +97 + | ++ + + + + + | + this.clientData = new CollectedClientData(this.clientDataJSON); |
+ +98 + | ++ + + + + + | + } |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + /** |
+ +101 + | ++ + + + + + | + * The user handle returned from the authenticator, or empty if the authenticator did not return a |
+ +102 + | ++ + + + + + | + * user handle. See <a |
+ +103 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-op-get-assertion">§6.3.3 The |
+ +104 + | ++ + + + + + | + * authenticatorGetAssertion Operation</a>. |
+ +105 + | ++ + + + + + | + */ |
+ +106 + | ++ + + + + + | + public Optional<ByteArray> getUserHandle() { |
+ +107 + | +
+
+1
+
+1. getUserHandle : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorAssertionResponse::getUserHandle → KILLED + + + + |
+ return Optional.ofNullable(userHandle); |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + public static AuthenticatorAssertionResponseBuilder.MandatoryStages builder() { |
+ +111 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAssertionResponse::builder → KILLED + + + + |
+ return new AuthenticatorAssertionResponseBuilder.MandatoryStages(); |
+ +112 + | ++ + + + + + | + } |
+ +113 + | ++ + + + + + | +|
+ +114 + | ++ + + + + + | + public static class AuthenticatorAssertionResponseBuilder { |
+ +115 + | ++ + + + + + | + private ByteArray userHandle = null; |
+ +116 + | ++ + + + + + | +|
+ +117 + | ++ + + + + + | + public static class MandatoryStages { |
+ +118 + | ++ + + + + + | + private final AuthenticatorAssertionResponseBuilder builder = |
+ +119 + | ++ + + + + + | + new AuthenticatorAssertionResponseBuilder(); |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + /** |
+ +122 + | ++ + + + + + | + * {@link AuthenticatorAssertionResponseBuilder#authenticatorData(ByteArray) |
+ +123 + | ++ + + + + + | + * authenticatorData} is a required parameter. |
+ +124 + | ++ + + + + + | + * |
+ +125 + | ++ + + + + + | + * @see AuthenticatorAssertionResponseBuilder#authenticatorData(ByteArray) |
+ +126 + | ++ + + + + + | + */ |
+ +127 + | ++ + + + + + | + public Step2 authenticatorData(ByteArray authenticatorData) { |
+ +128 + | ++ + + + + + | + builder.authenticatorData(authenticatorData); |
+ +129 + | +
+
+1
+
+1. authenticatorData : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAssertionResponse$AuthenticatorAssertionResponseBuilder$MandatoryStages::authenticatorData → KILLED + + + + |
+ return new Step2(); |
+ +130 + | ++ + + + + + | + } |
+ +131 + | ++ + + + + + | +|
+ +132 + | ++ + + + + + | + public class Step2 { |
+ +133 + | ++ + + + + + | + /** |
+ +134 + | ++ + + + + + | + * {@link AuthenticatorAssertionResponseBuilder#clientDataJSON(ByteArray) clientDataJSON} is |
+ +135 + | ++ + + + + + | + * a required parameter. |
+ +136 + | ++ + + + + + | + * |
+ +137 + | ++ + + + + + | + * @see AuthenticatorAssertionResponseBuilder#clientDataJSON(ByteArray) |
+ +138 + | ++ + + + + + | + */ |
+ +139 + | ++ + + + + + | + public Step3 clientDataJSON(ByteArray clientDataJSON) { |
+ +140 + | ++ + + + + + | + builder.clientDataJSON(clientDataJSON); |
+ +141 + | +
+
+1
+
+1. clientDataJSON : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAssertionResponse$AuthenticatorAssertionResponseBuilder$MandatoryStages$Step2::clientDataJSON → KILLED + + + + |
+ return new Step3(); |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | + } |
+ +144 + | ++ + + + + + | +|
+ +145 + | ++ + + + + + | + public class Step3 { |
+ +146 + | ++ + + + + + | + /** |
+ +147 + | ++ + + + + + | + * {@link AuthenticatorAssertionResponseBuilder#signature(ByteArray) signature} is a |
+ +148 + | ++ + + + + + | + * required parameter. |
+ +149 + | ++ + + + + + | + * |
+ +150 + | ++ + + + + + | + * @see AuthenticatorAssertionResponseBuilder#signature(ByteArray) |
+ +151 + | ++ + + + + + | + */ |
+ +152 + | ++ + + + + + | + public AuthenticatorAssertionResponseBuilder signature(ByteArray signature) { |
+ +153 + | +
+
+1
+
+1. signature : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAssertionResponse$AuthenticatorAssertionResponseBuilder$MandatoryStages$Step3::signature → KILLED + + + + |
+ return builder.signature(signature); |
+ +154 + | ++ + + + + + | + } |
+ +155 + | ++ + + + + + | + } |
+ +156 + | ++ + + + + + | + } |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + /** |
+ +159 + | ++ + + + + + | + * The user handle returned from the authenticator, or empty if the authenticator did not return |
+ +160 + | ++ + + + + + | + * a user handle. See <a |
+ +161 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-op-get-assertion">§6.3.3 The |
+ +162 + | ++ + + + + + | + * authenticatorGetAssertion Operation</a>. |
+ +163 + | ++ + + + + + | + */ |
+ +164 + | ++ + + + + + | + public AuthenticatorAssertionResponseBuilder userHandle( |
+ +165 + | +
+
+1
+
+1. userHandle : negated conditional → KILLED + + + + |
+ @NonNull Optional<ByteArray> userHandle) { |
+ +166 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAssertionResponse$AuthenticatorAssertionResponseBuilder::userHandle → KILLED + + + + |
+ return this.userHandle(userHandle.orElse(null)); |
+ +167 + | ++ + + + + + | + } |
+ +168 + | ++ + + + + + | +|
+ +169 + | ++ + + + + + | + /** |
+ +170 + | ++ + + + + + | + * The user handle returned from the authenticator, or empty if the authenticator did not return |
+ +171 + | ++ + + + + + | + * a user handle. See <a |
+ +172 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-op-get-assertion">§6.3.3 The |
+ +173 + | ++ + + + + + | + * authenticatorGetAssertion Operation</a>. |
+ +174 + | ++ + + + + + | + */ |
+ +175 + | ++ + + + + + | + public AuthenticatorAssertionResponseBuilder userHandle(ByteArray userHandle) { |
+ +176 + | ++ + + + + + | + this.userHandle = userHandle; |
+ +177 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAssertionResponse$AuthenticatorAssertionResponseBuilder::userHandle → KILLED + + + + |
+ return this; |
+ +178 + | ++ + + + + + | + } |
+ +179 + | ++ + + + + + | + } |
+ +180 + | ++ + + + + + | +} |
Mutations | ||
87 | ++ |
+
+
+
+ 1.1 |
+
88 | ++ |
+
+
+
+ 1.1 |
+
89 | ++ |
+
+
+
+ 1.1 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
111 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 |
+
153 | ++ |
+
+
+
+ 1.1 |
+
165 | ++ |
+
+
+
+ 1.1 |
+
166 | ++ |
+
+
+
+ 1.1 |
+
177 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +31 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +32 + | ++ + + + + + | +import lombok.Getter; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * This enumeration’s values describe authenticators' <a |
+ +37 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-attachment-modality">attachment |
+ +38 + | ++ + + + + + | + * modalities</a>. Relying Parties use this for two purposes: |
+ +39 + | ++ + + + + + | + * |
+ +40 + | ++ + + + + + | + * <ul> |
+ +41 + | ++ + + + + + | + * <li>to express a preferred authenticator attachment modality when calling <code> |
+ +42 + | ++ + + + + + | + * navigator.credentials.create()</code> to create a credential, and |
+ +43 + | ++ + + + + + | + * <li>to inform the client of the Relying Party's best belief about how to locate the managing |
+ +44 + | ++ + + + + + | + * authenticators of the credentials listed in {@link |
+ +45 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} when calling <code> |
+ +46 + | ++ + + + + + | + * navigator.credentials.get()</code>. |
+ +47 + | ++ + + + + + | + * </ul> |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * @see <a |
+ +50 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-authenticatorattachment">§5.4.5. |
+ +51 + | ++ + + + + + | + * Authenticator Attachment Enumeration (enum AuthenticatorAttachment) </a> |
+ +52 + | ++ + + + + + | + */ |
+ +53 + | ++ + + + + + | +@AllArgsConstructor |
+ +54 + | ++ + + + + + | +public enum AuthenticatorAttachment { |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** |
+ +57 + | ++ + + + + + | + * Indicates <a |
+ +58 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#cross-platform-attachment">cross-platform |
+ +59 + | ++ + + + + + | + * attachment</a>. |
+ +60 + | ++ + + + + + | + * |
+ +61 + | ++ + + + + + | + * <p>Authenticators of this class are removable from, and can "roam" among, client platforms. |
+ +62 + | ++ + + + + + | + */ |
+ +63 + | ++ + + + + + | + CROSS_PLATFORM("cross-platform"), |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + /** |
+ +66 + | ++ + + + + + | + * Indicates <a |
+ +67 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#platform-attachment">platform |
+ +68 + | ++ + + + + + | + * attachment</a>. |
+ +69 + | ++ + + + + + | + * |
+ +70 + | ++ + + + + + | + * <p>Usually, authenticators of this class are not removable from the platform. |
+ +71 + | ++ + + + + + | + */ |
+ +72 + | ++ + + + + + | + PLATFORM("platform"); |
+ +73 + | ++ + + + + + | +|
+ +74 + | ++ + + + + + | + @JsonValue @Getter @NonNull private final String value; |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + /** |
+ +77 + | ++ + + + + + | + * Attempt to parse a string as an {@link AuthenticatorAttachment}. |
+ +78 + | ++ + + + + + | + * |
+ +79 + | ++ + + + + + | + * @param value a {@link String} equal to the {@link #getValue() value} of a constant in {@link |
+ +80 + | ++ + + + + + | + * AuthenticatorAttachment} |
+ +81 + | ++ + + + + + | + * @return The {@link AuthenticatorAttachment} instance whose {@link #getValue() value} equals |
+ +82 + | ++ + + + + + | + * <code>value</code>, if any. |
+ +83 + | ++ + + + + + | + * @see <a |
+ +84 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-authenticatorattachment">§5.4.5. |
+ +85 + | ++ + + + + + | + * Authenticator Attachment Enumeration (enum AuthenticatorAttachment) </a> |
+ +86 + | ++ + + + + + | + */ |
+ +87 + | +
+
+1
+
+1. fromValue : negated conditional → KILLED + + + + |
+ public static Optional<AuthenticatorAttachment> fromValue(@NonNull String value) { |
+ +88 + | +
+
+3
+
+1. fromValue : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorAttachment::fromValue → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/data/AuthenticatorAttachment::lambda$fromValue$0 → KILLED +3. lambda$fromValue$0 : replaced boolean return with false for com/yubico/webauthn/data/AuthenticatorAttachment::lambda$fromValue$0 → KILLED + + + + |
+ return Stream.of(values()).filter(v -> v.value.equals(value)).findAny(); |
+ +89 + | ++ + + + + + | + } |
+ +90 + | ++ + + + + + | +|
+ +91 + | ++ + + + + + | + @JsonCreator |
+ +92 + | +
+
+1
+
+1. fromJsonString : negated conditional → KILLED + + + + |
+ private static AuthenticatorAttachment fromJsonString(@NonNull String value) { |
+ +93 + | +
+
+1
+
+1. fromJsonString : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAttachment::fromJsonString → KILLED + + + + |
+ return fromValue(value).orElse(null); |
+ +94 + | ++ + + + + + | + } |
+ +95 + | ++ + + + + + | +} |
Mutations | ||
87 | ++ |
+
+
+
+ 1.1 |
+
88 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
92 | ++ |
+
+
+
+ 1.1 |
+
93 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnore; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.Base64UrlException; |
+ +33 + | ++ + + + + + | +import java.io.IOException; |
+ +34 + | ++ + + + + + | +import java.util.Collections; |
+ +35 + | ++ + + + + + | +import java.util.Set; |
+ +36 + | ++ + + + + + | +import java.util.SortedSet; |
+ +37 + | ++ + + + + + | +import lombok.Builder; |
+ +38 + | ++ + + + + + | +import lombok.Getter; |
+ +39 + | ++ + + + + + | +import lombok.NonNull; |
+ +40 + | ++ + + + + + | +import lombok.Value; |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | +/** |
+ +43 + | ++ + + + + + | + * Represents the authenticator's response to a client's request for the creation of a new public |
+ +44 + | ++ + + + + + | + * key credential. It contains information about the new credential that can be used to identify it |
+ +45 + | ++ + + + + + | + * for later use, and metadata that can be used by the WebAuthn Relying Party to assess the |
+ +46 + | ++ + + + + + | + * characteristics of the credential during registration. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * @see <a |
+ +49 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticatorattestationresponse">§5.2.1. |
+ +50 + | ++ + + + + + | + * Information About Public Key Credential (interface AuthenticatorAttestationResponse) </a> |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | +@Value |
+ +53 + | ++ + + + + + | +@JsonIgnoreProperties({"publicKey", "publicKeyAlgorithm"}) |
+ +54 + | ++ + + + + + | +public class AuthenticatorAttestationResponse implements AuthenticatorResponse { |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** |
+ +57 + | ++ + + + + + | + * Contains an attestation object, which is opaque to, and cryptographically protected against |
+ +58 + | ++ + + + + + | + * tampering by, the client. The attestation object contains both authenticator data and an |
+ +59 + | ++ + + + + + | + * attestation statement. The former contains the AAGUID, a unique credential ID, and the |
+ +60 + | ++ + + + + + | + * credential public key. The contents of the attestation statement are determined by the |
+ +61 + | ++ + + + + + | + * attestation statement format used by the authenticator. It also contains any additional |
+ +62 + | ++ + + + + + | + * information that the Relying Party's server requires to validate the attestation statement, as |
+ +63 + | ++ + + + + + | + * well as to decode and validate the authenticator data along with the JSON-serialized client |
+ +64 + | ++ + + + + + | + * data. For more details, see <a |
+ +65 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4 |
+ +66 + | ++ + + + + + | + * Attestation</a>, <a |
+ +67 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-generating-an-attestation-object">§6.4.4 |
+ +68 + | ++ + + + + + | + * Generating an Attestation Object</a>, and <a |
+ +69 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#fig-attStructs">Figure 5</a>. |
+ +70 + | ++ + + + + + | + */ |
+ +71 + | ++ + + + + + | + @NonNull private final ByteArray attestationObject; |
+ +72 + | ++ + + + + + | +|
+ +73 + | ++ + + + + + | + @NonNull |
+ +74 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +75 + | ++ + + + + + | + private final ByteArray clientDataJSON; |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * The return value from the <code>AuthenticatorAttestationResponse.getTransports()</code> method. |
+ +79 + | ++ + + + + + | + * |
+ +80 + | ++ + + + + + | + * @see <a |
+ +81 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatorattestationresponse-gettransports">§5.2.1. |
+ +82 + | ++ + + + + + | + * Information About Public Key Credential (interface AuthenticatorAttestationResponse)</a> |
+ +83 + | ++ + + + + + | + */ |
+ +84 + | ++ + + + + + | + private final SortedSet<AuthenticatorTransport> transports; |
+ +85 + | ++ + + + + + | +|
+ +86 + | ++ + + + + + | + /** The {@link #attestationObject} parsed as a domain object. */ |
+ +87 + | ++ + + + + + | + @NonNull |
+ +88 + | ++ + + + + + | + @JsonIgnore |
+ +89 + | ++ + + + + + | + @Getter(onMethod = @__({@JsonIgnore})) |
+ +90 + | ++ + + + + + | + private final transient AttestationObject attestation; |
+ +91 + | ++ + + + + + | +|
+ +92 + | ++ + + + + + | + @NonNull |
+ +93 + | ++ + + + + + | + @JsonIgnore |
+ +94 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +95 + | ++ + + + + + | + private final transient CollectedClientData clientData; |
+ +96 + | ++ + + + + + | +|
+ +97 + | ++ + + + + + | + @Override |
+ +98 + | ++ + + + + + | + @JsonIgnore |
+ +99 + | ++ + + + + + | + public ByteArray getAuthenticatorData() { |
+ +100 + | +
+
+1
+
+1. getAuthenticatorData : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAttestationResponse::getAuthenticatorData → NO_COVERAGE + + + + |
+ return attestation.getAuthenticatorData().getBytes(); |
+ +101 + | ++ + + + + + | + } |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + @Builder(toBuilder = true) |
+ +104 + | ++ + + + + + | + @JsonCreator |
+ +105 + | ++ + + + + + | + private AuthenticatorAttestationResponse( |
+ +106 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("attestationObject") ByteArray attestationObject, |
+ +107 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("clientDataJSON") ByteArray clientDataJSON, |
+ +108 + | ++ + + + + + | + @JsonProperty("transports") Set<AuthenticatorTransport> transports) |
+ +109 + | ++ + + + + + | + throws IOException, Base64UrlException { |
+ +110 + | ++ + + + + + | + this.attestationObject = attestationObject; |
+ +111 + | ++ + + + + + | + this.clientDataJSON = clientDataJSON; |
+ +112 + | ++ + + + + + | + this.transports = |
+ +113 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ transports == null |
+ +114 + | ++ + + + + + | + ? Collections.emptySortedSet() |
+ +115 + | ++ + + + + + | + : CollectionUtil.immutableSortedSet(transports); |
+ +116 + | ++ + + + + + | +|
+ +117 + | ++ + + + + + | + attestation = new AttestationObject(attestationObject); |
+ +118 + | ++ + + + + + | + this.clientData = new CollectedClientData(clientDataJSON); |
+ +119 + | ++ + + + + + | + } |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + // The default getter in AuthenticatorResponse re-parses the authenticator data on every |
+ +122 + | ++ + + + + + | + // invocation. This "optimization" override has no measurable impact on performance, but it seems |
+ +123 + | ++ + + + + + | + // rude to obviously waste cycles for nothing. |
+ +124 + | ++ + + + + + | + @Override |
+ +125 + | ++ + + + + + | + public AuthenticatorData getParsedAuthenticatorData() { |
+ +126 + | +
+
+1
+
+1. getParsedAuthenticatorData : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAttestationResponse::getParsedAuthenticatorData → KILLED + + + + |
+ return attestation.getAuthenticatorData(); |
+ +127 + | ++ + + + + + | + } |
+ +128 + | ++ + + + + + | +|
+ +129 + | ++ + + + + + | + public static AuthenticatorAttestationResponseBuilder.MandatoryStages builder() { |
+ +130 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAttestationResponse::builder → KILLED + + + + |
+ return new AuthenticatorAttestationResponseBuilder.MandatoryStages(); |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + public static class AuthenticatorAttestationResponseBuilder { |
+ +134 + | ++ + + + + + | + public static class MandatoryStages { |
+ +135 + | ++ + + + + + | + private final AuthenticatorAttestationResponseBuilder builder = |
+ +136 + | ++ + + + + + | + new AuthenticatorAttestationResponseBuilder(); |
+ +137 + | ++ + + + + + | +|
+ +138 + | ++ + + + + + | + /** |
+ +139 + | ++ + + + + + | + * {@link AuthenticatorAttestationResponseBuilder#attestationObject(ByteArray) |
+ +140 + | ++ + + + + + | + * attestationObject} is a required parameter. |
+ +141 + | ++ + + + + + | + * |
+ +142 + | ++ + + + + + | + * @see AuthenticatorAttestationResponseBuilder#attestationObject(ByteArray) |
+ +143 + | ++ + + + + + | + */ |
+ +144 + | ++ + + + + + | + public Step2 attestationObject(ByteArray attestationObject) { |
+ +145 + | ++ + + + + + | + builder.attestationObject(attestationObject); |
+ +146 + | +
+
+1
+
+1. attestationObject : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAttestationResponse$AuthenticatorAttestationResponseBuilder$MandatoryStages::attestationObject → KILLED + + + + |
+ return new Step2(); |
+ +147 + | ++ + + + + + | + } |
+ +148 + | ++ + + + + + | +|
+ +149 + | ++ + + + + + | + public class Step2 { |
+ +150 + | ++ + + + + + | + /** |
+ +151 + | ++ + + + + + | + * {@link AuthenticatorAttestationResponseBuilder#clientDataJSON(ByteArray) clientDataJSON} |
+ +152 + | ++ + + + + + | + * is a required parameter. |
+ +153 + | ++ + + + + + | + * |
+ +154 + | ++ + + + + + | + * @see AuthenticatorAttestationResponseBuilder#clientDataJSON(ByteArray) |
+ +155 + | ++ + + + + + | + */ |
+ +156 + | ++ + + + + + | + public AuthenticatorAttestationResponseBuilder clientDataJSON(ByteArray clientDataJSON) { |
+ +157 + | +
+
+1
+
+1. clientDataJSON : replaced return value with null for com/yubico/webauthn/data/AuthenticatorAttestationResponse$AuthenticatorAttestationResponseBuilder$MandatoryStages$Step2::clientDataJSON → KILLED + + + + |
+ return builder.clientDataJSON(clientDataJSON); |
+ +158 + | ++ + + + + + | + } |
+ +159 + | ++ + + + + + | + } |
+ +160 + | ++ + + + + + | + } |
+ +161 + | ++ + + + + + | + } |
+ +162 + | ++ + + + + + | +} |
Mutations | ||
100 | ++ |
+
+
+
+ 1.1 |
+
106 | ++ |
+
+
+
+ 1.1 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
113 | ++ |
+
+
+
+ 1.1 |
+
126 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
146 | ++ |
+
+
+
+ 1.1 |
+
157 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +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.fasterxml.jackson.core.JsonGenerator; |
+ +31 + | ++ + + + + + | +import com.fasterxml.jackson.databind.SerializerProvider; |
+ +32 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +33 + | ++ + + + + + | +import com.upokecenter.cbor.CBORException; |
+ +34 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +35 + | ++ + + + + + | +import com.yubico.internal.util.BinaryUtil; |
+ +36 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +37 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +38 + | ++ + + + + + | +import java.io.ByteArrayInputStream; |
+ +39 + | ++ + + + + + | +import java.io.IOException; |
+ +40 + | ++ + + + + + | +import java.util.Arrays; |
+ +41 + | ++ + + + + + | +import java.util.Optional; |
+ +42 + | ++ + + + + + | +import lombok.NonNull; |
+ +43 + | ++ + + + + + | +import lombok.Value; |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | +/** |
+ +46 + | ++ + + + + + | + * The authenticator data structure is a byte array of 37 bytes or more. This class presents the |
+ +47 + | ++ + + + + + | + * authenticator data decoded as a high-level object. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * <p>The authenticator data structure encodes contextual bindings made by the authenticator. These |
+ +50 + | ++ + + + + + | + * bindings are controlled by the authenticator itself, and derive their trust from the WebAuthn |
+ +51 + | ++ + + + + + | + * Relying Party's assessment of the security properties of the authenticator. In one extreme case, |
+ +52 + | ++ + + + + + | + * the authenticator may be embedded in the client, and its bindings may be no more trustworthy than |
+ +53 + | ++ + + + + + | + * the client data. At the other extreme, the authenticator may be a discrete entity with |
+ +54 + | ++ + + + + + | + * high-security hardware and software, connected to the client over a secure channel. In both |
+ +55 + | ++ + + + + + | + * cases, the Relying Party receives the authenticator data in the same format, and uses its |
+ +56 + | ++ + + + + + | + * knowledge of the authenticator to make trust decisions. |
+ +57 + | ++ + + + + + | + * |
+ +58 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1. |
+ +59 + | ++ + + + + + | + * Authenticator Data</a> |
+ +60 + | ++ + + + + + | + */ |
+ +61 + | ++ + + + + + | +@Value |
+ +62 + | ++ + + + + + | +@JsonSerialize(using = AuthenticatorData.JsonSerializer.class) |
+ +63 + | ++ + + + + + | +public class AuthenticatorData { |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + /** |
+ +66 + | ++ + + + + + | + * The original raw byte array that this object is decoded from. This is a byte array of 37 bytes |
+ +67 + | ++ + + + + + | + * or more. |
+ +68 + | ++ + + + + + | + * |
+ +69 + | ++ + + + + + | + * @see <a |
+ +70 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1. |
+ +71 + | ++ + + + + + | + * Authenticator Data</a> |
+ +72 + | ++ + + + + + | + */ |
+ +73 + | ++ + + + + + | + @NonNull private final ByteArray bytes; |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + /** The flags bit field. */ |
+ +76 + | ++ + + + + + | + @NonNull private final transient AuthenticatorDataFlags flags; |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + /** |
+ +79 + | ++ + + + + + | + * Attested credential data, if present. |
+ +80 + | ++ + + + + + | + * |
+ +81 + | ++ + + + + + | + * <p>This member is present if and only if the {@link AuthenticatorDataFlags#AT} flag is set. |
+ +82 + | ++ + + + + + | + * |
+ +83 + | ++ + + + + + | + * @see #flags |
+ +84 + | ++ + + + + + | + */ |
+ +85 + | ++ + + + + + | + @JsonIgnore private final transient AttestedCredentialData attestedCredentialData; |
+ +86 + | ++ + + + + + | +|
+ +87 + | ++ + + + + + | + @JsonIgnore private final transient CBORObject extensions; |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + private static final int RP_ID_HASH_INDEX = 0; |
+ +90 + | ++ + + + + + | + private static final int RP_ID_HASH_END = RP_ID_HASH_INDEX + 32; |
+ +91 + | ++ + + + + + | +|
+ +92 + | ++ + + + + + | + private static final int FLAGS_INDEX = RP_ID_HASH_END; |
+ +93 + | ++ + + + + + | + private static final int FLAGS_END = FLAGS_INDEX + 1; |
+ +94 + | ++ + + + + + | +|
+ +95 + | ++ + + + + + | + private static final int COUNTER_INDEX = FLAGS_END; |
+ +96 + | ++ + + + + + | + private static final int COUNTER_END = COUNTER_INDEX + 4; |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + private static final int FIXED_LENGTH_PART_END_INDEX = COUNTER_END; |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + /** Decode an {@link AuthenticatorData} object from a raw authenticator data byte array. */ |
+ +101 + | ++ + + + + + | + @JsonCreator |
+ +102 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ public AuthenticatorData(@NonNull ByteArray bytes) { |
+ +103 + | +
+
+1
+
+1. <init> : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +104 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : changed conditional boundary → KILLED + + + + |
+ bytes.size() >= FIXED_LENGTH_PART_END_INDEX, |
+ +105 + | ++ + + + + + | + "%s byte array must be at least %d bytes, was %d: %s", |
+ +106 + | ++ + + + + + | + AuthenticatorData.class.getSimpleName(), |
+ +107 + | ++ + + + + + | + FIXED_LENGTH_PART_END_INDEX, |
+ +108 + | ++ + + + + + | + bytes.size(), |
+ +109 + | ++ + + + + + | + bytes.getBase64Url()); |
+ +110 + | ++ + + + + + | +|
+ +111 + | ++ + + + + + | + this.bytes = bytes; |
+ +112 + | ++ + + + + + | +|
+ +113 + | ++ + + + + + | + final byte[] rawBytes = bytes.getBytes(); |
+ +114 + | ++ + + + + + | +|
+ +115 + | ++ + + + + + | + this.flags = new AuthenticatorDataFlags(rawBytes[FLAGS_INDEX]); |
+ +116 + | ++ + + + + + | +|
+ +117 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (flags.AT) { |
+ +118 + | ++ + + + + + | + VariableLengthParseResult parseResult = |
+ +119 + | ++ + + + + + | + parseAttestedCredentialData( |
+ +120 + | ++ + + + + + | + flags, Arrays.copyOfRange(rawBytes, FIXED_LENGTH_PART_END_INDEX, rawBytes.length)); |
+ +121 + | ++ + + + + + | + attestedCredentialData = parseResult.getAttestedCredentialData(); |
+ +122 + | ++ + + + + + | + extensions = parseResult.getExtensions(); |
+ +123 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ } else if (flags.ED) { |
+ +124 + | ++ + + + + + | + attestedCredentialData = null; |
+ +125 + | ++ + + + + + | + extensions = |
+ +126 + | ++ + + + + + | + parseExtensions( |
+ +127 + | ++ + + + + + | + Arrays.copyOfRange(rawBytes, FIXED_LENGTH_PART_END_INDEX, rawBytes.length)); |
+ +128 + | ++ + + + + + | + } else { |
+ +129 + | ++ + + + + + | + attestedCredentialData = null; |
+ +130 + | ++ + + + + + | + extensions = null; |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | + } |
+ +133 + | ++ + + + + + | +|
+ +134 + | ++ + + + + + | + /** The SHA-256 hash of the RP ID the credential is scoped to. */ |
+ +135 + | ++ + + + + + | + @JsonProperty("rpIdHash") |
+ +136 + | ++ + + + + + | + public ByteArray getRpIdHash() { |
+ +137 + | +
+
+1
+
+1. getRpIdHash : replaced return value with null for com/yubico/webauthn/data/AuthenticatorData::getRpIdHash → KILLED + + + + |
+ return new ByteArray(Arrays.copyOfRange(bytes.getBytes(), RP_ID_HASH_INDEX, RP_ID_HASH_END)); |
+ +138 + | ++ + + + + + | + } |
+ +139 + | ++ + + + + + | +|
+ +140 + | ++ + + + + + | + /** The 32-bit unsigned signature counter. */ |
+ +141 + | ++ + + + + + | + public long getSignatureCounter() { |
+ +142 + | +
+
+1
+
+1. getSignatureCounter : replaced long return with 0 for com/yubico/webauthn/data/AuthenticatorData::getSignatureCounter → KILLED + + + + |
+ return BinaryUtil.getUint32(Arrays.copyOfRange(bytes.getBytes(), COUNTER_INDEX, COUNTER_END)); |
+ +143 + | ++ + + + + + | + } |
+ +144 + | ++ + + + + + | +|
+ +145 + | ++ + + + + + | + private static VariableLengthParseResult parseAttestedCredentialData( |
+ +146 + | ++ + + + + + | + AuthenticatorDataFlags flags, byte[] bytes) { |
+ +147 + | ++ + + + + + | + final int AAGUID_INDEX = 0; |
+ +148 + | ++ + + + + + | + final int AAGUID_END = AAGUID_INDEX + 16; |
+ +149 + | ++ + + + + + | +|
+ +150 + | ++ + + + + + | + final int CREDENTIAL_ID_LENGTH_INDEX = AAGUID_END; |
+ +151 + | ++ + + + + + | + final int CREDENTIAL_ID_LENGTH_END = CREDENTIAL_ID_LENGTH_INDEX + 2; |
+ +152 + | ++ + + + + + | +|
+ +153 + | +
+
+3
+
+1. parseAttestedCredentialData : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. parseAttestedCredentialData : changed conditional boundary → KILLED +3. parseAttestedCredentialData : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +154 + | ++ + + + + + | + bytes.length >= CREDENTIAL_ID_LENGTH_END, |
+ +155 + | ++ + + + + + | + "Attested credential data must contain at least %d bytes, was %d: %s", |
+ +156 + | ++ + + + + + | + CREDENTIAL_ID_LENGTH_END, |
+ +157 + | ++ + + + + + | + bytes.length, |
+ +158 + | ++ + + + + + | + new ByteArray(bytes)); |
+ +159 + | ++ + + + + + | +|
+ +160 + | ++ + + + + + | + byte[] credentialIdLengthBytes = |
+ +161 + | ++ + + + + + | + Arrays.copyOfRange(bytes, CREDENTIAL_ID_LENGTH_INDEX, CREDENTIAL_ID_LENGTH_END); |
+ +162 + | ++ + + + + + | +|
+ +163 + | ++ + + + + + | + final int L; |
+ +164 + | ++ + + + + + | + try { |
+ +165 + | ++ + + + + + | + L = BinaryUtil.getUint16(credentialIdLengthBytes); |
+ +166 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +167 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +168 + | ++ + + + + + | + "Invalid credential ID length bytes: " + Arrays.asList(credentialIdLengthBytes), e); |
+ +169 + | ++ + + + + + | + } |
+ +170 + | ++ + + + + + | +|
+ +171 + | ++ + + + + + | + final int CREDENTIAL_ID_INDEX = CREDENTIAL_ID_LENGTH_END; |
+ +172 + | +
+
+1
+
+1. parseAttestedCredentialData : Replaced integer addition with subtraction → KILLED + + + + |
+ final int CREDENTIAL_ID_END = CREDENTIAL_ID_INDEX + L; |
+ +173 + | ++ + + + + + | +|
+ +174 + | ++ + + + + + | + final int CREDENTIAL_PUBLIC_KEY_INDEX = CREDENTIAL_ID_END; |
+ +175 + | ++ + + + + + | + final int CREDENTIAL_PUBLIC_KEY_AND_EXTENSION_DATA_END = bytes.length; |
+ +176 + | ++ + + + + + | +|
+ +177 + | +
+
+3
+
+1. parseAttestedCredentialData : changed conditional boundary → SURVIVED +2. parseAttestedCredentialData : negated conditional → KILLED +3. parseAttestedCredentialData : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +178 + | ++ + + + + + | + bytes.length >= CREDENTIAL_ID_END, |
+ +179 + | ++ + + + + + | + "Expected credential ID of length %d, but attested credential data and extension data is only %d bytes: %s", |
+ +180 + | ++ + + + + + | + CREDENTIAL_ID_END, |
+ +181 + | ++ + + + + + | + bytes.length, |
+ +182 + | ++ + + + + + | + new ByteArray(bytes)); |
+ +183 + | ++ + + + + + | +|
+ +184 + | ++ + + + + + | + ByteArrayInputStream indefiniteLengthBytes = |
+ +185 + | ++ + + + + + | + new ByteArrayInputStream( |
+ +186 + | ++ + + + + + | + Arrays.copyOfRange( |
+ +187 + | ++ + + + + + | + bytes, CREDENTIAL_PUBLIC_KEY_INDEX, CREDENTIAL_PUBLIC_KEY_AND_EXTENSION_DATA_END)); |
+ +188 + | ++ + + + + + | +|
+ +189 + | ++ + + + + + | + final CBORObject credentialPublicKey = CBORObject.Read(indefiniteLengthBytes); |
+ +190 + | ++ + + + + + | + final CBORObject extensions; |
+ +191 + | ++ + + + + + | +|
+ +192 + | +
+
+2
+
+1. parseAttestedCredentialData : changed conditional boundary → KILLED +2. parseAttestedCredentialData : negated conditional → KILLED + + + + |
+ if (indefiniteLengthBytes.available() > 0) { |
+ +193 + | +
+
+1
+
+1. parseAttestedCredentialData : negated conditional → KILLED + + + + |
+ if (flags.ED) { |
+ +194 + | ++ + + + + + | + try { |
+ +195 + | ++ + + + + + | + extensions = CBORObject.Read(indefiniteLengthBytes); |
+ +196 + | ++ + + + + + | + } catch (CBORException e) { |
+ +197 + | ++ + + + + + | + throw new IllegalArgumentException("Failed to parse extension data", e); |
+ +198 + | ++ + + + + + | + } |
+ +199 + | ++ + + + + + | + } else { |
+ +200 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +201 + | ++ + + + + + | + String.format( |
+ +202 + | ++ + + + + + | + "Flags indicate no extension data, but %d bytes remain after attested credential data.", |
+ +203 + | ++ + + + + + | + indefiniteLengthBytes.available())); |
+ +204 + | ++ + + + + + | + } |
+ +205 + | ++ + + + + + | + } else { |
+ +206 + | +
+
+1
+
+1. parseAttestedCredentialData : negated conditional → KILLED + + + + |
+ if (flags.ED) { |
+ +207 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +208 + | ++ + + + + + | + "Flags indicate there should be extension data, but no bytes remain after attested credential data."); |
+ +209 + | ++ + + + + + | + } else { |
+ +210 + | ++ + + + + + | + extensions = null; |
+ +211 + | ++ + + + + + | + } |
+ +212 + | ++ + + + + + | + } |
+ +213 + | ++ + + + + + | +|
+ +214 + | +
+
+1
+
+1. parseAttestedCredentialData : replaced return value with null for com/yubico/webauthn/data/AuthenticatorData::parseAttestedCredentialData → KILLED + + + + |
+ return new VariableLengthParseResult( |
+ +215 + | ++ + + + + + | + AttestedCredentialData.builder() |
+ +216 + | ++ + + + + + | + .aaguid(new ByteArray(Arrays.copyOfRange(bytes, AAGUID_INDEX, AAGUID_END))) |
+ +217 + | ++ + + + + + | + .credentialId( |
+ +218 + | ++ + + + + + | + new ByteArray(Arrays.copyOfRange(bytes, CREDENTIAL_ID_INDEX, CREDENTIAL_ID_END))) |
+ +219 + | ++ + + + + + | + .credentialPublicKey(new ByteArray(credentialPublicKey.EncodeToBytes())) |
+ +220 + | ++ + + + + + | + .build(), |
+ +221 + | ++ + + + + + | + extensions); |
+ +222 + | ++ + + + + + | + } |
+ +223 + | ++ + + + + + | +|
+ +224 + | ++ + + + + + | + private static CBORObject parseExtensions(byte[] bytes) { |
+ +225 + | ++ + + + + + | + try { |
+ +226 + | +
+
+1
+
+1. parseExtensions : replaced return value with null for com/yubico/webauthn/data/AuthenticatorData::parseExtensions → KILLED + + + + |
+ return CBORObject.DecodeFromBytes(bytes); |
+ +227 + | ++ + + + + + | + } catch (CBORException e) { |
+ +228 + | ++ + + + + + | + throw new IllegalArgumentException("Failed to parse extension data", e); |
+ +229 + | ++ + + + + + | + } |
+ +230 + | ++ + + + + + | + } |
+ +231 + | ++ + + + + + | +|
+ +232 + | ++ + + + + + | + @Value |
+ +233 + | ++ + + + + + | + private static class VariableLengthParseResult { |
+ +234 + | ++ + + + + + | + AttestedCredentialData attestedCredentialData; |
+ +235 + | ++ + + + + + | + CBORObject extensions; |
+ +236 + | ++ + + + + + | + } |
+ +237 + | ++ + + + + + | +|
+ +238 + | ++ + + + + + | + /** |
+ +239 + | ++ + + + + + | + * Attested credential data, if present. |
+ +240 + | ++ + + + + + | + * |
+ +241 + | ++ + + + + + | + * <p>This member is present if and only if the {@link AuthenticatorDataFlags#AT} flag is set. |
+ +242 + | ++ + + + + + | + * |
+ +243 + | ++ + + + + + | + * @see #flags |
+ +244 + | ++ + + + + + | + */ |
+ +245 + | ++ + + + + + | + public Optional<AttestedCredentialData> getAttestedCredentialData() { |
+ +246 + | +
+
+1
+
+1. getAttestedCredentialData : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorData::getAttestedCredentialData → KILLED + + + + |
+ return Optional.ofNullable(attestedCredentialData); |
+ +247 + | ++ + + + + + | + } |
+ +248 + | ++ + + + + + | +|
+ +249 + | ++ + + + + + | + /** |
+ +250 + | ++ + + + + + | + * Extension-defined authenticator data, if present. |
+ +251 + | ++ + + + + + | + * |
+ +252 + | ++ + + + + + | + * <p>This member is present if and only if the {@link AuthenticatorDataFlags#ED} flag is set. |
+ +253 + | ++ + + + + + | + * |
+ +254 + | ++ + + + + + | + * <p>Changes to the returned value are not reflected in the {@link AuthenticatorData} object. |
+ +255 + | ++ + + + + + | + * |
+ +256 + | ++ + + + + + | + * @see #flags |
+ +257 + | ++ + + + + + | + */ |
+ +258 + | ++ + + + + + | + public Optional<CBORObject> getExtensions() { |
+ +259 + | +
+
+1
+
+1. getExtensions : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorData::getExtensions → KILLED + + + + |
+ return Optional.ofNullable(extensions).map(JacksonCodecs::deepCopy); |
+ +260 + | ++ + + + + + | + } |
+ +261 + | ++ + + + + + | +|
+ +262 + | ++ + + + + + | + static class JsonSerializer |
+ +263 + | ++ + + + + + | + extends com.fasterxml.jackson.databind.JsonSerializer<AuthenticatorData> { |
+ +264 + | ++ + + + + + | + @Override |
+ +265 + | ++ + + + + + | + public void serialize( |
+ +266 + | ++ + + + + + | + AuthenticatorData value, JsonGenerator gen, SerializerProvider serializers) |
+ +267 + | ++ + + + + + | + throws IOException { |
+ +268 + | +
+
+1
+
+1. serialize : removed call to com/fasterxml/jackson/core/JsonGenerator::writeString → KILLED + + + + |
+ gen.writeString(value.getBytes().getBase64Url()); |
+ +269 + | ++ + + + + + | + } |
+ +270 + | ++ + + + + + | + } |
+ +271 + | ++ + + + + + | +} |
Mutations | ||
102 | ++ |
+
+
+
+ 1.1 |
+
103 | ++ |
+
+
+
+ 1.1 |
+
104 | ++ |
+
+
+
+ 1.1 2.2 |
+
117 | ++ |
+
+
+
+ 1.1 |
+
123 | ++ |
+
+
+
+ 1.1 |
+
137 | ++ |
+
+
+
+ 1.1 |
+
142 | ++ |
+
+
+
+ 1.1 |
+
153 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
172 | ++ |
+
+
+
+ 1.1 |
+
177 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
192 | ++ |
+
+
+
+ 1.1 2.2 |
+
193 | ++ |
+
+
+
+ 1.1 |
+
206 | ++ |
+
+
+
+ 1.1 |
+
214 | ++ |
+
+
+
+ 1.1 |
+
226 | ++ |
+
+
+
+ 1.1 |
+
246 | ++ |
+
+
+
+ 1.1 |
+
259 | ++ |
+
+
+
+ 1.1 |
+
268 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import lombok.EqualsAndHashCode; |
+ +30 + | ++ + + + + + | +import lombok.ToString; |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | +/** |
+ +33 + | ++ + + + + + | + * The flags bit field of an authenticator data structure, decoded as a high-level object. |
+ +34 + | ++ + + + + + | + * |
+ +35 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#flags">Table 1</a> |
+ +36 + | ++ + + + + + | + */ |
+ +37 + | ++ + + + + + | +@ToString |
+ +38 + | ++ + + + + + | +@EqualsAndHashCode |
+ +39 + | ++ + + + + + | +public final class AuthenticatorDataFlags { |
+ +40 + | ++ + + + + + | + public final byte value; |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | + /** User present */ |
+ +43 + | ++ + + + + + | + public final boolean UP; |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** User verified */ |
+ +46 + | ++ + + + + + | + public final boolean UV; |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + /** |
+ +49 + | ++ + + + + + | + * Backup eligible: the credential can and is allowed to be backed up. |
+ +50 + | ++ + + + + + | + * |
+ +51 + | ++ + + + + + | + * <p>NOTE that this is only a hint and not a guarantee, unless backed by a trusted authenticator |
+ +52 + | ++ + + + + + | + * attestation. |
+ +53 + | ++ + + + + + | + * |
+ +54 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-be">§6.1. Authenticator Data</a> |
+ +55 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +56 + | ++ + + + + + | + * the standard matures. |
+ +57 + | ++ + + + + + | + */ |
+ +58 + | ++ + + + + + | + @Deprecated public final boolean BE; |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + /** |
+ +61 + | ++ + + + + + | + * Backup status: the credential is currently backed up. |
+ +62 + | ++ + + + + + | + * |
+ +63 + | ++ + + + + + | + * <p>NOTE that this is only a hint and not a guarantee, unless backed by a trusted authenticator |
+ +64 + | ++ + + + + + | + * attestation. |
+ +65 + | ++ + + + + + | + * |
+ +66 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">§6.1. Authenticator Data</a> |
+ +67 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +68 + | ++ + + + + + | + * the standard matures. |
+ +69 + | ++ + + + + + | + */ |
+ +70 + | ++ + + + + + | + @Deprecated public final boolean BS; |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + /** |
+ +73 + | ++ + + + + + | + * Attested credential data present. |
+ +74 + | ++ + + + + + | + * |
+ +75 + | ++ + + + + + | + * <p>Users of this library should not need to inspect this value directly. |
+ +76 + | ++ + + + + + | + * |
+ +77 + | ++ + + + + + | + * @see AuthenticatorData#getAttestedCredentialData() |
+ +78 + | ++ + + + + + | + */ |
+ +79 + | ++ + + + + + | + public final boolean AT; |
+ +80 + | ++ + + + + + | +|
+ +81 + | ++ + + + + + | + /** |
+ +82 + | ++ + + + + + | + * Extension data present. |
+ +83 + | ++ + + + + + | + * |
+ +84 + | ++ + + + + + | + * @see AuthenticatorData#getExtensions() |
+ +85 + | ++ + + + + + | + */ |
+ +86 + | ++ + + + + + | + public final boolean ED; |
+ +87 + | ++ + + + + + | +|
+ +88 + | ++ + + + + + | + /** Decode an {@link AuthenticatorDataFlags} object from a raw bit field byte. */ |
+ +89 + | ++ + + + + + | + @JsonCreator |
+ +90 + | ++ + + + + + | + public AuthenticatorDataFlags(@JsonProperty("value") byte value) { |
+ +91 + | ++ + + + + + | + this.value = value; |
+ +92 + | ++ + + + + + | +|
+ +93 + | +
+
+2
+
+1. <init> : Replaced bitwise AND with OR → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ UP = (value & Bitmasks.UP) != 0; |
+ +94 + | +
+
+2
+
+1. <init> : Replaced bitwise AND with OR → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ UV = (value & Bitmasks.UV) != 0; |
+ +95 + | +
+
+2
+
+1. <init> : Replaced bitwise AND with OR → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ BE = (value & Bitmasks.BE) != 0; |
+ +96 + | +
+
+2
+
+1. <init> : Replaced bitwise AND with OR → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ BS = (value & Bitmasks.BS) != 0; |
+ +97 + | +
+
+2
+
+1. <init> : Replaced bitwise AND with OR → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ AT = (value & Bitmasks.AT) != 0; |
+ +98 + | +
+
+2
+
+1. <init> : Replaced bitwise AND with OR → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ ED = (value & Bitmasks.ED) != 0; |
+ +99 + | ++ + + + + + | +|
+ +100 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ if (BS && !BE) { |
+ +101 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +102 + | ++ + + + + + | + String.format("Flag combination is invalid: BE=0, BS=1 in flags: 0x%02x", value)); |
+ +103 + | ++ + + + + + | + } |
+ +104 + | ++ + + + + + | + } |
+ +105 + | ++ + + + + + | +|
+ +106 + | ++ + + + + + | + private static final class Bitmasks { |
+ +107 + | ++ + + + + + | + static final byte UP = 0x01; |
+ +108 + | ++ + + + + + | + static final byte UV = 0x04; |
+ +109 + | ++ + + + + + | + static final byte BE = 0x08; |
+ +110 + | ++ + + + + + | + static final byte BS = 0x10; |
+ +111 + | ++ + + + + + | + static final byte AT = 0x40; |
+ +112 + | ++ + + + + + | + static final byte ED = -0x80; |
+ +113 + | ++ + + + + + | +|
+ +114 + | ++ + + + + + | + /* Reserved bits */ |
+ +115 + | ++ + + + + + | + // static final byte RFU1 = 0x02; |
+ +116 + | ++ + + + + + | + // static final byte RFU2 = 0x20; |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +} |
Mutations | ||
93 | ++ |
+
+
+
+ 1.1 2.2 |
+
94 | ++ |
+
+
+
+ 1.1 2.2 |
+
95 | ++ |
+
+
+
+ 1.1 2.2 |
+
96 | ++ |
+
+
+
+ 1.1 2.2 |
+
97 | ++ |
+
+
+
+ 1.1 2.2 |
+
98 | ++ |
+
+
+
+ 1.1 2.2 |
+
100 | ++ |
+
+
+
+ 1.1 2.2 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +30 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +32 + | ++ + + + + + | +import java.util.HashSet; |
+ +33 + | ++ + + + + + | +import java.util.List; |
+ +34 + | ++ + + + + + | +import java.util.Optional; |
+ +35 + | ++ + + + + + | +import java.util.Set; |
+ +36 + | ++ + + + + + | +import lombok.Builder; |
+ +37 + | ++ + + + + + | +import lombok.EqualsAndHashCode; |
+ +38 + | ++ + + + + + | +import lombok.Value; |
+ +39 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | +/** |
+ +42 + | ++ + + + + + | + * Contains <a |
+ +43 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +44 + | ++ + + + + + | + * extension outputs</a> from a <code>navigator.credentials.create()</code> operation. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * <p>Note that there is no guarantee that any extension input present in {@link |
+ +47 + | ++ + + + + + | + * RegistrationExtensionInputs} will have a corresponding output present here. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * <p>The values contained here are parsed from the {@link AuthenticatorData} structure. |
+ +50 + | ++ + + + + + | + * |
+ +51 + | ++ + + + + + | + * <p>The client extension outputs are represented by the {@link ClientRegistrationExtensionOutputs} |
+ +52 + | ++ + + + + + | + * type. |
+ +53 + | ++ + + + + + | + * |
+ +54 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9. WebAuthn |
+ +55 + | ++ + + + + + | + * Extensions</a> |
+ +56 + | ++ + + + + + | + */ |
+ +57 + | ++ + + + + + | +@Value |
+ +58 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +59 + | ++ + + + + + | +@Slf4j |
+ +60 + | ++ + + + + + | +@JsonIgnoreProperties(ignoreUnknown = true) |
+ +61 + | ++ + + + + + | +public final class AuthenticatorRegistrationExtensionOutputs |
+ +62 + | ++ + + + + + | + implements AuthenticatorExtensionOutputs { |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + private final List<Extensions.Uvm.UvmEntry> uvm; |
+ +65 + | ++ + + + + + | +|
+ +66 + | ++ + + + + + | + @JsonCreator |
+ +67 + | ++ + + + + + | + private AuthenticatorRegistrationExtensionOutputs( |
+ +68 + | ++ + + + + + | + @JsonProperty("uvm") List<Extensions.Uvm.UvmEntry> uvm) { |
+ +69 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ this.uvm = uvm == null ? null : CollectionUtil.immutableList(uvm); |
+ +70 + | ++ + + + + + | + } |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + /** |
+ +73 + | ++ + + + + + | + * Parse <a |
+ +74 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#registration-extension">registration</a> |
+ +75 + | ++ + + + + + | + * <a |
+ +76 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +77 + | ++ + + + + + | + * extension outputs</a> from the given authenticator data. |
+ +78 + | ++ + + + + + | + * |
+ +79 + | ++ + + + + + | + * <p>If the <code>authData</code> does not contain authenticator extension outputs, this returns |
+ +80 + | ++ + + + + + | + * an empty {@link Optional}. |
+ +81 + | ++ + + + + + | + * |
+ +82 + | ++ + + + + + | + * <p>Otherwise, this returns a present {@link Optional} containing an {@link |
+ +83 + | ++ + + + + + | + * AuthenticatorRegistrationExtensionOutputs} value with all validly-formatted <a |
+ +84 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#registration-extension">registration</a> |
+ +85 + | ++ + + + + + | + * <a |
+ +86 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">extension |
+ +87 + | ++ + + + + + | + * outputs</a> supported by this library. This silently ignores <a |
+ +88 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authentication-extension">authentication</a> |
+ +89 + | ++ + + + + + | + * extension outputs, malformed extension outputs, and unsupported extensions. The raw set of |
+ +90 + | ++ + + + + + | + * extension outputs can instead be obtained via {@link AuthenticatorData#getExtensions()}. |
+ +91 + | ++ + + + + + | + * |
+ +92 + | ++ + + + + + | + * <p>Note that a present {@link AuthenticatorRegistrationExtensionOutputs} may contain zero |
+ +93 + | ++ + + + + + | + * extension outputs. |
+ +94 + | ++ + + + + + | + * |
+ +95 + | ++ + + + + + | + * @param authData the <a |
+ +96 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-data">authenticator |
+ +97 + | ++ + + + + + | + * data</a> to parse extension outputs from |
+ +98 + | ++ + + + + + | + * @return an empty {@link Optional} if the <code>authData</code> does not contain authenticator |
+ +99 + | ++ + + + + + | + * extension outputs. Otherwise a present {@link Optional} containing parsed extension output |
+ +100 + | ++ + + + + + | + * values. |
+ +101 + | ++ + + + + + | + */ |
+ +102 + | ++ + + + + + | + public static Optional<AuthenticatorRegistrationExtensionOutputs> fromAuthenticatorData( |
+ +103 + | ++ + + + + + | + AuthenticatorData authData) { |
+ +104 + | +
+
+1
+
+1. fromAuthenticatorData : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorRegistrationExtensionOutputs::fromAuthenticatorData → KILLED + + + + |
+ return authData.getExtensions().flatMap(AuthenticatorRegistrationExtensionOutputs::fromCbor); |
+ +105 + | ++ + + + + + | + } |
+ +106 + | ++ + + + + + | +|
+ +107 + | ++ + + + + + | + static Optional<AuthenticatorRegistrationExtensionOutputs> fromCbor(CBORObject cbor) { |
+ +108 + | ++ + + + + + | + AuthenticatorRegistrationExtensionOutputsBuilder b = builder(); |
+ +109 + | ++ + + + + + | +|
+ +110 + | +
+
+1
+
+1. fromCbor : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ Extensions.Uvm.parseAuthenticatorExtensionOutput(cbor).ifPresent(b::uvm); |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + AuthenticatorRegistrationExtensionOutputs result = b.build(); |
+ +113 + | ++ + + + + + | +|
+ +114 + | +
+
+1
+
+1. fromCbor : negated conditional → KILLED + + + + |
+ if (result.getExtensionIds().isEmpty()) { |
+ +115 + | ++ + + + + + | + return Optional.empty(); |
+ +116 + | ++ + + + + + | + } else { |
+ +117 + | +
+
+1
+
+1. fromCbor : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorRegistrationExtensionOutputs::fromCbor → KILLED + + + + |
+ return Optional.of(result); |
+ +118 + | ++ + + + + + | + } |
+ +119 + | ++ + + + + + | + } |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + @Override |
+ +122 + | ++ + + + + + | + @EqualsAndHashCode.Include |
+ +123 + | ++ + + + + + | + public Set<String> getExtensionIds() { |
+ +124 + | ++ + + + + + | + HashSet<String> ids = new HashSet<>(); |
+ +125 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (uvm != null) { |
+ +126 + | ++ + + + + + | + ids.add(Extensions.Uvm.EXTENSION_ID); |
+ +127 + | ++ + + + + + | + } |
+ +128 + | +
+
+1
+
+1. getExtensionIds : replaced return value with Collections.emptySet for com/yubico/webauthn/data/AuthenticatorRegistrationExtensionOutputs::getExtensionIds → KILLED + + + + |
+ return ids; |
+ +129 + | ++ + + + + + | + } |
+ +130 + | ++ + + + + + | +|
+ +131 + | ++ + + + + + | + /** |
+ +132 + | ++ + + + + + | + * @return The <a |
+ +133 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +134 + | ++ + + + + + | + * extension output</a> for the <a |
+ +135 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">User |
+ +136 + | ++ + + + + + | + * Verification Method (<code>uvm</code>) extension</a>, if any. |
+ +137 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +138 + | ++ + + + + + | + * User Verification Method extension (uvm)</a> |
+ +139 + | ++ + + + + + | + */ |
+ +140 + | ++ + + + + + | + public Optional<List<Extensions.Uvm.UvmEntry>> getUvm() { |
+ +141 + | +
+
+1
+
+1. getUvm : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorRegistrationExtensionOutputs::getUvm → KILLED + + + + |
+ return Optional.ofNullable(uvm); |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | +} |
Mutations | ||
69 | ++ |
+
+
+
+ 1.1 |
+
104 | ++ |
+
+
+
+ 1.1 |
+
110 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
117 | ++ |
+
+
+
+ 1.1 |
+
125 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnore; |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | +/** |
+ +30 + | ++ + + + + + | + * Authenticators respond to Relying Party requests by returning an object derived from the {@link |
+ +31 + | ++ + + + + + | + * AuthenticatorResponse} interface. |
+ +32 + | ++ + + + + + | + * |
+ +33 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticatorresponse">§5.2. |
+ +34 + | ++ + + + + + | + * Authenticator Responses (interface AuthenticatorResponse) </a> |
+ +35 + | ++ + + + + + | + */ |
+ +36 + | ++ + + + + + | +public interface AuthenticatorResponse { |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + /** |
+ +39 + | ++ + + + + + | + * The authenticator data returned by the authenticator. See <a |
+ +40 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1 |
+ +41 + | ++ + + + + + | + * Authenticator Data</a>. |
+ +42 + | ++ + + + + + | + */ |
+ +43 + | ++ + + + + + | + ByteArray getAuthenticatorData(); |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** {@link #getAuthenticatorData()} parsed as a domain object. */ |
+ +46 + | ++ + + + + + | + @JsonIgnore |
+ +47 + | ++ + + + + + | + default AuthenticatorData getParsedAuthenticatorData() { |
+ +48 + | +
+
+1
+
+1. getParsedAuthenticatorData : replaced return value with null for com/yubico/webauthn/data/AuthenticatorResponse::getParsedAuthenticatorData → NO_COVERAGE + + + + |
+ return new AuthenticatorData(getAuthenticatorData()); |
+ +49 + | ++ + + + + + | + } |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + /** |
+ +52 + | ++ + + + + + | + * The JSON-serialized client data (see <a |
+ +53 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictionary-client-data">§5.10.1 |
+ +54 + | ++ + + + + + | + * Client Data Used in WebAuthn Signatures</a> (dictionary {@link CollectedClientData})) passed to |
+ +55 + | ++ + + + + + | + * the authenticator by the client in the call to either <code>navigator.credentials.create() |
+ +56 + | ++ + + + + + | + * </code> or <code>navigator.credentials.get()</code>. The exact JSON serialization MUST be |
+ +57 + | ++ + + + + + | + * preserved, as the hash of the serialized client data has been computed over it. |
+ +58 + | ++ + + + + + | + */ |
+ +59 + | ++ + + + + + | + ByteArray getClientDataJSON(); |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** {@link #getClientDataJSON()} parsed as a domain object. */ |
+ +62 + | ++ + + + + + | + @JsonIgnore |
+ +63 + | ++ + + + + + | + CollectedClientData getClientData(); |
+ +64 + | ++ + + + + + | +} |
Mutations | ||
48 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +import lombok.Builder; |
+ +31 + | ++ + + + + + | +import lombok.NonNull; |
+ +32 + | ++ + + + + + | +import lombok.Value; |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | +/** |
+ +35 + | ++ + + + + + | + * This class may be used to specify requirements regarding authenticator attributes. |
+ +36 + | ++ + + + + + | + * |
+ +37 + | ++ + + + + + | + * @see <a |
+ +38 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-authenticatorselectioncriteria">§5.4.4. |
+ +39 + | ++ + + + + + | + * Authenticator Selection Criteria (dictionary AuthenticatorSelectionCriteria) </a> |
+ +40 + | ++ + + + + + | + */ |
+ +41 + | ++ + + + + + | +@Value |
+ +42 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +43 + | ++ + + + + + | +public class AuthenticatorSelectionCriteria { |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** |
+ +46 + | ++ + + + + + | + * If present, eligible authenticators are filtered to only authenticators attached with the |
+ +47 + | ++ + + + + + | + * specified <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-attachment">§5.4.5 |
+ +48 + | ++ + + + + + | + * Authenticator Attachment Enumeration (enum AuthenticatorAttachment)</a>. |
+ +49 + | ++ + + + + + | + */ |
+ +50 + | ++ + + + + + | + private final AuthenticatorAttachment authenticatorAttachment; |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * Specifies the extent to which the Relying Party desires to create a client-side discoverable |
+ +54 + | ++ + + + + + | + * credential (passkey). For historical reasons the naming retains the deprecated “resident” |
+ +55 + | ++ + + + + + | + * terminology. |
+ +56 + | ++ + + + + + | + * |
+ +57 + | ++ + + + + + | + * <p>When this is set, {@link PublicKeyCredentialCreationOptions#toCredentialsCreateJson()} will |
+ +58 + | ++ + + + + + | + * also emit a <a |
+ +59 + | ++ + + + + + | + * href="https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-requireresidentkey"> |
+ +60 + | ++ + + + + + | + * <code>requireResidentKey</code></a> member for backwards compatibility with WebAuthn Level 1. |
+ +61 + | ++ + + + + + | + * It will be set to <code>true</code> if this is set to {@link ResidentKeyRequirement#REQUIRED |
+ +62 + | ++ + + + + + | + * REQUIRED} and <code>false</code> if this is set to anything else. When this is not set, a |
+ +63 + | ++ + + + + + | + * <code>requireResidentKey</code> member will not be emitted. |
+ +64 + | ++ + + + + + | + * |
+ +65 + | ++ + + + + + | + * <p>When not set, the default in the browser is {@link ResidentKeyRequirement#DISCOURAGED}. |
+ +66 + | ++ + + + + + | + * |
+ +67 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +68 + | ++ + + + + + | + * |
+ +69 + | ++ + + + + + | + * @see ResidentKeyRequirement |
+ +70 + | ++ + + + + + | + * @see <a |
+ +71 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +72 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +73 + | ++ + + + + + | + * @see <a |
+ +74 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +75 + | ++ + + + + + | + * discoverable Credential</a> |
+ +76 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +77 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +78 + | ++ + + + + + | + */ |
+ +79 + | ++ + + + + + | + private final ResidentKeyRequirement residentKey; |
+ +80 + | ++ + + + + + | +|
+ +81 + | ++ + + + + + | + /** |
+ +82 + | ++ + + + + + | + * Describes the Relying Party's requirements regarding <a |
+ +83 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">user |
+ +84 + | ++ + + + + + | + * verification</a> for the <code>navigator.credentials.create()</code> operation. Eligible |
+ +85 + | ++ + + + + + | + * authenticators are filtered to only those capable of satisfying this requirement. |
+ +86 + | ++ + + + + + | + * |
+ +87 + | ++ + + + + + | + * <p>By default, this is not set. When not set, the default in the browser is {@link |
+ +88 + | ++ + + + + + | + * UserVerificationRequirement#PREFERRED}. |
+ +89 + | ++ + + + + + | + * |
+ +90 + | ++ + + + + + | + * @see UserVerificationRequirement |
+ +91 + | ++ + + + + + | + * @see <a |
+ +92 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-userVerificationRequirement">§5.8.6. |
+ +93 + | ++ + + + + + | + * User Verification Requirement Enumeration (enum UserVerificationRequirement)</a> |
+ +94 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">User |
+ +95 + | ++ + + + + + | + * Verification</a> |
+ +96 + | ++ + + + + + | + */ |
+ +97 + | ++ + + + + + | + private UserVerificationRequirement userVerification; |
+ +98 + | ++ + + + + + | +|
+ +99 + | ++ + + + + + | + /** |
+ +100 + | ++ + + + + + | + * If present, eligible authenticators are filtered to only authenticators attached with the |
+ +101 + | ++ + + + + + | + * specified <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-attachment">§5.4.5 |
+ +102 + | ++ + + + + + | + * Authenticator Attachment Enumeration (enum AuthenticatorAttachment)</a>. |
+ +103 + | ++ + + + + + | + */ |
+ +104 + | ++ + + + + + | + public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() { |
+ +105 + | +
+
+1
+
+1. getAuthenticatorAttachment : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorSelectionCriteria::getAuthenticatorAttachment → KILLED + + + + |
+ return Optional.ofNullable(authenticatorAttachment); |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + /** |
+ +109 + | ++ + + + + + | + * Specifies the extent to which the Relying Party desires to create a client-side discoverable |
+ +110 + | ++ + + + + + | + * credential (passkey). For historical reasons the naming retains the deprecated “resident” |
+ +111 + | ++ + + + + + | + * terminology. |
+ +112 + | ++ + + + + + | + * |
+ +113 + | ++ + + + + + | + * <p>When this is set, {@link PublicKeyCredentialCreationOptions#toCredentialsCreateJson()} will |
+ +114 + | ++ + + + + + | + * also emit a <a |
+ +115 + | ++ + + + + + | + * href="https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-requireresidentkey"> |
+ +116 + | ++ + + + + + | + * <code>requireResidentKey</code></a> member for backwards compatibility with WebAuthn Level 1. |
+ +117 + | ++ + + + + + | + * It will be set to <code>true</code> if this is set to {@link ResidentKeyRequirement#REQUIRED |
+ +118 + | ++ + + + + + | + * REQUIRED} and <code>false</code> if this is set to anything else. When this is not set, a |
+ +119 + | ++ + + + + + | + * <code>requireResidentKey</code> member will not be emitted. |
+ +120 + | ++ + + + + + | + * |
+ +121 + | ++ + + + + + | + * <p>When not set, the default in the browser is {@link ResidentKeyRequirement#DISCOURAGED}. |
+ +122 + | ++ + + + + + | + * |
+ +123 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +124 + | ++ + + + + + | + * |
+ +125 + | ++ + + + + + | + * @see ResidentKeyRequirement |
+ +126 + | ++ + + + + + | + * @see <a |
+ +127 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +128 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +129 + | ++ + + + + + | + * @see <a |
+ +130 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +131 + | ++ + + + + + | + * discoverable Credential</a> |
+ +132 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +133 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +134 + | ++ + + + + + | + */ |
+ +135 + | ++ + + + + + | + public Optional<ResidentKeyRequirement> getResidentKey() { |
+ +136 + | +
+
+1
+
+1. getResidentKey : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorSelectionCriteria::getResidentKey → KILLED + + + + |
+ return Optional.ofNullable(residentKey); |
+ +137 + | ++ + + + + + | + } |
+ +138 + | ++ + + + + + | +|
+ +139 + | ++ + + + + + | + /** |
+ +140 + | ++ + + + + + | + * For backwards compatibility with <code>requireResidentKey</code>. |
+ +141 + | ++ + + + + + | + * |
+ +142 + | ++ + + + + + | + * @see <a |
+ +143 + | ++ + + + + + | + * href="https://www.w3.org/TR/webauthn-2/#dom-authenticatorselectioncriteria-requireresidentkey">5.4.4. |
+ +144 + | ++ + + + + + | + * Authenticator Selection Criteria (dictionary AuthenticatorSelectionCriteria) member |
+ +145 + | ++ + + + + + | + * requireResidentKey</a> |
+ +146 + | ++ + + + + + | + */ |
+ +147 + | ++ + + + + + | + @JsonProperty |
+ +148 + | ++ + + + + + | + private Boolean isRequireResidentKey() { |
+ +149 + | +
+
+4
+
+1. lambda$isRequireResidentKey$0 : negated conditional → KILLED +2. isRequireResidentKey : replaced Boolean return with False for com/yubico/webauthn/data/AuthenticatorSelectionCriteria::isRequireResidentKey → KILLED +3. lambda$isRequireResidentKey$0 : replaced Boolean return with True for com/yubico/webauthn/data/AuthenticatorSelectionCriteria::lambda$isRequireResidentKey$0 → KILLED +4. isRequireResidentKey : replaced Boolean return with True for com/yubico/webauthn/data/AuthenticatorSelectionCriteria::isRequireResidentKey → KILLED + + + + |
+ return getResidentKey().map(rk -> rk == ResidentKeyRequirement.REQUIRED).orElse(null); |
+ +150 + | ++ + + + + + | + } |
+ +151 + | ++ + + + + + | +|
+ +152 + | ++ + + + + + | + /** |
+ +153 + | ++ + + + + + | + * Describes the Relying Party's requirements regarding <a |
+ +154 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">user |
+ +155 + | ++ + + + + + | + * verification</a> for the <code>navigator.credentials.create()</code> operation. Eligible |
+ +156 + | ++ + + + + + | + * authenticators are filtered to only those capable of satisfying this requirement. |
+ +157 + | ++ + + + + + | + * |
+ +158 + | ++ + + + + + | + * <p>By default, this is not set. When not set, the default in the browser is {@link |
+ +159 + | ++ + + + + + | + * UserVerificationRequirement#PREFERRED}. |
+ +160 + | ++ + + + + + | + * |
+ +161 + | ++ + + + + + | + * @see UserVerificationRequirement |
+ +162 + | ++ + + + + + | + * @see <a |
+ +163 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-userVerificationRequirement">§5.8.6. |
+ +164 + | ++ + + + + + | + * User Verification Requirement Enumeration (enum UserVerificationRequirement)</a> |
+ +165 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">User |
+ +166 + | ++ + + + + + | + * Verification</a> |
+ +167 + | ++ + + + + + | + */ |
+ +168 + | ++ + + + + + | + public Optional<UserVerificationRequirement> getUserVerification() { |
+ +169 + | +
+
+1
+
+1. getUserVerification : replaced return value with Optional.empty for com/yubico/webauthn/data/AuthenticatorSelectionCriteria::getUserVerification → KILLED + + + + |
+ return Optional.ofNullable(userVerification); |
+ +170 + | ++ + + + + + | + } |
+ +171 + | ++ + + + + + | +|
+ +172 + | ++ + + + + + | + @JsonCreator |
+ +173 + | ++ + + + + + | + private AuthenticatorSelectionCriteria( |
+ +174 + | ++ + + + + + | + @JsonProperty("authenticatorAttachment") AuthenticatorAttachment authenticatorAttachment, |
+ +175 + | ++ + + + + + | + @JsonProperty("requireResidentKey") Boolean requireResidentKey, |
+ +176 + | ++ + + + + + | + @JsonProperty("residentKey") ResidentKeyRequirement residentKey, |
+ +177 + | ++ + + + + + | + @JsonProperty("userVerification") UserVerificationRequirement userVerification) { |
+ +178 + | ++ + + + + + | + this.authenticatorAttachment = authenticatorAttachment; |
+ +179 + | ++ + + + + + | +|
+ +180 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (residentKey != null) { |
+ +181 + | ++ + + + + + | + this.residentKey = residentKey; |
+ +182 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ } else if (requireResidentKey != null) { |
+ +183 + | ++ + + + + + | + this.residentKey = |
+ +184 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ requireResidentKey ? ResidentKeyRequirement.REQUIRED : ResidentKeyRequirement.DISCOURAGED; |
+ +185 + | ++ + + + + + | + } else { |
+ +186 + | ++ + + + + + | + this.residentKey = null; |
+ +187 + | ++ + + + + + | + } |
+ +188 + | ++ + + + + + | +|
+ +189 + | ++ + + + + + | + this.userVerification = userVerification; |
+ +190 + | ++ + + + + + | + } |
+ +191 + | ++ + + + + + | +|
+ +192 + | ++ + + + + + | + /** For use by the builder. */ |
+ +193 + | ++ + + + + + | + private AuthenticatorSelectionCriteria( |
+ +194 + | ++ + + + + + | + AuthenticatorAttachment authenticatorAttachment, |
+ +195 + | ++ + + + + + | + ResidentKeyRequirement residentKey, |
+ +196 + | ++ + + + + + | + UserVerificationRequirement userVerification) { |
+ +197 + | ++ + + + + + | + this(authenticatorAttachment, null, residentKey, userVerification); |
+ +198 + | ++ + + + + + | + } |
+ +199 + | ++ + + + + + | +|
+ +200 + | ++ + + + + + | + public static class AuthenticatorSelectionCriteriaBuilder { |
+ +201 + | ++ + + + + + | + private AuthenticatorAttachment authenticatorAttachment = null; |
+ +202 + | ++ + + + + + | +|
+ +203 + | ++ + + + + + | + /** |
+ +204 + | ++ + + + + + | + * If present, eligible authenticators are filtered to only authenticators attached with the |
+ +205 + | ++ + + + + + | + * specified <a |
+ +206 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-attachment">§5.4.5 |
+ +207 + | ++ + + + + + | + * Authenticator Attachment Enumeration (enum AuthenticatorAttachment)</a>. |
+ +208 + | ++ + + + + + | + */ |
+ +209 + | ++ + + + + + | + public AuthenticatorSelectionCriteriaBuilder authenticatorAttachment( |
+ +210 + | +
+
+1
+
+1. authenticatorAttachment : negated conditional → KILLED + + + + |
+ @NonNull Optional<AuthenticatorAttachment> authenticatorAttachment) { |
+ +211 + | +
+
+1
+
+1. authenticatorAttachment : replaced return value with null for com/yubico/webauthn/data/AuthenticatorSelectionCriteria$AuthenticatorSelectionCriteriaBuilder::authenticatorAttachment → KILLED + + + + |
+ return this.authenticatorAttachment(authenticatorAttachment.orElse(null)); |
+ +212 + | ++ + + + + + | + } |
+ +213 + | ++ + + + + + | +|
+ +214 + | ++ + + + + + | + /** |
+ +215 + | ++ + + + + + | + * If present, eligible authenticators are filtered to only authenticators attached with the |
+ +216 + | ++ + + + + + | + * specified <a |
+ +217 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-attachment">§5.4.5 |
+ +218 + | ++ + + + + + | + * Authenticator Attachment Enumeration (enum AuthenticatorAttachment)</a>. |
+ +219 + | ++ + + + + + | + */ |
+ +220 + | ++ + + + + + | + public AuthenticatorSelectionCriteriaBuilder authenticatorAttachment( |
+ +221 + | ++ + + + + + | + AuthenticatorAttachment authenticatorAttachment) { |
+ +222 + | ++ + + + + + | + this.authenticatorAttachment = authenticatorAttachment; |
+ +223 + | +
+
+1
+
+1. authenticatorAttachment : replaced return value with null for com/yubico/webauthn/data/AuthenticatorSelectionCriteria$AuthenticatorSelectionCriteriaBuilder::authenticatorAttachment → KILLED + + + + |
+ return this; |
+ +224 + | ++ + + + + + | + } |
+ +225 + | ++ + + + + + | + } |
+ +226 + | ++ + + + + + | +} |
Mutations | ||
105 | ++ |
+
+
+
+ 1.1 |
+
136 | ++ |
+
+
+
+ 1.1 |
+
149 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 |
+
169 | ++ |
+
+
+
+ 1.1 |
+
180 | ++ |
+
+
+
+ 1.1 |
+
182 | ++ |
+
+
+
+ 1.1 |
+
184 | ++ |
+
+
+
+ 1.1 |
+
210 | ++ |
+
+
+
+ 1.1 |
+
211 | ++ |
+
+
+
+ 1.1 |
+
223 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +30 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +31 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +32 + | ++ + + + + + | +import lombok.NonNull; |
+ +33 + | ++ + + + + + | +import lombok.Value; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * Authenticators may communicate with Clients using a variety of transports. This enumeration |
+ +37 + | ++ + + + + + | + * defines a hint as to how Clients might communicate with a particular Authenticator in order to |
+ +38 + | ++ + + + + + | + * obtain an assertion for a specific credential. Note that these hints represent the Relying |
+ +39 + | ++ + + + + + | + * Party's best belief as to how an Authenticator may be reached. A Relying Party may obtain a list |
+ +40 + | ++ + + + + + | + * of transports hints from some attestation statement formats or via some out-of-band mechanism; it |
+ +41 + | ++ + + + + + | + * is outside the scope of this specification to define that mechanism. |
+ +42 + | ++ + + + + + | + * |
+ +43 + | ++ + + + + + | + * <p>Authenticators may implement various transports for communicating with clients. This |
+ +44 + | ++ + + + + + | + * enumeration defines hints as to how clients might communicate with a particular authenticator in |
+ +45 + | ++ + + + + + | + * order to obtain an assertion for a specific credential. Note that these hints represent the |
+ +46 + | ++ + + + + + | + * WebAuthn Relying Party's best belief as to how an authenticator may be reached. A Relying Party |
+ +47 + | ++ + + + + + | + * may obtain a list of transports hints from some attestation statement formats or via some |
+ +48 + | ++ + + + + + | + * out-of-band mechanism; it is outside the scope of the Web Authentication specification to define |
+ +49 + | ++ + + + + + | + * that mechanism. |
+ +50 + | ++ + + + + + | + * |
+ +51 + | ++ + + + + + | + * @see <a |
+ +52 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-authenticatortransport">§5.10.4. |
+ +53 + | ++ + + + + + | + * Authenticator Transport Enumeration (enum AuthenticatorTransport)</a> |
+ +54 + | ++ + + + + + | + */ |
+ +55 + | ++ + + + + + | +@Value |
+ +56 + | ++ + + + + + | +@AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +57 + | ++ + + + + + | +public class AuthenticatorTransport implements Comparable<AuthenticatorTransport> { |
+ +58 + | ++ + + + + + | +|
+ +59 + | ++ + + + + + | + @JsonValue @NonNull private final String id; |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * Indicates the respective authenticator can be contacted over removable USB. |
+ +63 + | ++ + + + + + | + * |
+ +64 + | ++ + + + + + | + * @see <a |
+ +65 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatortransport-usb">5.8.4. |
+ +66 + | ++ + + + + + | + * Authenticator Transport Enumeration (enum AuthenticatorTransport)</a> |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + public static final AuthenticatorTransport USB = new AuthenticatorTransport("usb"); |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * Indicates the respective authenticator can be contacted over Near Field Communication (NFC). |
+ +72 + | ++ + + + + + | + * |
+ +73 + | ++ + + + + + | + * @see <a |
+ +74 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatortransport-nfc">5.8.4. |
+ +75 + | ++ + + + + + | + * Authenticator Transport Enumeration (enum AuthenticatorTransport)</a> |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + public static final AuthenticatorTransport NFC = new AuthenticatorTransport("nfc"); |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + /** |
+ +80 + | ++ + + + + + | + * Indicates the respective authenticator can be contacted over Bluetooth Smart (Bluetooth Low |
+ +81 + | ++ + + + + + | + * Energy / BLE). |
+ +82 + | ++ + + + + + | + * |
+ +83 + | ++ + + + + + | + * @see <a |
+ +84 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatortransport-ble">5.8.4. |
+ +85 + | ++ + + + + + | + * Authenticator Transport Enumeration (enum AuthenticatorTransport)</a> |
+ +86 + | ++ + + + + + | + */ |
+ +87 + | ++ + + + + + | + public static final AuthenticatorTransport BLE = new AuthenticatorTransport("ble"); |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + /** |
+ +90 + | ++ + + + + + | + * Indicates the respective authenticator can be contacted using a combination of (often separate) |
+ +91 + | ++ + + + + + | + * data-transport and proximity mechanisms. This supports, for example, authentication on a |
+ +92 + | ++ + + + + + | + * desktop computer using a smartphone. |
+ +93 + | ++ + + + + + | + * |
+ +94 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +95 + | ++ + + + + + | + * the standard matures. |
+ +96 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#dom-authenticatortransport-hybrid">5.8.4. |
+ +97 + | ++ + + + + + | + * Authenticator Transport Enumeration (enum AuthenticatorTransport)</a> |
+ +98 + | ++ + + + + + | + */ |
+ +99 + | ++ + + + + + | + @Deprecated |
+ +100 + | ++ + + + + + | + public static final AuthenticatorTransport HYBRID = new AuthenticatorTransport("hybrid"); |
+ +101 + | ++ + + + + + | +|
+ +102 + | ++ + + + + + | + /** |
+ +103 + | ++ + + + + + | + * Indicates the respective authenticator is contacted using a client device-specific transport. |
+ +104 + | ++ + + + + + | + * These authenticators are not removable from the client device. |
+ +105 + | ++ + + + + + | + * |
+ +106 + | ++ + + + + + | + * @see <a |
+ +107 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatortransport-internal">5.8.4. |
+ +108 + | ++ + + + + + | + * Authenticator Transport Enumeration (enum AuthenticatorTransport)</a> |
+ +109 + | ++ + + + + + | + */ |
+ +110 + | ++ + + + + + | + public static final AuthenticatorTransport INTERNAL = new AuthenticatorTransport("internal"); |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + /** |
+ +113 + | ++ + + + + + | + * @return An array containing all predefined values of {@link AuthenticatorTransport} known by |
+ +114 + | ++ + + + + + | + * this implementation. |
+ +115 + | ++ + + + + + | + */ |
+ +116 + | ++ + + + + + | + public static AuthenticatorTransport[] values() { |
+ +117 + | +
+
+1
+
+1. values : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::values → KILLED + + + + |
+ return new AuthenticatorTransport[] {USB, NFC, BLE, HYBRID, INTERNAL}; |
+ +118 + | ++ + + + + + | + } |
+ +119 + | ++ + + + + + | +|
+ +120 + | ++ + + + + + | + /** |
+ +121 + | ++ + + + + + | + * @return If <code>id</code> is the same as that of any of {@link #USB}, {@link #NFC}, {@link |
+ +122 + | ++ + + + + + | + * #BLE}, {@link #HYBRID} or {@link #INTERNAL}, returns that constant instance. Otherwise |
+ +123 + | ++ + + + + + | + * returns a new instance containing <code>id</code>. |
+ +124 + | ++ + + + + + | + * @see #valueOf(String) |
+ +125 + | ++ + + + + + | + */ |
+ +126 + | ++ + + + + + | + @JsonCreator |
+ +127 + | +
+
+1
+
+1. of : negated conditional → KILLED + + + + |
+ public static AuthenticatorTransport of(@NonNull String id) { |
+ +128 + | +
+
+1
+
+1. of : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::of → KILLED + + + + |
+ return Stream.of(values()) |
+ +129 + | +
+
+2
+
+1. lambda$of$0 : replaced boolean return with false for com/yubico/webauthn/data/AuthenticatorTransport::lambda$of$0 → KILLED +2. lambda$of$0 : replaced boolean return with true for com/yubico/webauthn/data/AuthenticatorTransport::lambda$of$0 → KILLED + + + + |
+ .filter(v -> v.getId().equals(id)) |
+ +130 + | ++ + + + + + | + .findAny() |
+ +131 + | +
+
+1
+
+1. lambda$of$1 : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::lambda$of$1 → KILLED + + + + |
+ .orElseGet(() -> new AuthenticatorTransport(id)); |
+ +132 + | ++ + + + + + | + } |
+ +133 + | ++ + + + + + | +|
+ +134 + | ++ + + + + + | + /** |
+ +135 + | ++ + + + + + | + * @return If <code>name</code> equals <code>"USB"</code>, <code>"NFC"</code>, <code>"BLE"</code>, |
+ +136 + | ++ + + + + + | + * <code>"HYBRID"</code> or <code>"INTERNAL"</code>, returns the constant by that name. |
+ +137 + | ++ + + + + + | + * @throws IllegalArgumentException if <code>name</code> is anything else. |
+ +138 + | ++ + + + + + | + * @see #of(String) |
+ +139 + | ++ + + + + + | + */ |
+ +140 + | ++ + + + + + | + public static AuthenticatorTransport valueOf(String name) { |
+ +141 + | ++ + + + + + | + switch (name) { |
+ +142 + | ++ + + + + + | + case "USB": |
+ +143 + | +
+
+1
+
+1. valueOf : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::valueOf → KILLED + + + + |
+ return USB; |
+ +144 + | ++ + + + + + | + case "NFC": |
+ +145 + | +
+
+1
+
+1. valueOf : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::valueOf → KILLED + + + + |
+ return NFC; |
+ +146 + | ++ + + + + + | + case "BLE": |
+ +147 + | +
+
+1
+
+1. valueOf : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::valueOf → KILLED + + + + |
+ return BLE; |
+ +148 + | ++ + + + + + | + case "HYBRID": |
+ +149 + | +
+
+1
+
+1. valueOf : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::valueOf → KILLED + + + + |
+ return HYBRID; |
+ +150 + | ++ + + + + + | + case "INTERNAL": |
+ +151 + | +
+
+1
+
+1. valueOf : replaced return value with null for com/yubico/webauthn/data/AuthenticatorTransport::valueOf → KILLED + + + + |
+ return INTERNAL; |
+ +152 + | ++ + + + + + | + default: |
+ +153 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +154 + | ++ + + + + + | + "No constant com.yubico.webauthn.data.AuthenticatorTransport." + name); |
+ +155 + | ++ + + + + + | + } |
+ +156 + | ++ + + + + + | + } |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + @Override |
+ +159 + | ++ + + + + + | + public int compareTo(AuthenticatorTransport other) { |
+ +160 + | +
+
+1
+
+1. compareTo : replaced int return with 0 for com/yubico/webauthn/data/AuthenticatorTransport::compareTo → KILLED + + + + |
+ return id.compareTo(other.id); |
+ +161 + | ++ + + + + + | + } |
+ +162 + | ++ + + + + + | +} |
Mutations | ||
117 | ++ |
+
+
+
+ 1.1 |
+
127 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 2.2 |
+
131 | ++ |
+
+
+
+ 1.1 |
+
143 | ++ |
+
+
+
+ 1.1 |
+
145 | ++ |
+
+
+
+ 1.1 |
+
147 | ++ |
+
+
+
+ 1.1 |
+
149 | ++ |
+
+
+
+ 1.1 |
+
151 | ++ |
+
+
+
+ 1.1 |
+
160 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import com.google.common.primitives.Bytes; |
+ +30 + | ++ + + + + + | +import com.yubico.internal.util.BinaryUtil; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.Base64UrlException; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.HexException; |
+ +33 + | ++ + + + + + | +import java.util.Base64; |
+ +34 + | ++ + + + + + | +import lombok.EqualsAndHashCode; |
+ +35 + | ++ + + + + + | +import lombok.NonNull; |
+ +36 + | ++ + + + + + | +import lombok.ToString; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | +/** An immutable byte array with support for encoding/decoding to/from various encodings. */ |
+ +39 + | ++ + + + + + | +@EqualsAndHashCode |
+ +40 + | ++ + + + + + | +@ToString(includeFieldNames = false, onlyExplicitlyIncluded = true) |
+ +41 + | ++ + + + + + | +public final class ByteArray implements Comparable<ByteArray> { |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder(); |
+ +44 + | ++ + + + + + | + private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder(); |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + private static final Base64.Encoder BASE64URL_ENCODER = Base64.getUrlEncoder().withoutPadding(); |
+ +47 + | ++ + + + + + | + private static final Base64.Decoder BASE64URL_DECODER = Base64.getUrlDecoder(); |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + @NonNull private final byte[] bytes; |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + @JsonValue @NonNull private final String base64url; |
+ +52 + | ++ + + + + + | +|
+ +53 + | ++ + + + + + | + /** Create a new instance by copying the contents of <code>bytes</code>. */ |
+ +54 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ public ByteArray(@NonNull byte[] bytes) { |
+ +55 + | ++ + + + + + | + this.bytes = BinaryUtil.copy(bytes); |
+ +56 + | ++ + + + + + | + this.base64url = BASE64URL_ENCODER.encodeToString(this.bytes); |
+ +57 + | ++ + + + + + | + } |
+ +58 + | ++ + + + + + | +|
+ +59 + | ++ + + + + + | + private ByteArray(String base64url) throws Base64UrlException { |
+ +60 + | ++ + + + + + | + try { |
+ +61 + | ++ + + + + + | + this.bytes = BASE64URL_DECODER.decode(base64url); |
+ +62 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +63 + | ++ + + + + + | + throw new Base64UrlException("Invalid Base64Url encoding: " + base64url, e); |
+ +64 + | ++ + + + + + | + } |
+ +65 + | ++ + + + + + | + this.base64url = base64url; |
+ +66 + | ++ + + + + + | + } |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + /** Create a new instance by decoding <code>base64</code> as classic Base64 data. */ |
+ +69 + | +
+
+1
+
+1. fromBase64 : negated conditional → KILLED + + + + |
+ public static ByteArray fromBase64(@NonNull final String base64) { |
+ +70 + | +
+
+1
+
+1. fromBase64 : replaced return value with null for com/yubico/webauthn/data/ByteArray::fromBase64 → KILLED + + + + |
+ return new ByteArray(BASE64_DECODER.decode(base64)); |
+ +71 + | ++ + + + + + | + } |
+ +72 + | ++ + + + + + | +|
+ +73 + | ++ + + + + + | + /** |
+ +74 + | ++ + + + + + | + * Create a new instance by decoding <code>base64url</code> as Base64Url data. |
+ +75 + | ++ + + + + + | + * |
+ +76 + | ++ + + + + + | + * @throws Base64UrlException if <code>base64url</code> is not valid Base64Url data. |
+ +77 + | ++ + + + + + | + */ |
+ +78 + | ++ + + + + + | + @JsonCreator |
+ +79 + | +
+
+1
+
+1. fromBase64Url : negated conditional → KILLED + + + + |
+ public static ByteArray fromBase64Url(@NonNull final String base64url) throws Base64UrlException { |
+ +80 + | +
+
+1
+
+1. fromBase64Url : replaced return value with null for com/yubico/webauthn/data/ByteArray::fromBase64Url → KILLED + + + + |
+ return new ByteArray(base64url.split("=")[0]); |
+ +81 + | ++ + + + + + | + } |
+ +82 + | ++ + + + + + | +|
+ +83 + | ++ + + + + + | + /** |
+ +84 + | ++ + + + + + | + * Create a new instance by decoding <code>hex</code> as hexadecimal data. |
+ +85 + | ++ + + + + + | + * |
+ +86 + | ++ + + + + + | + * @throws HexException if <code>hex</code> is not valid hexadecimal data. |
+ +87 + | ++ + + + + + | + */ |
+ +88 + | +
+
+1
+
+1. fromHex : negated conditional → KILLED + + + + |
+ public static ByteArray fromHex(@NonNull final String hex) throws HexException { |
+ +89 + | ++ + + + + + | + try { |
+ +90 + | +
+
+1
+
+1. fromHex : replaced return value with null for com/yubico/webauthn/data/ByteArray::fromHex → KILLED + + + + |
+ return new ByteArray(BinaryUtil.fromHex(hex)); |
+ +91 + | ++ + + + + + | + } catch (Exception e) { |
+ +92 + | ++ + + + + + | + throw new HexException("Invalid hexadecimal encoding: " + hex, e); |
+ +93 + | ++ + + + + + | + } |
+ +94 + | ++ + + + + + | + } |
+ +95 + | ++ + + + + + | +|
+ +96 + | ++ + + + + + | + /** |
+ +97 + | ++ + + + + + | + * @return a new instance containing a copy of this instance followed by a copy of <code>tail |
+ +98 + | ++ + + + + + | + * </code>. |
+ +99 + | ++ + + + + + | + */ |
+ +100 + | +
+
+1
+
+1. concat : negated conditional → KILLED + + + + |
+ public ByteArray concat(@NonNull ByteArray tail) { |
+ +101 + | +
+
+1
+
+1. concat : replaced return value with null for com/yubico/webauthn/data/ByteArray::concat → KILLED + + + + |
+ return new ByteArray(Bytes.concat(this.bytes, tail.bytes)); |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + public boolean isEmpty() { |
+ +105 + | +
+
+2
+
+1. isEmpty : negated conditional → KILLED +2. isEmpty : replaced boolean return with true for com/yubico/webauthn/data/ByteArray::isEmpty → KILLED + + + + |
+ return size() == 0; |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + public int size() { |
+ +109 + | +
+
+1
+
+1. size : replaced int return with 0 for com/yubico/webauthn/data/ByteArray::size → KILLED + + + + |
+ return this.bytes.length; |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + /** |
+ +113 + | ++ + + + + + | + * @return a copy of the raw byte contents. |
+ +114 + | ++ + + + + + | + */ |
+ +115 + | ++ + + + + + | + public byte[] getBytes() { |
+ +116 + | +
+
+1
+
+1. getBytes : replaced return value with null for com/yubico/webauthn/data/ByteArray::getBytes → KILLED + + + + |
+ return BinaryUtil.copy(bytes); |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * @return the content bytes encoded as classic Base64 data. |
+ +121 + | ++ + + + + + | + */ |
+ +122 + | ++ + + + + + | + public String getBase64() { |
+ +123 + | +
+
+1
+
+1. getBase64 : replaced return value with "" for com/yubico/webauthn/data/ByteArray::getBase64 → KILLED + + + + |
+ return BASE64_ENCODER.encodeToString(bytes); |
+ +124 + | ++ + + + + + | + } |
+ +125 + | ++ + + + + + | +|
+ +126 + | ++ + + + + + | + /** |
+ +127 + | ++ + + + + + | + * @return the content bytes encoded as Base64Url data, without padding. |
+ +128 + | ++ + + + + + | + */ |
+ +129 + | ++ + + + + + | + public String getBase64Url() { |
+ +130 + | +
+
+1
+
+1. getBase64Url : replaced return value with "" for com/yubico/webauthn/data/ByteArray::getBase64Url → KILLED + + + + |
+ return base64url; |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + /** |
+ +134 + | ++ + + + + + | + * @return the content bytes encoded as hexadecimal data. |
+ +135 + | ++ + + + + + | + */ |
+ +136 + | ++ + + + + + | + @ToString.Include |
+ +137 + | ++ + + + + + | + public String getHex() { |
+ +138 + | +
+
+1
+
+1. getHex : replaced return value with "" for com/yubico/webauthn/data/ByteArray::getHex → KILLED + + + + |
+ return BinaryUtil.toHex(bytes); |
+ +139 + | ++ + + + + + | + } |
+ +140 + | ++ + + + + + | +|
+ +141 + | ++ + + + + + | + @Override |
+ +142 + | ++ + + + + + | + public int compareTo(ByteArray other) { |
+ +143 + | +
+
+1
+
+1. compareTo : negated conditional → KILLED + + + + |
+ if (bytes.length != other.bytes.length) { |
+ +144 + | +
+
+2
+
+1. compareTo : Replaced integer subtraction with addition → KILLED +2. compareTo : replaced int return with 0 for com/yubico/webauthn/data/ByteArray::compareTo → KILLED + + + + |
+ return bytes.length - other.bytes.length; |
+ +145 + | ++ + + + + + | + } |
+ +146 + | ++ + + + + + | +|
+ +147 + | +
+
+2
+
+1. compareTo : changed conditional boundary → KILLED +2. compareTo : negated conditional → KILLED + + + + |
+ for (int i = 0; i < bytes.length; ++i) { |
+ +148 + | +
+
+1
+
+1. compareTo : negated conditional → KILLED + + + + |
+ if (bytes[i] != other.bytes[i]) { |
+ +149 + | +
+
+2
+
+1. compareTo : Replaced integer subtraction with addition → KILLED +2. compareTo : replaced int return with 0 for com/yubico/webauthn/data/ByteArray::compareTo → KILLED + + + + |
+ return bytes[i] - other.bytes[i]; |
+ +150 + | ++ + + + + + | + } |
+ +151 + | ++ + + + + + | + } |
+ +152 + | ++ + + + + + | +|
+ +153 + | ++ + + + + + | + return 0; |
+ +154 + | ++ + + + + + | + } |
+ +155 + | ++ + + + + + | +} |
Mutations | ||
54 | ++ |
+
+
+
+ 1.1 |
+
69 | ++ |
+
+
+
+ 1.1 |
+
70 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
80 | ++ |
+
+
+
+ 1.1 |
+
88 | ++ |
+
+
+
+ 1.1 |
+
90 | ++ |
+
+
+
+ 1.1 |
+
100 | ++ |
+
+
+
+ 1.1 |
+
101 | ++ |
+
+
+
+ 1.1 |
+
105 | ++ |
+
+
+
+ 1.1 2.2 |
+
109 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 |
+
123 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
138 | ++ |
+
+
+
+ 1.1 |
+
143 | ++ |
+
+
+
+ 1.1 |
+
144 | ++ |
+
+
+
+ 1.1 2.2 |
+
147 | ++ |
+
+
+
+ 1.1 2.2 |
+
148 | ++ |
+
+
+
+ 1.1 |
+
149 | ++ |
+
+
+
+ 1.1 2.2 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import com.upokecenter.cbor.CBORException; |
+ +30 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +33 + | ++ + + + + + | +import lombok.Getter; |
+ +34 + | ++ + + + + + | +import lombok.NonNull; |
+ +35 + | ++ + + + + + | +|
+ +36 + | ++ + + + + + | +/** |
+ +37 + | ++ + + + + + | + * A number identifying a cryptographic algorithm. The algorithm identifiers SHOULD be values |
+ +38 + | ++ + + + + + | + * registered in the IANA COSE Algorithms registry, for instance, -7 for "ES256" and -257 for |
+ +39 + | ++ + + + + + | + * "RS256". |
+ +40 + | ++ + + + + + | + * |
+ +41 + | ++ + + + + + | + * @see <a |
+ +42 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#typedefdef-cosealgorithmidentifier">§5.10.5. |
+ +43 + | ++ + + + + + | + * Cryptographic Algorithm Identifier (typedef COSEAlgorithmIdentifier)</a> |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | +public enum COSEAlgorithmIdentifier { |
+ +46 + | ++ + + + + + | + EdDSA(-8), |
+ +47 + | ++ + + + + + | + ES256(-7), |
+ +48 + | ++ + + + + + | + ES384(-35), |
+ +49 + | ++ + + + + + | + ES512(-36), |
+ +50 + | ++ + + + + + | + RS256(-257), |
+ +51 + | ++ + + + + + | + RS384(-258), |
+ +52 + | ++ + + + + + | + RS512(-259), |
+ +53 + | ++ + + + + + | + RS1(-65535); |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | + @JsonValue @Getter private final long id; |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + COSEAlgorithmIdentifier(long id) { |
+ +58 + | ++ + + + + + | + this.id = id; |
+ +59 + | ++ + + + + + | + } |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * Attempt to parse an integer as a {@link COSEAlgorithmIdentifier}. |
+ +63 + | ++ + + + + + | + * |
+ +64 + | ++ + + + + + | + * @param id an integer equal to the {@link #getId() id} of a constant in {@link |
+ +65 + | ++ + + + + + | + * COSEAlgorithmIdentifier} |
+ +66 + | ++ + + + + + | + * @return The {@link COSEAlgorithmIdentifier} instance whose {@link #getId() id} equals <code>id |
+ +67 + | ++ + + + + + | + * </code>, if any. |
+ +68 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-alg-identifier">§5.8.5. |
+ +69 + | ++ + + + + + | + * Cryptographic Algorithm Identifier (typedef COSEAlgorithmIdentifier)</a> |
+ +70 + | ++ + + + + + | + */ |
+ +71 + | ++ + + + + + | + public static Optional<COSEAlgorithmIdentifier> fromId(long id) { |
+ +72 + | +
+
+3
+
+1. lambda$fromId$0 : replaced boolean return with true for com/yubico/webauthn/data/COSEAlgorithmIdentifier::lambda$fromId$0 → KILLED +2. lambda$fromId$0 : negated conditional → KILLED +3. fromId : replaced return value with Optional.empty for com/yubico/webauthn/data/COSEAlgorithmIdentifier::fromId → KILLED + + + + |
+ return Stream.of(values()).filter(v -> v.id == id).findAny(); |
+ +73 + | ++ + + + + + | + } |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + /** |
+ +76 + | ++ + + + + + | + * Read the {@link COSEAlgorithmIdentifier} from a public key in COSE_Key format. |
+ +77 + | ++ + + + + + | + * |
+ +78 + | ++ + + + + + | + * @param publicKeyCose a public key in COSE_Key format. |
+ +79 + | ++ + + + + + | + * @return The <code>alg</code> of the <code>publicKeyCose</code> parsed as a {@link |
+ +80 + | ++ + + + + + | + * COSEAlgorithmIdentifier}, if possible. Returns empty if the {@link COSEAlgorithmIdentifier} |
+ +81 + | ++ + + + + + | + * enum has no constant matching the <code>alg</code> value. |
+ +82 + | ++ + + + + + | + * @throws IllegalArgumentException if <code>publicKeyCose</code> is not a well-formed COSE_Key. |
+ +83 + | ++ + + + + + | + */ |
+ +84 + | +
+
+1
+
+1. fromPublicKey : negated conditional → KILLED + + + + |
+ public static Optional<COSEAlgorithmIdentifier> fromPublicKey(@NonNull ByteArray publicKeyCose) { |
+ +85 + | ++ + + + + + | + final CBORObject ALG = CBORObject.FromObject(3); |
+ +86 + | ++ + + + + + | + final int alg; |
+ +87 + | ++ + + + + + | + try { |
+ +88 + | ++ + + + + + | + CBORObject cose = CBORObject.DecodeFromBytes(publicKeyCose.getBytes()); |
+ +89 + | +
+
+1
+
+1. fromPublicKey : negated conditional → KILLED + + + + |
+ if (!cose.ContainsKey(ALG)) { |
+ +90 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +91 + | ++ + + + + + | + "Public key does not contain an \"alg\"(3) value: " + publicKeyCose); |
+ +92 + | ++ + + + + + | + } |
+ +93 + | ++ + + + + + | + CBORObject algCbor = cose.get(ALG); |
+ +94 + | +
+
+2
+
+1. fromPublicKey : negated conditional → KILLED +2. fromPublicKey : negated conditional → KILLED + + + + |
+ if (!(algCbor.isNumber() && algCbor.AsNumber().IsInteger())) { |
+ +95 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +96 + | ++ + + + + + | + "Public key has non-integer \"alg\"(3) value: " + publicKeyCose); |
+ +97 + | ++ + + + + + | + } |
+ +98 + | ++ + + + + + | + alg = algCbor.AsInt32(); |
+ +99 + | ++ + + + + + | + } catch (CBORException e) { |
+ +100 + | ++ + + + + + | + throw new IllegalArgumentException("Failed to parse public key", e); |
+ +101 + | ++ + + + + + | + } |
+ +102 + | +
+
+1
+
+1. fromPublicKey : replaced return value with Optional.empty for com/yubico/webauthn/data/COSEAlgorithmIdentifier::fromPublicKey → KILLED + + + + |
+ return fromId(alg); |
+ +103 + | ++ + + + + + | + } |
+ +104 + | ++ + + + + + | +|
+ +105 + | ++ + + + + + | + @JsonCreator |
+ +106 + | ++ + + + + + | + private static COSEAlgorithmIdentifier fromJson(long id) { |
+ +107 + | +
+
+1
+
+1. fromJson : replaced return value with null for com/yubico/webauthn/data/COSEAlgorithmIdentifier::fromJson → KILLED + + + + |
+ return fromId(id) |
+ +108 + | ++ + + + + + | + .orElseThrow( |
+ +109 + | +
+
+1
+
+1. lambda$fromJson$1 : replaced return value with null for com/yubico/webauthn/data/COSEAlgorithmIdentifier::lambda$fromJson$1 → KILLED + + + + |
+ () -> new IllegalArgumentException("Unknown COSE algorithm identifier: " + id)); |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +} |
Mutations | ||
72 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
84 | ++ |
+
+
+
+ 1.1 |
+
89 | ++ |
+
+
+
+ 1.1 |
+
94 | ++ |
+
+
+
+ 1.1 2.2 |
+
102 | ++ |
+
+
+
+ 1.1 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
109 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +30 + | ++ + + + + + | +import java.util.HashSet; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import java.util.Set; |
+ +33 + | ++ + + + + + | +import lombok.Builder; |
+ +34 + | ++ + + + + + | +import lombok.EqualsAndHashCode; |
+ +35 + | ++ + + + + + | +import lombok.NonNull; |
+ +36 + | ++ + + + + + | +import lombok.Value; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | +/** |
+ +39 + | ++ + + + + + | + * Contains <a |
+ +40 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client |
+ +41 + | ++ + + + + + | + * extension outputs</a> from a <code>navigator.credentials.get()</code> operation. |
+ +42 + | ++ + + + + + | + * |
+ +43 + | ++ + + + + + | + * <p>Note that there is no guarantee that any extension input present in {@link |
+ +44 + | ++ + + + + + | + * AssertionExtensionInputs} will have a corresponding output present here. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * <p>The authenticator extension outputs are contained in the {@link AuthenticatorData} structure. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9. WebAuthn |
+ +49 + | ++ + + + + + | + * Extensions</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | +@Value |
+ +52 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +53 + | ++ + + + + + | +@JsonIgnoreProperties(ignoreUnknown = true) |
+ +54 + | ++ + + + + + | +public class ClientAssertionExtensionOutputs implements ClientExtensionOutputs { |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** |
+ +57 + | ++ + + + + + | + * The extension output for the FIDO AppID Extension (<code>appid</code>), if any. |
+ +58 + | ++ + + + + + | + * |
+ +59 + | ++ + + + + + | + * <p>This value should be ignored because its behaviour is underspecified, see: <a |
+ +60 + | ++ + + + + + | + * href="https://github.com/w3c/webauthn/issues/1034">https://github.com/w3c/webauthn/issues/1034</a>. |
+ +61 + | ++ + + + + + | + * |
+ +62 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +63 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +64 + | ++ + + + + + | + */ |
+ +65 + | ++ + + + + + | + private final Boolean appid; |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + private final Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob; |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + @JsonCreator |
+ +70 + | ++ + + + + + | + private ClientAssertionExtensionOutputs( |
+ +71 + | ++ + + + + + | + @JsonProperty("appid") Boolean appid, |
+ +72 + | ++ + + + + + | + @JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobAuthenticationOutput largeBlob) { |
+ +73 + | ++ + + + + + | + this.appid = appid; |
+ +74 + | ++ + + + + + | + this.largeBlob = largeBlob; |
+ +75 + | ++ + + + + + | + } |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + @Override |
+ +78 + | ++ + + + + + | + @EqualsAndHashCode.Include |
+ +79 + | ++ + + + + + | + public Set<String> getExtensionIds() { |
+ +80 + | ++ + + + + + | + HashSet<String> ids = new HashSet<>(); |
+ +81 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (appid != null) { |
+ +82 + | ++ + + + + + | + ids.add(Extensions.Appid.EXTENSION_ID); |
+ +83 + | ++ + + + + + | + } |
+ +84 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (largeBlob != null) { |
+ +85 + | ++ + + + + + | + ids.add(Extensions.LargeBlob.EXTENSION_ID); |
+ +86 + | ++ + + + + + | + } |
+ +87 + | +
+
+1
+
+1. getExtensionIds : replaced return value with Collections.emptySet for com/yubico/webauthn/data/ClientAssertionExtensionOutputs::getExtensionIds → KILLED + + + + |
+ return ids; |
+ +88 + | ++ + + + + + | + } |
+ +89 + | ++ + + + + + | +|
+ +90 + | ++ + + + + + | + /** |
+ +91 + | ++ + + + + + | + * The extension output for the FIDO AppID Extension (<code>appid</code>), if any. |
+ +92 + | ++ + + + + + | + * |
+ +93 + | ++ + + + + + | + * <p>This value should be ignored because its behaviour is underspecified, see: <a |
+ +94 + | ++ + + + + + | + * href="https://github.com/w3c/webauthn/issues/1034">https://github.com/w3c/webauthn/issues/1034</a>. |
+ +95 + | ++ + + + + + | + * |
+ +96 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +97 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +98 + | ++ + + + + + | + */ |
+ +99 + | ++ + + + + + | + public Optional<Boolean> getAppid() { |
+ +100 + | +
+
+1
+
+1. getAppid : replaced return value with Optional.empty for com/yubico/webauthn/data/ClientAssertionExtensionOutputs::getAppid → SURVIVED + + + + |
+ return Optional.ofNullable(appid); |
+ +101 + | ++ + + + + + | + } |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + /** |
+ +104 + | ++ + + + + + | + * The extension output for the <a |
+ +105 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">Large blob |
+ +106 + | ++ + + + + + | + * storage (<code>largeBlob</code>) extension</a>, if any. |
+ +107 + | ++ + + + + + | + * |
+ +108 + | ++ + + + + + | + * @see com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobRegistrationOutput |
+ +109 + | ++ + + + + + | + * @see <a |
+ +110 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5.Large |
+ +111 + | ++ + + + + + | + * blob storage extension (largeBlob)</a> |
+ +112 + | ++ + + + + + | + */ |
+ +113 + | ++ + + + + + | + public Optional<Extensions.LargeBlob.LargeBlobAuthenticationOutput> getLargeBlob() { |
+ +114 + | +
+
+1
+
+1. getLargeBlob : replaced return value with Optional.empty for com/yubico/webauthn/data/ClientAssertionExtensionOutputs::getLargeBlob → KILLED + + + + |
+ return Optional.ofNullable(largeBlob); |
+ +115 + | ++ + + + + + | + } |
+ +116 + | ++ + + + + + | +|
+ +117 + | ++ + + + + + | + public static class ClientAssertionExtensionOutputsBuilder { |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * The extension output for the FIDO AppID Extension (<code>appid</code>). |
+ +121 + | ++ + + + + + | + * |
+ +122 + | ++ + + + + + | + * <p>This value should be ignored because its behaviour is underspecified, see: <a |
+ +123 + | ++ + + + + + | + * href="https://github.com/w3c/webauthn/issues/1034">https://github.com/w3c/webauthn/issues/1034</a>. |
+ +124 + | ++ + + + + + | + * |
+ +125 + | ++ + + + + + | + * @see <a |
+ +126 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +127 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +128 + | ++ + + + + + | + */ |
+ +129 + | +
+
+1
+
+1. appid : negated conditional → KILLED + + + + |
+ public ClientAssertionExtensionOutputsBuilder appid(@NonNull Optional<Boolean> appid) { |
+ +130 + | ++ + + + + + | + this.appid = appid.orElse(null); |
+ +131 + | +
+
+1
+
+1. appid : replaced return value with null for com/yubico/webauthn/data/ClientAssertionExtensionOutputs$ClientAssertionExtensionOutputsBuilder::appid → KILLED + + + + |
+ return this; |
+ +132 + | ++ + + + + + | + } |
+ +133 + | ++ + + + + + | +|
+ +134 + | ++ + + + + + | + /* |
+ +135 + | ++ + + + + + | + * Workaround, see: https://github.com/rzwitserloot/lombok/issues/2623#issuecomment-714816001 |
+ +136 + | ++ + + + + + | + * Consider reverting this workaround if Lombok fixes that issue. |
+ +137 + | ++ + + + + + | + */ |
+ +138 + | ++ + + + + + | + private ClientAssertionExtensionOutputsBuilder appid(Boolean appid) { |
+ +139 + | +
+
+1
+
+1. appid : replaced return value with null for com/yubico/webauthn/data/ClientAssertionExtensionOutputs$ClientAssertionExtensionOutputsBuilder::appid → KILLED + + + + |
+ return this.appid(Optional.ofNullable(appid)); |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + /** |
+ +143 + | ++ + + + + + | + * The extension output for the FIDO AppID Extension (<code>appid</code>). |
+ +144 + | ++ + + + + + | + * |
+ +145 + | ++ + + + + + | + * <p>This value should be ignored because its behaviour is underspecified, see: <a |
+ +146 + | ++ + + + + + | + * href="https://github.com/w3c/webauthn/issues/1034">https://github.com/w3c/webauthn/issues/1034</a>. |
+ +147 + | ++ + + + + + | + * |
+ +148 + | ++ + + + + + | + * @see <a |
+ +149 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +150 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +151 + | ++ + + + + + | + */ |
+ +152 + | ++ + + + + + | + public ClientAssertionExtensionOutputsBuilder appid(boolean appid) { |
+ +153 + | +
+
+1
+
+1. appid : replaced return value with null for com/yubico/webauthn/data/ClientAssertionExtensionOutputs$ClientAssertionExtensionOutputsBuilder::appid → KILLED + + + + |
+ return this.appid(Optional.of(appid)); |
+ +154 + | ++ + + + + + | + } |
+ +155 + | ++ + + + + + | + } |
+ +156 + | ++ + + + + + | +} |
Mutations | ||
81 | ++ |
+
+
+
+ 1.1 |
+
84 | ++ |
+
+
+
+ 1.1 |
+
87 | ++ |
+
+
+
+ 1.1 |
+
100 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 |
+
131 | ++ |
+
+
+
+ 1.1 |
+
139 | ++ |
+
+
+
+ 1.1 |
+
153 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +30 + | ++ + + + + + | +import java.util.HashSet; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import java.util.Set; |
+ +33 + | ++ + + + + + | +import lombok.Builder; |
+ +34 + | ++ + + + + + | +import lombok.EqualsAndHashCode; |
+ +35 + | ++ + + + + + | +import lombok.Value; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | +/** |
+ +38 + | ++ + + + + + | + * Contains <a |
+ +39 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client |
+ +40 + | ++ + + + + + | + * extension outputs</a> from a <code>navigator.credentials.create()</code> operation. |
+ +41 + | ++ + + + + + | + * |
+ +42 + | ++ + + + + + | + * <p>Note that there is no guarantee that any extension input present in {@link |
+ +43 + | ++ + + + + + | + * AssertionExtensionInputs} will have a corresponding output present here. |
+ +44 + | ++ + + + + + | + * |
+ +45 + | ++ + + + + + | + * <p>The authenticator extension outputs are contained in the {@link AuthenticatorData} structure. |
+ +46 + | ++ + + + + + | + * |
+ +47 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9. WebAuthn |
+ +48 + | ++ + + + + + | + * Extensions</a> |
+ +49 + | ++ + + + + + | + */ |
+ +50 + | ++ + + + + + | +@Value |
+ +51 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +52 + | ++ + + + + + | +@JsonIgnoreProperties(ignoreUnknown = true) |
+ +53 + | ++ + + + + + | +public class ClientRegistrationExtensionOutputs implements ClientExtensionOutputs { |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | + private final Boolean appidExclude; |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + private final Extensions.CredentialProperties.CredentialPropertiesOutput credProps; |
+ +58 + | ++ + + + + + | +|
+ +59 + | ++ + + + + + | + private final Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob; |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + @JsonCreator |
+ +62 + | ++ + + + + + | + private ClientRegistrationExtensionOutputs( |
+ +63 + | ++ + + + + + | + @JsonProperty("appidExclude") Boolean appidExclude, |
+ +64 + | ++ + + + + + | + @JsonProperty("credProps") |
+ +65 + | ++ + + + + + | + Extensions.CredentialProperties.CredentialPropertiesOutput credProps, |
+ +66 + | ++ + + + + + | + @JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobRegistrationOutput largeBlob) { |
+ +67 + | ++ + + + + + | + this.appidExclude = appidExclude; |
+ +68 + | ++ + + + + + | + this.credProps = credProps; |
+ +69 + | ++ + + + + + | + this.largeBlob = largeBlob; |
+ +70 + | ++ + + + + + | + } |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + @Override |
+ +73 + | ++ + + + + + | + @EqualsAndHashCode.Include |
+ +74 + | ++ + + + + + | + public Set<String> getExtensionIds() { |
+ +75 + | ++ + + + + + | + HashSet<String> ids = new HashSet<>(); |
+ +76 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (appidExclude != null) { |
+ +77 + | ++ + + + + + | + ids.add(Extensions.AppidExclude.EXTENSION_ID); |
+ +78 + | ++ + + + + + | + } |
+ +79 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (credProps != null) { |
+ +80 + | ++ + + + + + | + ids.add(Extensions.CredentialProperties.EXTENSION_ID); |
+ +81 + | ++ + + + + + | + } |
+ +82 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (largeBlob != null) { |
+ +83 + | ++ + + + + + | + ids.add(Extensions.LargeBlob.EXTENSION_ID); |
+ +84 + | ++ + + + + + | + } |
+ +85 + | +
+
+1
+
+1. getExtensionIds : replaced return value with Collections.emptySet for com/yubico/webauthn/data/ClientRegistrationExtensionOutputs::getExtensionIds → KILLED + + + + |
+ return ids; |
+ +86 + | ++ + + + + + | + } |
+ +87 + | ++ + + + + + | +|
+ +88 + | ++ + + + + + | + /** |
+ +89 + | ++ + + + + + | + * The extension output for the <a |
+ +90 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">FIDO |
+ +91 + | ++ + + + + + | + * AppID Exclusion (<code>appidExclude</code>) Extension</a>, if any. |
+ +92 + | ++ + + + + + | + * |
+ +93 + | ++ + + + + + | + * <p>This value is generally not useful, as it only communicates whether the client supports the |
+ +94 + | ++ + + + + + | + * extension. |
+ +95 + | ++ + + + + + | + * |
+ +96 + | ++ + + + + + | + * @see <a |
+ +97 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2.FIDO |
+ +98 + | ++ + + + + + | + * AppID Exclusion Extension (appidExclude)</a> |
+ +99 + | ++ + + + + + | + */ |
+ +100 + | ++ + + + + + | + public Optional<Boolean> getAppidExclude() { |
+ +101 + | +
+
+1
+
+1. getAppidExclude : replaced return value with Optional.empty for com/yubico/webauthn/data/ClientRegistrationExtensionOutputs::getAppidExclude → KILLED + + + + |
+ return Optional.ofNullable(appidExclude); |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + /** |
+ +105 + | ++ + + + + + | + * The extension output for the Credential Properties Extension (<code>credProps</code>), if any. |
+ +106 + | ++ + + + + + | + * |
+ +107 + | ++ + + + + + | + * <p>This value MAY be present but have all members empty if the extension was successfully |
+ +108 + | ++ + + + + + | + * processed but no credential properties could be determined. |
+ +109 + | ++ + + + + + | + * |
+ +110 + | ++ + + + + + | + * @see com.yubico.webauthn.data.Extensions.CredentialProperties.CredentialPropertiesOutput |
+ +111 + | ++ + + + + + | + * @see <a |
+ +112 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4. |
+ +113 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +114 + | ++ + + + + + | + */ |
+ +115 + | ++ + + + + + | + public Optional<Extensions.CredentialProperties.CredentialPropertiesOutput> getCredProps() { |
+ +116 + | +
+
+1
+
+1. getCredProps : replaced return value with Optional.empty for com/yubico/webauthn/data/ClientRegistrationExtensionOutputs::getCredProps → KILLED + + + + |
+ return Optional.ofNullable(credProps); |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * The extension output for the Large blob storage extension (<code>largeBlob</code>), if any. |
+ +121 + | ++ + + + + + | + * |
+ +122 + | ++ + + + + + | + * @see com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobRegistrationOutput |
+ +123 + | ++ + + + + + | + * @see <a |
+ +124 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5.Large |
+ +125 + | ++ + + + + + | + * blob storage extension (largeBlob)</a> |
+ +126 + | ++ + + + + + | + */ |
+ +127 + | ++ + + + + + | + public Optional<Extensions.LargeBlob.LargeBlobRegistrationOutput> getLargeBlob() { |
+ +128 + | +
+
+1
+
+1. getLargeBlob : replaced return value with Optional.empty for com/yubico/webauthn/data/ClientRegistrationExtensionOutputs::getLargeBlob → KILLED + + + + |
+ return Optional.ofNullable(largeBlob); |
+ +129 + | ++ + + + + + | + } |
+ +130 + | ++ + + + + + | +} |
Mutations | ||
76 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
82 | ++ |
+
+
+
+ 1.1 |
+
85 | ++ |
+
+
+
+ 1.1 |
+
101 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonGenerator; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonNode; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.databind.SerializerProvider; |
+ +31 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +32 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ObjectNode; |
+ +33 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +34 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.Base64UrlException; |
+ +36 + | ++ + + + + + | +import java.io.IOException; |
+ +37 + | ++ + + + + + | +import java.util.Optional; |
+ +38 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +39 + | ++ + + + + + | +import lombok.Getter; |
+ +40 + | ++ + + + + + | +import lombok.NonNull; |
+ +41 + | ++ + + + + + | +import lombok.Value; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | +/** |
+ +44 + | ++ + + + + + | + * The client data represents the contextual bindings of both the Relying Party and the client. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * @see <a |
+ +47 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-collectedclientdata">§5.10.1. |
+ +48 + | ++ + + + + + | + * Client Data Used in WebAuthn Signatures (dictionary CollectedClientData) </a> |
+ +49 + | ++ + + + + + | + */ |
+ +50 + | ++ + + + + + | +@Value |
+ +51 + | ++ + + + + + | +@JsonSerialize(using = CollectedClientData.JsonSerializer.class) |
+ +52 + | ++ + + + + + | +public class CollectedClientData { |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + /** The client data returned from the client. */ |
+ +55 + | ++ + + + + + | + @NonNull |
+ +56 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +57 + | ++ + + + + + | + private final ByteArray clientDataJson; |
+ +58 + | ++ + + + + + | +|
+ +59 + | ++ + + + + + | + @NonNull |
+ +60 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +61 + | ++ + + + + + | + private final transient ObjectNode clientData; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + /** |
+ +64 + | ++ + + + + + | + * The base64url encoding of the challenge provided by the Relying Party. See the <a |
+ +65 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-cryptographic-challenges">§13.1 |
+ +66 + | ++ + + + + + | + * Cryptographic Challenges</a> security consideration. |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + @NonNull private final transient ByteArray challenge; |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * The fully qualified origin of the requester, as provided to the authenticator by the client, in |
+ +72 + | ++ + + + + + | + * the syntax defined by <a href="https://tools.ietf.org/html/rfc6454">RFC 6454</a>. |
+ +73 + | ++ + + + + + | + */ |
+ +74 + | ++ + + + + + | + @NonNull private final transient String origin; |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + /** The type of the requested operation, set by the client. */ |
+ +77 + | ++ + + + + + | + @NonNull private final transient String type; |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + @JsonCreator |
+ +80 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ public CollectedClientData(@NonNull ByteArray clientDataJSON) |
+ +81 + | ++ + + + + + | + throws IOException, Base64UrlException { |
+ +82 + | ++ + + + + + | + JsonNode clientData = JacksonCodecs.json().readTree(clientDataJSON.getBytes()); |
+ +83 + | ++ + + + + + | +|
+ +84 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +85 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ clientData != null && clientData.isObject(), "Collected client data must be JSON object."); |
+ +86 + | ++ + + + + + | +|
+ +87 + | ++ + + + + + | + this.clientDataJson = clientDataJSON; |
+ +88 + | ++ + + + + + | + this.clientData = (ObjectNode) clientData; |
+ +89 + | ++ + + + + + | +|
+ +90 + | ++ + + + + + | + try { |
+ +91 + | ++ + + + + + | + challenge = ByteArray.fromBase64Url(clientData.get("challenge").textValue()); |
+ +92 + | ++ + + + + + | + } catch (NullPointerException e) { |
+ +93 + | ++ + + + + + | + throw new IllegalArgumentException("Missing field: \"challenge\""); |
+ +94 + | ++ + + + + + | + } catch (Base64UrlException e) { |
+ +95 + | ++ + + + + + | + throw new Base64UrlException("Invalid \"challenge\" value", e); |
+ +96 + | ++ + + + + + | + } |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + try { |
+ +99 + | ++ + + + + + | + origin = clientData.get("origin").textValue(); |
+ +100 + | ++ + + + + + | + } catch (NullPointerException e) { |
+ +101 + | ++ + + + + + | + throw new IllegalArgumentException("Missing field: \"origin\""); |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + try { |
+ +105 + | ++ + + + + + | + type = clientData.get("type").textValue(); |
+ +106 + | ++ + + + + + | + } catch (NullPointerException e) { |
+ +107 + | ++ + + + + + | + throw new IllegalArgumentException("Missing field: \"type\""); |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | + } |
+ +110 + | ++ + + + + + | +|
+ +111 + | ++ + + + + + | + /** |
+ +112 + | ++ + + + + + | + * Information about the state of the <a href="https://tools.ietf.org/html/rfc8471">Token Binding |
+ +113 + | ++ + + + + + | + * protocol</a> used when communicating with the Relying Party. Its absence indicates that the |
+ +114 + | ++ + + + + + | + * client doesn't support token binding. |
+ +115 + | ++ + + + + + | + */ |
+ +116 + | ++ + + + + + | + public final Optional<TokenBindingInfo> getTokenBinding() { |
+ +117 + | +
+
+1
+
+1. getTokenBinding : replaced return value with Optional.empty for com/yubico/webauthn/data/CollectedClientData::getTokenBinding → KILLED + + + + |
+ return Optional.ofNullable(clientData.get("tokenBinding")) |
+ +118 + | ++ + + + + + | + .map( |
+ +119 + | ++ + + + + + | + tb -> { |
+ +120 + | +
+
+1
+
+1. lambda$getTokenBinding$1 : negated conditional → KILLED + + + + |
+ if (tb.isObject()) { |
+ +121 + | ++ + + + + + | + String status = tb.get("status").textValue(); |
+ +122 + | +
+
+1
+
+1. lambda$getTokenBinding$1 : replaced return value with null for com/yubico/webauthn/data/CollectedClientData::lambda$getTokenBinding$1 → KILLED + + + + |
+ return new TokenBindingInfo( |
+ +123 + | ++ + + + + + | + TokenBindingStatus.fromJsonString(status), |
+ +124 + | ++ + + + + + | + Optional.ofNullable(tb.get("id")) |
+ +125 + | ++ + + + + + | + .map(JsonNode::textValue) |
+ +126 + | ++ + + + + + | + .map( |
+ +127 + | ++ + + + + + | + id -> { |
+ +128 + | ++ + + + + + | + try { |
+ +129 + | +
+
+1
+
+1. lambda$getTokenBinding$0 : replaced return value with null for com/yubico/webauthn/data/CollectedClientData::lambda$getTokenBinding$0 → KILLED + + + + |
+ return ByteArray.fromBase64Url(id); |
+ +130 + | ++ + + + + + | + } catch (Base64UrlException e) { |
+ +131 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +132 + | ++ + + + + + | + "Property \"id\" is not valid Base64Url data", e); |
+ +133 + | ++ + + + + + | + } |
+ +134 + | ++ + + + + + | + })); |
+ +135 + | ++ + + + + + | + } else { |
+ +136 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +137 + | ++ + + + + + | + "Property \"tokenBinding\" missing from client data."); |
+ +138 + | ++ + + + + + | + } |
+ +139 + | ++ + + + + + | + }); |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + static class JsonSerializer |
+ +143 + | ++ + + + + + | + extends com.fasterxml.jackson.databind.JsonSerializer<CollectedClientData> { |
+ +144 + | ++ + + + + + | + @Override |
+ +145 + | ++ + + + + + | + public void serialize( |
+ +146 + | ++ + + + + + | + CollectedClientData value, JsonGenerator gen, SerializerProvider serializers) |
+ +147 + | ++ + + + + + | + throws IOException { |
+ +148 + | +
+
+1
+
+1. serialize : removed call to com/fasterxml/jackson/core/JsonGenerator::writeString → KILLED + + + + |
+ gen.writeString(value.clientDataJson.getBase64Url()); |
+ +149 + | ++ + + + + + | + } |
+ +150 + | ++ + + + + + | + } |
+ +151 + | ++ + + + + + | +} |
Mutations | ||
80 | ++ |
+
+
+
+ 1.1 |
+
84 | ++ |
+
+
+
+ 1.1 2.2 |
+
85 | ++ |
+
+
+
+ 1.1 |
+
117 | ++ |
+
+
+
+ 1.1 |
+
120 | ++ |
+
+
+
+ 1.1 |
+
122 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 |
+
148 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn.data; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +6 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +7 + | ++ + + + + + | +import com.upokecenter.cbor.CBORType; |
+ +8 + | ++ + + + + + | +import com.yubico.webauthn.StartRegistrationOptions; |
+ +9 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.KeyProtectionType; |
+ +10 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.MatcherProtectionType; |
+ +11 + | ++ + + + + + | +import com.yubico.webauthn.extension.uvm.UserVerificationMethod; |
+ +12 + | ++ + + + + + | +import java.util.List; |
+ +13 + | ++ + + + + + | +import java.util.Optional; |
+ +14 + | ++ + + + + + | +import java.util.Set; |
+ +15 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +16 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +17 + | ++ + + + + + | +import lombok.Builder; |
+ +18 + | ++ + + + + + | +import lombok.NonNull; |
+ +19 + | ++ + + + + + | +import lombok.Value; |
+ +20 + | ++ + + + + + | +import lombok.experimental.UtilityClass; |
+ +21 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +22 + | ++ + + + + + | +|
+ +23 + | ++ + + + + + | +/** Definitions for WebAuthn extensions. */ |
+ +24 + | ++ + + + + + | +@Slf4j |
+ +25 + | ++ + + + + + | +@UtilityClass |
+ +26 + | ++ + + + + + | +public class Extensions { |
+ +27 + | ++ + + + + + | +|
+ +28 + | ++ + + + + + | + /** |
+ +29 + | ++ + + + + + | + * Definitions for the FIDO AppID Extension (<code>appid</code>). |
+ +30 + | ++ + + + + + | + * |
+ +31 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +32 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +33 + | ++ + + + + + | + */ |
+ +34 + | ++ + + + + + | + public static class Appid { |
+ +35 + | ++ + + + + + | + static final String EXTENSION_ID = "appid"; |
+ +36 + | ++ + + + + + | + } |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + /** |
+ +39 + | ++ + + + + + | + * Definitions for the 10.2. FIDO AppID Exclusion Extension (<code>appidExclude</code>). |
+ +40 + | ++ + + + + + | + * |
+ +41 + | ++ + + + + + | + * @see <a |
+ +42 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">10.2. |
+ +43 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | + public static class AppidExclude { |
+ +46 + | ++ + + + + + | + static final String EXTENSION_ID = "appidExclude"; |
+ +47 + | ++ + + + + + | + } |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** |
+ +50 + | ++ + + + + + | + * Definitions for the Credential Properties Extension (<code>credProps</code>). |
+ +51 + | ++ + + + + + | + * |
+ +52 + | ++ + + + + + | + * @see <a |
+ +53 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4. |
+ +54 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | + public static class CredentialProperties { |
+ +57 + | ++ + + + + + | + static final String EXTENSION_ID = "credProps"; |
+ +58 + | ++ + + + + + | +|
+ +59 + | ++ + + + + + | + /** |
+ +60 + | ++ + + + + + | + * Extension outputs for the Credential Properties Extension (<code>credProps</code>). |
+ +61 + | ++ + + + + + | + * |
+ +62 + | ++ + + + + + | + * @see <a |
+ +63 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4. |
+ +64 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +65 + | ++ + + + + + | + */ |
+ +66 + | ++ + + + + + | + @Value |
+ +67 + | ++ + + + + + | + @Builder |
+ +68 + | ++ + + + + + | + public static class CredentialPropertiesOutput { |
+ +69 + | ++ + + + + + | + @JsonProperty("rk") |
+ +70 + | ++ + + + + + | + private final Boolean rk; |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + @JsonCreator |
+ +73 + | ++ + + + + + | + private CredentialPropertiesOutput(@JsonProperty("rk") Boolean rk) { |
+ +74 + | ++ + + + + + | + this.rk = rk; |
+ +75 + | ++ + + + + + | + } |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * This OPTIONAL property, known abstractly as the <b>resident key credential property</b> |
+ +79 + | ++ + + + + + | + * (i.e., <b>client-side discoverable credential property</b>), is a Boolean value indicating |
+ +80 + | ++ + + + + + | + * whether the {@link PublicKeyCredential} returned as a result of a registration ceremony is |
+ +81 + | ++ + + + + + | + * a <i>client-side discoverable credential</i> (passkey). |
+ +82 + | ++ + + + + + | + * |
+ +83 + | ++ + + + + + | + * <p>If this is <code>true</code>, the credential is a <i>discoverable credential</i> |
+ +84 + | ++ + + + + + | + * (passkey). |
+ +85 + | ++ + + + + + | + * |
+ +86 + | ++ + + + + + | + * <p>If this is <code>false</code>, the credential is a <i>server-side credential</i>. |
+ +87 + | ++ + + + + + | + * |
+ +88 + | ++ + + + + + | + * <p>If this is not present, it is not known whether the credential is a discoverable |
+ +89 + | ++ + + + + + | + * credential or a server-side credential. |
+ +90 + | ++ + + + + + | + * |
+ +91 + | ++ + + + + + | + * @see <a |
+ +92 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-credentialpropertiesoutput-rk">§10.4. |
+ +93 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +94 + | ++ + + + + + | + * @see <a |
+ +95 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +96 + | ++ + + + + + | + * discoverable Credential</a> |
+ +97 + | ++ + + + + + | + * @see <a |
+ +98 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#server-side-credential">Server-side |
+ +99 + | ++ + + + + + | + * Credential</a> |
+ +100 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +101 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +102 + | ++ + + + + + | + */ |
+ +103 + | ++ + + + + + | + public Optional<Boolean> getRk() { |
+ +104 + | +
+
+1
+
+1. getRk : replaced return value with Optional.empty for com/yubico/webauthn/data/Extensions$CredentialProperties$CredentialPropertiesOutput::getRk → KILLED + + + + |
+ return Optional.ofNullable(rk); |
+ +105 + | ++ + + + + + | + } |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | +|
+ +109 + | ++ + + + + + | + /** |
+ +110 + | ++ + + + + + | + * Definitions for the Large blob storage extension (<code>largeBlob</code>). |
+ +111 + | ++ + + + + + | + * |
+ +112 + | ++ + + + + + | + * @see <a |
+ +113 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +114 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +115 + | ++ + + + + + | + */ |
+ +116 + | ++ + + + + + | + public static class LargeBlob { |
+ +117 + | ++ + + + + + | + static final String EXTENSION_ID = "largeBlob"; |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * Extension inputs for the Large blob storage extension (<code>largeBlob</code>) in |
+ +121 + | ++ + + + + + | + * registration ceremonies. |
+ +122 + | ++ + + + + + | + * |
+ +123 + | ++ + + + + + | + * @see <a |
+ +124 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +125 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +126 + | ++ + + + + + | + */ |
+ +127 + | ++ + + + + + | + @Value |
+ +128 + | ++ + + + + + | + public static class LargeBlobRegistrationInput { |
+ +129 + | ++ + + + + + | + /** |
+ +130 + | ++ + + + + + | + * The Relying Party's preference of whether the created credential should support the <code> |
+ +131 + | ++ + + + + + | + * largeBlob</code> extension. |
+ +132 + | ++ + + + + + | + */ |
+ +133 + | ++ + + + + + | + @JsonProperty private final LargeBlobSupport support; |
+ +134 + | ++ + + + + + | +|
+ +135 + | ++ + + + + + | + @JsonCreator |
+ +136 + | ++ + + + + + | + public LargeBlobRegistrationInput( |
+ +137 + | ++ + + + + + | + /** |
+ +138 + | ++ + + + + + | + * The Relying Party's preference of whether the created credential should support the |
+ +139 + | ++ + + + + + | + * <code> |
+ +140 + | ++ + + + + + | + * largeBlob</code> extension. |
+ +141 + | ++ + + + + + | + * |
+ +142 + | ++ + + + + + | + * <p>Currently the only valid values are {@link LargeBlobSupport#REQUIRED} and {@link |
+ +143 + | ++ + + + + + | + * LargeBlobSupport#PREFERRED}, but custom values MAY be constructed in case more values |
+ +144 + | ++ + + + + + | + * are added in future revisions of the extension. |
+ +145 + | ++ + + + + + | + */ |
+ +146 + | ++ + + + + + | + @JsonProperty("support") LargeBlobSupport support) { |
+ +147 + | ++ + + + + + | + this.support = support; |
+ +148 + | ++ + + + + + | + } |
+ +149 + | ++ + + + + + | +|
+ +150 + | ++ + + + + + | + /** |
+ +151 + | ++ + + + + + | + * The known valid arguments for the Large blob storage extension (<code>largeBlob</code>) |
+ +152 + | ++ + + + + + | + * input in registration ceremonies. |
+ +153 + | ++ + + + + + | + * |
+ +154 + | ++ + + + + + | + * <p>Currently the only valid values are {@link LargeBlobSupport#REQUIRED} and {@link |
+ +155 + | ++ + + + + + | + * LargeBlobSupport#PREFERRED}, but custom values MAY be constructed in case more values are |
+ +156 + | ++ + + + + + | + * added in future revisions of the extension. |
+ +157 + | ++ + + + + + | + * |
+ +158 + | ++ + + + + + | + * @see #REQUIRED |
+ +159 + | ++ + + + + + | + * @see #PREFERRED |
+ +160 + | ++ + + + + + | + * @see <a |
+ +161 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +162 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +163 + | ++ + + + + + | + */ |
+ +164 + | ++ + + + + + | + @Value |
+ +165 + | ++ + + + + + | + public static class LargeBlobSupport { |
+ +166 + | ++ + + + + + | + /** |
+ +167 + | ++ + + + + + | + * The authenticator used for registration MUST support the <code>largeBlob</code> |
+ +168 + | ++ + + + + + | + * extension. |
+ +169 + | ++ + + + + + | + * |
+ +170 + | ++ + + + + + | + * <p>Note: If the client does not support the <code>largeBlob</code> extension, this |
+ +171 + | ++ + + + + + | + * requirement MAY be ignored. |
+ +172 + | ++ + + + + + | + * |
+ +173 + | ++ + + + + + | + * <p>Note: CTAP authenticators only support <code>largeBlob</code> in combination with |
+ +174 + | ++ + + + + + | + * {@link AuthenticatorSelectionCriteria#getResidentKey()} set to <code>REQUIRED</code> in |
+ +175 + | ++ + + + + + | + * {@link StartRegistrationOptions#getAuthenticatorSelection()}. |
+ +176 + | ++ + + + + + | + * |
+ +177 + | ++ + + + + + | + * @see <a |
+ +178 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +179 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +180 + | ++ + + + + + | + */ |
+ +181 + | ++ + + + + + | + public static final LargeBlobSupport REQUIRED = new LargeBlobSupport("required"); |
+ +182 + | ++ + + + + + | +|
+ +183 + | ++ + + + + + | + /** |
+ +184 + | ++ + + + + + | + * If the authenticator used for registration supports the <code>largeBlob</code> extension, |
+ +185 + | ++ + + + + + | + * it will be enabled for the created credential. If not supported, the credential will be |
+ +186 + | ++ + + + + + | + * created without large blob support. |
+ +187 + | ++ + + + + + | + * |
+ +188 + | ++ + + + + + | + * <p>Note: CTAP authenticators only support <code>largeBlob</code> in combination with |
+ +189 + | ++ + + + + + | + * {@link AuthenticatorSelectionCriteria#getResidentKey()} set to <code>REQUIRED</code> in |
+ +190 + | ++ + + + + + | + * {@link StartRegistrationOptions#getAuthenticatorSelection()}. |
+ +191 + | ++ + + + + + | + * |
+ +192 + | ++ + + + + + | + * @see <a |
+ +193 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +194 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +195 + | ++ + + + + + | + */ |
+ +196 + | ++ + + + + + | + public static final LargeBlobSupport PREFERRED = new LargeBlobSupport("preferred"); |
+ +197 + | ++ + + + + + | +|
+ +198 + | ++ + + + + + | + /** |
+ +199 + | ++ + + + + + | + * The underlying string value of this {@link LargeBlobSupport} value. |
+ +200 + | ++ + + + + + | + * |
+ +201 + | ++ + + + + + | + * @see #REQUIRED |
+ +202 + | ++ + + + + + | + * @see #PREFERRED |
+ +203 + | ++ + + + + + | + */ |
+ +204 + | ++ + + + + + | + @JsonValue private final String value; |
+ +205 + | ++ + + + + + | +|
+ +206 + | ++ + + + + + | + /** |
+ +207 + | ++ + + + + + | + * Returns a new {@link Set} containing the {@link #REQUIRED} and {@link #PREFERRED} values. |
+ +208 + | ++ + + + + + | + */ |
+ +209 + | ++ + + + + + | + public static Set<LargeBlobSupport> values() { |
+ +210 + | +
+
+1
+
+1. values : replaced return value with Collections.emptySet for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobRegistrationInput$LargeBlobSupport::values → KILLED + + + + |
+ return Stream.of(REQUIRED, PREFERRED).collect(Collectors.toSet()); |
+ +211 + | ++ + + + + + | + } |
+ +212 + | ++ + + + + + | + } |
+ +213 + | ++ + + + + + | + } |
+ +214 + | ++ + + + + + | +|
+ +215 + | ++ + + + + + | + /** |
+ +216 + | ++ + + + + + | + * Extension inputs for the Large blob storage extension (<code>largeBlob</code>) in |
+ +217 + | ++ + + + + + | + * authentication ceremonies. |
+ +218 + | ++ + + + + + | + * |
+ +219 + | ++ + + + + + | + * <p>Use the {@link #read()} and {@link #write(ByteArray)} factory functions to construct this |
+ +220 + | ++ + + + + + | + * type. |
+ +221 + | ++ + + + + + | + * |
+ +222 + | ++ + + + + + | + * @see <a |
+ +223 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +224 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +225 + | ++ + + + + + | + */ |
+ +226 + | ++ + + + + + | + @Value |
+ +227 + | ++ + + + + + | + public static class LargeBlobAuthenticationInput { |
+ +228 + | ++ + + + + + | + /** |
+ +229 + | ++ + + + + + | + * If <code>true</code>, indicates that the Relying Party would like to fetch the |
+ +230 + | ++ + + + + + | + * previously-written blob associated with the asserted credential. |
+ +231 + | ++ + + + + + | + * |
+ +232 + | ++ + + + + + | + * @see #read() |
+ +233 + | ++ + + + + + | + * @see <a |
+ +234 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargeblobinputs-read">§10.5. |
+ +235 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +236 + | ++ + + + + + | + */ |
+ +237 + | ++ + + + + + | + @JsonProperty private final boolean read; |
+ +238 + | ++ + + + + + | +|
+ +239 + | ++ + + + + + | + /** |
+ +240 + | ++ + + + + + | + * An opaque byte string that the Relying Party wishes to store with the existing credential. |
+ +241 + | ++ + + + + + | + * |
+ +242 + | ++ + + + + + | + * @see #write(ByteArray) |
+ +243 + | ++ + + + + + | + * @see <a |
+ +244 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargeblobinputs-write">§10.5. |
+ +245 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +246 + | ++ + + + + + | + */ |
+ +247 + | ++ + + + + + | + @JsonProperty private final ByteArray write; |
+ +248 + | ++ + + + + + | +|
+ +249 + | ++ + + + + + | + @JsonCreator |
+ +250 + | ++ + + + + + | + private LargeBlobAuthenticationInput( |
+ +251 + | ++ + + + + + | + @JsonProperty("read") final Boolean read, @JsonProperty("write") final ByteArray write) { |
+ +252 + | +
+
+3
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED +3. <init> : negated conditional → KILLED + + + + |
+ if (read != null && read && write != null) { |
+ +253 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +254 + | ++ + + + + + | + "Parameters \"read\" and \"write\" of largeBlob extension must not both be present."); |
+ +255 + | ++ + + + + + | + } |
+ +256 + | ++ + + + + + | +|
+ +257 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ this.read = read != null && read; |
+ +258 + | ++ + + + + + | + this.write = write; |
+ +259 + | ++ + + + + + | + } |
+ +260 + | ++ + + + + + | +|
+ +261 + | ++ + + + + + | + /** |
+ +262 + | ++ + + + + + | + * Configure the Large blob storage extension (<code>largeBlob</code>) to fetch the |
+ +263 + | ++ + + + + + | + * previously-written blob associated with the asserted credential. |
+ +264 + | ++ + + + + + | + * |
+ +265 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #write(ByteArray)}. |
+ +266 + | ++ + + + + + | + * |
+ +267 + | ++ + + + + + | + * @see <a |
+ +268 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargeblobinputs-read">§10.5. |
+ +269 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +270 + | ++ + + + + + | + */ |
+ +271 + | ++ + + + + + | + public static LargeBlobAuthenticationInput read() { |
+ +272 + | +
+
+1
+
+1. read : replaced return value with null for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationInput::read → KILLED + + + + |
+ return new LargeBlobAuthenticationInput(true, null); |
+ +273 + | ++ + + + + + | + } |
+ +274 + | ++ + + + + + | +|
+ +275 + | ++ + + + + + | + /** |
+ +276 + | ++ + + + + + | + * Configure the Large blob storage extension (<code>largeBlob</code>) to store the given byte |
+ +277 + | ++ + + + + + | + * array with the existing credential. |
+ +278 + | ++ + + + + + | + * |
+ +279 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #read()}. |
+ +280 + | ++ + + + + + | + * |
+ +281 + | ++ + + + + + | + * @see <a |
+ +282 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargeblobinputs-write">§10.5. |
+ +283 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +284 + | ++ + + + + + | + */ |
+ +285 + | +
+
+1
+
+1. write : negated conditional → KILLED + + + + |
+ public static LargeBlobAuthenticationInput write(@NonNull final ByteArray write) { |
+ +286 + | +
+
+1
+
+1. write : replaced return value with null for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationInput::write → SURVIVED + + + + |
+ return new LargeBlobAuthenticationInput(false, write); |
+ +287 + | ++ + + + + + | + } |
+ +288 + | ++ + + + + + | +|
+ +289 + | ++ + + + + + | + /** |
+ +290 + | ++ + + + + + | + * @return <code>true</code> if the <code>read</code> property is set to <code>true</code>, |
+ +291 + | ++ + + + + + | + * <code>false</code> otherwise. |
+ +292 + | ++ + + + + + | + * @see #read() |
+ +293 + | ++ + + + + + | + * @see <a |
+ +294 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargeblobinputs-read">§10.5. |
+ +295 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +296 + | ++ + + + + + | + */ |
+ +297 + | ++ + + + + + | + public boolean getRead() { |
+ +298 + | +
+
+2
+
+1. getRead : replaced boolean return with true for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationInput::getRead → KILLED +2. getRead : replaced boolean return with false for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationInput::getRead → KILLED + + + + |
+ return read; |
+ +299 + | ++ + + + + + | + } |
+ +300 + | ++ + + + + + | +|
+ +301 + | ++ + + + + + | + /** |
+ +302 + | ++ + + + + + | + * @return The value of the <code>write</code> property if configured, empty otherwise. |
+ +303 + | ++ + + + + + | + * @see #write(ByteArray) |
+ +304 + | ++ + + + + + | + * @see <a |
+ +305 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargeblobinputs-read">§10.5. |
+ +306 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +307 + | ++ + + + + + | + */ |
+ +308 + | ++ + + + + + | + public Optional<ByteArray> getWrite() { |
+ +309 + | +
+
+1
+
+1. getWrite : replaced return value with Optional.empty for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationInput::getWrite → SURVIVED + + + + |
+ return Optional.ofNullable(write); |
+ +310 + | ++ + + + + + | + } |
+ +311 + | ++ + + + + + | + } |
+ +312 + | ++ + + + + + | +|
+ +313 + | ++ + + + + + | + /** |
+ +314 + | ++ + + + + + | + * Extension outputs for the Large blob storage extension (<code>largeBlob</code>) in |
+ +315 + | ++ + + + + + | + * registration ceremonies. |
+ +316 + | ++ + + + + + | + * |
+ +317 + | ++ + + + + + | + * <p>Use the {@link #supported(boolean)} factory function to construct this type. |
+ +318 + | ++ + + + + + | + * |
+ +319 + | ++ + + + + + | + * @see <a |
+ +320 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +321 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +322 + | ++ + + + + + | + */ |
+ +323 + | ++ + + + + + | + @Value |
+ +324 + | ++ + + + + + | + public static class LargeBlobRegistrationOutput { |
+ +325 + | ++ + + + + + | + /** |
+ +326 + | ++ + + + + + | + * <code>true</code> if, and only if, the created credential supports storing large blobs. |
+ +327 + | ++ + + + + + | + * |
+ +328 + | ++ + + + + + | + * @see <a |
+ +329 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargebloboutputs-supported">§10.5. |
+ +330 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +331 + | ++ + + + + + | + * @see LargeBlobRegistrationInput#getSupport() |
+ +332 + | ++ + + + + + | + */ |
+ +333 + | ++ + + + + + | + @JsonProperty private final boolean supported; |
+ +334 + | ++ + + + + + | +|
+ +335 + | ++ + + + + + | + @JsonCreator |
+ +336 + | ++ + + + + + | + private LargeBlobRegistrationOutput(@JsonProperty("supported") boolean supported) { |
+ +337 + | ++ + + + + + | + this.supported = supported; |
+ +338 + | ++ + + + + + | + } |
+ +339 + | ++ + + + + + | +|
+ +340 + | ++ + + + + + | + /** |
+ +341 + | ++ + + + + + | + * Create a Large blob storage extension output with the <code>supported</code> output set to |
+ +342 + | ++ + + + + + | + * the given value. |
+ +343 + | ++ + + + + + | + * |
+ +344 + | ++ + + + + + | + * @see <a |
+ +345 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-authenticationextensionslargebloboutputs"> |
+ +346 + | ++ + + + + + | + * dictionary AuthenticationExtensionsLargeBlobOutputs</a> |
+ +347 + | ++ + + + + + | + */ |
+ +348 + | ++ + + + + + | + public static LargeBlobRegistrationOutput supported(boolean supported) { |
+ +349 + | +
+
+1
+
+1. supported : replaced return value with null for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobRegistrationOutput::supported → KILLED + + + + |
+ return new LargeBlobRegistrationOutput(supported); |
+ +350 + | ++ + + + + + | + } |
+ +351 + | ++ + + + + + | + } |
+ +352 + | ++ + + + + + | +|
+ +353 + | ++ + + + + + | + /** |
+ +354 + | ++ + + + + + | + * Extension outputs for the Large blob storage extension (<code>largeBlob</code>) in |
+ +355 + | ++ + + + + + | + * authentication ceremonies. |
+ +356 + | ++ + + + + + | + * |
+ +357 + | ++ + + + + + | + * @see <a |
+ +358 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +359 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +360 + | ++ + + + + + | + */ |
+ +361 + | ++ + + + + + | + @Value |
+ +362 + | ++ + + + + + | + public static class LargeBlobAuthenticationOutput { |
+ +363 + | ++ + + + + + | + @JsonProperty private final ByteArray blob; |
+ +364 + | ++ + + + + + | + @JsonProperty private final Boolean written; |
+ +365 + | ++ + + + + + | +|
+ +366 + | ++ + + + + + | + @JsonCreator |
+ +367 + | ++ + + + + + | + private LargeBlobAuthenticationOutput( |
+ +368 + | ++ + + + + + | + @JsonProperty("blob") ByteArray blob, @JsonProperty("written") Boolean written) { |
+ +369 + | ++ + + + + + | + this.blob = blob; |
+ +370 + | ++ + + + + + | + this.written = written; |
+ +371 + | ++ + + + + + | + } |
+ +372 + | ++ + + + + + | +|
+ +373 + | ++ + + + + + | + /** |
+ +374 + | ++ + + + + + | + * Create a Large blob storage extension output with the <code>blob</code> output set to the |
+ +375 + | ++ + + + + + | + * given value. |
+ +376 + | ++ + + + + + | + * |
+ +377 + | ++ + + + + + | + * <p>This corresponds to the extension input {@link LargeBlobAuthenticationInput#read() |
+ +378 + | ++ + + + + + | + * LargeBlobAuthenticationInput.read()}. |
+ +379 + | ++ + + + + + | + * |
+ +380 + | ++ + + + + + | + * @see <a |
+ +381 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-authenticationextensionslargebloboutputs"> |
+ +382 + | ++ + + + + + | + * dictionary AuthenticationExtensionsLargeBlobOutputs</a> |
+ +383 + | ++ + + + + + | + */ |
+ +384 + | ++ + + + + + | + public static LargeBlobAuthenticationOutput read(final ByteArray blob) { |
+ +385 + | +
+
+1
+
+1. read : replaced return value with null for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationOutput::read → KILLED + + + + |
+ return new LargeBlobAuthenticationOutput(blob, null); |
+ +386 + | ++ + + + + + | + } |
+ +387 + | ++ + + + + + | +|
+ +388 + | ++ + + + + + | + /** |
+ +389 + | ++ + + + + + | + * Create a Large blob storage extension output with the <code>written</code> output set to |
+ +390 + | ++ + + + + + | + * the given value. |
+ +391 + | ++ + + + + + | + * |
+ +392 + | ++ + + + + + | + * <p>This corresponds to the extension input {@link |
+ +393 + | ++ + + + + + | + * LargeBlobAuthenticationInput#write(ByteArray) |
+ +394 + | ++ + + + + + | + * LargeBlobAuthenticationInput.write(ByteArray)}. |
+ +395 + | ++ + + + + + | + * |
+ +396 + | ++ + + + + + | + * @see <a |
+ +397 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-authenticationextensionslargebloboutputs"> |
+ +398 + | ++ + + + + + | + * dictionary AuthenticationExtensionsLargeBlobOutputs</a> |
+ +399 + | ++ + + + + + | + */ |
+ +400 + | ++ + + + + + | + public static LargeBlobAuthenticationOutput write(final boolean write) { |
+ +401 + | +
+
+1
+
+1. write : replaced return value with null for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationOutput::write → KILLED + + + + |
+ return new LargeBlobAuthenticationOutput(null, write); |
+ +402 + | ++ + + + + + | + } |
+ +403 + | ++ + + + + + | +|
+ +404 + | ++ + + + + + | + /** |
+ +405 + | ++ + + + + + | + * The opaque byte string that was associated with the credential identified by {@link |
+ +406 + | ++ + + + + + | + * PublicKeyCredential#getId()}. Only valid if {@link LargeBlobAuthenticationInput#getRead()} |
+ +407 + | ++ + + + + + | + * was <code>true</code>. |
+ +408 + | ++ + + + + + | + * |
+ +409 + | ++ + + + + + | + * @return A present {@link Optional} if {@link LargeBlobAuthenticationInput#getRead()} was |
+ +410 + | ++ + + + + + | + * <code>true</code> and the blob content was successfully read. Otherwise (if {@link |
+ +411 + | ++ + + + + + | + * LargeBlobAuthenticationInput#getRead()} was <code>false</code> or the content failed to |
+ +412 + | ++ + + + + + | + * be read) an empty {@link Optional}. |
+ +413 + | ++ + + + + + | + * @see <a |
+ +414 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargebloboutputs-blob">§10.5. |
+ +415 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +416 + | ++ + + + + + | + */ |
+ +417 + | ++ + + + + + | + public Optional<ByteArray> getBlob() { |
+ +418 + | +
+
+1
+
+1. getBlob : replaced return value with Optional.empty for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationOutput::getBlob → KILLED + + + + |
+ return Optional.ofNullable(blob); |
+ +419 + | ++ + + + + + | + } |
+ +420 + | ++ + + + + + | +|
+ +421 + | ++ + + + + + | + /** |
+ +422 + | ++ + + + + + | + * A boolean that indicates that the contents of {@link |
+ +423 + | ++ + + + + + | + * LargeBlob.LargeBlobAuthenticationInput#write(ByteArray) |
+ +424 + | ++ + + + + + | + * LargeBlobAuthenticationInput#write(ByteArray)} were successfully stored on the |
+ +425 + | ++ + + + + + | + * authenticator, associated with the specified credential. |
+ +426 + | ++ + + + + + | + * |
+ +427 + | ++ + + + + + | + * @return Empty if {@link LargeBlobAuthenticationInput#getWrite()} was not present. Otherwise |
+ +428 + | ++ + + + + + | + * <code>true</code> if and only if the value of {@link |
+ +429 + | ++ + + + + + | + * LargeBlobAuthenticationInput#getWrite()} was successfully stored by the authenticator. |
+ +430 + | ++ + + + + + | + * @see <a |
+ +431 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticationextensionslargebloboutputs-written">§10.5. |
+ +432 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +433 + | ++ + + + + + | + */ |
+ +434 + | ++ + + + + + | + public Optional<Boolean> getWritten() { |
+ +435 + | +
+
+1
+
+1. getWritten : replaced return value with Optional.empty for com/yubico/webauthn/data/Extensions$LargeBlob$LargeBlobAuthenticationOutput::getWritten → KILLED + + + + |
+ return Optional.ofNullable(written); |
+ +436 + | ++ + + + + + | + } |
+ +437 + | ++ + + + + + | + } |
+ +438 + | ++ + + + + + | + } |
+ +439 + | ++ + + + + + | +|
+ +440 + | ++ + + + + + | + /** |
+ +441 + | ++ + + + + + | + * Definitions for the User Verification Method (<code>uvm</code>) Extension. |
+ +442 + | ++ + + + + + | + * |
+ +443 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +444 + | ++ + + + + + | + * User Verification Method Extension (uvm)</a> |
+ +445 + | ++ + + + + + | + */ |
+ +446 + | ++ + + + + + | + public static class Uvm { |
+ +447 + | ++ + + + + + | + static final String EXTENSION_ID = "uvm"; |
+ +448 + | ++ + + + + + | +|
+ +449 + | ++ + + + + + | + /** |
+ +450 + | ++ + + + + + | + * A <code>uvmEntry</code> as defined in <a |
+ +451 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. User |
+ +452 + | ++ + + + + + | + * Verification Method Extension (uvm)</a>. |
+ +453 + | ++ + + + + + | + * |
+ +454 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +455 + | ++ + + + + + | + * User Verification Method Extension (uvm)</a> |
+ +456 + | ++ + + + + + | + * @see UserVerificationMethod |
+ +457 + | ++ + + + + + | + * @see KeyProtectionType |
+ +458 + | ++ + + + + + | + * @see MatcherProtectionType |
+ +459 + | ++ + + + + + | + */ |
+ +460 + | ++ + + + + + | + @Value |
+ +461 + | ++ + + + + + | + public static class UvmEntry { |
+ +462 + | ++ + + + + + | + private final UserVerificationMethod userVerificationMethod; |
+ +463 + | ++ + + + + + | + private final KeyProtectionType keyProtectionType; |
+ +464 + | ++ + + + + + | + private final MatcherProtectionType matcherProtectionType; |
+ +465 + | ++ + + + + + | +|
+ +466 + | ++ + + + + + | + public UvmEntry( |
+ +467 + | ++ + + + + + | + @JsonProperty("userVerificationMethod") UserVerificationMethod userVerificationMethod, |
+ +468 + | ++ + + + + + | + @JsonProperty("keyProtectionType") KeyProtectionType keyProtectionType, |
+ +469 + | ++ + + + + + | + @JsonProperty("matcherProtectionType") MatcherProtectionType matcherProtectionType) { |
+ +470 + | ++ + + + + + | + this.userVerificationMethod = userVerificationMethod; |
+ +471 + | ++ + + + + + | + this.keyProtectionType = keyProtectionType; |
+ +472 + | ++ + + + + + | + this.matcherProtectionType = matcherProtectionType; |
+ +473 + | ++ + + + + + | + } |
+ +474 + | ++ + + + + + | + } |
+ +475 + | ++ + + + + + | +|
+ +476 + | ++ + + + + + | + static Optional<List<UvmEntry>> parseAuthenticatorExtensionOutput(CBORObject cbor) { |
+ +477 + | +
+
+1
+
+1. parseAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (validateAuthenticatorExtensionOutput(cbor)) { |
+ +478 + | +
+
+1
+
+1. parseAuthenticatorExtensionOutput : replaced return value with Optional.empty for com/yubico/webauthn/data/Extensions$Uvm::parseAuthenticatorExtensionOutput → KILLED + + + + |
+ return Optional.of( |
+ +479 + | ++ + + + + + | + cbor.get(EXTENSION_ID).getValues().stream() |
+ +480 + | ++ + + + + + | + .map( |
+ +481 + | ++ + + + + + | + uvmEntry -> |
+ +482 + | +
+
+1
+
+1. lambda$parseAuthenticatorExtensionOutput$0 : replaced return value with null for com/yubico/webauthn/data/Extensions$Uvm::lambda$parseAuthenticatorExtensionOutput$0 → KILLED + + + + |
+ new UvmEntry( |
+ +483 + | ++ + + + + + | + UserVerificationMethod.fromValue(uvmEntry.get(0).AsInt32Value()), |
+ +484 + | ++ + + + + + | + KeyProtectionType.fromValue( |
+ +485 + | ++ + + + + + | + uvmEntry.get(1).AsNumber().ToInt16IfExact()), |
+ +486 + | ++ + + + + + | + MatcherProtectionType.fromValue( |
+ +487 + | ++ + + + + + | + uvmEntry.get(2).AsNumber().ToInt16IfExact()))) |
+ +488 + | ++ + + + + + | + .collect(Collectors.toList())); |
+ +489 + | ++ + + + + + | + } else { |
+ +490 + | ++ + + + + + | + return Optional.empty(); |
+ +491 + | ++ + + + + + | + } |
+ +492 + | ++ + + + + + | + } |
+ +493 + | ++ + + + + + | +|
+ +494 + | ++ + + + + + | + private static boolean validateAuthenticatorExtensionOutput(CBORObject extensions) { |
+ +495 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (!extensions.ContainsKey(EXTENSION_ID)) { |
+ +496 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with true for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → NO_COVERAGE + + + + |
+ return false; |
+ +497 + | ++ + + + + + | + } |
+ +498 + | ++ + + + + + | +|
+ +499 + | ++ + + + + + | + CBORObject uvm = extensions.get(EXTENSION_ID); |
+ +500 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (uvm.getType() != CBORType.Array) { |
+ +501 + | ++ + + + + + | + log.debug( |
+ +502 + | ++ + + + + + | + "Invalid CBOR type for \"{}\" extension output: expected array, was: {}", |
+ +503 + | ++ + + + + + | + EXTENSION_ID, |
+ +504 + | ++ + + + + + | + uvm.getType()); |
+ +505 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with true for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → NO_COVERAGE + + + + |
+ return false; |
+ +506 + | ++ + + + + + | + } |
+ +507 + | ++ + + + + + | +|
+ +508 + | +
+
+4
+
+1. validateAuthenticatorExtensionOutput : changed conditional boundary → SURVIVED +2. validateAuthenticatorExtensionOutput : changed conditional boundary → SURVIVED +3. validateAuthenticatorExtensionOutput : negated conditional → KILLED +4. validateAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (uvm.size() < 1 || uvm.size() > 3) { |
+ +509 + | ++ + + + + + | + log.debug( |
+ +510 + | ++ + + + + + | + "Invalid length \"{}\" extension output array: expected 1 to 3 (inclusive), was: {}", |
+ +511 + | ++ + + + + + | + EXTENSION_ID, |
+ +512 + | ++ + + + + + | + uvm.size()); |
+ +513 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with true for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → NO_COVERAGE + + + + |
+ return false; |
+ +514 + | ++ + + + + + | + } |
+ +515 + | ++ + + + + + | +|
+ +516 + | ++ + + + + + | + for (CBORObject entry : uvm.getValues()) { |
+ +517 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (entry.getType() != CBORType.Array) { |
+ +518 + | ++ + + + + + | + log.debug("Invalid CBOR type for uvmEntry: expected array, was: {}", entry.getType()); |
+ +519 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with true for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → NO_COVERAGE + + + + |
+ return false; |
+ +520 + | ++ + + + + + | + } |
+ +521 + | ++ + + + + + | +|
+ +522 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (entry.size() != 3) { |
+ +523 + | ++ + + + + + | + log.debug("Invalid length for uvmEntry: expected 3, was: {}", entry.size()); |
+ +524 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with true for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → NO_COVERAGE + + + + |
+ return false; |
+ +525 + | ++ + + + + + | + } |
+ +526 + | ++ + + + + + | +|
+ +527 + | ++ + + + + + | + for (CBORObject i : entry.getValues()) { |
+ +528 + | +
+
+2
+
+1. validateAuthenticatorExtensionOutput : negated conditional → KILLED +2. validateAuthenticatorExtensionOutput : negated conditional → KILLED + + + + |
+ if (!(i.isNumber() && i.AsNumber().IsInteger())) { |
+ +529 + | ++ + + + + + | + log.debug("Invalid type for uvmEntry element: expected integer, was: {}", i.getType()); |
+ +530 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with true for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → NO_COVERAGE + + + + |
+ return false; |
+ +531 + | ++ + + + + + | + } |
+ +532 + | ++ + + + + + | + } |
+ +533 + | ++ + + + + + | + } |
+ +534 + | ++ + + + + + | +|
+ +535 + | +
+
+1
+
+1. validateAuthenticatorExtensionOutput : replaced boolean return with false for com/yubico/webauthn/data/Extensions$Uvm::validateAuthenticatorExtensionOutput → KILLED + + + + |
+ return true; |
+ +536 + | ++ + + + + + | + } |
+ +537 + | ++ + + + + + | + } |
+ +538 + | ++ + + + + + | +} |
Mutations | ||
104 | ++ |
+
+
+
+ 1.1 |
+
210 | ++ |
+
+
+
+ 1.1 |
+
252 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
257 | ++ |
+
+
+
+ 1.1 2.2 |
+
272 | ++ |
+
+
+
+ 1.1 |
+
285 | ++ |
+
+
+
+ 1.1 |
+
286 | ++ |
+
+
+
+ 1.1 |
+
298 | ++ |
+
+
+
+ 1.1 2.2 |
+
309 | ++ |
+
+
+
+ 1.1 |
+
349 | ++ |
+
+
+
+ 1.1 |
+
385 | ++ |
+
+
+
+ 1.1 |
+
401 | ++ |
+
+
+
+ 1.1 |
+
418 | ++ |
+
+
+
+ 1.1 |
+
435 | ++ |
+
+
+
+ 1.1 |
+
477 | ++ |
+
+
+
+ 1.1 |
+
478 | ++ |
+
+
+
+ 1.1 |
+
482 | ++ |
+
+
+
+ 1.1 |
+
495 | ++ |
+
+
+
+ 1.1 |
+
496 | ++ |
+
+
+
+ 1.1 |
+
500 | ++ |
+
+
+
+ 1.1 |
+
505 | ++ |
+
+
+
+ 1.1 |
+
508 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 |
+
513 | ++ |
+
+
+
+ 1.1 |
+
517 | ++ |
+
+
+
+ 1.1 |
+
519 | ++ |
+
+
+
+ 1.1 |
+
522 | ++ |
+
+
+
+ 1.1 |
+
524 | ++ |
+
+
+
+ 1.1 |
+
528 | ++ |
+
+
+
+ 1.1 2.2 |
+
530 | ++ |
+
+
+
+ 1.1 |
+
535 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.core.type.TypeReference; |
+ +30 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.AssertionResult; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.FinishAssertionOptions; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.FinishRegistrationOptions; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.RegistrationResult; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.RelyingParty; |
+ +36 + | ++ + + + + + | +import java.io.IOException; |
+ +37 + | ++ + + + + + | +import java.util.Optional; |
+ +38 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +39 + | ++ + + + + + | +import lombok.Builder; |
+ +40 + | ++ + + + + + | +import lombok.NonNull; |
+ +41 + | ++ + + + + + | +import lombok.Value; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | +/** |
+ +44 + | ++ + + + + + | + * The PublicKeyCredential interface inherits from Credential <a |
+ +45 + | ++ + + + + + | + * href="https://www.w3.org/TR/credential-management-1/">[CREDENTIAL-MANAGEMENT-1]</a>, and contains |
+ +46 + | ++ + + + + + | + * the attributes that are returned to the caller when a new credential is created, or a new |
+ +47 + | ++ + + + + + | + * assertion is requested. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#iface-pkcredential">§5.1. |
+ +50 + | ++ + + + + + | + * PublicKeyCredential Interface</a> |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | +@Value |
+ +53 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +54 + | ++ + + + + + | +public class PublicKeyCredential< |
+ +55 + | ++ + + + + + | + A extends AuthenticatorResponse, B extends ClientExtensionOutputs> { |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + /** |
+ +58 + | ++ + + + + + | + * The raw Credential ID of this credential, corresponding to the <code>rawId</code> attribute in |
+ +59 + | ++ + + + + + | + * the WebAuthn API. |
+ +60 + | ++ + + + + + | + */ |
+ +61 + | ++ + + + + + | + @NonNull private final ByteArray id; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + /** |
+ +64 + | ++ + + + + + | + * The authenticator's response to the client’s request to either create a public key credential, |
+ +65 + | ++ + + + + + | + * or generate an authentication assertion. |
+ +66 + | ++ + + + + + | + * |
+ +67 + | ++ + + + + + | + * <p>If the {@link PublicKeyCredential} was created in response to <code> |
+ +68 + | ++ + + + + + | + * navigator.credentials.create()</code>, this attribute’s value will be an {@link |
+ +69 + | ++ + + + + + | + * AuthenticatorAttestationResponse}, otherwise, the {@link PublicKeyCredential} was created in |
+ +70 + | ++ + + + + + | + * response to <code>navigator.credentials.get()</code>, and this attribute’s value will be an |
+ +71 + | ++ + + + + + | + * {@link AuthenticatorAssertionResponse}. |
+ +72 + | ++ + + + + + | + */ |
+ +73 + | ++ + + + + + | + @NonNull private final A response; |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + private final AuthenticatorAttachment authenticatorAttachment; |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * A map containing extension identifier → client extension output entries produced by the |
+ +79 + | ++ + + + + + | + * extension’s client extension processing. |
+ +80 + | ++ + + + + + | + */ |
+ +81 + | ++ + + + + + | + @NonNull private final B clientExtensionResults; |
+ +82 + | ++ + + + + + | +|
+ +83 + | ++ + + + + + | + /** The {@link PublicKeyCredential}'s type value is the string "public-key". */ |
+ +84 + | ++ + + + + + | + @NonNull @Builder.Default |
+ +85 + | ++ + + + + + | + private final PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY; |
+ +86 + | ++ + + + + + | +|
+ +87 + | ++ + + + + + | + @JsonCreator |
+ +88 + | ++ + + + + + | + private PublicKeyCredential( |
+ +89 + | ++ + + + + + | + @JsonProperty("id") ByteArray id, |
+ +90 + | ++ + + + + + | + @JsonProperty("rawId") ByteArray rawId, |
+ +91 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("response") A response, |
+ +92 + | ++ + + + + + | + @JsonProperty("authenticatorAttachment") AuthenticatorAttachment authenticatorAttachment, |
+ +93 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("clientExtensionResults") B clientExtensionResults, |
+ +94 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("type") PublicKeyCredentialType type) { |
+ +95 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ if (id == null && rawId == null) { |
+ +96 + | ++ + + + + + | + throw new NullPointerException("At least one of \"id\" and \"rawId\" must be non-null."); |
+ +97 + | ++ + + + + + | + } |
+ +98 + | +
+
+3
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED +3. <init> : negated conditional → KILLED + + + + |
+ if (id != null && rawId != null && !id.equals(rawId)) { |
+ +99 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +100 + | ++ + + + + + | + String.format("\"id\" and \"rawId\" are not equal: %s != %s", id, rawId)); |
+ +101 + | ++ + + + + + | + } |
+ +102 + | ++ + + + + + | +|
+ +103 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ this.id = id == null ? rawId : id; |
+ +104 + | ++ + + + + + | + this.response = response; |
+ +105 + | ++ + + + + + | + this.authenticatorAttachment = authenticatorAttachment; |
+ +106 + | ++ + + + + + | + this.clientExtensionResults = clientExtensionResults; |
+ +107 + | ++ + + + + + | + this.type = type; |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + private PublicKeyCredential( |
+ +111 + | ++ + + + + + | + ByteArray id, |
+ +112 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull A response, |
+ +113 + | ++ + + + + + | + AuthenticatorAttachment authenticatorAttachment, |
+ +114 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull B clientExtensionResults, |
+ +115 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull PublicKeyCredentialType type) { |
+ +116 + | ++ + + + + + | + this(id, null, response, authenticatorAttachment, clientExtensionResults, type); |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator |
+ +121 + | ++ + + + + + | + * attachment modality</a> in effect at the time the credential was created or used. |
+ +122 + | ++ + + + + + | + * |
+ +123 + | ++ + + + + + | + * <p>If parsed from JSON, this will be present if and only if the input was a valid value of |
+ +124 + | ++ + + + + + | + * {@link AuthenticatorAttachment}. |
+ +125 + | ++ + + + + + | + * |
+ +126 + | ++ + + + + + | + * <p>The same value will also be available via {@link |
+ +127 + | ++ + + + + + | + * RegistrationResult#getAuthenticatorAttachment()} or {@link |
+ +128 + | ++ + + + + + | + * AssertionResult#getAuthenticatorAttachment()} on the result from {@link |
+ +129 + | ++ + + + + + | + * RelyingParty#finishRegistration(FinishRegistrationOptions)} or {@link |
+ +130 + | ++ + + + + + | + * RelyingParty#finishAssertion(FinishAssertionOptions)}. |
+ +131 + | ++ + + + + + | + * |
+ +132 + | ++ + + + + + | + * @see RegistrationResult#getAuthenticatorAttachment() |
+ +133 + | ++ + + + + + | + * @see AssertionResult#getAuthenticatorAttachment() |
+ +134 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +135 + | ++ + + + + + | + * the standard matures. |
+ +136 + | ++ + + + + + | + */ |
+ +137 + | ++ + + + + + | + @Deprecated |
+ +138 + | ++ + + + + + | + public Optional<AuthenticatorAttachment> getAuthenticatorAttachment() { |
+ +139 + | +
+
+1
+
+1. getAuthenticatorAttachment : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredential::getAuthenticatorAttachment → KILLED + + + + |
+ return Optional.ofNullable(authenticatorAttachment); |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + public static <A extends AuthenticatorResponse, B extends ClientExtensionOutputs> |
+ +143 + | ++ + + + + + | + PublicKeyCredentialBuilder<A, B>.MandatoryStages builder() { |
+ +144 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential::builder → KILLED + + + + |
+ return new PublicKeyCredentialBuilder<A, B>().start(); |
+ +145 + | ++ + + + + + | + } |
+ +146 + | ++ + + + + + | +|
+ +147 + | ++ + + + + + | + public static class PublicKeyCredentialBuilder< |
+ +148 + | ++ + + + + + | + A extends AuthenticatorResponse, B extends ClientExtensionOutputs> { |
+ +149 + | ++ + + + + + | + private MandatoryStages start() { |
+ +150 + | +
+
+1
+
+1. start : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential$PublicKeyCredentialBuilder::start → KILLED + + + + |
+ return new MandatoryStages(this); |
+ +151 + | ++ + + + + + | + } |
+ +152 + | ++ + + + + + | +|
+ +153 + | ++ + + + + + | + @AllArgsConstructor |
+ +154 + | ++ + + + + + | + public class MandatoryStages { |
+ +155 + | ++ + + + + + | + private final PublicKeyCredentialBuilder<A, B> builder; |
+ +156 + | ++ + + + + + | +|
+ +157 + | ++ + + + + + | + /** |
+ +158 + | ++ + + + + + | + * {@link PublicKeyCredentialBuilder#id(ByteArray) id} is a required parameter. |
+ +159 + | ++ + + + + + | + * |
+ +160 + | ++ + + + + + | + * @see PublicKeyCredentialBuilder#id(ByteArray) |
+ +161 + | ++ + + + + + | + */ |
+ +162 + | ++ + + + + + | + public Step2 id(ByteArray id) { |
+ +163 + | ++ + + + + + | + builder.id(id); |
+ +164 + | +
+
+1
+
+1. id : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential$PublicKeyCredentialBuilder$MandatoryStages::id → KILLED + + + + |
+ return new Step2(); |
+ +165 + | ++ + + + + + | + } |
+ +166 + | ++ + + + + + | +|
+ +167 + | ++ + + + + + | + public class Step2 { |
+ +168 + | ++ + + + + + | + /** |
+ +169 + | ++ + + + + + | + * {@link PublicKeyCredentialBuilder#response(AuthenticatorResponse) response} is a required |
+ +170 + | ++ + + + + + | + * parameter. |
+ +171 + | ++ + + + + + | + * |
+ +172 + | ++ + + + + + | + * @see PublicKeyCredentialBuilder#response(AuthenticatorResponse) |
+ +173 + | ++ + + + + + | + */ |
+ +174 + | ++ + + + + + | + public Step3 response(A response) { |
+ +175 + | ++ + + + + + | + builder.response(response); |
+ +176 + | +
+
+1
+
+1. response : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential$PublicKeyCredentialBuilder$MandatoryStages$Step2::response → KILLED + + + + |
+ return new Step3(); |
+ +177 + | ++ + + + + + | + } |
+ +178 + | ++ + + + + + | + } |
+ +179 + | ++ + + + + + | +|
+ +180 + | ++ + + + + + | + public class Step3 { |
+ +181 + | ++ + + + + + | + /** |
+ +182 + | ++ + + + + + | + * {@link PublicKeyCredentialBuilder#clientExtensionResults(ClientExtensionOutputs) |
+ +183 + | ++ + + + + + | + * clientExtensionResults} is a required parameter. |
+ +184 + | ++ + + + + + | + * |
+ +185 + | ++ + + + + + | + * @see PublicKeyCredentialBuilder#clientExtensionResults(ClientExtensionOutputs) |
+ +186 + | ++ + + + + + | + */ |
+ +187 + | ++ + + + + + | + public PublicKeyCredentialBuilder<A, B> clientExtensionResults(B clientExtensionResults) { |
+ +188 + | +
+
+1
+
+1. clientExtensionResults : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential$PublicKeyCredentialBuilder$MandatoryStages$Step3::clientExtensionResults → KILLED + + + + |
+ return builder.clientExtensionResults(clientExtensionResults); |
+ +189 + | ++ + + + + + | + } |
+ +190 + | ++ + + + + + | + } |
+ +191 + | ++ + + + + + | + } |
+ +192 + | ++ + + + + + | + } |
+ +193 + | ++ + + + + + | +|
+ +194 + | ++ + + + + + | + /** |
+ +195 + | ++ + + + + + | + * Parse a {@link PublicKeyCredential} object from JSON. |
+ +196 + | ++ + + + + + | + * |
+ +197 + | ++ + + + + + | + * <p>The <code>json</code> should be of the following format: |
+ +198 + | ++ + + + + + | + * |
+ +199 + | ++ + + + + + | + * <pre> |
+ +200 + | ++ + + + + + | + * { |
+ +201 + | ++ + + + + + | + * "id": "(resp.id)", |
+ +202 + | ++ + + + + + | + * "response": { |
+ +203 + | ++ + + + + + | + * "attestationObject": "(Base64Url encoded resp.attestationObject)", |
+ +204 + | ++ + + + + + | + * "clientDataJSON": "(Base64Url encoded resp.clientDataJSON)" |
+ +205 + | ++ + + + + + | + * }, |
+ +206 + | ++ + + + + + | + * "clientExtensionResults": { (resp.getClientExtensionResults()) }, |
+ +207 + | ++ + + + + + | + * "type": "public-key" |
+ +208 + | ++ + + + + + | + * } |
+ +209 + | ++ + + + + + | + * </pre> |
+ +210 + | ++ + + + + + | + * |
+ +211 + | ++ + + + + + | + * <dl> |
+ +212 + | ++ + + + + + | + * <dt>resp: |
+ +213 + | ++ + + + + + | + * <dd>The <a |
+ +214 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#iface-pkcredential">PublicKeyCredential</a> |
+ +215 + | ++ + + + + + | + * object returned from a registration ceremony. |
+ +216 + | ++ + + + + + | + * <dt>id: |
+ +217 + | ++ + + + + + | + * <dd>The string value of <code>resp.id</code> |
+ +218 + | ++ + + + + + | + * <dt>response.attestationObject: |
+ +219 + | ++ + + + + + | + * <dd>The value of <code>resp.attestationObject</code>, Base64Url encoded as a string |
+ +220 + | ++ + + + + + | + * <dt>response.clientDataJSON: |
+ +221 + | ++ + + + + + | + * <dd>The value of <code>resp.clientDataJSON</code>, Base64Url encoded as a string |
+ +222 + | ++ + + + + + | + * <dt>clientExtensionResults: |
+ +223 + | ++ + + + + + | + * <dd>The return value of <code>resp.getClientExtensionResults()</code> |
+ +224 + | ++ + + + + + | + * <dt>type: |
+ +225 + | ++ + + + + + | + * <dd>The literal string value <code>"public-key"</code> |
+ +226 + | ++ + + + + + | + * </dl> |
+ +227 + | ++ + + + + + | + * |
+ +228 + | ++ + + + + + | + * @param json a JSON string of the above format |
+ +229 + | ++ + + + + + | + * @throws IOException if the <code>json</code> is invalid or cannot be decoded as a {@link |
+ +230 + | ++ + + + + + | + * PublicKeyCredential} |
+ +231 + | ++ + + + + + | + */ |
+ +232 + | ++ + + + + + | + public static PublicKeyCredential< |
+ +233 + | ++ + + + + + | + AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +234 + | ++ + + + + + | + parseRegistrationResponseJson(String json) throws IOException { |
+ +235 + | +
+
+1
+
+1. parseRegistrationResponseJson : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential::parseRegistrationResponseJson → KILLED + + + + |
+ return JacksonCodecs.json() |
+ +236 + | ++ + + + + + | + .readValue( |
+ +237 + | ++ + + + + + | + json, |
+ +238 + | ++ + + + + + | + new TypeReference< |
+ +239 + | ++ + + + + + | + PublicKeyCredential< |
+ +240 + | ++ + + + + + | + AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs>>() {}); |
+ +241 + | ++ + + + + + | + } |
+ +242 + | ++ + + + + + | +|
+ +243 + | ++ + + + + + | + /** |
+ +244 + | ++ + + + + + | + * Parse a {@link PublicKeyCredential} object from JSON. |
+ +245 + | ++ + + + + + | + * |
+ +246 + | ++ + + + + + | + * <p>The <code>json</code> should be of the following format: |
+ +247 + | ++ + + + + + | + * |
+ +248 + | ++ + + + + + | + * <pre> |
+ +249 + | ++ + + + + + | + * { |
+ +250 + | ++ + + + + + | + * "id": "(resp.id)", |
+ +251 + | ++ + + + + + | + * "response": { |
+ +252 + | ++ + + + + + | + * "authenticatorData": "(Base64Url encoded resp.authenticatorData)", |
+ +253 + | ++ + + + + + | + * "signature": "(Base64Url encoded resp.signature)", |
+ +254 + | ++ + + + + + | + * "clientDataJSON": "(Base64Url encoded resp.clientDataJSON)", |
+ +255 + | ++ + + + + + | + * "userHandle": "(null, undefined or Base64Url encoded resp.userHandle)" |
+ +256 + | ++ + + + + + | + * }, |
+ +257 + | ++ + + + + + | + * "clientExtensionResults": { (resp.getClientExtensionResults()) }, |
+ +258 + | ++ + + + + + | + * "type": "public-key" |
+ +259 + | ++ + + + + + | + * } |
+ +260 + | ++ + + + + + | + * </pre> |
+ +261 + | ++ + + + + + | + * |
+ +262 + | ++ + + + + + | + * <dl> |
+ +263 + | ++ + + + + + | + * <dt>resp: |
+ +264 + | ++ + + + + + | + * <dd>The <a |
+ +265 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#iface-pkcredential">PublicKeyCredential</a> |
+ +266 + | ++ + + + + + | + * object returned from an authentication ceremony. |
+ +267 + | ++ + + + + + | + * <dt>id: |
+ +268 + | ++ + + + + + | + * <dd>The string value of <code>resp.id</code> |
+ +269 + | ++ + + + + + | + * <dt>response.authenticatorData: |
+ +270 + | ++ + + + + + | + * <dd>The value of <code>resp.authenticatorData</code>, Base64Url encoded as a string |
+ +271 + | ++ + + + + + | + * <dt>response.signature: |
+ +272 + | ++ + + + + + | + * <dd>The value of <code>resp.signature</code>, Base64Url encoded as a string |
+ +273 + | ++ + + + + + | + * <dt>response.clientDataJSON: |
+ +274 + | ++ + + + + + | + * <dd>The value of <code>resp.clientDataJSON</code>, Base64Url encoded as a string |
+ +275 + | ++ + + + + + | + * <dt>response.userHandle: |
+ +276 + | ++ + + + + + | + * <dd>The value of <code>resp.userHandle</code> Base64Url encoded as a string if present, |
+ +277 + | ++ + + + + + | + * otherwise <code>null</code> or <code>undefined</code> |
+ +278 + | ++ + + + + + | + * <dt>clientExtensionResults: |
+ +279 + | ++ + + + + + | + * <dd>The return value of <code>resp.getClientExtensionResults()</code> |
+ +280 + | ++ + + + + + | + * <dt>type: |
+ +281 + | ++ + + + + + | + * <dd>The literal string value <code>"public-key"</code> |
+ +282 + | ++ + + + + + | + * </dl> |
+ +283 + | ++ + + + + + | + * |
+ +284 + | ++ + + + + + | + * @param json a JSON string of the above format |
+ +285 + | ++ + + + + + | + * @throws IOException if the <code>json</code> is invalid or cannot be decoded as a {@link |
+ +286 + | ++ + + + + + | + * PublicKeyCredential} |
+ +287 + | ++ + + + + + | + */ |
+ +288 + | ++ + + + + + | + public static PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> |
+ +289 + | ++ + + + + + | + parseAssertionResponseJson(String json) throws IOException { |
+ +290 + | +
+
+1
+
+1. parseAssertionResponseJson : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredential::parseAssertionResponseJson → KILLED + + + + |
+ return JacksonCodecs.json() |
+ +291 + | ++ + + + + + | + .readValue( |
+ +292 + | ++ + + + + + | + json, |
+ +293 + | ++ + + + + + | + new TypeReference< |
+ +294 + | ++ + + + + + | + PublicKeyCredential< |
+ +295 + | ++ + + + + + | + AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs>>() {}); |
+ +296 + | ++ + + + + + | + } |
+ +297 + | ++ + + + + + | +} |
Mutations | ||
91 | ++ |
+
+
+
+ 1.1 |
+
93 | ++ |
+
+
+
+ 1.1 |
+
94 | ++ |
+
+
+
+ 1.1 |
+
95 | ++ |
+
+
+
+ 1.1 2.2 |
+
98 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
103 | ++ |
+
+
+
+ 1.1 |
+
112 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
115 | ++ |
+
+
+
+ 1.1 |
+
139 | ++ |
+
+
+
+ 1.1 |
+
144 | ++ |
+
+
+
+ 1.1 |
+
150 | ++ |
+
+
+
+ 1.1 |
+
164 | ++ |
+
+
+
+ 1.1 |
+
176 | ++ |
+
+
+
+ 1.1 |
+
188 | ++ |
+
+
+
+ 1.1 |
+
235 | ++ |
+
+
+
+ 1.1 |
+
290 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonProcessingException; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.databind.ObjectMapper; |
+ +31 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ObjectNode; |
+ +32 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +33 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.FinishRegistrationOptions; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.RelyingParty; |
+ +36 + | ++ + + + + + | +import java.security.KeyFactory; |
+ +37 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +38 + | ++ + + + + + | +import java.security.Signature; |
+ +39 + | ++ + + + + + | +import java.util.Collections; |
+ +40 + | ++ + + + + + | +import java.util.List; |
+ +41 + | ++ + + + + + | +import java.util.Optional; |
+ +42 + | ++ + + + + + | +import java.util.Set; |
+ +43 + | ++ + + + + + | +import java.util.TreeSet; |
+ +44 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +45 + | ++ + + + + + | +import lombok.Builder; |
+ +46 + | ++ + + + + + | +import lombok.NonNull; |
+ +47 + | ++ + + + + + | +import lombok.Value; |
+ +48 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | +/** |
+ +51 + | ++ + + + + + | + * Parameters for a call to <code>navigator.credentials.create()</code>. |
+ +52 + | ++ + + + + + | + * |
+ +53 + | ++ + + + + + | + * @see <a |
+ +54 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-publickeycredentialcreationoptions">§5.4. |
+ +55 + | ++ + + + + + | + * Options for Credential Creation (dictionary PublicKeyCredentialCreationOptions)</a> |
+ +56 + | ++ + + + + + | + */ |
+ +57 + | ++ + + + + + | +@Slf4j |
+ +58 + | ++ + + + + + | +@Value |
+ +59 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +60 + | ++ + + + + + | +public class PublicKeyCredentialCreationOptions { |
+ +61 + | ++ + + + + + | +|
+ +62 + | ++ + + + + + | + /** |
+ +63 + | ++ + + + + + | + * Contains data about the Relying Party responsible for the request. |
+ +64 + | ++ + + + + + | + * |
+ +65 + | ++ + + + + + | + * <p>Its value's {@link RelyingPartyIdentity#getId() id} member specifies the <a |
+ +66 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#rp-id">RP ID</a> the credential |
+ +67 + | ++ + + + + + | + * should be scoped to. If omitted, its value will be set by the client. See {@link |
+ +68 + | ++ + + + + + | + * RelyingPartyIdentity} for further details. |
+ +69 + | ++ + + + + + | + */ |
+ +70 + | ++ + + + + + | + @NonNull private final RelyingPartyIdentity rp; |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + /** Contains data about the user account for which the Relying Party is requesting attestation. */ |
+ +73 + | ++ + + + + + | + @NonNull private final UserIdentity user; |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + /** |
+ +76 + | ++ + + + + + | + * A challenge intended to be used for generating the newly created credential’s attestation |
+ +77 + | ++ + + + + + | + * object. See the <a |
+ +78 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-cryptographic-challenges">§13.1 |
+ +79 + | ++ + + + + + | + * Cryptographic Challenges</a> security consideration. |
+ +80 + | ++ + + + + + | + */ |
+ +81 + | ++ + + + + + | + @NonNull private final ByteArray challenge; |
+ +82 + | ++ + + + + + | +|
+ +83 + | ++ + + + + + | + /** |
+ +84 + | ++ + + + + + | + * Information about the desired properties of the credential to be created. |
+ +85 + | ++ + + + + + | + * |
+ +86 + | ++ + + + + + | + * <p>The sequence is ordered from most preferred to least preferred. The client makes a |
+ +87 + | ++ + + + + + | + * best-effort to create the most preferred credential that it can. |
+ +88 + | ++ + + + + + | + */ |
+ +89 + | ++ + + + + + | + @NonNull private final List<PublicKeyCredentialParameters> pubKeyCredParams; |
+ +90 + | ++ + + + + + | +|
+ +91 + | ++ + + + + + | + /** |
+ +92 + | ++ + + + + + | + * A time, in milliseconds, that the caller is willing to wait for the call to complete. This is |
+ +93 + | ++ + + + + + | + * treated as a hint, and MAY be overridden by the client. |
+ +94 + | ++ + + + + + | + */ |
+ +95 + | ++ + + + + + | + private final Long timeout; |
+ +96 + | ++ + + + + + | +|
+ +97 + | ++ + + + + + | + /** |
+ +98 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to limit the creation of multiple credentials for |
+ +99 + | ++ + + + + + | + * the same account on a single authenticator. The client is requested to return an error if the |
+ +100 + | ++ + + + + + | + * new credential would be created on an authenticator that also contains one of the credentials |
+ +101 + | ++ + + + + + | + * enumerated in this parameter. |
+ +102 + | ++ + + + + + | + */ |
+ +103 + | ++ + + + + + | + private final Set<PublicKeyCredentialDescriptor> excludeCredentials; |
+ +104 + | ++ + + + + + | +|
+ +105 + | ++ + + + + + | + /** |
+ +106 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to select the appropriate authenticators to |
+ +107 + | ++ + + + + + | + * participate in the create() operation. |
+ +108 + | ++ + + + + + | + */ |
+ +109 + | ++ + + + + + | + private final AuthenticatorSelectionCriteria authenticatorSelection; |
+ +110 + | ++ + + + + + | +|
+ +111 + | ++ + + + + + | + /** |
+ +112 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to express their preference for attestation |
+ +113 + | ++ + + + + + | + * conveyance. The default is {@link AttestationConveyancePreference#NONE}. |
+ +114 + | ++ + + + + + | + */ |
+ +115 + | ++ + + + + + | + @NonNull private final AttestationConveyancePreference attestation; |
+ +116 + | ++ + + + + + | +|
+ +117 + | ++ + + + + + | + /** |
+ +118 + | ++ + + + + + | + * Additional parameters requesting additional processing by the client and authenticator. |
+ +119 + | ++ + + + + + | + * |
+ +120 + | ++ + + + + + | + * <p>For example, the caller may request that only authenticators with certain capabilities be |
+ +121 + | ++ + + + + + | + * used to create the credential, or that particular information be returned in the attestation |
+ +122 + | ++ + + + + + | + * object. Some extensions are defined in <a |
+ +123 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9 WebAuthn |
+ +124 + | ++ + + + + + | + * Extensions</a>; consult the IANA "WebAuthn Extension Identifier" registry established by <a |
+ +125 + | ++ + + + + + | + * href="https://tools.ietf.org/html/draft-hodges-webauthn-registries">[WebAuthn-Registries]</a> |
+ +126 + | ++ + + + + + | + * for an up-to-date list of registered WebAuthn Extensions. |
+ +127 + | ++ + + + + + | + */ |
+ +128 + | ++ + + + + + | + @NonNull private final RegistrationExtensionInputs extensions; |
+ +129 + | ++ + + + + + | +|
+ +130 + | ++ + + + + + | + @Builder |
+ +131 + | ++ + + + + + | + @JsonCreator |
+ +132 + | ++ + + + + + | + private PublicKeyCredentialCreationOptions( |
+ +133 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("rp") RelyingPartyIdentity rp, |
+ +134 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("user") UserIdentity user, |
+ +135 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("challenge") ByteArray challenge, |
+ +136 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("pubKeyCredParams") |
+ +137 + | ++ + + + + + | + List<PublicKeyCredentialParameters> pubKeyCredParams, |
+ +138 + | ++ + + + + + | + @JsonProperty("timeout") Long timeout, |
+ +139 + | ++ + + + + + | + @JsonProperty("excludeCredentials") Set<PublicKeyCredentialDescriptor> excludeCredentials, |
+ +140 + | ++ + + + + + | + @JsonProperty("authenticatorSelection") AuthenticatorSelectionCriteria authenticatorSelection, |
+ +141 + | ++ + + + + + | + @JsonProperty("attestation") AttestationConveyancePreference attestation, |
+ +142 + | ++ + + + + + | + @JsonProperty("extensions") RegistrationExtensionInputs extensions) { |
+ +143 + | ++ + + + + + | + this.rp = rp; |
+ +144 + | ++ + + + + + | + this.user = user; |
+ +145 + | ++ + + + + + | + this.challenge = challenge; |
+ +146 + | ++ + + + + + | + this.pubKeyCredParams = filterAvailableAlgorithms(pubKeyCredParams); |
+ +147 + | ++ + + + + + | + this.timeout = timeout; |
+ +148 + | ++ + + + + + | + this.excludeCredentials = |
+ +149 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ excludeCredentials == null |
+ +150 + | ++ + + + + + | + ? null |
+ +151 + | ++ + + + + + | + : CollectionUtil.immutableSortedSet(new TreeSet<>(excludeCredentials)); |
+ +152 + | ++ + + + + + | + this.authenticatorSelection = authenticatorSelection; |
+ +153 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ this.attestation = attestation == null ? AttestationConveyancePreference.NONE : attestation; |
+ +154 + | ++ + + + + + | + this.extensions = |
+ +155 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ extensions == null ? RegistrationExtensionInputs.builder().build() : extensions; |
+ +156 + | ++ + + + + + | + } |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + /** |
+ +159 + | ++ + + + + + | + * Serialize this {@link PublicKeyCredentialCreationOptions} value to JSON suitable for sending to |
+ +160 + | ++ + + + + + | + * the client. |
+ +161 + | ++ + + + + + | + * |
+ +162 + | ++ + + + + + | + * <p>Any {@link ByteArray} values in this data structure will be {@link ByteArray#getBase64Url() |
+ +163 + | ++ + + + + + | + * Base64Url} encoded. Those values MUST be decoded into <code>BufferSource</code> values (such as |
+ +164 + | ++ + + + + + | + * <code>Uint8Array</code>) on the client side before calling <code>navigator.credentials.create() |
+ +165 + | ++ + + + + + | + * </code>. |
+ +166 + | ++ + + + + + | + * |
+ +167 + | ++ + + + + + | + * <p>After decoding binary values, the resulting JavaScript object is suitable for passing as an |
+ +168 + | ++ + + + + + | + * argument to <code>navigator.credentials.create()</code>. |
+ +169 + | ++ + + + + + | + * |
+ +170 + | ++ + + + + + | + * @return a JSON value suitable for sending to the client and passing as an argument to <code> |
+ +171 + | ++ + + + + + | + * navigator.credentials.create()</code>, after decoding binary options from Base64Url |
+ +172 + | ++ + + + + + | + * strings. |
+ +173 + | ++ + + + + + | + * @throws JsonProcessingException if JSON serialization fails. |
+ +174 + | ++ + + + + + | + */ |
+ +175 + | ++ + + + + + | + public String toCredentialsCreateJson() throws JsonProcessingException { |
+ +176 + | ++ + + + + + | + ObjectMapper json = JacksonCodecs.json(); |
+ +177 + | ++ + + + + + | + ObjectNode result = json.createObjectNode(); |
+ +178 + | ++ + + + + + | + result.set("publicKey", json.valueToTree(this)); |
+ +179 + | +
+
+1
+
+1. toCredentialsCreateJson : replaced return value with "" for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::toCredentialsCreateJson → KILLED + + + + |
+ return json.writeValueAsString(result); |
+ +180 + | ++ + + + + + | + } |
+ +181 + | ++ + + + + + | +|
+ +182 + | ++ + + + + + | + /** |
+ +183 + | ++ + + + + + | + * Encode this {@link PublicKeyCredentialCreationOptions} to JSON. The inverse of {@link |
+ +184 + | ++ + + + + + | + * #fromJson(String)}. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * <p>This method is suitable for encoding the {@link PublicKeyCredentialCreationOptions} for |
+ +187 + | ++ + + + + + | + * temporary storage so that it can later be passed as an argument to {@link |
+ +188 + | ++ + + + + + | + * RelyingParty#finishRegistration(FinishRegistrationOptions)}. The {@link #fromJson(String)} |
+ +189 + | ++ + + + + + | + * factory function is guaranteed to restore an identical {@link |
+ +190 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions} instance. |
+ +191 + | ++ + + + + + | + * |
+ +192 + | ++ + + + + + | + * <p>Note that encoding might not be needed if you can simply keep the {@link |
+ +193 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions} instance in server memory. |
+ +194 + | ++ + + + + + | + * |
+ +195 + | ++ + + + + + | + * @return this {@link PublicKeyCredentialCreationOptions} encoded to JSON. |
+ +196 + | ++ + + + + + | + * @throws JsonProcessingException |
+ +197 + | ++ + + + + + | + */ |
+ +198 + | ++ + + + + + | + public String toJson() throws JsonProcessingException { |
+ +199 + | +
+
+1
+
+1. toJson : replaced return value with "" for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::toJson → KILLED + + + + |
+ return JacksonCodecs.json().writeValueAsString(this); |
+ +200 + | ++ + + + + + | + } |
+ +201 + | ++ + + + + + | +|
+ +202 + | ++ + + + + + | + /** |
+ +203 + | ++ + + + + + | + * Decode a {@link PublicKeyCredentialCreationOptions} from JSON. The inverse of {@link |
+ +204 + | ++ + + + + + | + * #toJson()}. |
+ +205 + | ++ + + + + + | + * |
+ +206 + | ++ + + + + + | + * <p>If the JSON was generated by the {@link #toJson()} method, then {@link #fromJson(String)} in |
+ +207 + | ++ + + + + + | + * the same library version guarantees to restore an identical {@link |
+ +208 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions} instance. This is not guaranteed between different library |
+ +209 + | ++ + + + + + | + * versions. |
+ +210 + | ++ + + + + + | + * |
+ +211 + | ++ + + + + + | + * @return a {@link PublicKeyCredentialCreationOptions} decoded from the input JSON. |
+ +212 + | ++ + + + + + | + * @throws JsonProcessingException |
+ +213 + | ++ + + + + + | + */ |
+ +214 + | ++ + + + + + | + public static PublicKeyCredentialCreationOptions fromJson(String json) |
+ +215 + | ++ + + + + + | + throws JsonProcessingException { |
+ +216 + | +
+
+1
+
+1. fromJson : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::fromJson → KILLED + + + + |
+ return JacksonCodecs.json().readValue(json, PublicKeyCredentialCreationOptions.class); |
+ +217 + | ++ + + + + + | + } |
+ +218 + | ++ + + + + + | +|
+ +219 + | ++ + + + + + | + public Optional<Long> getTimeout() { |
+ +220 + | +
+
+1
+
+1. getTimeout : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::getTimeout → KILLED + + + + |
+ return Optional.ofNullable(timeout); |
+ +221 + | ++ + + + + + | + } |
+ +222 + | ++ + + + + + | +|
+ +223 + | ++ + + + + + | + public Optional<Set<PublicKeyCredentialDescriptor>> getExcludeCredentials() { |
+ +224 + | +
+
+1
+
+1. getExcludeCredentials : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::getExcludeCredentials → KILLED + + + + |
+ return Optional.ofNullable(excludeCredentials); |
+ +225 + | ++ + + + + + | + } |
+ +226 + | ++ + + + + + | +|
+ +227 + | ++ + + + + + | + public Optional<AuthenticatorSelectionCriteria> getAuthenticatorSelection() { |
+ +228 + | +
+
+1
+
+1. getAuthenticatorSelection : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::getAuthenticatorSelection → KILLED + + + + |
+ return Optional.ofNullable(authenticatorSelection); |
+ +229 + | ++ + + + + + | + } |
+ +230 + | ++ + + + + + | +|
+ +231 + | ++ + + + + + | + public static PublicKeyCredentialCreationOptionsBuilder.MandatoryStages builder() { |
+ +232 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::builder → KILLED + + + + |
+ return new PublicKeyCredentialCreationOptionsBuilder.MandatoryStages(); |
+ +233 + | ++ + + + + + | + } |
+ +234 + | ++ + + + + + | +|
+ +235 + | ++ + + + + + | + public static class PublicKeyCredentialCreationOptionsBuilder { |
+ +236 + | ++ + + + + + | + private Long timeout = null; |
+ +237 + | ++ + + + + + | + private Set<PublicKeyCredentialDescriptor> excludeCredentials = null; |
+ +238 + | ++ + + + + + | + private AuthenticatorSelectionCriteria authenticatorSelection = null; |
+ +239 + | ++ + + + + + | +|
+ +240 + | ++ + + + + + | + public static class MandatoryStages { |
+ +241 + | ++ + + + + + | + private final PublicKeyCredentialCreationOptionsBuilder builder = |
+ +242 + | ++ + + + + + | + new PublicKeyCredentialCreationOptionsBuilder(); |
+ +243 + | ++ + + + + + | +|
+ +244 + | ++ + + + + + | + /** |
+ +245 + | ++ + + + + + | + * {@link PublicKeyCredentialCreationOptionsBuilder#rp(RelyingPartyIdentity) rp} is a required |
+ +246 + | ++ + + + + + | + * parameter. |
+ +247 + | ++ + + + + + | + * |
+ +248 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptionsBuilder#rp(RelyingPartyIdentity) |
+ +249 + | ++ + + + + + | + */ |
+ +250 + | ++ + + + + + | + public Step2 rp(RelyingPartyIdentity rp) { |
+ +251 + | ++ + + + + + | + builder.rp(rp); |
+ +252 + | +
+
+1
+
+1. rp : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder$MandatoryStages::rp → KILLED + + + + |
+ return new Step2(); |
+ +253 + | ++ + + + + + | + } |
+ +254 + | ++ + + + + + | +|
+ +255 + | ++ + + + + + | + public class Step2 { |
+ +256 + | ++ + + + + + | + /** |
+ +257 + | ++ + + + + + | + * {@link PublicKeyCredentialCreationOptionsBuilder#user(UserIdentity) user} is a required |
+ +258 + | ++ + + + + + | + * parameter. |
+ +259 + | ++ + + + + + | + * |
+ +260 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptionsBuilder#user(UserIdentity) |
+ +261 + | ++ + + + + + | + */ |
+ +262 + | ++ + + + + + | + public Step3 user(UserIdentity user) { |
+ +263 + | ++ + + + + + | + builder.user(user); |
+ +264 + | +
+
+1
+
+1. user : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder$MandatoryStages$Step2::user → KILLED + + + + |
+ return new Step3(); |
+ +265 + | ++ + + + + + | + } |
+ +266 + | ++ + + + + + | + } |
+ +267 + | ++ + + + + + | +|
+ +268 + | ++ + + + + + | + public class Step3 { |
+ +269 + | ++ + + + + + | + /** |
+ +270 + | ++ + + + + + | + * {@link PublicKeyCredentialCreationOptionsBuilder#challenge(ByteArray) challenge} is a |
+ +271 + | ++ + + + + + | + * required parameter. |
+ +272 + | ++ + + + + + | + * |
+ +273 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptionsBuilder#challenge(ByteArray) |
+ +274 + | ++ + + + + + | + */ |
+ +275 + | ++ + + + + + | + public Step4 challenge(ByteArray challenge) { |
+ +276 + | ++ + + + + + | + builder.challenge(challenge); |
+ +277 + | +
+
+1
+
+1. challenge : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder$MandatoryStages$Step3::challenge → KILLED + + + + |
+ return new Step4(); |
+ +278 + | ++ + + + + + | + } |
+ +279 + | ++ + + + + + | + } |
+ +280 + | ++ + + + + + | +|
+ +281 + | ++ + + + + + | + public class Step4 { |
+ +282 + | ++ + + + + + | + /** |
+ +283 + | ++ + + + + + | + * {@link PublicKeyCredentialCreationOptionsBuilder#pubKeyCredParams(List) pubKeyCredParams} |
+ +284 + | ++ + + + + + | + * is a required parameter. |
+ +285 + | ++ + + + + + | + * |
+ +286 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptionsBuilder#pubKeyCredParams(List) |
+ +287 + | ++ + + + + + | + */ |
+ +288 + | ++ + + + + + | + public PublicKeyCredentialCreationOptionsBuilder pubKeyCredParams( |
+ +289 + | ++ + + + + + | + List<PublicKeyCredentialParameters> pubKeyCredParams) { |
+ +290 + | +
+
+1
+
+1. pubKeyCredParams : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder$MandatoryStages$Step4::pubKeyCredParams → KILLED + + + + |
+ return builder.pubKeyCredParams(pubKeyCredParams); |
+ +291 + | ++ + + + + + | + } |
+ +292 + | ++ + + + + + | + } |
+ +293 + | ++ + + + + + | + } |
+ +294 + | ++ + + + + + | +|
+ +295 + | ++ + + + + + | + /** |
+ +296 + | ++ + + + + + | + * A time, in milliseconds, that the caller is willing to wait for the call to complete. This is |
+ +297 + | ++ + + + + + | + * treated as a hint, and MAY be overridden by the client. |
+ +298 + | ++ + + + + + | + */ |
+ +299 + | +
+
+1
+
+1. timeout : negated conditional → KILLED + + + + |
+ public PublicKeyCredentialCreationOptionsBuilder timeout(@NonNull Optional<Long> timeout) { |
+ +300 + | ++ + + + + + | + this.timeout = timeout.orElse(null); |
+ +301 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::timeout → KILLED + + + + |
+ return this; |
+ +302 + | ++ + + + + + | + } |
+ +303 + | ++ + + + + + | +|
+ +304 + | ++ + + + + + | + /* |
+ +305 + | ++ + + + + + | + * Workaround, see: https://github.com/rzwitserloot/lombok/issues/2623#issuecomment-714816001 |
+ +306 + | ++ + + + + + | + * Consider reverting this workaround if Lombok fixes that issue. |
+ +307 + | ++ + + + + + | + */ |
+ +308 + | ++ + + + + + | + private PublicKeyCredentialCreationOptionsBuilder timeout(Long timeout) { |
+ +309 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::timeout → KILLED + + + + |
+ return this.timeout(Optional.ofNullable(timeout)); |
+ +310 + | ++ + + + + + | + } |
+ +311 + | ++ + + + + + | +|
+ +312 + | ++ + + + + + | + /** |
+ +313 + | ++ + + + + + | + * A time, in milliseconds, that the caller is willing to wait for the call to complete. This is |
+ +314 + | ++ + + + + + | + * treated as a hint, and MAY be overridden by the client. |
+ +315 + | ++ + + + + + | + */ |
+ +316 + | ++ + + + + + | + public PublicKeyCredentialCreationOptionsBuilder timeout(long timeout) { |
+ +317 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::timeout → NO_COVERAGE + + + + |
+ return this.timeout(Optional.of(timeout)); |
+ +318 + | ++ + + + + + | + } |
+ +319 + | ++ + + + + + | +|
+ +320 + | ++ + + + + + | + /** |
+ +321 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to limit the creation of multiple credentials |
+ +322 + | ++ + + + + + | + * for the same account on a single authenticator. The client is requested to return an error if |
+ +323 + | ++ + + + + + | + * the new credential would be created on an authenticator that also contains one of the |
+ +324 + | ++ + + + + + | + * credentials enumerated in this parameter. |
+ +325 + | ++ + + + + + | + */ |
+ +326 + | ++ + + + + + | + public PublicKeyCredentialCreationOptionsBuilder excludeCredentials( |
+ +327 + | ++ + + + + + | + Optional<Set<PublicKeyCredentialDescriptor>> excludeCredentials) { |
+ +328 + | +
+
+1
+
+1. excludeCredentials : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::excludeCredentials → KILLED + + + + |
+ return this.excludeCredentials(excludeCredentials.orElse(null)); |
+ +329 + | ++ + + + + + | + } |
+ +330 + | ++ + + + + + | +|
+ +331 + | ++ + + + + + | + /** |
+ +332 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to limit the creation of multiple credentials |
+ +333 + | ++ + + + + + | + * for the same account on a single authenticator. The client is requested to return an error if |
+ +334 + | ++ + + + + + | + * the new credential would be created on an authenticator that also contains one of the |
+ +335 + | ++ + + + + + | + * credentials enumerated in this parameter. |
+ +336 + | ++ + + + + + | + */ |
+ +337 + | ++ + + + + + | + public PublicKeyCredentialCreationOptionsBuilder excludeCredentials( |
+ +338 + | ++ + + + + + | + Set<PublicKeyCredentialDescriptor> excludeCredentials) { |
+ +339 + | ++ + + + + + | + this.excludeCredentials = excludeCredentials; |
+ +340 + | +
+
+1
+
+1. excludeCredentials : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::excludeCredentials → KILLED + + + + |
+ return this; |
+ +341 + | ++ + + + + + | + } |
+ +342 + | ++ + + + + + | +|
+ +343 + | ++ + + + + + | + /** |
+ +344 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to select the appropriate authenticators to |
+ +345 + | ++ + + + + + | + * participate in the create() operation. |
+ +346 + | ++ + + + + + | + */ |
+ +347 + | ++ + + + + + | + public PublicKeyCredentialCreationOptionsBuilder authenticatorSelection( |
+ +348 + | +
+
+1
+
+1. authenticatorSelection : negated conditional → KILLED + + + + |
+ @NonNull Optional<AuthenticatorSelectionCriteria> authenticatorSelection) { |
+ +349 + | +
+
+1
+
+1. authenticatorSelection : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::authenticatorSelection → KILLED + + + + |
+ return this.authenticatorSelection(authenticatorSelection.orElse(null)); |
+ +350 + | ++ + + + + + | + } |
+ +351 + | ++ + + + + + | +|
+ +352 + | ++ + + + + + | + /** |
+ +353 + | ++ + + + + + | + * Intended for use by Relying Parties that wish to select the appropriate authenticators to |
+ +354 + | ++ + + + + + | + * participate in the create() operation. |
+ +355 + | ++ + + + + + | + */ |
+ +356 + | ++ + + + + + | + public PublicKeyCredentialCreationOptionsBuilder authenticatorSelection( |
+ +357 + | ++ + + + + + | + AuthenticatorSelectionCriteria authenticatorSelection) { |
+ +358 + | ++ + + + + + | + this.authenticatorSelection = authenticatorSelection; |
+ +359 + | +
+
+1
+
+1. authenticatorSelection : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions$PublicKeyCredentialCreationOptionsBuilder::authenticatorSelection → KILLED + + + + |
+ return this; |
+ +360 + | ++ + + + + + | + } |
+ +361 + | ++ + + + + + | + } |
+ +362 + | ++ + + + + + | +|
+ +363 + | ++ + + + + + | + /* |
+ +364 + | ++ + + + + + | + * Essentially a copy of RelyingParty.filterAvailableAlgorithms(List) because that method and WebAuthnCodecs are not visible here. |
+ +365 + | ++ + + + + + | + */ |
+ +366 + | ++ + + + + + | + private static List<PublicKeyCredentialParameters> filterAvailableAlgorithms( |
+ +367 + | ++ + + + + + | + List<PublicKeyCredentialParameters> pubKeyCredParams) { |
+ +368 + | +
+
+1
+
+1. filterAvailableAlgorithms : replaced return value with Collections.emptyList for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::filterAvailableAlgorithms → KILLED + + + + |
+ return Collections.unmodifiableList( |
+ +369 + | ++ + + + + + | + pubKeyCredParams.stream() |
+ +370 + | ++ + + + + + | + .filter( |
+ +371 + | ++ + + + + + | + param -> { |
+ +372 + | ++ + + + + + | + try { |
+ +373 + | ++ + + + + + | + switch (param.getAlg()) { |
+ +374 + | ++ + + + + + | + case EdDSA: |
+ +375 + | ++ + + + + + | + KeyFactory.getInstance("EdDSA"); |
+ +376 + | ++ + + + + + | + break; |
+ +377 + | ++ + + + + + | +|
+ +378 + | ++ + + + + + | + case ES256: |
+ +379 + | ++ + + + + + | + case ES384: |
+ +380 + | ++ + + + + + | + case ES512: |
+ +381 + | ++ + + + + + | + KeyFactory.getInstance("EC"); |
+ +382 + | ++ + + + + + | + break; |
+ +383 + | ++ + + + + + | +|
+ +384 + | ++ + + + + + | + case RS256: |
+ +385 + | ++ + + + + + | + case RS384: |
+ +386 + | ++ + + + + + | + case RS512: |
+ +387 + | ++ + + + + + | + case RS1: |
+ +388 + | ++ + + + + + | + KeyFactory.getInstance("RSA"); |
+ +389 + | ++ + + + + + | + break; |
+ +390 + | ++ + + + + + | +|
+ +391 + | ++ + + + + + | + default: |
+ +392 + | ++ + + + + + | + log.warn( |
+ +393 + | ++ + + + + + | + "Unknown algorithm: {}. Please file a bug report.", param.getAlg()); |
+ +394 + | ++ + + + + + | + } |
+ +395 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +396 + | ++ + + + + + | + log.warn( |
+ +397 + | ++ + + + + + | + "Unsupported algorithm in PublicKeyCredentialCreationOptions.pubKeyCredParams: {}. No KeyFactory available; registrations with this key algorithm will fail. You may need to add a dependency and load a provider using java.security.Security.addProvider().", |
+ +398 + | ++ + + + + + | + param.getAlg()); |
+ +399 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with true for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::lambda$filterAvailableAlgorithms$0 → KILLED + + + + |
+ return false; |
+ +400 + | ++ + + + + + | + } |
+ +401 + | ++ + + + + + | +|
+ +402 + | ++ + + + + + | + try { |
+ +403 + | ++ + + + + + | + switch (param.getAlg()) { |
+ +404 + | ++ + + + + + | + case EdDSA: |
+ +405 + | ++ + + + + + | + Signature.getInstance("EDDSA"); |
+ +406 + | ++ + + + + + | + break; |
+ +407 + | ++ + + + + + | +|
+ +408 + | ++ + + + + + | + case ES256: |
+ +409 + | ++ + + + + + | + Signature.getInstance("SHA256withECDSA"); |
+ +410 + | ++ + + + + + | + break; |
+ +411 + | ++ + + + + + | +|
+ +412 + | ++ + + + + + | + case ES384: |
+ +413 + | ++ + + + + + | + Signature.getInstance("SHA384withECDSA"); |
+ +414 + | ++ + + + + + | + break; |
+ +415 + | ++ + + + + + | +|
+ +416 + | ++ + + + + + | + case ES512: |
+ +417 + | ++ + + + + + | + Signature.getInstance("SHA512withECDSA"); |
+ +418 + | ++ + + + + + | + break; |
+ +419 + | ++ + + + + + | +|
+ +420 + | ++ + + + + + | + case RS256: |
+ +421 + | ++ + + + + + | + Signature.getInstance("SHA256withRSA"); |
+ +422 + | ++ + + + + + | + break; |
+ +423 + | ++ + + + + + | +|
+ +424 + | ++ + + + + + | + case RS384: |
+ +425 + | ++ + + + + + | + Signature.getInstance("SHA384withRSA"); |
+ +426 + | ++ + + + + + | + break; |
+ +427 + | ++ + + + + + | +|
+ +428 + | ++ + + + + + | + case RS512: |
+ +429 + | ++ + + + + + | + Signature.getInstance("SHA512withRSA"); |
+ +430 + | ++ + + + + + | + break; |
+ +431 + | ++ + + + + + | +|
+ +432 + | ++ + + + + + | + case RS1: |
+ +433 + | ++ + + + + + | + Signature.getInstance("SHA1withRSA"); |
+ +434 + | ++ + + + + + | + break; |
+ +435 + | ++ + + + + + | +|
+ +436 + | ++ + + + + + | + default: |
+ +437 + | ++ + + + + + | + log.warn( |
+ +438 + | ++ + + + + + | + "Unknown algorithm: {}. Please file a bug report.", param.getAlg()); |
+ +439 + | ++ + + + + + | + } |
+ +440 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +441 + | ++ + + + + + | + log.warn( |
+ +442 + | ++ + + + + + | + "Unsupported algorithm in PublicKeyCredentialCreationOptions.pubKeyCredParams: {}. No Signature available; registrations with this key algorithm will fail. You may need to add a dependency and load a provider using java.security.Security.addProvider().", |
+ +443 + | ++ + + + + + | + param.getAlg()); |
+ +444 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with true for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::lambda$filterAvailableAlgorithms$0 → NO_COVERAGE + + + + |
+ return false; |
+ +445 + | ++ + + + + + | + } |
+ +446 + | ++ + + + + + | +|
+ +447 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with false for com/yubico/webauthn/data/PublicKeyCredentialCreationOptions::lambda$filterAvailableAlgorithms$0 → KILLED + + + + |
+ return true; |
+ +448 + | ++ + + + + + | + }) |
+ +449 + | ++ + + + + + | + .collect(Collectors.toList())); |
+ +450 + | ++ + + + + + | + } |
+ +451 + | ++ + + + + + | +} |
Mutations | ||
133 | ++ |
+
+
+
+ 1.1 |
+
134 | ++ |
+
+
+
+ 1.1 |
+
135 | ++ |
+
+
+
+ 1.1 |
+
136 | ++ |
+
+
+
+ 1.1 |
+
149 | ++ |
+
+
+
+ 1.1 |
+
153 | ++ |
+
+
+
+ 1.1 |
+
155 | ++ |
+
+
+
+ 1.1 |
+
179 | ++ |
+
+
+
+ 1.1 |
+
199 | ++ |
+
+
+
+ 1.1 |
+
216 | ++ |
+
+
+
+ 1.1 |
+
220 | ++ |
+
+
+
+ 1.1 |
+
224 | ++ |
+
+
+
+ 1.1 |
+
228 | ++ |
+
+
+
+ 1.1 |
+
232 | ++ |
+
+
+
+ 1.1 |
+
252 | ++ |
+
+
+
+ 1.1 |
+
264 | ++ |
+
+
+
+ 1.1 |
+
277 | ++ |
+
+
+
+ 1.1 |
+
290 | ++ |
+
+
+
+ 1.1 |
+
299 | ++ |
+
+
+
+ 1.1 |
+
301 | ++ |
+
+
+
+ 1.1 |
+
309 | ++ |
+
+
+
+ 1.1 |
+
317 | ++ |
+
+
+
+ 1.1 |
+
328 | ++ |
+
+
+
+ 1.1 |
+
340 | ++ |
+
+
+
+ 1.1 |
+
348 | ++ |
+
+
+
+ 1.1 |
+
349 | ++ |
+
+
+
+ 1.1 |
+
359 | ++ |
+
+
+
+ 1.1 |
+
368 | ++ |
+
+
+
+ 1.1 |
+
399 | ++ |
+
+
+
+ 1.1 |
+
444 | ++ |
+
+
+
+ 1.1 |
+
447 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +30 + | ++ + + + + + | +import com.yubico.internal.util.ComparableUtil; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.RegistrationResult; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.ToPublicKeyCredentialDescriptor; |
+ +33 + | ++ + + + + + | +import java.util.Optional; |
+ +34 + | ++ + + + + + | +import java.util.Set; |
+ +35 + | ++ + + + + + | +import java.util.SortedSet; |
+ +36 + | ++ + + + + + | +import java.util.TreeSet; |
+ +37 + | ++ + + + + + | +import lombok.Builder; |
+ +38 + | ++ + + + + + | +import lombok.NonNull; |
+ +39 + | ++ + + + + + | +import lombok.Value; |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | +/** |
+ +42 + | ++ + + + + + | + * The attributes that are specified by a caller when referring to a public key credential as an |
+ +43 + | ++ + + + + + | + * input parameter to the <code>navigator.credentials.create()</code> or <code> |
+ +44 + | ++ + + + + + | + * navigator.credentials.get()</code> methods. It mirrors the fields of the {@link |
+ +45 + | ++ + + + + + | + * PublicKeyCredential} object returned by the latter methods. |
+ +46 + | ++ + + + + + | + * |
+ +47 + | ++ + + + + + | + * @see <a |
+ +48 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-publickeycredentialdescriptor">§5.10.3. |
+ +49 + | ++ + + + + + | + * Credential Descriptor (dictionary PublicKeyCredentialDescriptor)</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | +@Value |
+ +52 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +53 + | ++ + + + + + | +public class PublicKeyCredentialDescriptor |
+ +54 + | ++ + + + + + | + implements Comparable<PublicKeyCredentialDescriptor>, ToPublicKeyCredentialDescriptor { |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** The type of the credential the caller is referring to. */ |
+ +57 + | ++ + + + + + | + @NonNull @Builder.Default |
+ +58 + | ++ + + + + + | + private final PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY; |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + /** The credential ID of the public key credential the caller is referring to. */ |
+ +61 + | ++ + + + + + | + @NonNull private final ByteArray id; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + /** |
+ +64 + | ++ + + + + + | + * An OPTIONAL hint as to how the client might communicate with the managing authenticator of the |
+ +65 + | ++ + + + + + | + * public key credential the caller is referring to. |
+ +66 + | ++ + + + + + | + * |
+ +67 + | ++ + + + + + | + * <p>This SHOULD be stored along with the {@link #getId() id} and used unmodified whenever |
+ +68 + | ++ + + + + + | + * creating a {@link PublicKeyCredentialDescriptor} for this credential. |
+ +69 + | ++ + + + + + | + */ |
+ +70 + | ++ + + + + + | + private final SortedSet<AuthenticatorTransport> transports; |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + @JsonCreator |
+ +73 + | ++ + + + + + | + private PublicKeyCredentialDescriptor( |
+ +74 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("type") PublicKeyCredentialType type, |
+ +75 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("id") ByteArray id, |
+ +76 + | ++ + + + + + | + @JsonProperty("transports") Set<AuthenticatorTransport> transports) { |
+ +77 + | ++ + + + + + | + this.type = type; |
+ +78 + | ++ + + + + + | + this.id = id; |
+ +79 + | ++ + + + + + | + this.transports = |
+ +80 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ transports == null ? null : CollectionUtil.immutableSortedSet(new TreeSet<>(transports)); |
+ +81 + | ++ + + + + + | + } |
+ +82 + | ++ + + + + + | +|
+ +83 + | ++ + + + + + | + @Override |
+ +84 + | ++ + + + + + | + public int compareTo(PublicKeyCredentialDescriptor other) { |
+ +85 + | ++ + + + + + | + int idComparison = id.compareTo(other.id); |
+ +86 + | +
+
+1
+
+1. compareTo : negated conditional → KILLED + + + + |
+ if (idComparison != 0) { |
+ +87 + | +
+
+1
+
+1. compareTo : replaced int return with 0 for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::compareTo → KILLED + + + + |
+ return idComparison; |
+ +88 + | ++ + + + + + | + } |
+ +89 + | ++ + + + + + | +|
+ +90 + | +
+
+1
+
+1. compareTo : negated conditional → KILLED + + + + |
+ if (type.compareTo(other.type) != 0) { |
+ +91 + | +
+
+1
+
+1. compareTo : replaced int return with 0 for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::compareTo → NO_COVERAGE + + + + |
+ return type.compareTo(other.type); |
+ +92 + | ++ + + + + + | + } |
+ +93 + | ++ + + + + + | +|
+ +94 + | +
+
+2
+
+1. compareTo : negated conditional → KILLED +2. compareTo : negated conditional → KILLED + + + + |
+ if (!getTransports().isPresent() && other.getTransports().isPresent()) { |
+ +95 + | +
+
+1
+
+1. compareTo : replaced int return with 0 for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::compareTo → SURVIVED + + + + |
+ return -1; |
+ +96 + | +
+
+2
+
+1. compareTo : negated conditional → KILLED +2. compareTo : negated conditional → KILLED + + + + |
+ } else if (getTransports().isPresent() && !other.getTransports().isPresent()) { |
+ +97 + | +
+
+1
+
+1. compareTo : replaced int return with 0 for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::compareTo → KILLED + + + + |
+ return 1; |
+ +98 + | +
+
+2
+
+1. compareTo : negated conditional → KILLED +2. compareTo : negated conditional → KILLED + + + + |
+ } else if (getTransports().isPresent() && other.getTransports().isPresent()) { |
+ +99 + | ++ + + + + + | + int transportsComparison = |
+ +100 + | ++ + + + + + | + ComparableUtil.compareComparableSets(getTransports().get(), other.getTransports().get()); |
+ +101 + | +
+
+1
+
+1. compareTo : negated conditional → KILLED + + + + |
+ if (transportsComparison != 0) { |
+ +102 + | +
+
+1
+
+1. compareTo : replaced int return with 0 for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::compareTo → KILLED + + + + |
+ return transportsComparison; |
+ +103 + | ++ + + + + + | + } |
+ +104 + | ++ + + + + + | + } |
+ +105 + | ++ + + + + + | +|
+ +106 + | ++ + + + + + | + return 0; |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | +|
+ +109 + | ++ + + + + + | + public static PublicKeyCredentialDescriptorBuilder.MandatoryStages builder() { |
+ +110 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::builder → KILLED + + + + |
+ return new PublicKeyCredentialDescriptorBuilder.MandatoryStages(); |
+ +111 + | ++ + + + + + | + } |
+ +112 + | ++ + + + + + | +|
+ +113 + | ++ + + + + + | + /** |
+ +114 + | ++ + + + + + | + * This implementation of {@link |
+ +115 + | ++ + + + + + | + * ToPublicKeyCredentialDescriptor#toPublicKeyCredentialDescriptor()} is a no-op which returns |
+ +116 + | ++ + + + + + | + * <code>this</code> unchanged. |
+ +117 + | ++ + + + + + | + * |
+ +118 + | ++ + + + + + | + * @return <code>this</code>. |
+ +119 + | ++ + + + + + | + */ |
+ +120 + | ++ + + + + + | + @Override |
+ +121 + | ++ + + + + + | + public PublicKeyCredentialDescriptor toPublicKeyCredentialDescriptor() { |
+ +122 + | +
+
+1
+
+1. toPublicKeyCredentialDescriptor : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::toPublicKeyCredentialDescriptor → KILLED + + + + |
+ return this; |
+ +123 + | ++ + + + + + | + } |
+ +124 + | ++ + + + + + | +|
+ +125 + | ++ + + + + + | + public static class PublicKeyCredentialDescriptorBuilder { |
+ +126 + | ++ + + + + + | + private Set<AuthenticatorTransport> transports = null; |
+ +127 + | ++ + + + + + | +|
+ +128 + | ++ + + + + + | + public static class MandatoryStages { |
+ +129 + | ++ + + + + + | + private final PublicKeyCredentialDescriptorBuilder builder = |
+ +130 + | ++ + + + + + | + new PublicKeyCredentialDescriptorBuilder(); |
+ +131 + | ++ + + + + + | +|
+ +132 + | ++ + + + + + | + /** |
+ +133 + | ++ + + + + + | + * {@link PublicKeyCredentialDescriptorBuilder#id(ByteArray) id} is a required parameter. |
+ +134 + | ++ + + + + + | + * |
+ +135 + | ++ + + + + + | + * @see PublicKeyCredentialDescriptorBuilder#id(ByteArray) |
+ +136 + | ++ + + + + + | + */ |
+ +137 + | ++ + + + + + | + public PublicKeyCredentialDescriptorBuilder id(ByteArray id) { |
+ +138 + | +
+
+1
+
+1. id : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialDescriptor$PublicKeyCredentialDescriptorBuilder$MandatoryStages::id → KILLED + + + + |
+ return builder.id(id); |
+ +139 + | ++ + + + + + | + } |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + /** |
+ +143 + | ++ + + + + + | + * An OPTIONAL hint as to how the client might communicate with the managing authenticator of |
+ +144 + | ++ + + + + + | + * the public key credential the caller is referring to. |
+ +145 + | ++ + + + + + | + * |
+ +146 + | ++ + + + + + | + * <p>This SHOULD be set to the unmodified value returned from {@link |
+ +147 + | ++ + + + + + | + * RegistrationResult#getKeyId()}.{@link #getTransports()} when the credential was registered. |
+ +148 + | ++ + + + + + | + */ |
+ +149 + | ++ + + + + + | + public PublicKeyCredentialDescriptorBuilder transports( |
+ +150 + | +
+
+1
+
+1. transports : negated conditional → KILLED + + + + |
+ @NonNull Optional<Set<AuthenticatorTransport>> transports) { |
+ +151 + | +
+
+1
+
+1. transports : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialDescriptor$PublicKeyCredentialDescriptorBuilder::transports → KILLED + + + + |
+ return this.transports(transports.orElse(null)); |
+ +152 + | ++ + + + + + | + } |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + /** |
+ +155 + | ++ + + + + + | + * An OPTIONAL hint as to how the client might communicate with the managing authenticator of |
+ +156 + | ++ + + + + + | + * the public key credential the caller is referring to. |
+ +157 + | ++ + + + + + | + * |
+ +158 + | ++ + + + + + | + * <p>This SHOULD be set to the unmodified value returned from {@link |
+ +159 + | ++ + + + + + | + * RegistrationResult#getKeyId()}.{@link #getTransports()} when the credential was registered. |
+ +160 + | ++ + + + + + | + */ |
+ +161 + | ++ + + + + + | + public PublicKeyCredentialDescriptorBuilder transports(Set<AuthenticatorTransport> transports) { |
+ +162 + | ++ + + + + + | + this.transports = transports; |
+ +163 + | +
+
+1
+
+1. transports : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialDescriptor$PublicKeyCredentialDescriptorBuilder::transports → KILLED + + + + |
+ return this; |
+ +164 + | ++ + + + + + | + } |
+ +165 + | ++ + + + + + | + } |
+ +166 + | ++ + + + + + | +|
+ +167 + | ++ + + + + + | + public Optional<SortedSet<AuthenticatorTransport>> getTransports() { |
+ +168 + | +
+
+1
+
+1. getTransports : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialDescriptor::getTransports → KILLED + + + + |
+ return Optional.ofNullable(transports); |
+ +169 + | ++ + + + + + | + } |
+ +170 + | ++ + + + + + | +} |
Mutations | ||
74 | ++ |
+
+
+
+ 1.1 |
+
75 | ++ |
+
+
+
+ 1.1 |
+
80 | ++ |
+
+
+
+ 1.1 |
+
86 | ++ |
+
+
+
+ 1.1 |
+
87 | ++ |
+
+
+
+ 1.1 |
+
90 | ++ |
+
+
+
+ 1.1 |
+
91 | ++ |
+
+
+
+ 1.1 |
+
94 | ++ |
+
+
+
+ 1.1 2.2 |
+
95 | ++ |
+
+
+
+ 1.1 |
+
96 | ++ |
+
+
+
+ 1.1 2.2 |
+
97 | ++ |
+
+
+
+ 1.1 |
+
98 | ++ |
+
+
+
+ 1.1 2.2 |
+
101 | ++ |
+
+
+
+ 1.1 |
+
102 | ++ |
+
+
+
+ 1.1 |
+
110 | ++ |
+
+
+
+ 1.1 |
+
122 | ++ |
+
+
+
+ 1.1 |
+
138 | ++ |
+
+
+
+ 1.1 |
+
150 | ++ |
+
+
+
+ 1.1 |
+
151 | ++ |
+
+
+
+ 1.1 |
+
163 | ++ |
+
+
+
+ 1.1 |
+
168 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +28 + | ++ + + + + + | +import lombok.Builder; |
+ +29 + | ++ + + + + + | +import lombok.NonNull; |
+ +30 + | ++ + + + + + | +import lombok.Value; |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | +/** |
+ +33 + | ++ + + + + + | + * Used to supply additional parameters when creating a new credential. |
+ +34 + | ++ + + + + + | + * |
+ +35 + | ++ + + + + + | + * @see <a |
+ +36 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-publickeycredentialparameters">§5.3. |
+ +37 + | ++ + + + + + | + * Parameters for Credential Generation (dictionary PublicKeyCredentialParameters) </a> |
+ +38 + | ++ + + + + + | + */ |
+ +39 + | ++ + + + + + | +@Value |
+ +40 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +41 + | ++ + + + + + | +public class PublicKeyCredentialParameters { |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + /** |
+ +44 + | ++ + + + + + | + * Specifies the cryptographic signature algorithm with which the newly generated credential will |
+ +45 + | ++ + + + + + | + * be used, and thus also the type of asymmetric key pair to be generated, e.g., RSA or Elliptic |
+ +46 + | ++ + + + + + | + * Curve. |
+ +47 + | ++ + + + + + | + */ |
+ +48 + | ++ + + + + + | + @NonNull private final COSEAlgorithmIdentifier alg; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + /** Specifies the type of credential to be created. */ |
+ +51 + | ++ + + + + + | + @NonNull @Builder.Default |
+ +52 + | ++ + + + + + | + private final PublicKeyCredentialType type = PublicKeyCredentialType.PUBLIC_KEY; |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + private PublicKeyCredentialParameters( |
+ +55 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("alg") COSEAlgorithmIdentifier alg, |
+ +56 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("type") PublicKeyCredentialType type) { |
+ +57 + | ++ + + + + + | + this.alg = alg; |
+ +58 + | ++ + + + + + | + this.type = type; |
+ +59 + | ++ + + + + + | + } |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#EdDSA} and type {@link |
+ +63 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +64 + | ++ + + + + + | + */ |
+ +65 + | ++ + + + + + | + public static final PublicKeyCredentialParameters EdDSA = |
+ +66 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.EdDSA).build(); |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + /** |
+ +69 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#ES256} and type {@link |
+ +70 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +71 + | ++ + + + + + | + */ |
+ +72 + | ++ + + + + + | + public static final PublicKeyCredentialParameters ES256 = |
+ +73 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.ES256).build(); |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + /** |
+ +76 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#ES384} and type {@link |
+ +77 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +78 + | ++ + + + + + | + */ |
+ +79 + | ++ + + + + + | + public static final PublicKeyCredentialParameters ES384 = |
+ +80 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.ES384).build(); |
+ +81 + | ++ + + + + + | +|
+ +82 + | ++ + + + + + | + /** |
+ +83 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#ES512} and type {@link |
+ +84 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +85 + | ++ + + + + + | + */ |
+ +86 + | ++ + + + + + | + public static final PublicKeyCredentialParameters ES512 = |
+ +87 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.ES512).build(); |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + /** |
+ +90 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#RS1} and type {@link |
+ +91 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +92 + | ++ + + + + + | + */ |
+ +93 + | ++ + + + + + | + public static final PublicKeyCredentialParameters RS1 = |
+ +94 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.RS1).build(); |
+ +95 + | ++ + + + + + | +|
+ +96 + | ++ + + + + + | + /** |
+ +97 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#RS256} and type {@link |
+ +98 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +99 + | ++ + + + + + | + */ |
+ +100 + | ++ + + + + + | + public static final PublicKeyCredentialParameters RS256 = |
+ +101 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.RS256).build(); |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + /** |
+ +104 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#RS384} and type {@link |
+ +105 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +106 + | ++ + + + + + | + */ |
+ +107 + | ++ + + + + + | + public static final PublicKeyCredentialParameters RS384 = |
+ +108 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.RS384).build(); |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + /** |
+ +111 + | ++ + + + + + | + * Algorithm {@link COSEAlgorithmIdentifier#RS512} and type {@link |
+ +112 + | ++ + + + + + | + * PublicKeyCredentialType#PUBLIC_KEY}. |
+ +113 + | ++ + + + + + | + */ |
+ +114 + | ++ + + + + + | + public static final PublicKeyCredentialParameters RS512 = |
+ +115 + | ++ + + + + + | + builder().alg(COSEAlgorithmIdentifier.RS512).build(); |
+ +116 + | ++ + + + + + | +|
+ +117 + | ++ + + + + + | + public static PublicKeyCredentialParametersBuilder.MandatoryStages builder() { |
+ +118 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialParameters::builder → KILLED + + + + |
+ return new PublicKeyCredentialParametersBuilder.MandatoryStages(); |
+ +119 + | ++ + + + + + | + } |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + public static class PublicKeyCredentialParametersBuilder { |
+ +122 + | ++ + + + + + | + public static class MandatoryStages { |
+ +123 + | ++ + + + + + | + private final PublicKeyCredentialParametersBuilder builder = |
+ +124 + | ++ + + + + + | + new PublicKeyCredentialParametersBuilder(); |
+ +125 + | ++ + + + + + | +|
+ +126 + | ++ + + + + + | + /** |
+ +127 + | ++ + + + + + | + * {@link PublicKeyCredentialParametersBuilder#alg(COSEAlgorithmIdentifier) alg} is a required |
+ +128 + | ++ + + + + + | + * parameter. |
+ +129 + | ++ + + + + + | + * |
+ +130 + | ++ + + + + + | + * @see PublicKeyCredentialParametersBuilder#alg(COSEAlgorithmIdentifier) |
+ +131 + | ++ + + + + + | + */ |
+ +132 + | ++ + + + + + | + public PublicKeyCredentialParametersBuilder alg(COSEAlgorithmIdentifier alg) { |
+ +133 + | +
+
+1
+
+1. alg : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialParameters$PublicKeyCredentialParametersBuilder$MandatoryStages::alg → KILLED + + + + |
+ return builder.alg(alg); |
+ +134 + | ++ + + + + + | + } |
+ +135 + | ++ + + + + + | + } |
+ +136 + | ++ + + + + + | + } |
+ +137 + | ++ + + + + + | +} |
Mutations | ||
55 | ++ |
+
+
+
+ 1.1 |
+
56 | ++ |
+
+
+
+ 1.1 |
+
118 | ++ |
+
+
+
+ 1.1 |
+
133 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonProcessingException; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.databind.ObjectMapper; |
+ +31 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ObjectNode; |
+ +32 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +33 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +34 + | ++ + + + + + | +import java.util.List; |
+ +35 + | ++ + + + + + | +import java.util.Optional; |
+ +36 + | ++ + + + + + | +import lombok.Builder; |
+ +37 + | ++ + + + + + | +import lombok.NonNull; |
+ +38 + | ++ + + + + + | +import lombok.Value; |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | +/** |
+ +41 + | ++ + + + + + | + * The PublicKeyCredentialRequestOptions dictionary supplies get() with the data it needs to |
+ +42 + | ++ + + + + + | + * generate an assertion. |
+ +43 + | ++ + + + + + | + * |
+ +44 + | ++ + + + + + | + * <p>Its `challenge` member must be present, while its other members are optional. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * @see <a |
+ +47 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-publickeycredentialrequestoptions">§5.5. |
+ +48 + | ++ + + + + + | + * Options for Assertion Generation (dictionary PublicKeyCredentialRequestOptions) </a> |
+ +49 + | ++ + + + + + | + */ |
+ +50 + | ++ + + + + + | +@Value |
+ +51 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +52 + | ++ + + + + + | +public class PublicKeyCredentialRequestOptions { |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + /** |
+ +55 + | ++ + + + + + | + * A challenge that the selected authenticator signs, along with other data, when producing an |
+ +56 + | ++ + + + + + | + * authentication assertion. See the <a |
+ +57 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-cryptographic-challenges">§13.1 |
+ +58 + | ++ + + + + + | + * Cryptographic Challenges</a> security consideration. |
+ +59 + | ++ + + + + + | + */ |
+ +60 + | ++ + + + + + | + @NonNull private final ByteArray challenge; |
+ +61 + | ++ + + + + + | +|
+ +62 + | ++ + + + + + | + /** |
+ +63 + | ++ + + + + + | + * Specifies a time, in milliseconds, that the caller is willing to wait for the call to complete. |
+ +64 + | ++ + + + + + | + * |
+ +65 + | ++ + + + + + | + * <p>This is treated as a hint, and MAY be overridden by the client. |
+ +66 + | ++ + + + + + | + */ |
+ +67 + | ++ + + + + + | + private final Long timeout; |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + /** |
+ +70 + | ++ + + + + + | + * Specifies the relying party identifier claimed by the caller. |
+ +71 + | ++ + + + + + | + * |
+ +72 + | ++ + + + + + | + * <p>If omitted, its value will be set by the client. |
+ +73 + | ++ + + + + + | + */ |
+ +74 + | ++ + + + + + | + private final String rpId; |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + /** |
+ +77 + | ++ + + + + + | + * A list of {@link PublicKeyCredentialDescriptor} objects representing public key credentials |
+ +78 + | ++ + + + + + | + * acceptable to the caller, in descending order of the caller’s preference (the first item in the |
+ +79 + | ++ + + + + + | + * list is the most preferred credential, and so on down the list). |
+ +80 + | ++ + + + + + | + */ |
+ +81 + | ++ + + + + + | + private final List<PublicKeyCredentialDescriptor> allowCredentials; |
+ +82 + | ++ + + + + + | +|
+ +83 + | ++ + + + + + | + /** |
+ +84 + | ++ + + + + + | + * Describes the Relying Party's requirements regarding <a |
+ +85 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">user |
+ +86 + | ++ + + + + + | + * verification</a> for the <code>navigator.credentials.get()</code> operation. |
+ +87 + | ++ + + + + + | + * |
+ +88 + | ++ + + + + + | + * <p>Eligible authenticators are filtered to only those capable of satisfying this requirement. |
+ +89 + | ++ + + + + + | + * |
+ +90 + | ++ + + + + + | + * <p>By default, this is not set. When not set, the default in the browser is {@link |
+ +91 + | ++ + + + + + | + * UserVerificationRequirement#PREFERRED}. |
+ +92 + | ++ + + + + + | + * |
+ +93 + | ++ + + + + + | + * @see UserVerificationRequirement |
+ +94 + | ++ + + + + + | + * @see <a |
+ +95 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-userVerificationRequirement">§5.8.6. |
+ +96 + | ++ + + + + + | + * User Verification Requirement Enumeration (enum UserVerificationRequirement)</a> |
+ +97 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">User |
+ +98 + | ++ + + + + + | + * Verification</a> |
+ +99 + | ++ + + + + + | + */ |
+ +100 + | ++ + + + + + | + private final UserVerificationRequirement userVerification; |
+ +101 + | ++ + + + + + | +|
+ +102 + | ++ + + + + + | + /** |
+ +103 + | ++ + + + + + | + * Additional parameters requesting additional processing by the client and authenticator. |
+ +104 + | ++ + + + + + | + * |
+ +105 + | ++ + + + + + | + * <p>For example, if transaction confirmation is sought from the user, then the prompt string |
+ +106 + | ++ + + + + + | + * might be included as an extension. |
+ +107 + | ++ + + + + + | + */ |
+ +108 + | ++ + + + + + | + @NonNull @Builder.Default |
+ +109 + | ++ + + + + + | + private final AssertionExtensionInputs extensions = AssertionExtensionInputs.builder().build(); |
+ +110 + | ++ + + + + + | +|
+ +111 + | ++ + + + + + | + @JsonCreator |
+ +112 + | ++ + + + + + | + private PublicKeyCredentialRequestOptions( |
+ +113 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("challenge") ByteArray challenge, |
+ +114 + | ++ + + + + + | + @JsonProperty("timeout") Long timeout, |
+ +115 + | ++ + + + + + | + @JsonProperty("rpId") String rpId, |
+ +116 + | ++ + + + + + | + @JsonProperty("allowCredentials") List<PublicKeyCredentialDescriptor> allowCredentials, |
+ +117 + | ++ + + + + + | + @JsonProperty("userVerification") UserVerificationRequirement userVerification, |
+ +118 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("extensions") AssertionExtensionInputs extensions) { |
+ +119 + | ++ + + + + + | + this.challenge = challenge; |
+ +120 + | ++ + + + + + | + this.timeout = timeout; |
+ +121 + | ++ + + + + + | + this.rpId = rpId; |
+ +122 + | ++ + + + + + | + this.allowCredentials = |
+ +123 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ allowCredentials == null ? null : CollectionUtil.immutableList(allowCredentials); |
+ +124 + | ++ + + + + + | + this.userVerification = userVerification; |
+ +125 + | ++ + + + + + | + this.extensions = extensions; |
+ +126 + | ++ + + + + + | + } |
+ +127 + | ++ + + + + + | +|
+ +128 + | ++ + + + + + | + public Optional<Long> getTimeout() { |
+ +129 + | +
+
+1
+
+1. getTimeout : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions::getTimeout → KILLED + + + + |
+ return Optional.ofNullable(timeout); |
+ +130 + | ++ + + + + + | + } |
+ +131 + | ++ + + + + + | +|
+ +132 + | ++ + + + + + | + public Optional<List<PublicKeyCredentialDescriptor>> getAllowCredentials() { |
+ +133 + | +
+
+1
+
+1. getAllowCredentials : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions::getAllowCredentials → KILLED + + + + |
+ return Optional.ofNullable(allowCredentials); |
+ +134 + | ++ + + + + + | + } |
+ +135 + | ++ + + + + + | +|
+ +136 + | ++ + + + + + | + public Optional<UserVerificationRequirement> getUserVerification() { |
+ +137 + | +
+
+1
+
+1. getUserVerification : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions::getUserVerification → KILLED + + + + |
+ return Optional.ofNullable(userVerification); |
+ +138 + | ++ + + + + + | + } |
+ +139 + | ++ + + + + + | +|
+ +140 + | ++ + + + + + | + /** |
+ +141 + | ++ + + + + + | + * Serialize this {@link PublicKeyCredentialRequestOptions} value to JSON suitable for sending to |
+ +142 + | ++ + + + + + | + * the client. |
+ +143 + | ++ + + + + + | + * |
+ +144 + | ++ + + + + + | + * <p>Any {@link ByteArray} values in this data structure will be {@link ByteArray#getBase64Url() |
+ +145 + | ++ + + + + + | + * Base64Url} encoded. Those values MUST be decoded into <code>BufferSource</code> values (such as |
+ +146 + | ++ + + + + + | + * <code>Uint8Array</code>) on the client side before calling <code>navigator.credentials.get() |
+ +147 + | ++ + + + + + | + * </code>. |
+ +148 + | ++ + + + + + | + * |
+ +149 + | ++ + + + + + | + * <p>After decoding binary values, the resulting JavaScript object is suitable for passing as an |
+ +150 + | ++ + + + + + | + * argument to <code>navigator.credentials.get()</code>. |
+ +151 + | ++ + + + + + | + * |
+ +152 + | ++ + + + + + | + * @return a JSON value suitable for sending to the client and passing as an argument to <code> |
+ +153 + | ++ + + + + + | + * navigator.credentials.get()</code>, after decoding binary options from Base64Url strings. |
+ +154 + | ++ + + + + + | + * @throws JsonProcessingException if JSON serialization fails. |
+ +155 + | ++ + + + + + | + */ |
+ +156 + | ++ + + + + + | + public String toCredentialsGetJson() throws JsonProcessingException { |
+ +157 + | ++ + + + + + | + ObjectMapper json = JacksonCodecs.json(); |
+ +158 + | ++ + + + + + | + ObjectNode result = json.createObjectNode(); |
+ +159 + | ++ + + + + + | + result.set("publicKey", json.valueToTree(this)); |
+ +160 + | +
+
+1
+
+1. toCredentialsGetJson : replaced return value with "" for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions::toCredentialsGetJson → KILLED + + + + |
+ return json.writeValueAsString(result); |
+ +161 + | ++ + + + + + | + } |
+ +162 + | ++ + + + + + | +|
+ +163 + | ++ + + + + + | + public static PublicKeyCredentialRequestOptionsBuilder.MandatoryStages builder() { |
+ +164 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions::builder → KILLED + + + + |
+ return new PublicKeyCredentialRequestOptionsBuilder.MandatoryStages(); |
+ +165 + | ++ + + + + + | + } |
+ +166 + | ++ + + + + + | +|
+ +167 + | ++ + + + + + | + public static class PublicKeyCredentialRequestOptionsBuilder { |
+ +168 + | ++ + + + + + | + private Long timeout = null; |
+ +169 + | ++ + + + + + | + private String rpId = null; |
+ +170 + | ++ + + + + + | + private List<PublicKeyCredentialDescriptor> allowCredentials = null; |
+ +171 + | ++ + + + + + | +|
+ +172 + | ++ + + + + + | + public static class MandatoryStages { |
+ +173 + | ++ + + + + + | + private final PublicKeyCredentialRequestOptionsBuilder builder = |
+ +174 + | ++ + + + + + | + new PublicKeyCredentialRequestOptionsBuilder(); |
+ +175 + | ++ + + + + + | +|
+ +176 + | ++ + + + + + | + /** |
+ +177 + | ++ + + + + + | + * {@link PublicKeyCredentialRequestOptionsBuilder#challenge(ByteArray) challenge} is a |
+ +178 + | ++ + + + + + | + * required parameter. |
+ +179 + | ++ + + + + + | + * |
+ +180 + | ++ + + + + + | + * @see PublicKeyCredentialRequestOptionsBuilder#challenge(ByteArray) |
+ +181 + | ++ + + + + + | + */ |
+ +182 + | ++ + + + + + | + public PublicKeyCredentialRequestOptionsBuilder challenge(ByteArray challenge) { |
+ +183 + | +
+
+1
+
+1. challenge : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder$MandatoryStages::challenge → KILLED + + + + |
+ return builder.challenge(challenge); |
+ +184 + | ++ + + + + + | + } |
+ +185 + | ++ + + + + + | + } |
+ +186 + | ++ + + + + + | +|
+ +187 + | ++ + + + + + | + /** |
+ +188 + | ++ + + + + + | + * Specifies a time, in milliseconds, that the caller is willing to wait for the call to |
+ +189 + | ++ + + + + + | + * complete. |
+ +190 + | ++ + + + + + | + * |
+ +191 + | ++ + + + + + | + * <p>This is treated as a hint, and MAY be overridden by the client. |
+ +192 + | ++ + + + + + | + */ |
+ +193 + | +
+
+1
+
+1. timeout : negated conditional → KILLED + + + + |
+ public PublicKeyCredentialRequestOptionsBuilder timeout(@NonNull Optional<Long> timeout) { |
+ +194 + | ++ + + + + + | + this.timeout = timeout.orElse(null); |
+ +195 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::timeout → KILLED + + + + |
+ return this; |
+ +196 + | ++ + + + + + | + } |
+ +197 + | ++ + + + + + | +|
+ +198 + | ++ + + + + + | + /* |
+ +199 + | ++ + + + + + | + * Workaround, see: https://github.com/rzwitserloot/lombok/issues/2623#issuecomment-714816001 |
+ +200 + | ++ + + + + + | + * Consider reverting this workaround if Lombok fixes that issue. |
+ +201 + | ++ + + + + + | + */ |
+ +202 + | ++ + + + + + | + private PublicKeyCredentialRequestOptionsBuilder timeout(Long timeout) { |
+ +203 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::timeout → KILLED + + + + |
+ return this.timeout(Optional.ofNullable(timeout)); |
+ +204 + | ++ + + + + + | + } |
+ +205 + | ++ + + + + + | +|
+ +206 + | ++ + + + + + | + /** |
+ +207 + | ++ + + + + + | + * Specifies a time, in milliseconds, that the caller is willing to wait for the call to |
+ +208 + | ++ + + + + + | + * complete. |
+ +209 + | ++ + + + + + | + * |
+ +210 + | ++ + + + + + | + * <p>This is treated as a hint, and MAY be overridden by the client. |
+ +211 + | ++ + + + + + | + */ |
+ +212 + | ++ + + + + + | + public PublicKeyCredentialRequestOptionsBuilder timeout(long timeout) { |
+ +213 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::timeout → NO_COVERAGE + + + + |
+ return this.timeout(Optional.of(timeout)); |
+ +214 + | ++ + + + + + | + } |
+ +215 + | ++ + + + + + | +|
+ +216 + | ++ + + + + + | + /** |
+ +217 + | ++ + + + + + | + * Specifies the relying party identifier claimed by the caller. |
+ +218 + | ++ + + + + + | + * |
+ +219 + | ++ + + + + + | + * <p>If omitted, its value will be set by the client. |
+ +220 + | ++ + + + + + | + */ |
+ +221 + | +
+
+1
+
+1. rpId : negated conditional → KILLED + + + + |
+ public PublicKeyCredentialRequestOptionsBuilder rpId(@NonNull Optional<String> rpId) { |
+ +222 + | +
+
+1
+
+1. rpId : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::rpId → KILLED + + + + |
+ return this.rpId(rpId.orElse(null)); |
+ +223 + | ++ + + + + + | + } |
+ +224 + | ++ + + + + + | +|
+ +225 + | ++ + + + + + | + /** |
+ +226 + | ++ + + + + + | + * Specifies the relying party identifier claimed by the caller. |
+ +227 + | ++ + + + + + | + * |
+ +228 + | ++ + + + + + | + * <p>If omitted, its value will be set by the client. |
+ +229 + | ++ + + + + + | + */ |
+ +230 + | ++ + + + + + | + public PublicKeyCredentialRequestOptionsBuilder rpId(String rpId) { |
+ +231 + | ++ + + + + + | + this.rpId = rpId; |
+ +232 + | +
+
+1
+
+1. rpId : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::rpId → KILLED + + + + |
+ return this; |
+ +233 + | ++ + + + + + | + } |
+ +234 + | ++ + + + + + | +|
+ +235 + | ++ + + + + + | + /** |
+ +236 + | ++ + + + + + | + * A list of {@link PublicKeyCredentialDescriptor} objects representing public key credentials |
+ +237 + | ++ + + + + + | + * acceptable to the caller, in descending order of the caller’s preference (the first item in |
+ +238 + | ++ + + + + + | + * the list is the most preferred credential, and so on down the list). |
+ +239 + | ++ + + + + + | + */ |
+ +240 + | ++ + + + + + | + public PublicKeyCredentialRequestOptionsBuilder allowCredentials( |
+ +241 + | +
+
+1
+
+1. allowCredentials : negated conditional → KILLED + + + + |
+ @NonNull Optional<List<PublicKeyCredentialDescriptor>> allowCredentials) { |
+ +242 + | +
+
+1
+
+1. allowCredentials : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::allowCredentials → KILLED + + + + |
+ return this.allowCredentials(allowCredentials.orElse(null)); |
+ +243 + | ++ + + + + + | + } |
+ +244 + | ++ + + + + + | +|
+ +245 + | ++ + + + + + | + /** |
+ +246 + | ++ + + + + + | + * A list of {@link PublicKeyCredentialDescriptor} objects representing public key credentials |
+ +247 + | ++ + + + + + | + * acceptable to the caller, in descending order of the caller’s preference (the first item in |
+ +248 + | ++ + + + + + | + * the list is the most preferred credential, and so on down the list). |
+ +249 + | ++ + + + + + | + */ |
+ +250 + | ++ + + + + + | + public PublicKeyCredentialRequestOptionsBuilder allowCredentials( |
+ +251 + | ++ + + + + + | + List<PublicKeyCredentialDescriptor> allowCredentials) { |
+ +252 + | ++ + + + + + | + this.allowCredentials = allowCredentials; |
+ +253 + | +
+
+1
+
+1. allowCredentials : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialRequestOptions$PublicKeyCredentialRequestOptionsBuilder::allowCredentials → KILLED + + + + |
+ return this; |
+ +254 + | ++ + + + + + | + } |
+ +255 + | ++ + + + + + | + } |
+ +256 + | ++ + + + + + | +} |
Mutations | ||
113 | ++ |
+
+
+
+ 1.1 |
+
118 | ++ |
+
+
+
+ 1.1 |
+
123 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 |
+
133 | ++ |
+
+
+
+ 1.1 |
+
137 | ++ |
+
+
+
+ 1.1 |
+
160 | ++ |
+
+
+
+ 1.1 |
+
164 | ++ |
+
+
+
+ 1.1 |
+
183 | ++ |
+
+
+
+ 1.1 |
+
193 | ++ |
+
+
+
+ 1.1 |
+
195 | ++ |
+
+
+
+ 1.1 |
+
203 | ++ |
+
+
+
+ 1.1 |
+
213 | ++ |
+
+
+
+ 1.1 |
+
221 | ++ |
+
+
+
+ 1.1 |
+
222 | ++ |
+
+
+
+ 1.1 |
+
232 | ++ |
+
+
+
+ 1.1 |
+
241 | ++ |
+
+
+
+ 1.1 |
+
242 | ++ |
+
+
+
+ 1.1 |
+
253 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +31 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +32 + | ++ + + + + + | +import lombok.Getter; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * Defines the valid credential types. |
+ +37 + | ++ + + + + + | + * |
+ +38 + | ++ + + + + + | + * <p>It is an extensions point; values may be added to it in the future, as more credential types |
+ +39 + | ++ + + + + + | + * are defined. The values of this enumeration are used for versioning the Authentication Assertion |
+ +40 + | ++ + + + + + | + * and attestation structures according to the type of the authenticator. |
+ +41 + | ++ + + + + + | + * |
+ +42 + | ++ + + + + + | + * <p>Currently one credential type is defined, namely {@link #PUBLIC_KEY}. |
+ +43 + | ++ + + + + + | + * |
+ +44 + | ++ + + + + + | + * @see <a |
+ +45 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-publickeycredentialtype">§5.10.2. |
+ +46 + | ++ + + + + + | + * Credential Type Enumeration (enum PublicKeyCredentialType)</a> |
+ +47 + | ++ + + + + + | + */ |
+ +48 + | ++ + + + + + | +@AllArgsConstructor |
+ +49 + | ++ + + + + + | +public enum PublicKeyCredentialType { |
+ +50 + | ++ + + + + + | + PUBLIC_KEY("public-key"); |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + @JsonValue @Getter @NonNull private final String id; |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + /** |
+ +55 + | ++ + + + + + | + * Attempt to parse a string as a {@link PublicKeyCredentialType}. |
+ +56 + | ++ + + + + + | + * |
+ +57 + | ++ + + + + + | + * @param id a {@link String} equal to the {@link #getId() id} of a constant in {@link |
+ +58 + | ++ + + + + + | + * PublicKeyCredentialType} |
+ +59 + | ++ + + + + + | + * @return The {@link AuthenticatorAttachment} instance whose {@link #getId() id} equals <code>id |
+ +60 + | ++ + + + + + | + * </code>, if any. |
+ +61 + | ++ + + + + + | + * @see <a |
+ +62 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-publickeycredentialtype">§5.10.2. |
+ +63 + | ++ + + + + + | + * Credential Type Enumeration (enum PublicKeyCredentialType)</a> |
+ +64 + | ++ + + + + + | + */ |
+ +65 + | +
+
+1
+
+1. fromId : negated conditional → KILLED + + + + |
+ public static Optional<PublicKeyCredentialType> fromId(@NonNull String id) { |
+ +66 + | +
+
+3
+
+1. fromId : replaced return value with Optional.empty for com/yubico/webauthn/data/PublicKeyCredentialType::fromId → KILLED +2. lambda$fromId$0 : replaced boolean return with true for com/yubico/webauthn/data/PublicKeyCredentialType::lambda$fromId$0 → KILLED +3. lambda$fromId$0 : replaced boolean return with false for com/yubico/webauthn/data/PublicKeyCredentialType::lambda$fromId$0 → KILLED + + + + |
+ return Stream.of(values()).filter(v -> v.id.equals(id)).findAny(); |
+ +67 + | ++ + + + + + | + } |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + @JsonCreator |
+ +70 + | +
+
+1
+
+1. fromJsonString : negated conditional → KILLED + + + + |
+ private static PublicKeyCredentialType fromJsonString(@NonNull String id) { |
+ +71 + | +
+
+1
+
+1. fromJsonString : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialType::fromJsonString → KILLED + + + + |
+ return fromId(id) |
+ +72 + | ++ + + + + + | + .orElseThrow( |
+ +73 + | ++ + + + + + | + () -> |
+ +74 + | +
+
+1
+
+1. lambda$fromJsonString$1 : replaced return value with null for com/yubico/webauthn/data/PublicKeyCredentialType::lambda$fromJsonString$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +75 + | ++ + + + + + | + String.format( |
+ +76 + | ++ + + + + + | + "Unknown %s value: %s", |
+ +77 + | ++ + + + + + | + PublicKeyCredentialType.class.getSimpleName(), id))); |
+ +78 + | ++ + + + + + | + } |
+ +79 + | ++ + + + + + | +} |
Mutations | ||
65 | ++ |
+
+
+
+ 1.1 |
+
66 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
70 | ++ |
+
+
+
+ 1.1 |
+
71 | ++ |
+
+
+
+ 1.1 |
+
74 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.RelyingParty; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.StartRegistrationOptions; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.extension.appid.AppId; |
+ +33 + | ++ + + + + + | +import java.util.Collections; |
+ +34 + | ++ + + + + + | +import java.util.HashSet; |
+ +35 + | ++ + + + + + | +import java.util.Optional; |
+ +36 + | ++ + + + + + | +import java.util.Set; |
+ +37 + | ++ + + + + + | +import lombok.Builder; |
+ +38 + | ++ + + + + + | +import lombok.Value; |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | +/** |
+ +41 + | ++ + + + + + | + * Contains <a |
+ +42 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-input">client |
+ +43 + | ++ + + + + + | + * extension inputs</a> to a <code>navigator.credentials.create()</code> operation. All members are |
+ +44 + | ++ + + + + + | + * optional. |
+ +45 + | ++ + + + + + | + * |
+ +46 + | ++ + + + + + | + * <p>The authenticator extension inputs are derived from these client extension inputs. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extensions">§9. WebAuthn |
+ +49 + | ++ + + + + + | + * Extensions</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | +@Value |
+ +52 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +53 + | ++ + + + + + | +@JsonIgnoreProperties(ignoreUnknown = true) |
+ +54 + | ++ + + + + + | +public final class RegistrationExtensionInputs implements ExtensionInputs { |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + private final AppId appidExclude; |
+ +57 + | ++ + + + + + | + private final Boolean credProps; |
+ +58 + | ++ + + + + + | + private final Extensions.LargeBlob.LargeBlobRegistrationInput largeBlob; |
+ +59 + | ++ + + + + + | + private final Boolean uvm; |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + @JsonCreator |
+ +62 + | ++ + + + + + | + private RegistrationExtensionInputs( |
+ +63 + | ++ + + + + + | + @JsonProperty("appidExclude") AppId appidExclude, |
+ +64 + | ++ + + + + + | + @JsonProperty("credProps") Boolean credProps, |
+ +65 + | ++ + + + + + | + @JsonProperty("largeBlob") Extensions.LargeBlob.LargeBlobRegistrationInput largeBlob, |
+ +66 + | ++ + + + + + | + @JsonProperty("uvm") Boolean uvm) { |
+ +67 + | ++ + + + + + | + this.appidExclude = appidExclude; |
+ +68 + | ++ + + + + + | + this.credProps = credProps; |
+ +69 + | ++ + + + + + | + this.largeBlob = largeBlob; |
+ +70 + | ++ + + + + + | + this.uvm = uvm; |
+ +71 + | ++ + + + + + | + } |
+ +72 + | ++ + + + + + | +|
+ +73 + | ++ + + + + + | + /** |
+ +74 + | ++ + + + + + | + * Merge <code>other</code> into <code>this</code>. Non-null field values from <code>this</code> |
+ +75 + | ++ + + + + + | + * take precedence. |
+ +76 + | ++ + + + + + | + * |
+ +77 + | ++ + + + + + | + * @return a new {@link RegistrationExtensionInputs} instance with the settings from both <code> |
+ +78 + | ++ + + + + + | + * this</code> and <code>other</code>. |
+ +79 + | ++ + + + + + | + */ |
+ +80 + | ++ + + + + + | + public RegistrationExtensionInputs merge(RegistrationExtensionInputs other) { |
+ +81 + | +
+
+1
+
+1. merge : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs::merge → KILLED + + + + |
+ return new RegistrationExtensionInputs( |
+ +82 + | +
+
+1
+
+1. merge : negated conditional → KILLED + + + + |
+ this.appidExclude != null ? this.appidExclude : other.appidExclude, |
+ +83 + | +
+
+1
+
+1. merge : negated conditional → KILLED + + + + |
+ this.credProps != null ? this.credProps : other.credProps, |
+ +84 + | +
+
+1
+
+1. merge : negated conditional → SURVIVED + + + + |
+ this.largeBlob != null ? this.largeBlob : other.largeBlob, |
+ +85 + | +
+
+1
+
+1. merge : negated conditional → KILLED + + + + |
+ this.uvm != null ? this.uvm : other.uvm); |
+ +86 + | ++ + + + + + | + } |
+ +87 + | ++ + + + + + | +|
+ +88 + | ++ + + + + + | + /** |
+ +89 + | ++ + + + + + | + * @return The value of the FIDO AppID Exclusion Extension (<code>appidExclude</code>) input if |
+ +90 + | ++ + + + + + | + * configured, empty otherwise. |
+ +91 + | ++ + + + + + | + * @see RegistrationExtensionInputsBuilder#appidExclude(AppId) |
+ +92 + | ++ + + + + + | + * @see <a |
+ +93 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +94 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +95 + | ++ + + + + + | + */ |
+ +96 + | ++ + + + + + | + public Optional<AppId> getAppidExclude() { |
+ +97 + | +
+
+1
+
+1. getAppidExclude : replaced return value with Optional.empty for com/yubico/webauthn/data/RegistrationExtensionInputs::getAppidExclude → KILLED + + + + |
+ return Optional.ofNullable(appidExclude); |
+ +98 + | ++ + + + + + | + } |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + /** |
+ +101 + | ++ + + + + + | + * @return <code>true</code> if the Credential Properties Extension (<code>credProps</code>) is |
+ +102 + | ++ + + + + + | + * enabled, <code>false</code> otherwise. |
+ +103 + | ++ + + + + + | + * @see RegistrationExtensionInputsBuilder#credProps() |
+ +104 + | ++ + + + + + | + * @see <a |
+ +105 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4. |
+ +106 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +107 + | ++ + + + + + | + */ |
+ +108 + | ++ + + + + + | + public boolean getCredProps() { |
+ +109 + | +
+
+3
+
+1. getCredProps : negated conditional → KILLED +2. getCredProps : replaced boolean return with true for com/yubico/webauthn/data/RegistrationExtensionInputs::getCredProps → KILLED +3. getCredProps : negated conditional → KILLED + + + + |
+ return credProps != null && credProps; |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + /** For JSON serialization, to omit false values. */ |
+ +113 + | ++ + + + + + | + @JsonProperty("credProps") |
+ +114 + | ++ + + + + + | + private Boolean getCredPropsJson() { |
+ +115 + | +
+
+3
+
+1. getCredPropsJson : negated conditional → KILLED +2. getCredPropsJson : replaced Boolean return with False for com/yubico/webauthn/data/RegistrationExtensionInputs::getCredPropsJson → KILLED +3. getCredPropsJson : replaced Boolean return with True for com/yubico/webauthn/data/RegistrationExtensionInputs::getCredPropsJson → KILLED + + + + |
+ return getCredProps() ? true : null; |
+ +116 + | ++ + + + + + | + } |
+ +117 + | ++ + + + + + | +|
+ +118 + | ++ + + + + + | + /** |
+ +119 + | ++ + + + + + | + * @return The value of the Large blob storage extension (<code>largeBlob</code>) input if |
+ +120 + | ++ + + + + + | + * configured, empty otherwise. |
+ +121 + | ++ + + + + + | + * @see |
+ +122 + | ++ + + + + + | + * RegistrationExtensionInputsBuilder#largeBlob(Extensions.LargeBlob.LargeBlobRegistrationInput) |
+ +123 + | ++ + + + + + | + * @see |
+ +124 + | ++ + + + + + | + * RegistrationExtensionInputsBuilder#largeBlob(Extensions.LargeBlob.LargeBlobRegistrationInput.LargeBlobSupport) |
+ +125 + | ++ + + + + + | + * @see <a |
+ +126 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +127 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +128 + | ++ + + + + + | + */ |
+ +129 + | ++ + + + + + | + public Optional<Extensions.LargeBlob.LargeBlobRegistrationInput> getLargeBlob() { |
+ +130 + | +
+
+1
+
+1. getLargeBlob : replaced return value with Optional.empty for com/yubico/webauthn/data/RegistrationExtensionInputs::getLargeBlob → KILLED + + + + |
+ return Optional.ofNullable(largeBlob); |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + /** |
+ +134 + | ++ + + + + + | + * @return <code>true</code> if the User Verification Method Extension (<code>uvm</code>) is |
+ +135 + | ++ + + + + + | + * enabled, <code>false</code> otherwise. |
+ +136 + | ++ + + + + + | + * @see RegistrationExtensionInputsBuilder#uvm() |
+ +137 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +138 + | ++ + + + + + | + * User Verification Method Extension (uvm)</a> |
+ +139 + | ++ + + + + + | + */ |
+ +140 + | ++ + + + + + | + public boolean getUvm() { |
+ +141 + | +
+
+3
+
+1. getUvm : negated conditional → KILLED +2. getUvm : negated conditional → KILLED +3. getUvm : replaced boolean return with true for com/yubico/webauthn/data/RegistrationExtensionInputs::getUvm → KILLED + + + + |
+ return uvm != null && uvm; |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | +|
+ +144 + | ++ + + + + + | + /** For JSON serialization, to omit false values. */ |
+ +145 + | ++ + + + + + | + @JsonProperty("uvm") |
+ +146 + | ++ + + + + + | + private Boolean getUvmJson() { |
+ +147 + | +
+
+3
+
+1. getUvmJson : replaced Boolean return with False for com/yubico/webauthn/data/RegistrationExtensionInputs::getUvmJson → KILLED +2. getUvmJson : replaced Boolean return with True for com/yubico/webauthn/data/RegistrationExtensionInputs::getUvmJson → KILLED +3. getUvmJson : negated conditional → KILLED + + + + |
+ return getUvm() ? true : null; |
+ +148 + | ++ + + + + + | + } |
+ +149 + | ++ + + + + + | +|
+ +150 + | ++ + + + + + | + /** |
+ +151 + | ++ + + + + + | + * @return The extension identifiers of all extensions configured. |
+ +152 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-extension-id">§9.1. |
+ +153 + | ++ + + + + + | + * Extension Identifiers</a> |
+ +154 + | ++ + + + + + | + */ |
+ +155 + | ++ + + + + + | + @Override |
+ +156 + | ++ + + + + + | + public Set<String> getExtensionIds() { |
+ +157 + | ++ + + + + + | + Set<String> ids = new HashSet<>(); |
+ +158 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (appidExclude != null) { |
+ +159 + | ++ + + + + + | + ids.add(Extensions.AppidExclude.EXTENSION_ID); |
+ +160 + | ++ + + + + + | + } |
+ +161 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (getCredProps()) { |
+ +162 + | ++ + + + + + | + ids.add(Extensions.CredentialProperties.EXTENSION_ID); |
+ +163 + | ++ + + + + + | + } |
+ +164 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (largeBlob != null) { |
+ +165 + | ++ + + + + + | + ids.add(Extensions.LargeBlob.EXTENSION_ID); |
+ +166 + | ++ + + + + + | + } |
+ +167 + | +
+
+1
+
+1. getExtensionIds : negated conditional → KILLED + + + + |
+ if (getUvm()) { |
+ +168 + | ++ + + + + + | + ids.add(Extensions.Uvm.EXTENSION_ID); |
+ +169 + | ++ + + + + + | + } |
+ +170 + | +
+
+1
+
+1. getExtensionIds : replaced return value with Collections.emptySet for com/yubico/webauthn/data/RegistrationExtensionInputs::getExtensionIds → KILLED + + + + |
+ return Collections.unmodifiableSet(ids); |
+ +171 + | ++ + + + + + | + } |
+ +172 + | ++ + + + + + | +|
+ +173 + | ++ + + + + + | + public static class RegistrationExtensionInputsBuilder { |
+ +174 + | ++ + + + + + | + /** |
+ +175 + | ++ + + + + + | + * Enable or disable the FIDO AppID Exclusion Extension (<code>appidExclude</code>). |
+ +176 + | ++ + + + + + | + * |
+ +177 + | ++ + + + + + | + * <p>You usually do not need to call this method explicitly; if {@link RelyingParty#getAppId()} |
+ +178 + | ++ + + + + + | + * is present, then {@link RelyingParty#startRegistration(StartRegistrationOptions)} will enable |
+ +179 + | ++ + + + + + | + * this extension automatically. |
+ +180 + | ++ + + + + + | + * |
+ +181 + | ++ + + + + + | + * <p>If this is set to empty, then {@link |
+ +182 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)} may overwrite it. |
+ +183 + | ++ + + + + + | + * |
+ +184 + | ++ + + + + + | + * @see RelyingParty#startRegistration(StartRegistrationOptions) |
+ +185 + | ++ + + + + + | + * @see <a |
+ +186 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +187 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +188 + | ++ + + + + + | + */ |
+ +189 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder appidExclude(Optional<AppId> appidExclude) { |
+ +190 + | ++ + + + + + | + this.appidExclude = appidExclude.orElse(null); |
+ +191 + | +
+
+1
+
+1. appidExclude : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::appidExclude → KILLED + + + + |
+ return this; |
+ +192 + | ++ + + + + + | + } |
+ +193 + | ++ + + + + + | +|
+ +194 + | ++ + + + + + | + /** |
+ +195 + | ++ + + + + + | + * Enable the FIDO AppID Exclusion Extension (<code>appidExclude</code>). |
+ +196 + | ++ + + + + + | + * |
+ +197 + | ++ + + + + + | + * <p>You usually do not need to call this method explicitly; if {@link RelyingParty#getAppId()} |
+ +198 + | ++ + + + + + | + * is present, then {@link RelyingParty#startRegistration(StartRegistrationOptions)} will enable |
+ +199 + | ++ + + + + + | + * this extension automatically. |
+ +200 + | ++ + + + + + | + * |
+ +201 + | ++ + + + + + | + * <p>If this is set to null, then {@link |
+ +202 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)} may overwrite it. |
+ +203 + | ++ + + + + + | + * |
+ +204 + | ++ + + + + + | + * @see RelyingParty#startRegistration(StartRegistrationOptions) |
+ +205 + | ++ + + + + + | + * @see <a |
+ +206 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +207 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +208 + | ++ + + + + + | + */ |
+ +209 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder appidExclude(AppId appidExclude) { |
+ +210 + | ++ + + + + + | + this.appidExclude = appidExclude; |
+ +211 + | +
+
+1
+
+1. appidExclude : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::appidExclude → KILLED + + + + |
+ return this; |
+ +212 + | ++ + + + + + | + } |
+ +213 + | ++ + + + + + | +|
+ +214 + | ++ + + + + + | + /** |
+ +215 + | ++ + + + + + | + * Enable the Credential Properties (<code>credProps</code>) Extension. |
+ +216 + | ++ + + + + + | + * |
+ +217 + | ++ + + + + + | + * @see <a |
+ +218 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4. |
+ +219 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +220 + | ++ + + + + + | + */ |
+ +221 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder credProps() { |
+ +222 + | ++ + + + + + | + this.credProps = true; |
+ +223 + | +
+
+1
+
+1. credProps : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::credProps → KILLED + + + + |
+ return this; |
+ +224 + | ++ + + + + + | + } |
+ +225 + | ++ + + + + + | +|
+ +226 + | ++ + + + + + | + /** |
+ +227 + | ++ + + + + + | + * Enable or disable the Credential Properties (<code>credProps</code>) Extension. |
+ +228 + | ++ + + + + + | + * |
+ +229 + | ++ + + + + + | + * <p>A <code>true</code> argument enables the extension. A <code>false</code> argument disables |
+ +230 + | ++ + + + + + | + * the extension, and will not be overwritten by {@link |
+ +231 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)}. A null argument disables the |
+ +232 + | ++ + + + + + | + * extension, and will be overwritten by {@link |
+ +233 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)}. |
+ +234 + | ++ + + + + + | + * |
+ +235 + | ++ + + + + + | + * @see RelyingParty#startRegistration(StartRegistrationOptions) |
+ +236 + | ++ + + + + + | + * @see <a |
+ +237 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension">§10.4. |
+ +238 + | ++ + + + + + | + * Credential Properties Extension (credProps)</a> |
+ +239 + | ++ + + + + + | + */ |
+ +240 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder credProps(Boolean credProps) { |
+ +241 + | ++ + + + + + | + this.credProps = credProps; |
+ +242 + | +
+
+1
+
+1. credProps : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::credProps → KILLED + + + + |
+ return this; |
+ +243 + | ++ + + + + + | + } |
+ +244 + | ++ + + + + + | +|
+ +245 + | ++ + + + + + | + /** |
+ +246 + | ++ + + + + + | + * Enable the Large blob storage extension (<code>largeBlob</code>). |
+ +247 + | ++ + + + + + | + * |
+ +248 + | ++ + + + + + | + * <p>Alias of <code>largeBlob(new Extensions.LargeBlob.LargeBlobRegistrationInput(support)) |
+ +249 + | ++ + + + + + | + * </code>. |
+ +250 + | ++ + + + + + | + * |
+ +251 + | ++ + + + + + | + * @param support an {@link |
+ +252 + | ++ + + + + + | + * com.yubico.webauthn.data.Extensions.LargeBlob.LargeBlobRegistrationInput.LargeBlobSupport} |
+ +253 + | ++ + + + + + | + * value to set as the <code>support</code> attribute of the <code>largeBlob</code> |
+ +254 + | ++ + + + + + | + * extension input. |
+ +255 + | ++ + + + + + | + * @see #largeBlob(Extensions.LargeBlob.LargeBlobRegistrationInput) |
+ +256 + | ++ + + + + + | + * @see <a |
+ +257 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +258 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +259 + | ++ + + + + + | + */ |
+ +260 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder largeBlob( |
+ +261 + | ++ + + + + + | + Extensions.LargeBlob.LargeBlobRegistrationInput.LargeBlobSupport support) { |
+ +262 + | ++ + + + + + | + this.largeBlob = new Extensions.LargeBlob.LargeBlobRegistrationInput(support); |
+ +263 + | +
+
+1
+
+1. largeBlob : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::largeBlob → KILLED + + + + |
+ return this; |
+ +264 + | ++ + + + + + | + } |
+ +265 + | ++ + + + + + | +|
+ +266 + | ++ + + + + + | + /** |
+ +267 + | ++ + + + + + | + * Enable the Large blob storage extension (<code>largeBlob</code>). |
+ +268 + | ++ + + + + + | + * |
+ +269 + | ++ + + + + + | + * @see #largeBlob(Extensions.LargeBlob.LargeBlobRegistrationInput.LargeBlobSupport) |
+ +270 + | ++ + + + + + | + * @see <a |
+ +271 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-large-blob-extension">§10.5. |
+ +272 + | ++ + + + + + | + * Large blob storage extension (largeBlob)</a> |
+ +273 + | ++ + + + + + | + */ |
+ +274 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder largeBlob( |
+ +275 + | ++ + + + + + | + Extensions.LargeBlob.LargeBlobRegistrationInput largeBlob) { |
+ +276 + | ++ + + + + + | + this.largeBlob = largeBlob; |
+ +277 + | +
+
+1
+
+1. largeBlob : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::largeBlob → KILLED + + + + |
+ return this; |
+ +278 + | ++ + + + + + | + } |
+ +279 + | ++ + + + + + | +|
+ +280 + | ++ + + + + + | + /** |
+ +281 + | ++ + + + + + | + * Enable the User Verification Method Extension (<code>uvm</code>). |
+ +282 + | ++ + + + + + | + * |
+ +283 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-uvm-extension">§10.3. |
+ +284 + | ++ + + + + + | + * User Verification Method Extension (uvm)</a> |
+ +285 + | ++ + + + + + | + */ |
+ +286 + | ++ + + + + + | + public RegistrationExtensionInputsBuilder uvm() { |
+ +287 + | ++ + + + + + | + this.uvm = true; |
+ +288 + | +
+
+1
+
+1. uvm : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::uvm → KILLED + + + + |
+ return this; |
+ +289 + | ++ + + + + + | + } |
+ +290 + | ++ + + + + + | +|
+ +291 + | ++ + + + + + | + /** For compatibility with {@link Builder}(toBuilder = true) */ |
+ +292 + | ++ + + + + + | + private RegistrationExtensionInputsBuilder uvm(Boolean uvm) { |
+ +293 + | ++ + + + + + | + this.uvm = uvm; |
+ +294 + | +
+
+1
+
+1. uvm : replaced return value with null for com/yubico/webauthn/data/RegistrationExtensionInputs$RegistrationExtensionInputsBuilder::uvm → KILLED + + + + |
+ return this; |
+ +295 + | ++ + + + + + | + } |
+ +296 + | ++ + + + + + | + } |
+ +297 + | ++ + + + + + | +} |
Mutations | ||
81 | ++ |
+
+
+
+ 1.1 |
+
82 | ++ |
+
+
+
+ 1.1 |
+
83 | ++ |
+
+
+
+ 1.1 |
+
84 | ++ |
+
+
+
+ 1.1 |
+
85 | ++ |
+
+
+
+ 1.1 |
+
97 | ++ |
+
+
+
+ 1.1 |
+
109 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
115 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
147 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
158 | ++ |
+
+
+
+ 1.1 |
+
161 | ++ |
+
+
+
+ 1.1 |
+
164 | ++ |
+
+
+
+ 1.1 |
+
167 | ++ |
+
+
+
+ 1.1 |
+
170 | ++ |
+
+
+
+ 1.1 |
+
191 | ++ |
+
+
+
+ 1.1 |
+
211 | ++ |
+
+
+
+ 1.1 |
+
223 | ++ |
+
+
+
+ 1.1 |
+
242 | ++ |
+
+
+
+ 1.1 |
+
263 | ++ |
+
+
+
+ 1.1 |
+
277 | ++ |
+
+
+
+ 1.1 |
+
288 | ++ |
+
+
+
+ 1.1 |
+
294 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import lombok.Builder; |
+ +30 + | ++ + + + + + | +import lombok.Getter; |
+ +31 + | ++ + + + + + | +import lombok.NonNull; |
+ +32 + | ++ + + + + + | +import lombok.Value; |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | +/** |
+ +35 + | ++ + + + + + | + * Used to supply additional Relying Party attributes when creating a new credential. |
+ +36 + | ++ + + + + + | + * |
+ +37 + | ++ + + + + + | + * @see <a |
+ +38 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-publickeycredentialrpentity">§5.4.2. |
+ +39 + | ++ + + + + + | + * Relying Party Parameters for Credential Generation (dictionary PublicKeyCredentialRpEntity) |
+ +40 + | ++ + + + + + | + * </a> |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | +@Value |
+ +43 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +44 + | ++ + + + + + | +public class RelyingPartyIdentity implements PublicKeyCredentialEntity { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** |
+ +47 + | ++ + + + + + | + * The human-palatable name of the Relaying Party. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * <p>For example: "ACME Corporation", "Wonderful Widgets, Inc." or "ОАО Примертех". |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | + @NonNull |
+ +52 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +53 + | ++ + + + + + | + private final String name; |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | + /** |
+ +56 + | ++ + + + + + | + * A unique identifier for the Relying Party, which sets the <a |
+ +57 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#rp-id">RP ID</a>. |
+ +58 + | ++ + + + + + | + * |
+ +59 + | ++ + + + + + | + * <p>This defines the domains where users' credentials are valid. See <a |
+ +60 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#scope">RP ID: scope</a> for details |
+ +61 + | ++ + + + + + | + * and examples. |
+ +62 + | ++ + + + + + | + * |
+ +63 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#rp-id">RP ID</a> |
+ +64 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#scope">RP ID: scope</a> |
+ +65 + | ++ + + + + + | + */ |
+ +66 + | ++ + + + + + | + @NonNull private final String id; |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + @JsonCreator |
+ +69 + | ++ + + + + + | + private RelyingPartyIdentity( |
+ +70 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("name") String name, @NonNull @JsonProperty("id") String id) { |
+ +71 + | ++ + + + + + | + this.name = name; |
+ +72 + | ++ + + + + + | + this.id = id; |
+ +73 + | ++ + + + + + | + } |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + public static RelyingPartyIdentityBuilder.MandatoryStages builder() { |
+ +76 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/RelyingPartyIdentity::builder → KILLED + + + + |
+ return new RelyingPartyIdentityBuilder.MandatoryStages(); |
+ +77 + | ++ + + + + + | + } |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + public static class RelyingPartyIdentityBuilder { |
+ +80 + | ++ + + + + + | +|
+ +81 + | ++ + + + + + | + public static class MandatoryStages { |
+ +82 + | ++ + + + + + | + private final RelyingPartyIdentityBuilder builder = new RelyingPartyIdentityBuilder(); |
+ +83 + | ++ + + + + + | +|
+ +84 + | ++ + + + + + | + /** |
+ +85 + | ++ + + + + + | + * A unique identifier for the Relying Party, which sets the <a |
+ +86 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#rp-id">RP ID</a>. |
+ +87 + | ++ + + + + + | + * |
+ +88 + | ++ + + + + + | + * <p>This defines the domains where users' credentials are valid. See <a |
+ +89 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#scope">RP ID: scope</a> for |
+ +90 + | ++ + + + + + | + * details and examples. |
+ +91 + | ++ + + + + + | + * |
+ +92 + | ++ + + + + + | + * @see RelyingPartyIdentityBuilder#id(String) |
+ +93 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#rp-id">RP ID</a> |
+ +94 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#scope">RP ID: scope</a> |
+ +95 + | ++ + + + + + | + */ |
+ +96 + | ++ + + + + + | + public Step2 id(String id) { |
+ +97 + | ++ + + + + + | + builder.id(id); |
+ +98 + | +
+
+1
+
+1. id : replaced return value with null for com/yubico/webauthn/data/RelyingPartyIdentity$RelyingPartyIdentityBuilder$MandatoryStages::id → KILLED + + + + |
+ return new Step2(); |
+ +99 + | ++ + + + + + | + } |
+ +100 + | ++ + + + + + | +|
+ +101 + | ++ + + + + + | + public class Step2 { |
+ +102 + | ++ + + + + + | + /** |
+ +103 + | ++ + + + + + | + * The human-palatable name of the Relaying Party. |
+ +104 + | ++ + + + + + | + * |
+ +105 + | ++ + + + + + | + * <p>For example: "ACME Corporation", "Wonderful Widgets, Inc." or "ОАО Примертех". |
+ +106 + | ++ + + + + + | + * |
+ +107 + | ++ + + + + + | + * @see RelyingPartyIdentityBuilder#name(String) |
+ +108 + | ++ + + + + + | + */ |
+ +109 + | ++ + + + + + | + public RelyingPartyIdentityBuilder name(String name) { |
+ +110 + | +
+
+1
+
+1. name : replaced return value with null for com/yubico/webauthn/data/RelyingPartyIdentity$RelyingPartyIdentityBuilder$MandatoryStages$Step2::name → KILLED + + + + |
+ return builder.name(name); |
+ +111 + | ++ + + + + + | + } |
+ +112 + | ++ + + + + + | + } |
+ +113 + | ++ + + + + + | + } |
+ +114 + | ++ + + + + + | + } |
+ +115 + | ++ + + + + + | +} |
Mutations | ||
70 | ++ |
+
+
+
+ 1.1 2.2 |
+
76 | ++ |
+
+
+
+ 1.1 |
+
98 | ++ |
+
+
+
+ 1.1 |
+
110 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +// Copyright (c) 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.webauthn.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +31 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +32 + | ++ + + + + + | +import lombok.Getter; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * This enumeration's values describe the Relying Party's requirements for client-side discoverable |
+ +37 + | ++ + + + + + | + * credentials, also known as <i>passkeys</i> (formerly known as resident credentials or resident |
+ +38 + | ++ + + + + + | + * keys). |
+ +39 + | ++ + + + + + | + * |
+ +40 + | ++ + + + + + | + * @see <a |
+ +41 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +42 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +43 + | ++ + + + + + | + * @see <a |
+ +44 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +45 + | ++ + + + + + | + * discoverable Credential</a> |
+ +46 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +47 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +48 + | ++ + + + + + | + */ |
+ +49 + | ++ + + + + + | +@AllArgsConstructor |
+ +50 + | ++ + + + + + | +public enum ResidentKeyRequirement { |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * The client and authenticator will try to create a server-side credential if possible, and a |
+ +54 + | ++ + + + + + | + * discoverable credential (passkey) otherwise. |
+ +55 + | ++ + + + + + | + * |
+ +56 + | ++ + + + + + | + * @see <a |
+ +57 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +58 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +59 + | ++ + + + + + | + * @see <a |
+ +60 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +61 + | ++ + + + + + | + * discoverable Credential</a> |
+ +62 + | ++ + + + + + | + * @see <a |
+ +63 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#server-side-credential">Server-side |
+ +64 + | ++ + + + + + | + * Credential</a> |
+ +65 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +66 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + DISCOURAGED("discouraged"), |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * The client and authenticator will try to create a discoverable credential (passkey) if |
+ +72 + | ++ + + + + + | + * possible, and a server-side credential otherwise. |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * @see <a |
+ +75 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +76 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +77 + | ++ + + + + + | + * @see <a |
+ +78 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +79 + | ++ + + + + + | + * discoverable Credential</a> |
+ +80 + | ++ + + + + + | + * @see <a |
+ +81 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#server-side-credential">Server-side |
+ +82 + | ++ + + + + + | + * Credential</a> |
+ +83 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +84 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +85 + | ++ + + + + + | + */ |
+ +86 + | ++ + + + + + | + PREFERRED("preferred"), |
+ +87 + | ++ + + + + + | +|
+ +88 + | ++ + + + + + | + /** |
+ +89 + | ++ + + + + + | + * The client and authenticator will try to create a discoverable credential (passkey), and fail |
+ +90 + | ++ + + + + + | + * the registration if that is not possible. |
+ +91 + | ++ + + + + + | + * |
+ +92 + | ++ + + + + + | + * @see <a |
+ +93 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +94 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +95 + | ++ + + + + + | + * @see <a |
+ +96 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-credential">Client-side |
+ +97 + | ++ + + + + + | + * discoverable Credential</a> |
+ +98 + | ++ + + + + + | + * @see <a |
+ +99 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#server-side-credential">Server-side |
+ +100 + | ++ + + + + + | + * Credential</a> |
+ +101 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +102 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +103 + | ++ + + + + + | + */ |
+ +104 + | ++ + + + + + | + REQUIRED("required"); |
+ +105 + | ++ + + + + + | +|
+ +106 + | ++ + + + + + | + @JsonValue @Getter @NonNull private final String value; |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + /** |
+ +109 + | ++ + + + + + | + * Attempt to parse a string as a {@link ResidentKeyRequirement}. |
+ +110 + | ++ + + + + + | + * |
+ +111 + | ++ + + + + + | + * @param value a {@link String} equal to the {@link #getValue() value} of a constant in {@link |
+ +112 + | ++ + + + + + | + * ResidentKeyRequirement} |
+ +113 + | ++ + + + + + | + * @return The {@link ResidentKeyRequirement} instance whose {@link #getValue() value} equals |
+ +114 + | ++ + + + + + | + * <code>value</code>, if any. |
+ +115 + | ++ + + + + + | + * @see <a |
+ +116 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enum-residentKeyRequirement">§5.4.6. |
+ +117 + | ++ + + + + + | + * Resident Key Requirement Enumeration (enum ResidentKeyRequirement)</a> |
+ +118 + | ++ + + + + + | + */ |
+ +119 + | +
+
+1
+
+1. fromValue : negated conditional → KILLED + + + + |
+ public static Optional<ResidentKeyRequirement> fromValue(@NonNull String value) { |
+ +120 + | +
+
+3
+
+1. fromValue : replaced return value with Optional.empty for com/yubico/webauthn/data/ResidentKeyRequirement::fromValue → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/data/ResidentKeyRequirement::lambda$fromValue$0 → KILLED +3. lambda$fromValue$0 : replaced boolean return with false for com/yubico/webauthn/data/ResidentKeyRequirement::lambda$fromValue$0 → KILLED + + + + |
+ return Stream.of(values()).filter(v -> v.value.equals(value)).findAny(); |
+ +121 + | ++ + + + + + | + } |
+ +122 + | ++ + + + + + | +|
+ +123 + | ++ + + + + + | + @JsonCreator |
+ +124 + | +
+
+1
+
+1. fromJsonString : negated conditional → KILLED + + + + |
+ private static ResidentKeyRequirement fromJsonString(@NonNull String value) { |
+ +125 + | +
+
+1
+
+1. fromJsonString : replaced return value with null for com/yubico/webauthn/data/ResidentKeyRequirement::fromJsonString → KILLED + + + + |
+ return fromValue(value) |
+ +126 + | ++ + + + + + | + .orElseThrow( |
+ +127 + | ++ + + + + + | + () -> |
+ +128 + | +
+
+1
+
+1. lambda$fromJsonString$1 : replaced return value with null for com/yubico/webauthn/data/ResidentKeyRequirement::lambda$fromJsonString$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +129 + | ++ + + + + + | + String.format( |
+ +130 + | ++ + + + + + | + "Unknown %s value: %s", |
+ +131 + | ++ + + + + + | + ResidentKeyRequirement.class.getSimpleName(), value))); |
+ +132 + | ++ + + + + + | + } |
+ +133 + | ++ + + + + + | +} |
Mutations | ||
119 | ++ |
+
+
+
+ 1.1 |
+
120 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
124 | ++ |
+
+
+
+ 1.1 |
+
125 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import static com.yubico.internal.util.ExceptionUtil.assertTrue; |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import lombok.NonNull; |
+ +33 + | ++ + + + + + | +import lombok.Value; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * Information about the state of the <a href="https://tools.ietf.org/html/rfc8471">Token Binding |
+ +37 + | ++ + + + + + | + * protocol</a> used when communicating with the Relying Party. |
+ +38 + | ++ + + + + + | + * |
+ +39 + | ++ + + + + + | + * @see <a |
+ +40 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-tokenbinding">dictionary |
+ +41 + | ++ + + + + + | + * TokenBinding</a> |
+ +42 + | ++ + + + + + | + */ |
+ +43 + | ++ + + + + + | +@Value |
+ +44 + | ++ + + + + + | +public class TokenBindingInfo { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + @NonNull private final TokenBindingStatus status; |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + /** |
+ +49 + | ++ + + + + + | + * This member MUST be present if {@link #status} is {@link TokenBindingStatus#PRESENT PRESENT}, |
+ +50 + | ++ + + + + + | + * and MUST be the Token Binding ID that was used when communicating with the Relying Party. |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | + private final ByteArray id; |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + @JsonCreator |
+ +55 + | ++ + + + + + | + TokenBindingInfo( |
+ +56 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("status") TokenBindingStatus status, |
+ +57 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("id") Optional<ByteArray> id) { |
+ +58 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (status == TokenBindingStatus.PRESENT) { |
+ +59 + | +
+
+1
+
+1. <init> : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ assertTrue( |
+ +60 + | ++ + + + + + | + id.isPresent(), |
+ +61 + | ++ + + + + + | + "Token binding ID must be present if status is \"%s\".", |
+ +62 + | ++ + + + + + | + TokenBindingStatus.PRESENT); |
+ +63 + | ++ + + + + + | + } else { |
+ +64 + | +
+
+1
+
+1. <init> : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ assertTrue( |
+ +65 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ !id.isPresent(), |
+ +66 + | ++ + + + + + | + "Token binding ID must not be present if status is not \"%s\".", |
+ +67 + | ++ + + + + + | + TokenBindingStatus.PRESENT); |
+ +68 + | ++ + + + + + | + } |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + this.status = status; |
+ +71 + | ++ + + + + + | + this.id = id.orElse(null); |
+ +72 + | ++ + + + + + | + } |
+ +73 + | ++ + + + + + | +|
+ +74 + | +
+
+1
+
+1. present : negated conditional → KILLED + + + + |
+ public static TokenBindingInfo present(@NonNull ByteArray id) { |
+ +75 + | +
+
+1
+
+1. present : replaced return value with null for com/yubico/webauthn/data/TokenBindingInfo::present → KILLED + + + + |
+ return new TokenBindingInfo(TokenBindingStatus.PRESENT, Optional.of(id)); |
+ +76 + | ++ + + + + + | + } |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + public static TokenBindingInfo supported() { |
+ +79 + | +
+
+1
+
+1. supported : replaced return value with null for com/yubico/webauthn/data/TokenBindingInfo::supported → SURVIVED + + + + |
+ return new TokenBindingInfo(TokenBindingStatus.SUPPORTED, Optional.empty()); |
+ +80 + | ++ + + + + + | + } |
+ +81 + | ++ + + + + + | +|
+ +82 + | ++ + + + + + | + public Optional<ByteArray> getId() { |
+ +83 + | +
+
+1
+
+1. getId : replaced return value with Optional.empty for com/yubico/webauthn/data/TokenBindingInfo::getId → KILLED + + + + |
+ return Optional.ofNullable(id); |
+ +84 + | ++ + + + + + | + } |
+ +85 + | ++ + + + + + | +} |
Mutations | ||
56 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 |
+
58 | ++ |
+
+
+
+ 1.1 |
+
59 | ++ |
+
+
+
+ 1.1 |
+
64 | ++ |
+
+
+
+ 1.1 |
+
65 | ++ |
+
+
+
+ 1.1 |
+
74 | ++ |
+
+
+
+ 1.1 |
+
75 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
83 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.Arrays; |
+ +30 + | ++ + + + + + | +import java.util.Optional; |
+ +31 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +32 + | ++ + + + + + | +import lombok.Getter; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * Indicators of whether a {@link TokenBindingInfo}'s {@link TokenBindingInfo#getId() id} member is |
+ +37 + | ++ + + + + + | + * present and, if not, whether the client supports token binding. |
+ +38 + | ++ + + + + + | + * |
+ +39 + | ++ + + + + + | + * @see <a |
+ +40 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-tokenbindingstatus">enum |
+ +41 + | ++ + + + + + | + * TokenBindingStatus</a> |
+ +42 + | ++ + + + + + | + * @see TokenBindingInfo |
+ +43 + | ++ + + + + + | + */ |
+ +44 + | ++ + + + + + | +@AllArgsConstructor |
+ +45 + | ++ + + + + + | +public enum TokenBindingStatus { |
+ +46 + | ++ + + + + + | +|
+ +47 + | ++ + + + + + | + /** |
+ +48 + | ++ + + + + + | + * Indicates token binding was used when communicating with the Relying Party. In this case, the |
+ +49 + | ++ + + + + + | + * {@link TokenBindingInfo#getId()} member MUST be present. |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | + PRESENT("present"), |
+ +52 + | ++ + + + + + | +|
+ +53 + | ++ + + + + + | + /** |
+ +54 + | ++ + + + + + | + * Indicates the client supports token binding, but it was not negotiated when communicating with |
+ +55 + | ++ + + + + + | + * the Relying Party. |
+ +56 + | ++ + + + + + | + */ |
+ +57 + | ++ + + + + + | + SUPPORTED("supported"); |
+ +58 + | ++ + + + + + | +|
+ +59 + | ++ + + + + + | + @JsonValue @Getter @NonNull private final String value; |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * Attempt to parse a string as a {@link TokenBindingStatus}. |
+ +63 + | ++ + + + + + | + * |
+ +64 + | ++ + + + + + | + * @param value a {@link String} equal to the {@link #getValue() value} of a constant in {@link |
+ +65 + | ++ + + + + + | + * TokenBindingStatus} |
+ +66 + | ++ + + + + + | + * @return The {@link TokenBindingStatus} instance whose {@link #getValue() value} equals <code> |
+ +67 + | ++ + + + + + | + * value</code>, if any. |
+ +68 + | ++ + + + + + | + * @see <a |
+ +69 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-tokenbindingstatus">enum |
+ +70 + | ++ + + + + + | + * TokenBindingStatus</a> |
+ +71 + | ++ + + + + + | + */ |
+ +72 + | +
+
+1
+
+1. fromValue : negated conditional → KILLED + + + + |
+ public static Optional<TokenBindingStatus> fromValue(@NonNull String value) { |
+ +73 + | +
+
+3
+
+1. fromValue : replaced return value with Optional.empty for com/yubico/webauthn/data/TokenBindingStatus::fromValue → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/data/TokenBindingStatus::lambda$fromValue$0 → KILLED +3. lambda$fromValue$0 : replaced boolean return with false for com/yubico/webauthn/data/TokenBindingStatus::lambda$fromValue$0 → KILLED + + + + |
+ return Arrays.stream(values()).filter(v -> v.value.equals(value)).findAny(); |
+ +74 + | ++ + + + + + | + } |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + @JsonCreator |
+ +77 + | +
+
+1
+
+1. fromJsonString : negated conditional → KILLED + + + + |
+ static TokenBindingStatus fromJsonString(@NonNull String value) { |
+ +78 + | +
+
+1
+
+1. fromJsonString : replaced return value with null for com/yubico/webauthn/data/TokenBindingStatus::fromJsonString → KILLED + + + + |
+ return fromValue(value) |
+ +79 + | ++ + + + + + | + .orElseThrow( |
+ +80 + | ++ + + + + + | + () -> |
+ +81 + | +
+
+1
+
+1. lambda$fromJsonString$1 : replaced return value with null for com/yubico/webauthn/data/TokenBindingStatus::lambda$fromJsonString$1 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +82 + | ++ + + + + + | + String.format( |
+ +83 + | ++ + + + + + | + "Unknown %s value: %s", TokenBindingStatus.class.getSimpleName(), value))); |
+ +84 + | ++ + + + + + | + } |
+ +85 + | ++ + + + + + | +} |
Mutations | ||
72 | ++ |
+
+
+
+ 1.1 |
+
73 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
77 | ++ |
+
+
+
+ 1.1 |
+
78 | ++ |
+
+
+
+ 1.1 |
+
81 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +29 + | ++ + + + + + | +import lombok.Builder; |
+ +30 + | ++ + + + + + | +import lombok.Getter; |
+ +31 + | ++ + + + + + | +import lombok.NonNull; |
+ +32 + | ++ + + + + + | +import lombok.Value; |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | +/** |
+ +35 + | ++ + + + + + | + * Describes a user account, with which public key credentials can be associated. |
+ +36 + | ++ + + + + + | + * |
+ +37 + | ++ + + + + + | + * @see <a |
+ +38 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictdef-publickeycredentialuserentity">§5.4.3. |
+ +39 + | ++ + + + + + | + * User Account Parameters for Credential Generation (dictionary PublicKeyCredentialUserEntity) |
+ +40 + | ++ + + + + + | + * </a> |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | +@Value |
+ +43 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +44 + | ++ + + + + + | +public class UserIdentity implements PublicKeyCredentialEntity { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** |
+ +47 + | ++ + + + + + | + * A human-palatable identifier for a user account. It is intended only for display, i.e., aiding |
+ +48 + | ++ + + + + + | + * the user in determining the difference between user accounts with similar {@link |
+ +49 + | ++ + + + + + | + * #displayName}s. |
+ +50 + | ++ + + + + + | + * |
+ +51 + | ++ + + + + + | + * <p>For example: "alexm", "alex.p.mueller@example.com" or "+14255551234". |
+ +52 + | ++ + + + + + | + */ |
+ +53 + | ++ + + + + + | + @NonNull |
+ +54 + | ++ + + + + + | + @Getter(onMethod = @__({@Override})) |
+ +55 + | ++ + + + + + | + private final String name; |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + /** |
+ +58 + | ++ + + + + + | + * A human-palatable name for the user account, intended only for display. For example, "Alex P. |
+ +59 + | ++ + + + + + | + * Müller" or "田中 倫". The Relying Party SHOULD let the user choose this, and SHOULD NOT restrict |
+ +60 + | ++ + + + + + | + * the choice more than necessary. |
+ +61 + | ++ + + + + + | + * |
+ +62 + | ++ + + + + + | + * <ul> |
+ +63 + | ++ + + + + + | + * <li>Relying Parties SHOULD perform enforcement, as prescribed in Section 2.3 of [RFC8266] for |
+ +64 + | ++ + + + + + | + * the Nickname Profile of the PRECIS FreeformClass [RFC8264], when setting {@link |
+ +65 + | ++ + + + + + | + * #displayName}'s value, or displaying the value to the user. |
+ +66 + | ++ + + + + + | + * <li>Clients SHOULD perform enforcement, as prescribed in Section 2.3 of [RFC8266] for the |
+ +67 + | ++ + + + + + | + * Nickname Profile of the PRECIS FreeformClass [RFC8264], on {@link #displayName}'s value |
+ +68 + | ++ + + + + + | + * prior to displaying the value to the user or including the value as a parameter of the |
+ +69 + | ++ + + + + + | + * <code>authenticatorMakeCredential</code> operation. |
+ +70 + | ++ + + + + + | + * </ul> |
+ +71 + | ++ + + + + + | + * |
+ +72 + | ++ + + + + + | + * <p>When clients, client platforms, or authenticators display a {@link #displayName}'s value, |
+ +73 + | ++ + + + + + | + * they should always use UI elements to provide a clear boundary around the displayed value, and |
+ +74 + | ++ + + + + + | + * not allow overflow into other elements. |
+ +75 + | ++ + + + + + | + * |
+ +76 + | ++ + + + + + | + * <p>Authenticators MUST accept and store a 64-byte minimum length for a {@link #displayName} |
+ +77 + | ++ + + + + + | + * member's value. Authenticators MAY truncate a {@link #displayName} member's value to a length |
+ +78 + | ++ + + + + + | + * equal to or greater than 64 bytes. |
+ +79 + | ++ + + + + + | + * |
+ +80 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8264">RFC 8264</a> |
+ +81 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8266">RFC 8266</a> |
+ +82 + | ++ + + + + + | + */ |
+ +83 + | ++ + + + + + | + @NonNull private final String displayName; |
+ +84 + | ++ + + + + + | +|
+ +85 + | ++ + + + + + | + /** |
+ +86 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">user handle</a> |
+ +87 + | ++ + + + + + | + * for the account, specified by the Relying Party. |
+ +88 + | ++ + + + + + | + * |
+ +89 + | ++ + + + + + | + * <p>A user handle is an opaque byte sequence with a maximum size of 64 bytes. User handles are |
+ +90 + | ++ + + + + + | + * not meant to be displayed to users. The user handle SHOULD NOT contain personally identifying |
+ +91 + | ++ + + + + + | + * information about the user, such as a username or e-mail address; see <a |
+ +92 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-user-handle-privacy">§14.9 User |
+ +93 + | ++ + + + + + | + * Handle Contents</a> for details. |
+ +94 + | ++ + + + + + | + * |
+ +95 + | ++ + + + + + | + * <p>To ensure secure operation, authentication and authorization decisions MUST be made on the |
+ +96 + | ++ + + + + + | + * basis of this {@link #id} member, not the {@link #displayName} nor {@link #name} members. See |
+ +97 + | ++ + + + + + | + * <a href="https://tools.ietf.org/html/rfc8266#section-6.1">Section 6.1 of RFC 8266</a>. |
+ +98 + | ++ + + + + + | + * |
+ +99 + | ++ + + + + + | + * <p>An authenticator will never contain more than one credential for a given Relying Party under |
+ +100 + | ++ + + + + + | + * the same user handle. |
+ +101 + | ++ + + + + + | + */ |
+ +102 + | ++ + + + + + | + @NonNull private final ByteArray id; |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + @JsonCreator |
+ +105 + | ++ + + + + + | + private UserIdentity( |
+ +106 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("name") String name, |
+ +107 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("displayName") String displayName, |
+ +108 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("id") ByteArray id) { |
+ +109 + | ++ + + + + + | + this.name = name; |
+ +110 + | ++ + + + + + | + this.displayName = displayName; |
+ +111 + | ++ + + + + + | + this.id = id; |
+ +112 + | ++ + + + + + | + } |
+ +113 + | ++ + + + + + | +|
+ +114 + | ++ + + + + + | + public static UserIdentityBuilder.MandatoryStages builder() { |
+ +115 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/data/UserIdentity::builder → KILLED + + + + |
+ return new UserIdentityBuilder.MandatoryStages(); |
+ +116 + | ++ + + + + + | + } |
+ +117 + | ++ + + + + + | +|
+ +118 + | ++ + + + + + | + public static class UserIdentityBuilder { |
+ +119 + | ++ + + + + + | +|
+ +120 + | ++ + + + + + | + public static class MandatoryStages { |
+ +121 + | ++ + + + + + | + private final UserIdentityBuilder builder = new UserIdentityBuilder(); |
+ +122 + | ++ + + + + + | +|
+ +123 + | ++ + + + + + | + /** |
+ +124 + | ++ + + + + + | + * {@link UserIdentityBuilder#name(String) name} is a required parameter. |
+ +125 + | ++ + + + + + | + * |
+ +126 + | ++ + + + + + | + * @see UserIdentityBuilder#name(String) |
+ +127 + | ++ + + + + + | + */ |
+ +128 + | ++ + + + + + | + public Step2 name(String name) { |
+ +129 + | ++ + + + + + | + builder.name(name); |
+ +130 + | +
+
+1
+
+1. name : replaced return value with null for com/yubico/webauthn/data/UserIdentity$UserIdentityBuilder$MandatoryStages::name → KILLED + + + + |
+ return new Step2(); |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + public class Step2 { |
+ +134 + | ++ + + + + + | + /** |
+ +135 + | ++ + + + + + | + * {@link UserIdentityBuilder#displayName(String) displayName} is a required parameter. |
+ +136 + | ++ + + + + + | + * |
+ +137 + | ++ + + + + + | + * @see UserIdentityBuilder#displayName(String) |
+ +138 + | ++ + + + + + | + */ |
+ +139 + | ++ + + + + + | + public Step3 displayName(String displayName) { |
+ +140 + | ++ + + + + + | + builder.displayName(displayName); |
+ +141 + | +
+
+1
+
+1. displayName : replaced return value with null for com/yubico/webauthn/data/UserIdentity$UserIdentityBuilder$MandatoryStages$Step2::displayName → KILLED + + + + |
+ return new Step3(); |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | + } |
+ +144 + | ++ + + + + + | +|
+ +145 + | ++ + + + + + | + public class Step3 { |
+ +146 + | ++ + + + + + | + /** |
+ +147 + | ++ + + + + + | + * {@link UserIdentityBuilder#id(ByteArray) id} is a required parameter. |
+ +148 + | ++ + + + + + | + * |
+ +149 + | ++ + + + + + | + * @see UserIdentityBuilder#id(ByteArray) |
+ +150 + | ++ + + + + + | + */ |
+ +151 + | ++ + + + + + | + public UserIdentityBuilder id(ByteArray id) { |
+ +152 + | +
+
+1
+
+1. id : replaced return value with null for com/yubico/webauthn/data/UserIdentity$UserIdentityBuilder$MandatoryStages$Step3::id → KILLED + + + + |
+ return builder.id(id); |
+ +153 + | ++ + + + + + | + } |
+ +154 + | ++ + + + + + | + } |
+ +155 + | ++ + + + + + | + } |
+ +156 + | ++ + + + + + | + } |
+ +157 + | ++ + + + + + | +} |
Mutations | ||
106 | ++ |
+
+
+
+ 1.1 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
108 | ++ |
+
+
+
+ 1.1 |
+
115 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 |
+
+ +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.data; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +31 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +32 + | ++ + + + + + | +import lombok.Getter; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | +/** |
+ +36 + | ++ + + + + + | + * A WebAuthn Relying Party may require <a |
+ +37 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">user |
+ +38 + | ++ + + + + + | + * verification</a> for some of its operations but not for others, and may use this type to express |
+ +39 + | ++ + + + + + | + * its needs. |
+ +40 + | ++ + + + + + | + * |
+ +41 + | ++ + + + + + | + * @see <a |
+ +42 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-userverificationrequirement">§5.10.6. |
+ +43 + | ++ + + + + + | + * User Verification Requirement Enumeration (enum UserVerificationRequirement)</a> |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | +@AllArgsConstructor |
+ +46 + | ++ + + + + + | +public enum UserVerificationRequirement { |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + /** |
+ +49 + | ++ + + + + + | + * This value indicates that the Relying Party does not want user verification employed during the |
+ +50 + | ++ + + + + + | + * operation (e.g., in the interest of minimizing disruption to the user interaction flow). |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | + DISCOURAGED("discouraged"), |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + /** |
+ +55 + | ++ + + + + + | + * This value indicates that the Relying Party prefers user verification for the operation if |
+ +56 + | ++ + + + + + | + * possible, but will not fail the operation if the response does not have the {@link |
+ +57 + | ++ + + + + + | + * AuthenticatorDataFlags#UV} flag set. |
+ +58 + | ++ + + + + + | + */ |
+ +59 + | ++ + + + + + | + PREFERRED("preferred"), |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + /** |
+ +62 + | ++ + + + + + | + * Indicates that the Relying Party requires user verification for the operation and will fail the |
+ +63 + | ++ + + + + + | + * operation if the response does not have the {@link AuthenticatorDataFlags#UV} flag set. |
+ +64 + | ++ + + + + + | + */ |
+ +65 + | ++ + + + + + | + REQUIRED("required"); |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + @JsonValue @Getter @NonNull private final String value; |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + /** |
+ +70 + | ++ + + + + + | + * Attempt to parse a string as a {@link UserVerificationRequirement}. |
+ +71 + | ++ + + + + + | + * |
+ +72 + | ++ + + + + + | + * @param value a {@link String} equal to the {@link #getValue() value} of a constant in {@link |
+ +73 + | ++ + + + + + | + * UserVerificationRequirement} |
+ +74 + | ++ + + + + + | + * @return The {@link UserVerificationRequirement} instance whose {@link #getValue() value} equals |
+ +75 + | ++ + + + + + | + * <code>value</code>, if any. |
+ +76 + | ++ + + + + + | + * @see <a |
+ +77 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#enumdef-userverificationrequirement">§5.10.6. |
+ +78 + | ++ + + + + + | + * User Verification Requirement Enumeration (enum UserVerificationRequirement)</a> |
+ +79 + | ++ + + + + + | + */ |
+ +80 + | +
+
+1
+
+1. fromValue : negated conditional → KILLED + + + + |
+ public static Optional<UserVerificationRequirement> fromValue(@NonNull String value) { |
+ +81 + | +
+
+3
+
+1. lambda$fromValue$0 : replaced boolean return with false for com/yubico/webauthn/data/UserVerificationRequirement::lambda$fromValue$0 → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/data/UserVerificationRequirement::lambda$fromValue$0 → KILLED +3. fromValue : replaced return value with Optional.empty for com/yubico/webauthn/data/UserVerificationRequirement::fromValue → KILLED + + + + |
+ return Stream.of(values()).filter(v -> v.value.equals(value)).findAny(); |
+ +82 + | ++ + + + + + | + } |
+ +83 + | ++ + + + + + | +|
+ +84 + | ++ + + + + + | + @JsonCreator |
+ +85 + | +
+
+1
+
+1. fromJsonString : negated conditional → KILLED + + + + |
+ private static UserVerificationRequirement fromJsonString(@NonNull String value) { |
+ +86 + | +
+
+1
+
+1. fromJsonString : replaced return value with null for com/yubico/webauthn/data/UserVerificationRequirement::fromJsonString → KILLED + + + + |
+ return fromValue(value) |
+ +87 + | ++ + + + + + | + .orElseThrow( |
+ +88 + | ++ + + + + + | + () -> |
+ +89 + | +
+
+1
+
+1. lambda$fromJsonString$1 : replaced return value with null for com/yubico/webauthn/data/UserVerificationRequirement::lambda$fromJsonString$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +90 + | ++ + + + + + | + String.format( |
+ +91 + | ++ + + + + + | + "Unknown %s value: %s", |
+ +92 + | ++ + + + + + | + UserVerificationRequirement.class.getSimpleName(), value))); |
+ +93 + | ++ + + + + + | + } |
+ +94 + | ++ + + + + + | +} |
Mutations | ||
80 | ++ |
+
+
+
+ 1.1 |
+
81 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
85 | ++ |
+
+
+
+ 1.1 |
+
86 | ++ |
+
+
+
+ 1.1 |
+
89 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
33 | +94% | +94% | +97% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
AssertionExtensionInputs.java | +100% |
+ 93% |
+ 93% |
+
AttestationConveyancePreference.java | +93% |
+ 100% |
+ 100% |
+
AttestationObject.java | +100% |
+ 100% |
+ 100% |
+
AttestedCredentialData.java | +100% |
+ 100% |
+ 100% |
+
AuthenticatorAssertionExtensionOutputs.java | +89% |
+ 100% |
+ 100% |
+
AuthenticatorAssertionResponse.java | +100% |
+ 100% |
+ 100% |
+
AuthenticatorAttachment.java | +100% |
+ 100% |
+ 100% |
+
AuthenticatorAttestationResponse.java | +96% |
+ 88% |
+ 100% |
+
AuthenticatorData.java | +93% |
+ 96% |
+ 96% |
+
AuthenticatorDataFlags.java | +93% |
+ 100% |
+ 100% |
+
AuthenticatorRegistrationExtensionOutputs.java | +89% |
+ 100% |
+ 100% |
+
AuthenticatorResponse.java | +0% |
+ 0% |
+ 100% |
+
AuthenticatorSelectionCriteria.java | +100% |
+ 100% |
+ 100% |
+
AuthenticatorTransport.java | +100% |
+ 100% |
+ 100% |
+
ByteArray.java | +100% |
+ 100% |
+ 100% |
+
COSEAlgorithmIdentifier.java | +87% |
+ 100% |
+ 100% |
+
ClientAssertionExtensionOutputs.java | +100% |
+ 89% |
+ 89% |
+
ClientRegistrationExtensionOutputs.java | +100% |
+ 100% |
+ 100% |
+
CollectedClientData.java | +79% |
+ 100% |
+ 100% |
+
Extensions.java | +79% |
+ 74% |
+ 88% |
+
PublicKeyCredential.java | +100% |
+ 100% |
+ 100% |
+
PublicKeyCredentialCreationOptions.java | +92% |
+ 94% |
+ 100% |
+
PublicKeyCredentialDescriptor.java | +97% |
+ 92% |
+ 96% |
+
PublicKeyCredentialParameters.java | +100% |
+ 100% |
+ 100% |
+
PublicKeyCredentialRequestOptions.java | +98% |
+ 95% |
+ 100% |
+
PublicKeyCredentialType.java | +91% |
+ 100% |
+ 100% |
+
RegistrationExtensionInputs.java | +100% |
+ 97% |
+ 97% |
+
RelyingPartyIdentity.java | +100% |
+ 100% |
+ 100% |
+
ResidentKeyRequirement.java | +92% |
+ 100% |
+ 100% |
+
TokenBindingInfo.java | +100% |
+ 70% |
+ 70% |
+
TokenBindingStatus.java | +75% |
+ 86% |
+ 100% |
+
UserIdentity.java | +100% |
+ 100% |
+ 100% |
+
UserVerificationRequirement.java | +92% |
+ 100% |
+ 100% |
+
+ +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.extension.appid; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonGenerator; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.databind.SerializerProvider; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
+ +31 + | ++ + + + + + | +import com.google.common.net.InetAddresses; |
+ +32 + | ++ + + + + + | +import java.io.IOException; |
+ +33 + | ++ + + + + + | +import java.net.URI; |
+ +34 + | ++ + + + + + | +import java.net.URISyntaxException; |
+ +35 + | ++ + + + + + | +import lombok.NonNull; |
+ +36 + | ++ + + + + + | +import lombok.Value; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | +/** |
+ +39 + | ++ + + + + + | + * A FIDO AppID verified to be syntactically valid. |
+ +40 + | ++ + + + + + | + * |
+ +41 + | ++ + + + + + | + * @see <a |
+ +42 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html">FIDO |
+ +43 + | ++ + + + + + | + * AppID and Facet Specification</a> |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | +@Value |
+ +46 + | ++ + + + + + | +@JsonSerialize(using = AppId.JsonSerializer.class) |
+ +47 + | ++ + + + + + | +public class AppId { |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** The underlying string representation of this AppID. */ |
+ +50 + | ++ + + + + + | + private final String id; |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * Verify that the <code>appId</code> is a valid FIDO AppID, and wrap it as an {@link AppId}. |
+ +54 + | ++ + + + + + | + * |
+ +55 + | ++ + + + + + | + * @throws InvalidAppIdException if <code>appId</code> is not a valid FIDO AppID. |
+ +56 + | ++ + + + + + | + * @see <a |
+ +57 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-appid-and-facets-v2.0-id-20180227.html">FIDO |
+ +58 + | ++ + + + + + | + * AppID and Facet Specification</a> |
+ +59 + | ++ + + + + + | + */ |
+ +60 + | ++ + + + + + | + @JsonCreator |
+ +61 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ public AppId(@NonNull String appId) throws InvalidAppIdException { |
+ +62 + | +
+
+1
+
+1. <init> : removed call to com/yubico/webauthn/extension/appid/AppId::checkIsValid → KILLED + + + + |
+ checkIsValid(appId); |
+ +63 + | ++ + + + + + | + this.id = appId; |
+ +64 + | ++ + + + + + | + } |
+ +65 + | ++ + + + + + | +|
+ +66 + | ++ + + + + + | + /** |
+ +67 + | ++ + + + + + | + * Throws {@link InvalidAppIdException} if the given App ID is found to be incompatible with the |
+ +68 + | ++ + + + + + | + * U2F specification or any major U2F Client implementation. |
+ +69 + | ++ + + + + + | + * |
+ +70 + | ++ + + + + + | + * @param appId the App ID to be validated |
+ +71 + | ++ + + + + + | + */ |
+ +72 + | ++ + + + + + | + private static void checkIsValid(String appId) throws InvalidAppIdException { |
+ +73 + | +
+
+1
+
+1. checkIsValid : negated conditional → KILLED + + + + |
+ if (!appId.contains(":")) { |
+ +74 + | ++ + + + + + | + throw new InvalidAppIdException( |
+ +75 + | ++ + + + + + | + "App ID does not look like a valid facet or URL. Web facets must start with 'https://'."); |
+ +76 + | ++ + + + + + | + } |
+ +77 + | +
+
+1
+
+1. checkIsValid : negated conditional → KILLED + + + + |
+ if (appId.startsWith("http:")) { |
+ +78 + | ++ + + + + + | + throw new InvalidAppIdException( |
+ +79 + | ++ + + + + + | + "HTTP is not supported for App IDs (by Chrome). Use HTTPS instead."); |
+ +80 + | ++ + + + + + | + } |
+ +81 + | +
+
+1
+
+1. checkIsValid : negated conditional → KILLED + + + + |
+ if (appId.startsWith("https://")) { |
+ +82 + | ++ + + + + + | + URI url = checkValidUrl(appId); |
+ +83 + | +
+
+1
+
+1. checkIsValid : removed call to com/yubico/webauthn/extension/appid/AppId::checkPathIsNotSlash → KILLED + + + + |
+ checkPathIsNotSlash(url); |
+ +84 + | +
+
+1
+
+1. checkIsValid : removed call to com/yubico/webauthn/extension/appid/AppId::checkNotIpAddress → KILLED + + + + |
+ checkNotIpAddress(url); |
+ +85 + | ++ + + + + + | + } |
+ +86 + | ++ + + + + + | + } |
+ +87 + | ++ + + + + + | +|
+ +88 + | ++ + + + + + | + private static void checkPathIsNotSlash(URI url) throws InvalidAppIdException { |
+ +89 + | +
+
+1
+
+1. checkPathIsNotSlash : negated conditional → KILLED + + + + |
+ if ("/".equals(url.getPath())) { |
+ +90 + | ++ + + + + + | + throw new InvalidAppIdException( |
+ +91 + | ++ + + + + + | + "The path of the URL set as App ID is '/'. This is probably not what you want -- remove the trailing slash of the App ID URL."); |
+ +92 + | ++ + + + + + | + } |
+ +93 + | ++ + + + + + | + } |
+ +94 + | ++ + + + + + | +|
+ +95 + | ++ + + + + + | + private static URI checkValidUrl(String appId) throws InvalidAppIdException { |
+ +96 + | ++ + + + + + | + try { |
+ +97 + | +
+
+1
+
+1. checkValidUrl : replaced return value with null for com/yubico/webauthn/extension/appid/AppId::checkValidUrl → KILLED + + + + |
+ return new URI(appId); |
+ +98 + | ++ + + + + + | + } catch (URISyntaxException e) { |
+ +99 + | ++ + + + + + | + throw new InvalidAppIdException("App ID looks like a HTTPS URL, but has syntax errors.", e); |
+ +100 + | ++ + + + + + | + } |
+ +101 + | ++ + + + + + | + } |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + private static void checkNotIpAddress(URI url) throws InvalidAppIdException { |
+ +104 + | +
+
+1
+
+1. checkNotIpAddress : negated conditional → KILLED + + + + |
+ if (InetAddresses.isInetAddress(url.getAuthority()) |
+ +105 + | +
+
+2
+
+1. checkNotIpAddress : negated conditional → KILLED +2. checkNotIpAddress : negated conditional → KILLED + + + + |
+ || (url.getHost() != null && InetAddresses.isInetAddress(url.getHost()))) { |
+ +106 + | ++ + + + + + | + throw new InvalidAppIdException( |
+ +107 + | ++ + + + + + | + "App ID must not be an IP-address, since it is not supported (by Chrome). Use a host name instead."); |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | + } |
+ +110 + | ++ + + + + + | +|
+ +111 + | ++ + + + + + | + static class JsonSerializer extends com.fasterxml.jackson.databind.JsonSerializer<AppId> { |
+ +112 + | ++ + + + + + | + @Override |
+ +113 + | ++ + + + + + | + public void serialize(AppId value, JsonGenerator gen, SerializerProvider serializers) |
+ +114 + | ++ + + + + + | + throws IOException { |
+ +115 + | +
+
+1
+
+1. serialize : removed call to com/fasterxml/jackson/core/JsonGenerator::writeString → KILLED + + + + |
+ gen.writeString(value.getId()); |
+ +116 + | ++ + + + + + | + } |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +} |
Mutations | ||
61 | ++ |
+
+
+
+ 1.1 |
+
62 | ++ |
+
+
+
+ 1.1 |
+
73 | ++ |
+
+
+
+ 1.1 |
+
77 | ++ |
+
+
+
+ 1.1 |
+
81 | ++ |
+
+
+
+ 1.1 |
+
83 | ++ |
+
+
+
+ 1.1 |
+
84 | ++ |
+
+
+
+ 1.1 |
+
89 | ++ |
+
+
+
+ 1.1 |
+
97 | ++ |
+
+
+
+ 1.1 |
+
104 | ++ |
+
+
+
+ 1.1 |
+
105 | ++ |
+
+
+
+ 1.1 2.2 |
+
115 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
1 | +100% | +100% | +100% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
AppId.java | +100% |
+ 100% |
+ 100% |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn.extension.uvm; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +5 + | ++ + + + + + | +import java.security.Key; |
+ +6 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +7 + | ++ + + + + + | +import lombok.Getter; |
+ +8 + | ++ + + + + + | +|
+ +9 + | ++ + + + + + | +/** |
+ +10 + | ++ + + + + + | + * The KEY_PROTECTION constants are flags in a bit field represented as a 16 bit long integer. They |
+ +11 + | ++ + + + + + | + * describe the method an authenticator uses to protect the private key material for FIDO |
+ +12 + | ++ + + + + + | + * registrations. Refer to [UAFAuthnrCommands] for more details on the relevance of keys and key |
+ +13 + | ++ + + + + + | + * protection. These constants are reported and queried through the UAF Discovery APIs and used to |
+ +14 + | ++ + + + + + | + * form authenticator policies in UAF protocol messages. Each constant has a case-sensitive string |
+ +15 + | ++ + + + + + | + * representation (in quotes), which is used in the authoritative metadata for FIDO authenticators. |
+ +16 + | ++ + + + + + | + * |
+ +17 + | ++ + + + + + | + * @see #fromValue(short) |
+ +18 + | ++ + + + + + | + * @see #fromName(String) |
+ +19 + | ++ + + + + + | + * @see <a |
+ +20 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#key-protection-types">FIDO |
+ +21 + | ++ + + + + + | + * Registry of Predefined Values §3.2 Key Protection Types</a> |
+ +22 + | ++ + + + + + | + */ |
+ +23 + | ++ + + + + + | +@Getter |
+ +24 + | ++ + + + + + | +public enum KeyProtectionType { |
+ +25 + | ++ + + + + + | +|
+ +26 + | ++ + + + + + | + /** |
+ +27 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses software-based key management. Exclusive in |
+ +28 + | ++ + + + + + | + * authenticator metadata with {@link #KEY_PROTECTION_HARDWARE}, {@link #KEY_PROTECTION_TEE}, |
+ +29 + | ++ + + + + + | + * {@link #KEY_PROTECTION_SECURE_ELEMENT}. |
+ +30 + | ++ + + + + + | + * |
+ +31 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +32 + | ++ + + + + + | + * |
+ +33 + | ++ + + + + + | + * @see <a |
+ +34 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#key-protection-types">FIDO |
+ +35 + | ++ + + + + + | + * Registry of Predefined Values §3.2 Key Protection Types</a> |
+ +36 + | ++ + + + + + | + */ |
+ +37 + | ++ + + + + + | + KEY_PROTECTION_SOFTWARE((short) 0x0001, "software"), |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | + /** |
+ +40 + | ++ + + + + + | + * This flag SHOULD be set if the authenticator uses hardware-based key management. Exclusive in |
+ +41 + | ++ + + + + + | + * authenticator metadata with {@link #KEY_PROTECTION_SOFTWARE}. |
+ +42 + | ++ + + + + + | + * |
+ +43 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +44 + | ++ + + + + + | + * |
+ +45 + | ++ + + + + + | + * @see <a |
+ +46 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#key-protection-types">FIDO |
+ +47 + | ++ + + + + + | + * Registry of Predefined Values §3.2 Key Protection Types</a> |
+ +48 + | ++ + + + + + | + */ |
+ +49 + | ++ + + + + + | + KEY_PROTECTION_HARDWARE((short) 0x0002, "hardware"), |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + /** |
+ +52 + | ++ + + + + + | + * This flag SHOULD be set if the authenticator uses the Trusted Execution Environment [TEE] for |
+ +53 + | ++ + + + + + | + * key management. In authenticator metadata, this flag should be set in conjunction with {@link |
+ +54 + | ++ + + + + + | + * #KEY_PROTECTION_HARDWARE}. Mutually exclusive in authenticator metadata with {@link |
+ +55 + | ++ + + + + + | + * #KEY_PROTECTION_SOFTWARE}, {@link #KEY_PROTECTION_SECURE_ELEMENT}. |
+ +56 + | ++ + + + + + | + * |
+ +57 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +58 + | ++ + + + + + | + * |
+ +59 + | ++ + + + + + | + * @see <a |
+ +60 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#key-protection-types">FIDO |
+ +61 + | ++ + + + + + | + * Registry of Predefined Values §3.2 Key Protection Types</a> |
+ +62 + | ++ + + + + + | + */ |
+ +63 + | ++ + + + + + | + KEY_PROTECTION_TEE((short) 0x0004, "tee"), |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + /** |
+ +66 + | ++ + + + + + | + * This flag SHOULD be set if the authenticator uses a Secure Element [SecureElement] for key |
+ +67 + | ++ + + + + + | + * management. In authenticator metadata, this flag should be set in conjunction with {@link |
+ +68 + | ++ + + + + + | + * #KEY_PROTECTION_HARDWARE}. Mutually exclusive in authenticator metadata with {@link |
+ +69 + | ++ + + + + + | + * #KEY_PROTECTION_TEE}, {@link #KEY_PROTECTION_SOFTWARE}. |
+ +70 + | ++ + + + + + | + * |
+ +71 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +72 + | ++ + + + + + | + * |
+ +73 + | ++ + + + + + | + * @see <a |
+ +74 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#key-protection-types">FIDO |
+ +75 + | ++ + + + + + | + * Registry of Predefined Values §3.2 Key Protection Types</a> |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + KEY_PROTECTION_SECURE_ELEMENT((short) 0x0008, "secure_element"), |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + /** |
+ +80 + | ++ + + + + + | + * This flag MUST be set if the authenticator does not store (wrapped) UAuth keys at the client, |
+ +81 + | ++ + + + + + | + * but relies on a server-provided key handle. This flag MUST be set in conjunction with one of |
+ +82 + | ++ + + + + + | + * the other KEY_PROTECTION flags to indicate how the local key handle wrapping key and operations |
+ +83 + | ++ + + + + + | + * are protected. Servers MAY unset this flag in authenticator policy if they are not prepared to |
+ +84 + | ++ + + + + + | + * store and return key handles, for example, if they have a requirement to respond |
+ +85 + | ++ + + + + + | + * indistinguishably to authentication attempts against userIDs that do and do not exist. Refer to |
+ +86 + | ++ + + + + + | + * [<a |
+ +87 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-uaf-v1.2-rd-20171128/fido-uaf-protocol-v1.2-rd-20171128.html">UAFProtocol</a>] |
+ +88 + | ++ + + + + + | + * for more details. |
+ +89 + | ++ + + + + + | + * |
+ +90 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +91 + | ++ + + + + + | + * |
+ +92 + | ++ + + + + + | + * @see <a |
+ +93 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#key-protection-types">FIDO |
+ +94 + | ++ + + + + + | + * Registry of Predefined Values §3.2 Key Protection Types</a> |
+ +95 + | ++ + + + + + | + * @see <a |
+ +96 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-uaf-v1.2-rd-20171128/fido-uaf-protocol-v1.2-rd-20171128.html">FIDO |
+ +97 + | ++ + + + + + | + * UAF Protocol Specification [UAFProtocol]</a> |
+ +98 + | ++ + + + + + | + */ |
+ +99 + | ++ + + + + + | + KEY_PROTECTION_REMOTE_HANDLE((short) 0x0010, "remote_handle"); |
+ +100 + | ++ + + + + + | +|
+ +101 + | ++ + + + + + | + private final short value; |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + @JsonValue private final String name; |
+ +104 + | ++ + + + + + | +|
+ +105 + | ++ + + + + + | + KeyProtectionType(short value, String name) { |
+ +106 + | ++ + + + + + | + this.value = value; |
+ +107 + | ++ + + + + + | + this.name = name; |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + /** |
+ +111 + | ++ + + + + + | + * @return If <code>value</code> matches any {@link KeyProtectionType} constant, returns that |
+ +112 + | ++ + + + + + | + * constant instance. Otherwise throws {@link IllegalArgumentException}. |
+ +113 + | ++ + + + + + | + */ |
+ +114 + | ++ + + + + + | + public static KeyProtectionType fromValue(short value) { |
+ +115 + | +
+
+1
+
+1. fromValue : replaced return value with null for com/yubico/webauthn/extension/uvm/KeyProtectionType::fromValue → KILLED + + + + |
+ return Stream.of(values()) |
+ +116 + | +
+
+2
+
+1. lambda$fromValue$0 : negated conditional → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/extension/uvm/KeyProtectionType::lambda$fromValue$0 → KILLED + + + + |
+ .filter(v -> v.value == value) |
+ +117 + | ++ + + + + + | + .findAny() |
+ +118 + | ++ + + + + + | + .orElseThrow( |
+ +119 + | ++ + + + + + | + () -> |
+ +120 + | +
+
+1
+
+1. lambda$fromValue$1 : replaced return value with null for com/yubico/webauthn/extension/uvm/KeyProtectionType::lambda$fromValue$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +121 + | ++ + + + + + | + String.format("Unknown %s value: 0x%04x", KeyProtectionType.class, value))); |
+ +122 + | ++ + + + + + | + } |
+ +123 + | ++ + + + + + | +|
+ +124 + | ++ + + + + + | + /** |
+ +125 + | ++ + + + + + | + * @return If <code>name</code> matches any {@link Key} constant, returns that constant instance. |
+ +126 + | ++ + + + + + | + * Otherwise throws {@link IllegalArgumentException}. |
+ +127 + | ++ + + + + + | + */ |
+ +128 + | ++ + + + + + | + @JsonCreator |
+ +129 + | ++ + + + + + | + public static KeyProtectionType fromName(String name) { |
+ +130 + | +
+
+1
+
+1. fromName : replaced return value with null for com/yubico/webauthn/extension/uvm/KeyProtectionType::fromName → NO_COVERAGE + + + + |
+ return Stream.of(values()) |
+ +131 + | +
+
+2
+
+1. lambda$fromName$2 : replaced boolean return with false for com/yubico/webauthn/extension/uvm/KeyProtectionType::lambda$fromName$2 → NO_COVERAGE +2. lambda$fromName$2 : replaced boolean return with true for com/yubico/webauthn/extension/uvm/KeyProtectionType::lambda$fromName$2 → NO_COVERAGE + + + + |
+ .filter(v -> v.name.equals(name)) |
+ +132 + | ++ + + + + + | + .findAny() |
+ +133 + | ++ + + + + + | + .orElseThrow( |
+ +134 + | ++ + + + + + | + () -> |
+ +135 + | +
+
+1
+
+1. lambda$fromName$3 : replaced return value with null for com/yubico/webauthn/extension/uvm/KeyProtectionType::lambda$fromName$3 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +136 + | ++ + + + + + | + String.format("Unknown %s name: %s", KeyProtectionType.class, name))); |
+ +137 + | ++ + + + + + | + } |
+ +138 + | ++ + + + + + | +} |
Mutations | ||
115 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 2.2 |
+
120 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
131 | ++ |
+
+
+
+ 1.1 2.2 |
+
135 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn.extension.uvm; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +5 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +6 + | ++ + + + + + | +import lombok.Getter; |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | +/** |
+ +9 + | ++ + + + + + | + * The MATCHER_PROTECTION constants are flags in a bit field represented as a 16 bit long integer. |
+ +10 + | ++ + + + + + | + * They describe the method an authenticator uses to protect the matcher that performs user |
+ +11 + | ++ + + + + + | + * verification. These constants are reported and queried through the UAF Discovery APIs and used to |
+ +12 + | ++ + + + + + | + * form authenticator policies in UAF protocol messages. Refer to [UAFAuthnrCommands] for more |
+ +13 + | ++ + + + + + | + * details on the matcher component. Each constant has a case-sensitive string representation (in |
+ +14 + | ++ + + + + + | + * quotes), which is used in the authoritative metadata for FIDO authenticators. |
+ +15 + | ++ + + + + + | + * |
+ +16 + | ++ + + + + + | + * @see #fromValue(int) |
+ +17 + | ++ + + + + + | + * @see #fromName(String) |
+ +18 + | ++ + + + + + | + * @see <a |
+ +19 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#matcher-protection-types">FIDO |
+ +20 + | ++ + + + + + | + * Registry of Predefined Values §3.3 Matcher Protection Types</a> |
+ +21 + | ++ + + + + + | + */ |
+ +22 + | ++ + + + + + | +@Getter |
+ +23 + | ++ + + + + + | +public enum MatcherProtectionType { |
+ +24 + | ++ + + + + + | +|
+ +25 + | ++ + + + + + | + /** |
+ +26 + | ++ + + + + + | + * This flag MUST be set if the authenticator's matcher is running in software. Exclusive in |
+ +27 + | ++ + + + + + | + * authenticator metadata with {@link #MATCHER_PROTECTION_TEE}, {@link |
+ +28 + | ++ + + + + + | + * #MATCHER_PROTECTION_ON_CHIP}. |
+ +29 + | ++ + + + + + | + * |
+ +30 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +31 + | ++ + + + + + | + * |
+ +32 + | ++ + + + + + | + * @see <a |
+ +33 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#matcher-protection-types">FIDO |
+ +34 + | ++ + + + + + | + * Registry of Predefined Values §3.3 Matcher Protection Types</a> |
+ +35 + | ++ + + + + + | + */ |
+ +36 + | ++ + + + + + | + MATCHER_PROTECTION_SOFTWARE((short) 0x0001, "software"), |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + /** |
+ +39 + | ++ + + + + + | + * This flag SHOULD be set if the authenticator's matcher is running inside the Trusted Execution |
+ +40 + | ++ + + + + + | + * Environment [TEE]. Mutually exclusive in authenticator metadata with {@link |
+ +41 + | ++ + + + + + | + * #MATCHER_PROTECTION_SOFTWARE}, {@link #MATCHER_PROTECTION_ON_CHIP}. |
+ +42 + | ++ + + + + + | + * |
+ +43 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +44 + | ++ + + + + + | + * |
+ +45 + | ++ + + + + + | + * @see <a |
+ +46 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#matcher-protection-types">FIDO |
+ +47 + | ++ + + + + + | + * Registry of Predefined Values §3.3 Matcher Protection Types</a> |
+ +48 + | ++ + + + + + | + */ |
+ +49 + | ++ + + + + + | + MATCHER_PROTECTION_TEE((short) 0x0002, "tee"), |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + /** |
+ +52 + | ++ + + + + + | + * This flag SHOULD be set if the authenticator's matcher is running on the chip. Mutually |
+ +53 + | ++ + + + + + | + * exclusive in authenticator metadata with {@link #MATCHER_PROTECTION_TEE}, {@link |
+ +54 + | ++ + + + + + | + * #MATCHER_PROTECTION_SOFTWARE} |
+ +55 + | ++ + + + + + | + * |
+ +56 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +57 + | ++ + + + + + | + * |
+ +58 + | ++ + + + + + | + * @see <a |
+ +59 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#matcher-protection-types">FIDO |
+ +60 + | ++ + + + + + | + * Registry of Predefined Values §3.3 Matcher Protection Types</a> |
+ +61 + | ++ + + + + + | + */ |
+ +62 + | ++ + + + + + | + MATCHER_PROTECTION_ON_CHIP((short) 0x0004, "on_chip"); |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + private final short value; |
+ +65 + | ++ + + + + + | +|
+ +66 + | ++ + + + + + | + @JsonValue private final String name; |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + MatcherProtectionType(short value, String name) { |
+ +69 + | ++ + + + + + | + this.value = value; |
+ +70 + | ++ + + + + + | + this.name = name; |
+ +71 + | ++ + + + + + | + } |
+ +72 + | ++ + + + + + | +|
+ +73 + | ++ + + + + + | + /** |
+ +74 + | ++ + + + + + | + * @return If <code>value</code> matches any {@link MatcherProtectionType} constant, returns that |
+ +75 + | ++ + + + + + | + * constant instance. Otherwise throws {@link IllegalArgumentException}. |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + public static MatcherProtectionType fromValue(int value) { |
+ +78 + | +
+
+1
+
+1. fromValue : replaced return value with null for com/yubico/webauthn/extension/uvm/MatcherProtectionType::fromValue → KILLED + + + + |
+ return Stream.of(values()) |
+ +79 + | +
+
+2
+
+1. lambda$fromValue$0 : negated conditional → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/extension/uvm/MatcherProtectionType::lambda$fromValue$0 → KILLED + + + + |
+ .filter(v -> v.value == value) |
+ +80 + | ++ + + + + + | + .findAny() |
+ +81 + | ++ + + + + + | + .orElseThrow( |
+ +82 + | ++ + + + + + | + () -> |
+ +83 + | +
+
+1
+
+1. lambda$fromValue$1 : replaced return value with null for com/yubico/webauthn/extension/uvm/MatcherProtectionType::lambda$fromValue$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +84 + | ++ + + + + + | + String.format("Unknown %s value: 0x%04x", MatcherProtectionType.class, value))); |
+ +85 + | ++ + + + + + | + } |
+ +86 + | ++ + + + + + | +|
+ +87 + | ++ + + + + + | + /** |
+ +88 + | ++ + + + + + | + * @return If <code>name</code> matches any {@link MatcherProtectionType} constant, returns that |
+ +89 + | ++ + + + + + | + * constant instance. Otherwise throws {@link IllegalArgumentException}. |
+ +90 + | ++ + + + + + | + */ |
+ +91 + | ++ + + + + + | + @JsonCreator |
+ +92 + | ++ + + + + + | + public static MatcherProtectionType fromName(String name) { |
+ +93 + | +
+
+1
+
+1. fromName : replaced return value with null for com/yubico/webauthn/extension/uvm/MatcherProtectionType::fromName → NO_COVERAGE + + + + |
+ return Stream.of(values()) |
+ +94 + | +
+
+2
+
+1. lambda$fromName$2 : replaced boolean return with true for com/yubico/webauthn/extension/uvm/MatcherProtectionType::lambda$fromName$2 → NO_COVERAGE +2. lambda$fromName$2 : replaced boolean return with false for com/yubico/webauthn/extension/uvm/MatcherProtectionType::lambda$fromName$2 → NO_COVERAGE + + + + |
+ .filter(v -> v.name.equals(name)) |
+ +95 + | ++ + + + + + | + .findAny() |
+ +96 + | ++ + + + + + | + .orElseThrow( |
+ +97 + | ++ + + + + + | + () -> |
+ +98 + | +
+
+1
+
+1. lambda$fromName$3 : replaced return value with null for com/yubico/webauthn/extension/uvm/MatcherProtectionType::lambda$fromName$3 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +99 + | ++ + + + + + | + String.format("Unknown %s name: %s", MatcherProtectionType.class, name))); |
+ +100 + | ++ + + + + + | + } |
+ +101 + | ++ + + + + + | +} |
Mutations | ||
78 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 2.2 |
+
83 | ++ |
+
+
+
+ 1.1 |
+
93 | ++ |
+
+
+
+ 1.1 |
+
94 | ++ |
+
+
+
+ 1.1 2.2 |
+
98 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn.extension.uvm; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +5 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +6 + | ++ + + + + + | +import lombok.Getter; |
+ +7 + | ++ + + + + + | +|
+ +8 + | ++ + + + + + | +/** |
+ +9 + | ++ + + + + + | + * The USER_VERIFY constants are flags in a bitfield represented as a 32 bit long integer. They |
+ +10 + | ++ + + + + + | + * describe the methods and capabilities of a FIDO authenticator for locally verifying a user. The |
+ +11 + | ++ + + + + + | + * operational details of these methods are opaque to the server. These constants are used in the |
+ +12 + | ++ + + + + + | + * authoritative metadata for FIDO authenticators, reported and queried through the UAF Discovery |
+ +13 + | ++ + + + + + | + * APIs, and used to form authenticator policies in UAF protocol messages. Each constant has a |
+ +14 + | ++ + + + + + | + * case-sensitive string representation (in quotes), which is used in the authoritative metadata for |
+ +15 + | ++ + + + + + | + * FIDO authenticators. |
+ +16 + | ++ + + + + + | + * |
+ +17 + | ++ + + + + + | + * @see #fromValue(int) |
+ +18 + | ++ + + + + + | + * @see #fromName(String) |
+ +19 + | ++ + + + + + | + * @see <a |
+ +20 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +21 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +22 + | ++ + + + + + | + */ |
+ +23 + | ++ + + + + + | +@Getter |
+ +24 + | ++ + + + + + | +public enum UserVerificationMethod { |
+ +25 + | ++ + + + + + | +|
+ +26 + | ++ + + + + + | + /** |
+ +27 + | ++ + + + + + | + * This flag MUST be set if the authenticator is able to confirm user presence in any fashion. If |
+ +28 + | ++ + + + + + | + * this flag and no other is set for user verification, the guarantee is only that the |
+ +29 + | ++ + + + + + | + * authenticator cannot be operated without some human intervention, not necessarily that the |
+ +30 + | ++ + + + + + | + * sensing of "presence" provides any level of user verification (e.g. a device that requires a |
+ +31 + | ++ + + + + + | + * button press to activate). |
+ +32 + | ++ + + + + + | + * |
+ +33 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +34 + | ++ + + + + + | + * |
+ +35 + | ++ + + + + + | + * @see <a |
+ +36 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +37 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +38 + | ++ + + + + + | + */ |
+ +39 + | ++ + + + + + | + USER_VERIFY_PRESENCE_INTERNAL(0x00000001, "presence_internal"), |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | + /** |
+ +42 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses any type of measurement of a fingerprint for |
+ +43 + | ++ + + + + + | + * user verification. |
+ +44 + | ++ + + + + + | + * |
+ +45 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +46 + | ++ + + + + + | + * |
+ +47 + | ++ + + + + + | + * @see <a |
+ +48 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +49 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | + USER_VERIFY_FINGERPRINT_INTERNAL(0x00000002, "fingerprint_internal"), |
+ +52 + | ++ + + + + + | +|
+ +53 + | ++ + + + + + | + /** |
+ +54 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses a local-only passcode (i.e. a passcode not |
+ +55 + | ++ + + + + + | + * known by the server) for user verification. |
+ +56 + | ++ + + + + + | + * |
+ +57 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +58 + | ++ + + + + + | + * |
+ +59 + | ++ + + + + + | + * @see <a |
+ +60 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +61 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +62 + | ++ + + + + + | + */ |
+ +63 + | ++ + + + + + | + USER_VERIFY_PASSCODE_INTERNAL(0x00000004, "passcode_internal"), |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + /** |
+ +66 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses a voiceprint (also known as speaker |
+ +67 + | ++ + + + + + | + * recognition) for user verification. |
+ +68 + | ++ + + + + + | + * |
+ +69 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +70 + | ++ + + + + + | + * |
+ +71 + | ++ + + + + + | + * @see <a |
+ +72 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +73 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +74 + | ++ + + + + + | + */ |
+ +75 + | ++ + + + + + | + USER_VERIFY_VOICEPRINT_INTERNAL(0x00000008, "voiceprint_internal"), |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses any manner of face recognition to verify the |
+ +79 + | ++ + + + + + | + * user. |
+ +80 + | ++ + + + + + | + * |
+ +81 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +82 + | ++ + + + + + | + * |
+ +83 + | ++ + + + + + | + * @see <a |
+ +84 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +85 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +86 + | ++ + + + + + | + */ |
+ +87 + | ++ + + + + + | + USER_VERIFY_FACEPRINT_INTERNAL(0x00000010, "faceprint_internal"), |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + /** |
+ +90 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses any form of location sensor or measurement for |
+ +91 + | ++ + + + + + | + * user verification. |
+ +92 + | ++ + + + + + | + * |
+ +93 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +94 + | ++ + + + + + | + * |
+ +95 + | ++ + + + + + | + * @see <a |
+ +96 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +97 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +98 + | ++ + + + + + | + */ |
+ +99 + | ++ + + + + + | + USER_VERIFY_LOCATION_INTERNAL(0x00000020, "location_internal"), |
+ +100 + | ++ + + + + + | +|
+ +101 + | ++ + + + + + | + /** |
+ +102 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses any form of eye biometrics for user |
+ +103 + | ++ + + + + + | + * verification. |
+ +104 + | ++ + + + + + | + * |
+ +105 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +106 + | ++ + + + + + | + * |
+ +107 + | ++ + + + + + | + * @see <a |
+ +108 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +109 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +110 + | ++ + + + + + | + */ |
+ +111 + | ++ + + + + + | + USER_VERIFY_EYEPRINT_INTERNAL(0x00000040, "eyeprint_internal"), |
+ +112 + | ++ + + + + + | +|
+ +113 + | ++ + + + + + | + /** |
+ +114 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses a drawn pattern for user verification. |
+ +115 + | ++ + + + + + | + * |
+ +116 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +117 + | ++ + + + + + | + * |
+ +118 + | ++ + + + + + | + * @see <a |
+ +119 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +120 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +121 + | ++ + + + + + | + */ |
+ +122 + | ++ + + + + + | + USER_VERIFY_PATTERN_INTERNAL(0x00000080, "pattern_internal"), |
+ +123 + | ++ + + + + + | +|
+ +124 + | ++ + + + + + | + /** |
+ +125 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses any measurement of a full hand (including |
+ +126 + | ++ + + + + + | + * palm-print, hand geometry or vein geometry) for user verification. |
+ +127 + | ++ + + + + + | + * |
+ +128 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +129 + | ++ + + + + + | + * |
+ +130 + | ++ + + + + + | + * @see <a |
+ +131 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +132 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +133 + | ++ + + + + + | + */ |
+ +134 + | ++ + + + + + | + USER_VERIFY_HANDPRINT_INTERNAL(0x00000100, "handprint_internal"), |
+ +135 + | ++ + + + + + | +|
+ +136 + | ++ + + + + + | + /** |
+ +137 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses a local-only passcode (i.e. a passcode not |
+ +138 + | ++ + + + + + | + * known by the server) for user verification that might be gathered outside the authenticator |
+ +139 + | ++ + + + + + | + * boundary. |
+ +140 + | ++ + + + + + | + * |
+ +141 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +142 + | ++ + + + + + | + * |
+ +143 + | ++ + + + + + | + * @see <a |
+ +144 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +145 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +146 + | ++ + + + + + | + */ |
+ +147 + | ++ + + + + + | + USER_VERIFY_PASSCODE_EXTERNAL(0x00000800, "passcode_external"), |
+ +148 + | ++ + + + + + | +|
+ +149 + | ++ + + + + + | + /** |
+ +150 + | ++ + + + + + | + * This flag MUST be set if the authenticator uses a drawn pattern for user verification that |
+ +151 + | ++ + + + + + | + * might be gathered outside the authenticator boundary. |
+ +152 + | ++ + + + + + | + * |
+ +153 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +154 + | ++ + + + + + | + * |
+ +155 + | ++ + + + + + | + * @see <a |
+ +156 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +157 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +158 + | ++ + + + + + | + */ |
+ +159 + | ++ + + + + + | + USER_VERIFY_PATTERN_EXTERNAL(0x00001000, "pattern_external"), |
+ +160 + | ++ + + + + + | +|
+ +161 + | ++ + + + + + | + /** |
+ +162 + | ++ + + + + + | + * This flag MUST be set if the authenticator will respond without any user interaction (e.g. |
+ +163 + | ++ + + + + + | + * Silent Authenticator). |
+ +164 + | ++ + + + + + | + * |
+ +165 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +166 + | ++ + + + + + | + * |
+ +167 + | ++ + + + + + | + * @see <a |
+ +168 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +169 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +170 + | ++ + + + + + | + */ |
+ +171 + | ++ + + + + + | + USER_VERIFY_NONE(0x00000200, "none"), |
+ +172 + | ++ + + + + + | +|
+ +173 + | ++ + + + + + | + /** |
+ +174 + | ++ + + + + + | + * If an authenticator sets multiple flags for the "_INTERNAL" and/or "_EXTERNAL" user |
+ +175 + | ++ + + + + + | + * verification types, it MAY also set this flag to indicate that all verification methods with |
+ +176 + | ++ + + + + + | + * respective flags set will be enforced (e.g. faceprint AND voiceprint). If flags for multiple |
+ +177 + | ++ + + + + + | + * user verification methods are set and this flag is not set, verification with only one is |
+ +178 + | ++ + + + + + | + * necessary (e.g. fingerprint OR passcode). |
+ +179 + | ++ + + + + + | + * |
+ +180 + | ++ + + + + + | + * <p>NOTE: The above requirements apply to authenticators; this library DOES NOT enforce them. |
+ +181 + | ++ + + + + + | + * |
+ +182 + | ++ + + + + + | + * @see <a |
+ +183 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/common-specs/fido-registry-v2.1-ps-20191217.html#user-verification-methods">FIDO |
+ +184 + | ++ + + + + + | + * Registry of Predefined Values §3.1 User Verification Methods</a> |
+ +185 + | ++ + + + + + | + */ |
+ +186 + | ++ + + + + + | + USER_VERIFY_ALL(0x00000400, "all"); |
+ +187 + | ++ + + + + + | +|
+ +188 + | ++ + + + + + | + private final int value; |
+ +189 + | ++ + + + + + | +|
+ +190 + | ++ + + + + + | + @JsonValue private final String name; |
+ +191 + | ++ + + + + + | +|
+ +192 + | ++ + + + + + | + UserVerificationMethod(int value, String name) { |
+ +193 + | ++ + + + + + | + this.value = value; |
+ +194 + | ++ + + + + + | + this.name = name; |
+ +195 + | ++ + + + + + | + } |
+ +196 + | ++ + + + + + | +|
+ +197 + | ++ + + + + + | + /** |
+ +198 + | ++ + + + + + | + * @return If <code>value</code> matches any {@link UserVerificationMethod} constant, returns that |
+ +199 + | ++ + + + + + | + * constant instance. Otherwise throws {@link IllegalArgumentException}. |
+ +200 + | ++ + + + + + | + */ |
+ +201 + | ++ + + + + + | + public static UserVerificationMethod fromValue(int value) { |
+ +202 + | +
+
+1
+
+1. fromValue : replaced return value with null for com/yubico/webauthn/extension/uvm/UserVerificationMethod::fromValue → KILLED + + + + |
+ return Stream.of(values()) |
+ +203 + | +
+
+2
+
+1. lambda$fromValue$0 : negated conditional → KILLED +2. lambda$fromValue$0 : replaced boolean return with true for com/yubico/webauthn/extension/uvm/UserVerificationMethod::lambda$fromValue$0 → KILLED + + + + |
+ .filter(v -> v.value == value) |
+ +204 + | ++ + + + + + | + .findAny() |
+ +205 + | ++ + + + + + | + .orElseThrow( |
+ +206 + | ++ + + + + + | + () -> |
+ +207 + | +
+
+1
+
+1. lambda$fromValue$1 : replaced return value with null for com/yubico/webauthn/extension/uvm/UserVerificationMethod::lambda$fromValue$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +208 + | ++ + + + + + | + String.format( |
+ +209 + | ++ + + + + + | + "Unknown %s value: 0x%04x", UserVerificationMethod.class, value))); |
+ +210 + | ++ + + + + + | + } |
+ +211 + | ++ + + + + + | +|
+ +212 + | ++ + + + + + | + /** |
+ +213 + | ++ + + + + + | + * @return If <code>name</code> matches any {@link UserVerificationMethod} constant, returns that |
+ +214 + | ++ + + + + + | + * constant instance. Otherwise throws {@link IllegalArgumentException}. |
+ +215 + | ++ + + + + + | + */ |
+ +216 + | ++ + + + + + | + @JsonCreator |
+ +217 + | ++ + + + + + | + public static UserVerificationMethod fromName(String name) { |
+ +218 + | +
+
+1
+
+1. fromName : replaced return value with null for com/yubico/webauthn/extension/uvm/UserVerificationMethod::fromName → NO_COVERAGE + + + + |
+ return Stream.of(values()) |
+ +219 + | +
+
+2
+
+1. lambda$fromName$2 : replaced boolean return with true for com/yubico/webauthn/extension/uvm/UserVerificationMethod::lambda$fromName$2 → NO_COVERAGE +2. lambda$fromName$2 : replaced boolean return with false for com/yubico/webauthn/extension/uvm/UserVerificationMethod::lambda$fromName$2 → NO_COVERAGE + + + + |
+ .filter(v -> v.name.equals(name)) |
+ +220 + | ++ + + + + + | + .findAny() |
+ +221 + | ++ + + + + + | + .orElseThrow( |
+ +222 + | ++ + + + + + | + () -> |
+ +223 + | +
+
+1
+
+1. lambda$fromName$3 : replaced return value with null for com/yubico/webauthn/extension/uvm/UserVerificationMethod::lambda$fromName$3 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +224 + | ++ + + + + + | + String.format("Unknown %s name: %s", UserVerificationMethod.class, name))); |
+ +225 + | ++ + + + + + | + } |
+ +226 + | ++ + + + + + | +} |
Mutations | ||
202 | ++ |
+
+
+
+ 1.1 |
+
203 | ++ |
+
+
+
+ 1.1 2.2 |
+
207 | ++ |
+
+
+
+ 1.1 |
+
218 | ++ |
+
+
+
+ 1.1 |
+
219 | ++ |
+
+
+
+ 1.1 2.2 |
+
223 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
3 | +73% | +50% | +100% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
KeyProtectionType.java | +71% |
+ 50% |
+ 100% |
+
MatcherProtectionType.java | +68% |
+ 50% |
+ 100% |
+
UserVerificationMethod.java | +79% |
+ 50% |
+ 100% |
+
+ +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.meta; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonValue; |
+ +28 + | ++ + + + + + | +import java.util.Optional; |
+ +29 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +30 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +31 + | ++ + + + + + | +import lombok.NonNull; |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | +/** A representation of Web Authentication specification document statuses. */ |
+ +34 + | ++ + + + + + | +@AllArgsConstructor |
+ +35 + | ++ + + + + + | +public enum DocumentStatus { |
+ +36 + | ++ + + + + + | + /** An editor's draft is a changing work in progress. */ |
+ +37 + | ++ + + + + + | + EDITORS_DRAFT("editors-draft"), |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | + /** A working draft is a named snapshot of a particular state of an editor's draft. */ |
+ +40 + | ++ + + + + + | + WORKING_DRAFT("working-draft"), |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | + /** A candidate recommendation is a specification release candidate. */ |
+ +43 + | ++ + + + + + | + CANDIDATE_RECOMMENDATION("candidate-recommendation"), |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** A proposed recommendation is a finished draft intended for release. */ |
+ +46 + | ++ + + + + + | + PROPOSED_RECOMMENDATION("proposed-recommendation"), |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + /** A recommendation is a finished and released specification. */ |
+ +49 + | ++ + + + + + | + RECOMMENDATION("recommendation"); |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + @JsonValue private final String id; |
+ +52 + | ++ + + + + + | +|
+ +53 + | +
+
+1
+
+1. fromString : negated conditional → NO_COVERAGE + + + + |
+ static Optional<DocumentStatus> fromString(@NonNull String id) { |
+ +54 + | +
+
+3
+
+1. lambda$fromString$0 : replaced boolean return with false for com/yubico/webauthn/meta/DocumentStatus::lambda$fromString$0 → NO_COVERAGE +2. fromString : replaced return value with Optional.empty for com/yubico/webauthn/meta/DocumentStatus::fromString → NO_COVERAGE +3. lambda$fromString$0 : replaced boolean return with true for com/yubico/webauthn/meta/DocumentStatus::lambda$fromString$0 → NO_COVERAGE + + + + |
+ return Stream.of(values()).filter(v -> v.id.equals(id)).findAny(); |
+ +55 + | ++ + + + + + | + } |
+ +56 + | ++ + + + + + | +} |
Mutations | ||
53 | ++ |
+
+
+
+ 1.1 |
+
54 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
+ +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.meta; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import java.net.URL; |
+ +28 + | ++ + + + + + | +import java.time.LocalDate; |
+ +29 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +30 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +31 + | ++ + + + + + | +import lombok.Builder; |
+ +32 + | ++ + + + + + | +import lombok.Value; |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | +/** Reference to a particular version of a specification document. */ |
+ +35 + | ++ + + + + + | +@Value |
+ +36 + | ++ + + + + + | +@AllArgsConstructor(access = AccessLevel.PRIVATE) |
+ +37 + | ++ + + + + + | +@Builder |
+ +38 + | ++ + + + + + | +public class Specification { |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | + /** Address to this version of the specification. */ |
+ +41 + | ++ + + + + + | + private final URL url; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + /** Address to the latest version of this specification. */ |
+ +44 + | ++ + + + + + | + private final URL latestVersionUrl; |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** An object indicating the status of the specification document. */ |
+ +47 + | ++ + + + + + | + private final DocumentStatus status; |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** The release date of the specification document. */ |
+ +50 + | ++ + + + + + | + private final LocalDate releaseDate; |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + static SpecificationBuilder builder() { |
+ +53 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/meta/Specification::builder → NO_COVERAGE + + + + |
+ return new SpecificationBuilder(); |
+ +54 + | ++ + + + + + | + } |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + static class SpecificationBuilder {} |
+ +57 + | ++ + + + + + | +} |
Mutations | ||
53 | ++ |
+
+
+
+ 1.1 |
+
+ +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.meta; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +28 + | ++ + + + + + | +import java.io.IOException; |
+ +29 + | ++ + + + + + | +import java.net.URL; |
+ +30 + | ++ + + + + + | +import java.time.LocalDate; |
+ +31 + | ++ + + + + + | +import java.util.Enumeration; |
+ +32 + | ++ + + + + + | +import java.util.NoSuchElementException; |
+ +33 + | ++ + + + + + | +import java.util.jar.Manifest; |
+ +34 + | ++ + + + + + | +import lombok.Value; |
+ +35 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | +/** |
+ +38 + | ++ + + + + + | + * Contains version information for the com.yubico.webauthn package. |
+ +39 + | ++ + + + + + | + * |
+ +40 + | ++ + + + + + | + * @see Specification |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | +@Slf4j |
+ +43 + | ++ + + + + + | +@Value |
+ +44 + | ++ + + + + + | +public class VersionInfo { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + private static VersionInfo instance; |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + public static VersionInfo getInstance() { |
+ +49 + | +
+
+1
+
+1. getInstance : negated conditional → NO_COVERAGE + + + + |
+ if (instance == null) { |
+ +50 + | ++ + + + + + | + try { |
+ +51 + | ++ + + + + + | + instance = new VersionInfo(); |
+ +52 + | ++ + + + + + | + } catch (IOException e) { |
+ +53 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog(log, "Failed to create VersionInfo", e); |
+ +54 + | ++ + + + + + | + } |
+ +55 + | ++ + + + + + | + } |
+ +56 + | ++ + + + + + | +|
+ +57 + | +
+
+1
+
+1. getInstance : replaced return value with null for com/yubico/webauthn/meta/VersionInfo::getInstance → NO_COVERAGE + + + + |
+ return instance; |
+ +58 + | ++ + + + + + | + } |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + /** Represents the specification this implementation is based on */ |
+ +61 + | ++ + + + + + | + private final Specification specification = |
+ +62 + | ++ + + + + + | + Specification.builder() |
+ +63 + | ++ + + + + + | + .url(new URL(findValueInManifest("Specification-Url"))) |
+ +64 + | ++ + + + + + | + .latestVersionUrl(new URL(findValueInManifest("Specification-Url-Latest"))) |
+ +65 + | ++ + + + + + | + .status(DocumentStatus.fromString(findValueInManifest("Specification-W3c-Status")).get()) |
+ +66 + | ++ + + + + + | + .releaseDate(LocalDate.parse(findValueInManifest("Specification-Release-Date"))) |
+ +67 + | ++ + + + + + | + .build(); |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + /** Description of this version of this library */ |
+ +70 + | ++ + + + + + | + private final Implementation implementation = |
+ +71 + | ++ + + + + + | + new Implementation( |
+ +72 + | ++ + + + + + | + findValueInManifest("Implementation-Version"), |
+ +73 + | ++ + + + + + | + new URL(findValueInManifest("Implementation-Source-Url")), |
+ +74 + | ++ + + + + + | + findValueInManifest("Git-Commit")); |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + private VersionInfo() throws IOException {} |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + private String findValueInManifest(String key) throws IOException { |
+ +79 + | ++ + + + + + | + final Enumeration<URL> resources = |
+ +80 + | ++ + + + + + | + getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); |
+ +81 + | ++ + + + + + | +|
+ +82 + | +
+
+1
+
+1. findValueInManifest : negated conditional → NO_COVERAGE + + + + |
+ while (resources.hasMoreElements()) { |
+ +83 + | ++ + + + + + | + final URL resource = resources.nextElement(); |
+ +84 + | ++ + + + + + | + final Manifest manifest = new Manifest(resource.openStream()); |
+ +85 + | ++ + + + + + | + if ("java-webauthn-server" |
+ +86 + | +
+
+1
+
+1. findValueInManifest : negated conditional → NO_COVERAGE + + + + |
+ .equals(manifest.getMainAttributes().getValue("Implementation-Id"))) { |
+ +87 + | +
+
+1
+
+1. findValueInManifest : replaced return value with "" for com/yubico/webauthn/meta/VersionInfo::findValueInManifest → NO_COVERAGE + + + + |
+ return manifest.getMainAttributes().getValue(key); |
+ +88 + | ++ + + + + + | + } |
+ +89 + | ++ + + + + + | + } |
+ +90 + | ++ + + + + + | + throw new NoSuchElementException("Could not find \"" + key + "\" in manifest."); |
+ +91 + | ++ + + + + + | + } |
+ +92 + | ++ + + + + + | +} |
Mutations | ||
49 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 |
+
82 | ++ |
+
+
+
+ 1.1 |
+
86 | ++ |
+
+
+
+ 1.1 |
+
87 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
3 | +0% | +0% | +100% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
DocumentStatus.java | +0% |
+ 0% |
+ 100% |
+
Specification.java | +0% |
+ 0% |
+ 100% |
+
VersionInfo.java | +0% |
+ 0% |
+ 100% |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonNode; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.databind.ObjectMapper; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ArrayNode; |
+ +6 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.JsonNodeFactory; |
+ +7 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +8 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +9 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +10 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +11 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +12 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +13 + | ++ + + + + + | +import com.yubico.webauthn.data.exception.Base64UrlException; |
+ +14 + | ++ + + + + + | +import java.io.IOException; |
+ +15 + | ++ + + + + + | +import java.nio.charset.StandardCharsets; |
+ +16 + | ++ + + + + + | +import java.security.InvalidKeyException; |
+ +17 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +18 + | ++ + + + + + | +import java.security.Signature; |
+ +19 + | ++ + + + + + | +import java.security.SignatureException; |
+ +20 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +21 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +22 + | ++ + + + + + | +import java.util.ArrayList; |
+ +23 + | ++ + + + + + | +import java.util.List; |
+ +24 + | ++ + + + + + | +import javax.net.ssl.SSLException; |
+ +25 + | ++ + + + + + | +import lombok.Value; |
+ +26 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +27 + | ++ + + + + + | +import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier; |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | +@Slf4j |
+ +30 + | ++ + + + + + | +class AndroidSafetynetAttestationStatementVerifier |
+ +31 + | ++ + + + + + | + implements AttestationStatementVerifier, X5cAttestationStatementVerifier { |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | + private static final DefaultHostnameVerifier HOSTNAME_VERIFIER = new DefaultHostnameVerifier(); |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + @Override |
+ +36 + | ++ + + + + + | + public AttestationType getAttestationType(AttestationObject attestation) { |
+ +37 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.BASIC; |
+ +38 + | ++ + + + + + | + } |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | + @Override |
+ +41 + | ++ + + + + + | + public JsonNode getX5cArray(AttestationObject attestationObject) { |
+ +42 + | ++ + + + + + | + JsonNodeFactory jsonFactory = JsonNodeFactory.instance; |
+ +43 + | ++ + + + + + | + ArrayNode array = jsonFactory.arrayNode(); |
+ +44 + | ++ + + + + + | + for (JsonNode cert : parseJws(attestationObject).getHeader().get("x5c")) { |
+ +45 + | ++ + + + + + | + array.add(jsonFactory.binaryNode(ByteArray.fromBase64(cert.textValue()).getBytes())); |
+ +46 + | ++ + + + + + | + } |
+ +47 + | +
+
+1
+
+1. getX5cArray : replaced return value with null for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::getX5cArray → KILLED + + + + |
+ return array; |
+ +48 + | ++ + + + + + | + } |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + @Override |
+ +51 + | ++ + + + + + | + public boolean verifyAttestationSignature( |
+ +52 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +53 + | ++ + + + + + | + final JsonNode ver = attestationObject.getAttestationStatement().get("ver"); |
+ +54 + | ++ + + + + + | +|
+ +55 + | +
+
+2
+
+1. verifyAttestationSignature : negated conditional → KILLED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ if (ver == null || !ver.isTextual()) { |
+ +56 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +57 + | ++ + + + + + | + "Property \"ver\" of android-safetynet attestation statement must be a string, was: " |
+ +58 + | ++ + + + + + | + + ver); |
+ +59 + | ++ + + + + + | + } |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + JsonWebSignatureCustom jws = parseJws(attestationObject); |
+ +62 + | ++ + + + + + | +|
+ +63 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ if (!verifySignature(jws)) { |
+ +64 + | +
+
+1
+
+1. verifyAttestationSignature : replaced boolean return with true for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return false; |
+ +65 + | ++ + + + + + | + } |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + JsonNode payload = jws.getPayload(); |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + ByteArray signedData = |
+ +70 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getBytes().concat(clientDataJsonHash); |
+ +71 + | ++ + + + + + | + ByteArray hashSignedData = Crypto.sha256(signedData); |
+ +72 + | ++ + + + + + | + ByteArray nonceByteArray = ByteArray.fromBase64(payload.get("nonce").textValue()); |
+ +73 + | +
+
+1
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +74 + | ++ + + + + + | + hashSignedData.equals(nonceByteArray), |
+ +75 + | ++ + + + + + | + "Nonce does not equal authenticator data + client data. Expected nonce: %s, was nonce: %s", |
+ +76 + | ++ + + + + + | + hashSignedData.getBase64Url(), |
+ +77 + | ++ + + + + + | + nonceByteArray.getBase64Url()); |
+ +78 + | ++ + + + + + | +|
+ +79 + | +
+
+1
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +80 + | ++ + + + + + | + payload.get("ctsProfileMatch").booleanValue(), |
+ +81 + | ++ + + + + + | + "Expected ctsProfileMatch to be true, was: %s", |
+ +82 + | ++ + + + + + | + payload.get("ctsProfileMatch")); |
+ +83 + | ++ + + + + + | +|
+ +84 + | +
+
+1
+
+1. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return true; |
+ +85 + | ++ + + + + + | + } |
+ +86 + | ++ + + + + + | +|
+ +87 + | ++ + + + + + | + private static JsonWebSignatureCustom parseJws(AttestationObject attestationObject) { |
+ +88 + | +
+
+1
+
+1. parseJws : replaced return value with null for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::parseJws → KILLED + + + + |
+ return new JsonWebSignatureCustom( |
+ +89 + | ++ + + + + + | + new String(getResponseBytes(attestationObject).getBytes(), StandardCharsets.UTF_8)); |
+ +90 + | ++ + + + + + | + } |
+ +91 + | ++ + + + + + | +|
+ +92 + | ++ + + + + + | + private static ByteArray getResponseBytes(AttestationObject attestationObject) { |
+ +93 + | ++ + + + + + | + final JsonNode response = attestationObject.getAttestationStatement().get("response"); |
+ +94 + | +
+
+2
+
+1. getResponseBytes : negated conditional → KILLED +2. getResponseBytes : negated conditional → KILLED + + + + |
+ if (response == null || !response.isBinary()) { |
+ +95 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +96 + | ++ + + + + + | + "Property \"response\" of android-safetynet attestation statement must be a binary value, was: " |
+ +97 + | ++ + + + + + | + + response); |
+ +98 + | ++ + + + + + | + } |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + try { |
+ +101 + | +
+
+1
+
+1. getResponseBytes : replaced return value with null for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::getResponseBytes → KILLED + + + + |
+ return new ByteArray(response.binaryValue()); |
+ +102 + | ++ + + + + + | + } catch (IOException ioe) { |
+ +103 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +104 + | ++ + + + + + | + log, "response.isBinary() was true but response.binaryValue failed: " + response, ioe); |
+ +105 + | ++ + + + + + | + } |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + private boolean verifySignature(JsonWebSignatureCustom jws) { |
+ +109 + | ++ + + + + + | + // Verify the signature of the JWS and retrieve the signature certificate. |
+ +110 + | ++ + + + + + | + X509Certificate attestationCertificate = jws.getX5c().get(0); |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + String signatureAlgorithmName = |
+ +113 + | ++ + + + + + | + WebAuthnCodecs.jwsAlgorithmNameToJavaAlgorithmName(jws.getAlgorithm()); |
+ +114 + | ++ + + + + + | +|
+ +115 + | ++ + + + + + | + Signature signatureVerifier; |
+ +116 + | ++ + + + + + | + try { |
+ +117 + | ++ + + + + + | + signatureVerifier = Signature.getInstance(signatureAlgorithmName); |
+ +118 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +119 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +120 + | ++ + + + + + | + log, "Failed to get a Signature instance for " + signatureAlgorithmName, e); |
+ +121 + | ++ + + + + + | + } |
+ +122 + | ++ + + + + + | + try { |
+ +123 + | +
+
+1
+
+1. verifySignature : removed call to java/security/Signature::initVerify → KILLED + + + + |
+ signatureVerifier.initVerify(attestationCertificate.getPublicKey()); |
+ +124 + | ++ + + + + + | + } catch (InvalidKeyException e) { |
+ +125 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +126 + | ++ + + + + + | + log, "Attestation key is invalid: " + attestationCertificate, e); |
+ +127 + | ++ + + + + + | + } |
+ +128 + | ++ + + + + + | + try { |
+ +129 + | +
+
+1
+
+1. verifySignature : removed call to java/security/Signature::update → KILLED + + + + |
+ signatureVerifier.update(jws.getSignedBytes().getBytes()); |
+ +130 + | ++ + + + + + | + } catch (SignatureException e) { |
+ +131 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +132 + | ++ + + + + + | + log, "Signature object in invalid state: " + signatureVerifier, e); |
+ +133 + | ++ + + + + + | + } |
+ +134 + | ++ + + + + + | +|
+ +135 + | ++ + + + + + | + // Verify the hostname of the certificate. |
+ +136 + | +
+
+1
+
+1. verifySignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +137 + | ++ + + + + + | + verifyHostname(attestationCertificate), |
+ +138 + | ++ + + + + + | + "Certificate isn't issued for the hostname attest.android.com: %s", |
+ +139 + | ++ + + + + + | + attestationCertificate); |
+ +140 + | ++ + + + + + | +|
+ +141 + | ++ + + + + + | + try { |
+ +142 + | +
+
+2
+
+1. verifySignature : replaced boolean return with true for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::verifySignature → KILLED +2. verifySignature : replaced boolean return with false for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::verifySignature → KILLED + + + + |
+ return signatureVerifier.verify(jws.getSignature().getBytes()); |
+ +143 + | ++ + + + + + | + } catch (SignatureException e) { |
+ +144 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog(log, "Failed to verify signature of JWS: " + jws, e); |
+ +145 + | ++ + + + + + | + } |
+ +146 + | ++ + + + + + | + } |
+ +147 + | ++ + + + + + | +|
+ +148 + | ++ + + + + + | + @Value |
+ +149 + | ++ + + + + + | + private static class JsonWebSignatureCustom { |
+ +150 + | ++ + + + + + | + public final JsonNode header; |
+ +151 + | ++ + + + + + | + public final JsonNode payload; |
+ +152 + | ++ + + + + + | + public final ByteArray signedBytes; |
+ +153 + | ++ + + + + + | + public final ByteArray signature; |
+ +154 + | ++ + + + + + | + public final List<X509Certificate> x5c; |
+ +155 + | ++ + + + + + | + public final String algorithm; |
+ +156 + | ++ + + + + + | +|
+ +157 + | ++ + + + + + | + JsonWebSignatureCustom(String jwsCompact) { |
+ +158 + | ++ + + + + + | + String[] parts = jwsCompact.split("\\."); |
+ +159 + | ++ + + + + + | + ObjectMapper json = JacksonCodecs.json(); |
+ +160 + | ++ + + + + + | +|
+ +161 + | ++ + + + + + | + try { |
+ +162 + | ++ + + + + + | + final ByteArray header = ByteArray.fromBase64Url(parts[0]); |
+ +163 + | ++ + + + + + | + final ByteArray payload = ByteArray.fromBase64Url(parts[1]); |
+ +164 + | ++ + + + + + | +|
+ +165 + | ++ + + + + + | + this.header = json.readTree(header.getBytes()); |
+ +166 + | ++ + + + + + | + this.payload = json.readTree(payload.getBytes()); |
+ +167 + | ++ + + + + + | + this.signedBytes = |
+ +168 + | ++ + + + + + | + new ByteArray((parts[0] + "." + parts[1]).getBytes(StandardCharsets.UTF_8)); |
+ +169 + | ++ + + + + + | + this.signature = ByteArray.fromBase64Url(parts[2]); |
+ +170 + | ++ + + + + + | + this.x5c = getX5c(this.header); |
+ +171 + | ++ + + + + + | + this.algorithm = this.header.get("alg").textValue(); |
+ +172 + | ++ + + + + + | + } catch (IOException | Base64UrlException e) { |
+ +173 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog(log, "Failed to parse JWS: " + jwsCompact, e); |
+ +174 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +175 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +176 + | ++ + + + + + | + log, "Failed to parse attestation certificates in JWS header: " + jwsCompact, e); |
+ +177 + | ++ + + + + + | + } |
+ +178 + | ++ + + + + + | + } |
+ +179 + | ++ + + + + + | +|
+ +180 + | ++ + + + + + | + private static List<X509Certificate> getX5c(JsonNode header) |
+ +181 + | ++ + + + + + | + throws IOException, CertificateException { |
+ +182 + | ++ + + + + + | + List<X509Certificate> result = new ArrayList<>(); |
+ +183 + | ++ + + + + + | + for (JsonNode jsonNode : header.get("x5c")) { |
+ +184 + | ++ + + + + + | + result.add(CertificateParser.parseDer(jsonNode.binaryValue())); |
+ +185 + | ++ + + + + + | + } |
+ +186 + | +
+
+1
+
+1. getX5c : replaced return value with Collections.emptyList for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier$JsonWebSignatureCustom::getX5c → KILLED + + + + |
+ return result; |
+ +187 + | ++ + + + + + | + } |
+ +188 + | ++ + + + + + | + } |
+ +189 + | ++ + + + + + | +|
+ +190 + | ++ + + + + + | + /** Verifies that the certificate matches the hostname "attest.android.com". */ |
+ +191 + | ++ + + + + + | + private static boolean verifyHostname(X509Certificate leafCert) { |
+ +192 + | ++ + + + + + | + try { |
+ +193 + | +
+
+1
+
+1. verifyHostname : removed call to org/apache/hc/client5/http/ssl/DefaultHostnameVerifier::verify → KILLED + + + + |
+ HOSTNAME_VERIFIER.verify("attest.android.com", leafCert); |
+ +194 + | +
+
+1
+
+1. verifyHostname : replaced boolean return with false for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::verifyHostname → KILLED + + + + |
+ return true; |
+ +195 + | ++ + + + + + | + } catch (SSLException e) { |
+ +196 + | +
+
+1
+
+1. verifyHostname : replaced boolean return with true for com/yubico/webauthn/AndroidSafetynetAttestationStatementVerifier::verifyHostname → KILLED + + + + |
+ return false; |
+ +197 + | ++ + + + + + | + } |
+ +198 + | ++ + + + + + | + } |
+ +199 + | ++ + + + + + | +} |
Mutations | ||
37 | ++ |
+
+
+
+ 1.1 |
+
47 | ++ |
+
+
+
+ 1.1 |
+
55 | ++ |
+
+
+
+ 1.1 2.2 |
+
63 | ++ |
+
+
+
+ 1.1 |
+
64 | ++ |
+
+
+
+ 1.1 |
+
73 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
84 | ++ |
+
+
+
+ 1.1 |
+
88 | ++ |
+
+
+
+ 1.1 |
+
94 | ++ |
+
+
+
+ 1.1 2.2 |
+
101 | ++ |
+
+
+
+ 1.1 |
+
123 | ++ |
+
+
+
+ 1.1 |
+
129 | ++ |
+
+
+
+ 1.1 |
+
136 | ++ |
+
+
+
+ 1.1 |
+
142 | ++ |
+
+
+
+ 1.1 2.2 |
+
186 | ++ |
+
+
+
+ 1.1 |
+
193 | ++ |
+
+
+
+ 1.1 |
+
194 | ++ |
+
+
+
+ 1.1 |
+
196 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.internal.util.ExceptionUtil; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +31 + | ++ + + + + + | +import java.security.PublicKey; |
+ +32 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +33 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +34 + | ++ + + + + + | +import java.util.Optional; |
+ +35 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | +@Slf4j |
+ +38 + | ++ + + + + + | +final class AppleAttestationStatementVerifier |
+ +39 + | ++ + + + + + | + implements AttestationStatementVerifier, X5cAttestationStatementVerifier { |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | + private static final String NONCE_EXTENSION_OID = "1.2.840.113635.100.8.2"; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + @Override |
+ +44 + | ++ + + + + + | + public AttestationType getAttestationType(AttestationObject attestation) { |
+ +45 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/AppleAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.ANONYMIZATION_CA; |
+ +46 + | ++ + + + + + | + } |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + @Override |
+ +49 + | ++ + + + + + | + public boolean verifyAttestationSignature( |
+ +50 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +51 + | ++ + + + + + | + final Optional<X509Certificate> attestationCert; |
+ +52 + | ++ + + + + + | + try { |
+ +53 + | ++ + + + + + | + attestationCert = getX5cAttestationCertificate(attestationObject); |
+ +54 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +55 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +56 + | ++ + + + + + | + log, |
+ +57 + | ++ + + + + + | + String.format( |
+ +58 + | ++ + + + + + | + "Failed to parse X.509 certificate from attestation object: %s", attestationObject), |
+ +59 + | ++ + + + + + | + e); |
+ +60 + | ++ + + + + + | + } |
+ +61 + | ++ + + + + + | +|
+ +62 + | +
+
+2
+
+1. verifyAttestationSignature : replaced boolean return with true for com/yubico/webauthn/AppleAttestationStatementVerifier::verifyAttestationSignature → SURVIVED +2. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/AppleAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return attestationCert |
+ +63 + | ++ + + + + + | + .map( |
+ +64 + | ++ + + + + + | + attestationCertificate -> { |
+ +65 + | ++ + + + + + | + final ByteArray nonceToHash = |
+ +66 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getBytes().concat(clientDataJsonHash); |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + final ByteArray nonce = Crypto.sha256(nonceToHash); |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + byte[] nonceExtension = attestationCertificate.getExtensionValue(NONCE_EXTENSION_OID); |
+ +71 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$0 : negated conditional → KILLED + + + + |
+ if (nonceExtension == null) { |
+ +72 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +73 + | ++ + + + + + | + "Apple anonymous attestation certificate must contain extension OID: " |
+ +74 + | ++ + + + + + | + + NONCE_EXTENSION_OID); |
+ +75 + | ++ + + + + + | + } |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + // X.509 extension values is a DER octet string: 0x0426 |
+ +78 + | ++ + + + + + | + // Then the extension contains a 1-element sequence: 0x3024 |
+ +79 + | ++ + + + + + | + // The element has context-specific tag "[1]": 0xa122 |
+ +80 + | ++ + + + + + | + // Then the sequence contains a 32-byte octet string: 0x0420 |
+ +81 + | ++ + + + + + | + final ByteArray expectedExtensionValue = |
+ +82 + | ++ + + + + + | + new ByteArray( |
+ +83 + | ++ + + + + + | + new byte[] { |
+ +84 + | ++ + + + + + | + 0x04, 0x26, 0x30, 0x24, (-128) + (0xa1 - 128), 0x22, 0x04, 0x20 |
+ +85 + | ++ + + + + + | + }) |
+ +86 + | ++ + + + + + | + .concat(nonce); |
+ +87 + | ++ + + + + + | +|
+ +88 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$0 : negated conditional → KILLED + + + + |
+ if (!expectedExtensionValue.equals(new ByteArray(nonceExtension))) { |
+ +89 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +90 + | ++ + + + + + | + String.format( |
+ +91 + | ++ + + + + + | + "Apple anonymous attestation certificate extension %s must equal nonceToHash. Expected: %s, was: %s", |
+ +92 + | ++ + + + + + | + NONCE_EXTENSION_OID, |
+ +93 + | ++ + + + + + | + expectedExtensionValue, |
+ +94 + | ++ + + + + + | + new ByteArray(nonceExtension))); |
+ +95 + | ++ + + + + + | + } |
+ +96 + | ++ + + + + + | +|
+ +97 + | ++ + + + + + | + final PublicKey credentialPublicKey; |
+ +98 + | ++ + + + + + | + try { |
+ +99 + | ++ + + + + + | + credentialPublicKey = |
+ +100 + | ++ + + + + + | + WebAuthnCodecs.importCosePublicKey( |
+ +101 + | ++ + + + + + | + attestationObject |
+ +102 + | ++ + + + + + | + .getAuthenticatorData() |
+ +103 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +104 + | ++ + + + + + | + .get() |
+ +105 + | ++ + + + + + | + .getCredentialPublicKey()); |
+ +106 + | ++ + + + + + | + } catch (Exception e) { |
+ +107 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog(log, "Failed to import credential public key", e); |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + final PublicKey certPublicKey = attestationCertificate.getPublicKey(); |
+ +111 + | ++ + + + + + | +|
+ +112 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$0 : negated conditional → KILLED + + + + |
+ if (!credentialPublicKey.equals(certPublicKey)) { |
+ +113 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +114 + | ++ + + + + + | + String.format( |
+ +115 + | ++ + + + + + | + "Apple anonymous attestation certificate subject public key must equal credential public key. Expected: %s, was: %s", |
+ +116 + | ++ + + + + + | + credentialPublicKey, certPublicKey)); |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +|
+ +119 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$0 : replaced Boolean return with False for com/yubico/webauthn/AppleAttestationStatementVerifier::lambda$verifyAttestationSignature$0 → KILLED + + + + |
+ return true; |
+ +120 + | ++ + + + + + | + }) |
+ +121 + | ++ + + + + + | + .orElseThrow( |
+ +122 + | ++ + + + + + | + () -> |
+ +123 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$1 : replaced return value with null for com/yubico/webauthn/AppleAttestationStatementVerifier::lambda$verifyAttestationSignature$1 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +124 + | ++ + + + + + | + "Failed to parse attestation certificate from \"apple\" attestation statement.")); |
+ +125 + | ++ + + + + + | + } |
+ +126 + | ++ + + + + + | +} |
Mutations | ||
45 | ++ |
+
+
+
+ 1.1 |
+
62 | ++ |
+
+
+
+ 1.1 2.2 |
+
71 | ++ |
+
+
+
+ 1.1 |
+
88 | ++ |
+
+
+
+ 1.1 |
+
112 | ++ |
+
+
+
+ 1.1 |
+
119 | ++ |
+
+
+
+ 1.1 |
+
123 | ++ |
+
+
+
+ 1.1 |
+
+ +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.JsonProperty; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.core.JsonProcessingException; |
+ +30 + | ++ + + + + + | +import com.yubico.internal.util.JacksonCodecs; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; |
+ +33 + | ++ + + + + + | +import java.util.Optional; |
+ +34 + | ++ + + + + + | +import lombok.Builder; |
+ +35 + | ++ + + + + + | +import lombok.NonNull; |
+ +36 + | ++ + + + + + | +import lombok.Value; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | +/** |
+ +39 + | ++ + + + + + | + * A combination of a {@link PublicKeyCredentialRequestOptions} and, optionally, a {@link |
+ +40 + | ++ + + + + + | + * #getUsername() username} or {@link #getUserHandle() user handle}. |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | +@Value |
+ +43 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +44 + | ++ + + + + + | +public class AssertionRequest { |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** |
+ +47 + | ++ + + + + + | + * An object that can be serialized to JSON and passed as the <code>publicKey</code> argument to |
+ +48 + | ++ + + + + + | + * <code>navigator.credentials.get()</code>. |
+ +49 + | ++ + + + + + | + */ |
+ +50 + | ++ + + + + + | + @NonNull private final PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions; |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +54 + | ++ + + + + + | + * |
+ +55 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #getUserHandle() userHandle}; setting this will unset |
+ +56 + | ++ + + + + + | + * {@link #getUserHandle() userHandle}. When parsing from JSON, {@link #getUserHandle() |
+ +57 + | ++ + + + + + | + * userHandle} takes precedence over this. |
+ +58 + | ++ + + + + + | + * |
+ +59 + | ++ + + + + + | + * <p>If both this and {@link #getUserHandle() userHandle} are empty, this indicates that this is |
+ +60 + | ++ + + + + + | + * a request for an assertion by a <a |
+ +61 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +62 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +63 + | ++ + + + + + | + * is received. |
+ +64 + | ++ + + + + + | + * |
+ +65 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +66 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + private final String username; |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +72 + | ++ + + + + + | + * |
+ +73 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #getUsername() username}; setting this will unset |
+ +74 + | ++ + + + + + | + * {@link #getUsername() username}. When parsing from JSON, this takes precedence over {@link |
+ +75 + | ++ + + + + + | + * #getUsername() username}. |
+ +76 + | ++ + + + + + | + * |
+ +77 + | ++ + + + + + | + * <p>If both this and {@link #getUsername() username} are empty, this indicates that this is a |
+ +78 + | ++ + + + + + | + * request for an assertion by a <a |
+ +79 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +80 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +81 + | ++ + + + + + | + * is received. |
+ +82 + | ++ + + + + + | + * |
+ +83 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +84 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +85 + | ++ + + + + + | + */ |
+ +86 + | ++ + + + + + | + private final ByteArray userHandle; |
+ +87 + | ++ + + + + + | +|
+ +88 + | ++ + + + + + | + @JsonCreator |
+ +89 + | ++ + + + + + | + private AssertionRequest( |
+ +90 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("publicKeyCredentialRequestOptions") |
+ +91 + | ++ + + + + + | + PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions, |
+ +92 + | ++ + + + + + | + @JsonProperty("username") String username, |
+ +93 + | ++ + + + + + | + @JsonProperty("userHandle") ByteArray userHandle) { |
+ +94 + | ++ + + + + + | + this.publicKeyCredentialRequestOptions = publicKeyCredentialRequestOptions; |
+ +95 + | ++ + + + + + | +|
+ +96 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ if (userHandle != null) { |
+ +97 + | ++ + + + + + | + this.username = null; |
+ +98 + | ++ + + + + + | + this.userHandle = userHandle; |
+ +99 + | ++ + + + + + | + } else { |
+ +100 + | ++ + + + + + | + this.username = username; |
+ +101 + | ++ + + + + + | + this.userHandle = null; |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | + } |
+ +104 + | ++ + + + + + | +|
+ +105 + | ++ + + + + + | + /** |
+ +106 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +107 + | ++ + + + + + | + * |
+ +108 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #getUserHandle()}; if this is present, then {@link |
+ +109 + | ++ + + + + + | + * #getUserHandle()} will be empty. |
+ +110 + | ++ + + + + + | + * |
+ +111 + | ++ + + + + + | + * <p>If both this and {@link #getUserHandle()} are empty, this indicates that this is a request |
+ +112 + | ++ + + + + + | + * for an assertion by a <a |
+ +113 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +114 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +115 + | ++ + + + + + | + * is received. |
+ +116 + | ++ + + + + + | + * |
+ +117 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +118 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +119 + | ++ + + + + + | + */ |
+ +120 + | ++ + + + + + | + public Optional<String> getUsername() { |
+ +121 + | +
+
+1
+
+1. getUsername : replaced return value with Optional.empty for com/yubico/webauthn/AssertionRequest::getUsername → KILLED + + + + |
+ return Optional.ofNullable(username); |
+ +122 + | ++ + + + + + | + } |
+ +123 + | ++ + + + + + | +|
+ +124 + | ++ + + + + + | + /** |
+ +125 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +126 + | ++ + + + + + | + * |
+ +127 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #getUsername()}; if this is present, then {@link |
+ +128 + | ++ + + + + + | + * #getUsername()} will be empty. |
+ +129 + | ++ + + + + + | + * |
+ +130 + | ++ + + + + + | + * <p>If both this and {@link #getUsername()} are empty, this indicates that this is a request for |
+ +131 + | ++ + + + + + | + * an assertion by a <a |
+ +132 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +133 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +134 + | ++ + + + + + | + * is received. |
+ +135 + | ++ + + + + + | + * |
+ +136 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +137 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +138 + | ++ + + + + + | + */ |
+ +139 + | ++ + + + + + | + public Optional<ByteArray> getUserHandle() { |
+ +140 + | +
+
+1
+
+1. getUserHandle : replaced return value with Optional.empty for com/yubico/webauthn/AssertionRequest::getUserHandle → KILLED + + + + |
+ return Optional.ofNullable(userHandle); |
+ +141 + | ++ + + + + + | + } |
+ +142 + | ++ + + + + + | +|
+ +143 + | ++ + + + + + | + /** |
+ +144 + | ++ + + + + + | + * Serialize this {@link AssertionRequest} value to JSON suitable for sending to the client. |
+ +145 + | ++ + + + + + | + * |
+ +146 + | ++ + + + + + | + * <p>This is an alias of <code>getPublicKeyCredentialRequestOptions().toCredentialsGetJson() |
+ +147 + | ++ + + + + + | + * </code>. |
+ +148 + | ++ + + + + + | + * |
+ +149 + | ++ + + + + + | + * <p>Any {@link ByteArray} values in this data structure will be {@link ByteArray#getBase64Url() |
+ +150 + | ++ + + + + + | + * Base64Url} encoded. Those values MUST be decoded into <code>BufferSource</code> values (such as |
+ +151 + | ++ + + + + + | + * <code>Uint8Array</code>) on the client side before calling <code>navigator.credentials.get() |
+ +152 + | ++ + + + + + | + * </code>. |
+ +153 + | ++ + + + + + | + * |
+ +154 + | ++ + + + + + | + * <p>After decoding binary values, the resulting JavaScript object is suitable for passing as an |
+ +155 + | ++ + + + + + | + * argument to <code>navigator.credentials.get()</code>. |
+ +156 + | ++ + + + + + | + * |
+ +157 + | ++ + + + + + | + * @return a JSON value suitable for sending to the client and passing as an argument to <code> |
+ +158 + | ++ + + + + + | + * navigator.credentials.get()</code>, after decoding binary options from Base64Url strings. |
+ +159 + | ++ + + + + + | + * @throws JsonProcessingException if JSON serialization fails. |
+ +160 + | ++ + + + + + | + */ |
+ +161 + | ++ + + + + + | + public String toCredentialsGetJson() throws JsonProcessingException { |
+ +162 + | +
+
+1
+
+1. toCredentialsGetJson : replaced return value with "" for com/yubico/webauthn/AssertionRequest::toCredentialsGetJson → KILLED + + + + |
+ return publicKeyCredentialRequestOptions.toCredentialsGetJson(); |
+ +163 + | ++ + + + + + | + } |
+ +164 + | ++ + + + + + | +|
+ +165 + | ++ + + + + + | + /** |
+ +166 + | ++ + + + + + | + * Encode this {@link AssertionRequest} to JSON. The inverse of {@link #fromJson(String)}. |
+ +167 + | ++ + + + + + | + * |
+ +168 + | ++ + + + + + | + * <p>This method is suitable for encoding the {@link AssertionRequest} for temporary storage so |
+ +169 + | ++ + + + + + | + * that it can later be passed as an argument to {@link |
+ +170 + | ++ + + + + + | + * RelyingParty#finishAssertion(FinishAssertionOptions)}. The {@link #fromJson(String)} factory |
+ +171 + | ++ + + + + + | + * function is guaranteed to restore an identical {@link AssertionRequest} instance. |
+ +172 + | ++ + + + + + | + * |
+ +173 + | ++ + + + + + | + * <p>Note that encoding might not be needed if you can simply keep the {@link AssertionRequest} |
+ +174 + | ++ + + + + + | + * instance in server memory. |
+ +175 + | ++ + + + + + | + * |
+ +176 + | ++ + + + + + | + * @return this {@link AssertionRequest} encoded to JSON. |
+ +177 + | ++ + + + + + | + * @throws JsonProcessingException |
+ +178 + | ++ + + + + + | + */ |
+ +179 + | ++ + + + + + | + public String toJson() throws JsonProcessingException { |
+ +180 + | +
+
+1
+
+1. toJson : replaced return value with "" for com/yubico/webauthn/AssertionRequest::toJson → KILLED + + + + |
+ return JacksonCodecs.json().writeValueAsString(this); |
+ +181 + | ++ + + + + + | + } |
+ +182 + | ++ + + + + + | +|
+ +183 + | ++ + + + + + | + /** |
+ +184 + | ++ + + + + + | + * Decode an {@link AssertionRequest} from JSON. The inverse of {@link #toJson()}. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * <p>If the JSON was generated by the {@link #toJson()} method, then {@link #fromJson(String)} in |
+ +187 + | ++ + + + + + | + * the same library version guarantees to restore an identical {@link AssertionRequest} instance. |
+ +188 + | ++ + + + + + | + * This is not guaranteed between different library versions. |
+ +189 + | ++ + + + + + | + * |
+ +190 + | ++ + + + + + | + * @return a {@link AssertionRequest} decoded from the input JSON. |
+ +191 + | ++ + + + + + | + * @throws JsonProcessingException |
+ +192 + | ++ + + + + + | + */ |
+ +193 + | ++ + + + + + | + public static AssertionRequest fromJson(String json) throws JsonProcessingException { |
+ +194 + | +
+
+1
+
+1. fromJson : replaced return value with null for com/yubico/webauthn/AssertionRequest::fromJson → KILLED + + + + |
+ return JacksonCodecs.json().readValue(json, AssertionRequest.class); |
+ +195 + | ++ + + + + + | + } |
+ +196 + | ++ + + + + + | +|
+ +197 + | ++ + + + + + | + public static AssertionRequestBuilder.MandatoryStages builder() { |
+ +198 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/AssertionRequest::builder → KILLED + + + + |
+ return new AssertionRequestBuilder.MandatoryStages(); |
+ +199 + | ++ + + + + + | + } |
+ +200 + | ++ + + + + + | +|
+ +201 + | ++ + + + + + | + public static class AssertionRequestBuilder { |
+ +202 + | ++ + + + + + | + private String username = null; |
+ +203 + | ++ + + + + + | + private ByteArray userHandle = null; |
+ +204 + | ++ + + + + + | +|
+ +205 + | ++ + + + + + | + public static class MandatoryStages { |
+ +206 + | ++ + + + + + | + private final AssertionRequestBuilder builder = new AssertionRequestBuilder(); |
+ +207 + | ++ + + + + + | +|
+ +208 + | ++ + + + + + | + /** |
+ +209 + | ++ + + + + + | + * {@link |
+ +210 + | ++ + + + + + | + * AssertionRequestBuilder#publicKeyCredentialRequestOptions(PublicKeyCredentialRequestOptions) |
+ +211 + | ++ + + + + + | + * publicKeyCredentialRequestOptions} is a required parameter. |
+ +212 + | ++ + + + + + | + * |
+ +213 + | ++ + + + + + | + * @see |
+ +214 + | ++ + + + + + | + * AssertionRequestBuilder#publicKeyCredentialRequestOptions(PublicKeyCredentialRequestOptions) |
+ +215 + | ++ + + + + + | + */ |
+ +216 + | ++ + + + + + | + public AssertionRequestBuilder publicKeyCredentialRequestOptions( |
+ +217 + | ++ + + + + + | + PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions) { |
+ +218 + | +
+
+1
+
+1. publicKeyCredentialRequestOptions : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder$MandatoryStages::publicKeyCredentialRequestOptions → KILLED + + + + |
+ return builder.publicKeyCredentialRequestOptions(publicKeyCredentialRequestOptions); |
+ +219 + | ++ + + + + + | + } |
+ +220 + | ++ + + + + + | + } |
+ +221 + | ++ + + + + + | +|
+ +222 + | ++ + + + + + | + /** |
+ +223 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +224 + | ++ + + + + + | + * |
+ +225 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #userHandle(ByteArray)}; setting this to non-empty |
+ +226 + | ++ + + + + + | + * will unset {@link #userHandle(ByteArray)}. |
+ +227 + | ++ + + + + + | + * |
+ +228 + | ++ + + + + + | + * <p>If this is empty, this indicates that this is a request for an assertion by a <a |
+ +229 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +230 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +231 + | ++ + + + + + | + * is received. |
+ +232 + | ++ + + + + + | + * |
+ +233 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +234 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +235 + | ++ + + + + + | + */ |
+ +236 + | +
+
+1
+
+1. username : negated conditional → KILLED + + + + |
+ public AssertionRequestBuilder username(@NonNull Optional<String> username) { |
+ +237 + | +
+
+1
+
+1. username : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::username → KILLED + + + + |
+ return this.username(username.orElse(null)); |
+ +238 + | ++ + + + + + | + } |
+ +239 + | ++ + + + + + | +|
+ +240 + | ++ + + + + + | + /** |
+ +241 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +242 + | ++ + + + + + | + * |
+ +243 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #userHandle(ByteArray)}; setting this to non-<code> |
+ +244 + | ++ + + + + + | + * null</code> will unset {@link #userHandle(ByteArray)}. |
+ +245 + | ++ + + + + + | + * |
+ +246 + | ++ + + + + + | + * <p>If this is empty, this indicates that this is a request for an assertion by a <a |
+ +247 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +248 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +249 + | ++ + + + + + | + * is received. |
+ +250 + | ++ + + + + + | + * |
+ +251 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +252 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +253 + | ++ + + + + + | + */ |
+ +254 + | ++ + + + + + | + public AssertionRequestBuilder username(String username) { |
+ +255 + | ++ + + + + + | + this.username = username; |
+ +256 + | +
+
+1
+
+1. username : negated conditional → KILLED + + + + |
+ if (username != null) { |
+ +257 + | ++ + + + + + | + this.userHandle = null; |
+ +258 + | ++ + + + + + | + } |
+ +259 + | +
+
+1
+
+1. username : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::username → KILLED + + + + |
+ return this; |
+ +260 + | ++ + + + + + | + } |
+ +261 + | ++ + + + + + | +|
+ +262 + | ++ + + + + + | + /** |
+ +263 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +264 + | ++ + + + + + | + * |
+ +265 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #username(String)}; setting this to non-empty will |
+ +266 + | ++ + + + + + | + * unset {@link #username(String)}. |
+ +267 + | ++ + + + + + | + * |
+ +268 + | ++ + + + + + | + * <p>If both this and {@link #username(String)} are empty, this indicates that this is a |
+ +269 + | ++ + + + + + | + * request for an assertion by a <a |
+ +270 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +271 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +272 + | ++ + + + + + | + * is received. |
+ +273 + | ++ + + + + + | + * |
+ +274 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +275 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +276 + | ++ + + + + + | + */ |
+ +277 + | +
+
+1
+
+1. userHandle : negated conditional → KILLED + + + + |
+ public AssertionRequestBuilder userHandle(@NonNull Optional<ByteArray> userHandle) { |
+ +278 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::userHandle → KILLED + + + + |
+ return this.userHandle(userHandle.orElse(null)); |
+ +279 + | ++ + + + + + | + } |
+ +280 + | ++ + + + + + | +|
+ +281 + | ++ + + + + + | + /** |
+ +282 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +283 + | ++ + + + + + | + * |
+ +284 + | ++ + + + + + | + * <p>This is mutually exclusive with {@link #username(String)}; setting this to non-<code>null |
+ +285 + | ++ + + + + + | + * </code> will unset {@link #username(String)}. |
+ +286 + | ++ + + + + + | + * |
+ +287 + | ++ + + + + + | + * <p>If both this and {@link #username(String)} are empty, this indicates that this is a |
+ +288 + | ++ + + + + + | + * request for an assertion by a <a |
+ +289 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable |
+ +290 + | ++ + + + + + | + * credential</a> (passkey). Identification of the user is therefore deferred until the response |
+ +291 + | ++ + + + + + | + * is received. |
+ +292 + | ++ + + + + + | + * |
+ +293 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +294 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +295 + | ++ + + + + + | + */ |
+ +296 + | ++ + + + + + | + public AssertionRequestBuilder userHandle(ByteArray userHandle) { |
+ +297 + | +
+
+1
+
+1. userHandle : negated conditional → KILLED + + + + |
+ if (userHandle != null) { |
+ +298 + | ++ + + + + + | + this.username = null; |
+ +299 + | ++ + + + + + | + } |
+ +300 + | ++ + + + + + | + this.userHandle = userHandle; |
+ +301 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::userHandle → KILLED + + + + |
+ return this; |
+ +302 + | ++ + + + + + | + } |
+ +303 + | ++ + + + + + | + } |
+ +304 + | ++ + + + + + | +} |
Mutations | ||
90 | ++ |
+
+
+
+ 1.1 |
+
96 | ++ |
+
+
+
+ 1.1 |
+
121 | ++ |
+
+
+
+ 1.1 |
+
140 | ++ |
+
+
+
+ 1.1 |
+
162 | ++ |
+
+
+
+ 1.1 |
+
180 | ++ |
+
+
+
+ 1.1 |
+
194 | ++ |
+
+
+
+ 1.1 |
+
198 | ++ |
+
+
+
+ 1.1 |
+
218 | ++ |
+
+
+
+ 1.1 |
+
236 | ++ |
+
+
+
+ 1.1 |
+
237 | ++ |
+
+
+
+ 1.1 |
+
256 | ++ |
+
+
+
+ 1.1 |
+
259 | ++ |
+
+
+
+ 1.1 |
+
277 | ++ |
+
+
+
+ 1.1 |
+
278 | ++ |
+
+
+
+ 1.1 |
+
297 | ++ |
+
+
+
+ 1.1 |
+
301 | ++ |
+
+
+
+ 1.1 |
+
+ +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 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
108 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
146 | ++ |
+
+
+
+ 1.1 |
+
165 | ++ |
+
+
+
+ 1.1 2.2 |
+
192 | ++ |
+
+
+
+ 1.1 2.2 |
+
217 | ++ |
+
+
+
+ 1.1 2.2 |
+
231 | ++ |
+
+
+
+ 1.1 |
+
244 | ++ |
+
+
+
+ 1.1 |
+
262 | ++ |
+
+
+
+ 1.1 |
+
263 | ++ |
+
+
+
+ 1.1 2.2 |
+
281 | ++ |
+
+
+
+ 1.1 |
+
+ +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 java.util.Optional; |
+ +40 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +41 + | ++ + + + + + | +import lombok.Getter; |
+ +42 + | ++ + + + + + | +import lombok.NonNull; |
+ +43 + | ++ + + + + + | +import lombok.Value; |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | +/** |
+ +46 + | ++ + + + + + | + * The result of a call to {@link RelyingPartyV2#finishAssertion(FinishAssertionOptions)}. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +49 + | ++ + + + + + | + * before reaching a mature release. |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | +@Deprecated |
+ +52 + | ++ + + + + + | +@Value |
+ +53 + | ++ + + + + + | +public class AssertionResultV2<C extends CredentialRecord> { |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | + /** <code>true</code> if the assertion was verified successfully. */ |
+ +56 + | ++ + + + + + | + private final boolean success; |
+ +57 + | ++ + + + + + | +|
+ +58 + | ++ + + + + + | + @JsonProperty |
+ +59 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +60 + | ++ + + + + + | + private final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> |
+ +61 + | ++ + + + + + | + credentialResponse; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | + /** |
+ +64 + | ++ + + + + + | + * The {@link CredentialRecord} that was returned by {@link |
+ +65 + | ++ + + + + + | + * CredentialRepositoryV2#lookup(ByteArray, ByteArray)} and whose public key was used to |
+ +66 + | ++ + + + + + | + * successfully verify the assertion signature. |
+ +67 + | ++ + + + + + | + * |
+ +68 + | ++ + + + + + | + * <p>NOTE: The {@link CredentialRecord#getSignatureCount() signature count}, {@link |
+ +69 + | ++ + + + + + | + * CredentialRecord#isBackupEligible() backup eligibility} and {@link |
+ +70 + | ++ + + + + + | + * CredentialRecord#isBackedUp() backup state} properties in this object will reflect the state |
+ +71 + | ++ + + + + + | + * <i>before</i> the assertion operation, not the new state. When updating your database state, |
+ +72 + | ++ + + + + + | + * use the signature counter and backup state from {@link #getSignatureCount()}, {@link |
+ +73 + | ++ + + + + + | + * #isBackupEligible()} and {@link #isBackedUp()} instead. |
+ +74 + | ++ + + + + + | + * |
+ +75 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +76 + | ++ + + + + + | + * before reaching a mature release. |
+ +77 + | ++ + + + + + | + */ |
+ +78 + | ++ + + + + + | + @Deprecated private final C credential; |
+ +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 CredentialRecord#getSignatureCount() the |
+ +86 + | ++ + + + + + | + * stored one}. |
+ +87 + | ++ + + + + + | + * <li>The {@link AuthenticatorData#getSignatureCounter() signature counter value} in the |
+ +88 + | ++ + + + + + | + * assertion and {@link CredentialRecord#getSignatureCount() the stored one} were both zero. |
+ +89 + | ++ + + + + + | + * </ul> |
+ +90 + | ++ + + + + + | + * |
+ +91 + | ++ + + + + + | + * @see <a |
+ +92 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1. |
+ +93 + | ++ + + + + + | + * Authenticator Data</a> |
+ +94 + | ++ + + + + + | + * @see AuthenticatorData#getSignatureCounter() |
+ +95 + | ++ + + + + + | + * @see CredentialRecord#getSignatureCount() |
+ +96 + | ++ + + + + + | + * @see RelyingParty.RelyingPartyBuilder#validateSignatureCounter(boolean) |
+ +97 + | ++ + + + + + | + */ |
+ +98 + | ++ + + + + + | + private final boolean signatureCounterValid; |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + @JsonCreator |
+ +101 + | ++ + + + + + | + AssertionResultV2( |
+ +102 + | ++ + + + + + | + @JsonProperty("success") boolean success, |
+ +103 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("credentialResponse") |
+ +104 + | ++ + + + + + | + PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> |
+ +105 + | ++ + + + + + | + credentialResponse, |
+ +106 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("credential") C credential, |
+ +107 + | ++ + + + + + | + @JsonProperty("signatureCounterValid") boolean signatureCounterValid) { |
+ +108 + | ++ + + + + + | + this.success = success; |
+ +109 + | ++ + + + + + | + this.credentialResponse = credentialResponse; |
+ +110 + | ++ + + + + + | + this.credential = credential; |
+ +111 + | ++ + + + + + | + this.signatureCounterValid = signatureCounterValid; |
+ +112 + | ++ + + + + + | + } |
+ +113 + | ++ + + + + + | +|
+ +114 + | ++ + + + + + | + /** |
+ +115 + | ++ + + + + + | + * Check whether the <a href="https://www.w3.org/TR/webauthn/#user-verification">user |
+ +116 + | ++ + + + + + | + * verification</a> as performed during the authentication ceremony. |
+ +117 + | ++ + + + + + | + * |
+ +118 + | ++ + + + + + | + * <p>This flag is also available via <code> |
+ +119 + | ++ + + + + + | + * {@link PublicKeyCredential}.{@link PublicKeyCredential#getResponse() getResponse()}.{@link AuthenticatorResponse#getParsedAuthenticatorData() getParsedAuthenticatorData()}.{@link AuthenticatorData#getFlags() getFlags()}.{@link AuthenticatorDataFlags#UV UV} |
+ +120 + | ++ + + + + + | + * </code>. |
+ +121 + | ++ + + + + + | + * |
+ +122 + | ++ + + + + + | + * @return <code>true</code> if and only if the authenticator claims to have performed user |
+ +123 + | ++ + + + + + | + * verification during the authentication ceremony. |
+ +124 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/webauthn/#user-verification">User Verification</a> |
+ +125 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-uv">UV flag in §6.1. Authenticator |
+ +126 + | ++ + + + + + | + * Data</a> |
+ +127 + | ++ + + + + + | + */ |
+ +128 + | ++ + + + + + | + @JsonIgnore |
+ +129 + | ++ + + + + + | + public boolean isUserVerified() { |
+ +130 + | +
+
+2
+
+1. isUserVerified : replaced boolean return with false for com/yubico/webauthn/AssertionResultV2::isUserVerified → KILLED +2. isUserVerified : replaced boolean return with true for com/yubico/webauthn/AssertionResultV2::isUserVerified → KILLED + + + + |
+ return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().UV; |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + /** |
+ +134 + | ++ + + + + + | + * Check whether the asserted credential is <a |
+ +135 + | ++ + + + + + | + * href="https://w3c.github.io/webauthn/#backup-eligible">backup eligible</a>, using the <a |
+ +136 + | ++ + + + + + | + * href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> in the authenticator data. |
+ +137 + | ++ + + + + + | + * |
+ +138 + | ++ + + + + + | + * <p>You SHOULD store this value in your representation of the corresponding {@link |
+ +139 + | ++ + + + + + | + * CredentialRecord} if no value is stored yet. {@link CredentialRepository} implementations |
+ +140 + | ++ + + + + + | + * SHOULD set this value when reconstructing that {@link CredentialRecord}. |
+ +141 + | ++ + + + + + | + * |
+ +142 + | ++ + + + + + | + * @return <code>true</code> if and only if the created credential is backup eligible. NOTE that |
+ +143 + | ++ + + + + + | + * this is only a hint and not a guarantee, unless backed by a trusted authenticator |
+ +144 + | ++ + + + + + | + * attestation. |
+ +145 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#backup-eligible">Backup Eligible in §4. |
+ +146 + | ++ + + + + + | + * Terminology</a> |
+ +147 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag in §6.1. Authenticator |
+ +148 + | ++ + + + + + | + * Data</a> |
+ +149 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +150 + | ++ + + + + + | + * the standard matures. |
+ +151 + | ++ + + + + + | + */ |
+ +152 + | ++ + + + + + | + @Deprecated |
+ +153 + | ++ + + + + + | + @JsonIgnore |
+ +154 + | ++ + + + + + | + public boolean isBackupEligible() { |
+ +155 + | +
+
+2
+
+1. isBackupEligible : replaced boolean return with false for com/yubico/webauthn/AssertionResultV2::isBackupEligible → KILLED +2. isBackupEligible : replaced boolean return with true for com/yubico/webauthn/AssertionResultV2::isBackupEligible → KILLED + + + + |
+ return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().BE; |
+ +156 + | ++ + + + + + | + } |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + /** |
+ +159 + | ++ + + + + + | + * Get the current <a href="https://w3c.github.io/webauthn/#backup-state">backup state</a> of the |
+ +160 + | ++ + + + + + | + * asserted credential, using the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS |
+ +161 + | ++ + + + + + | + * flag</a> in the authenticator data. |
+ +162 + | ++ + + + + + | + * |
+ +163 + | ++ + + + + + | + * <p>You SHOULD update this value in your representation of a {@link CredentialRecord}. {@link |
+ +164 + | ++ + + + + + | + * CredentialRepository} implementations SHOULD set this value when reconstructing that {@link |
+ +165 + | ++ + + + + + | + * CredentialRecord}. |
+ +166 + | ++ + + + + + | + * |
+ +167 + | ++ + + + + + | + * @return <code>true</code> if and only if the created credential is believed to currently be |
+ +168 + | ++ + + + + + | + * backed up. NOTE that this is only a hint and not a guarantee, unless backed by a trusted |
+ +169 + | ++ + + + + + | + * authenticator attestation. |
+ +170 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#backup-state">Backup State in §4. Terminology</a> |
+ +171 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS flag in §6.1. Authenticator |
+ +172 + | ++ + + + + + | + * Data</a> |
+ +173 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +174 + | ++ + + + + + | + * the standard matures. |
+ +175 + | ++ + + + + + | + */ |
+ +176 + | ++ + + + + + | + @Deprecated |
+ +177 + | ++ + + + + + | + @JsonIgnore |
+ +178 + | ++ + + + + + | + public boolean isBackedUp() { |
+ +179 + | +
+
+2
+
+1. isBackedUp : replaced boolean return with false for com/yubico/webauthn/AssertionResultV2::isBackedUp → KILLED +2. isBackedUp : replaced boolean return with true for com/yubico/webauthn/AssertionResultV2::isBackedUp → KILLED + + + + |
+ return credentialResponse.getResponse().getParsedAuthenticatorData().getFlags().BS; |
+ +180 + | ++ + + + + + | + } |
+ +181 + | ++ + + + + + | +|
+ +182 + | ++ + + + + + | + /** |
+ +183 + | ++ + + + + + | + * The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator |
+ +184 + | ++ + + + + + | + * attachment modality</a> in effect at the time the asserted credential was used. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * @see PublicKeyCredential#getAuthenticatorAttachment() |
+ +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 Optional<AuthenticatorAttachment> getAuthenticatorAttachment() { |
+ +193 + | +
+
+1
+
+1. getAuthenticatorAttachment : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResultV2::getAuthenticatorAttachment → KILLED + + + + |
+ return credentialResponse.getAuthenticatorAttachment(); |
+ +194 + | ++ + + + + + | + } |
+ +195 + | ++ + + + + + | +|
+ +196 + | ++ + + + + + | + /** |
+ +197 + | ++ + + + + + | + * The new <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature |
+ +198 + | ++ + + + + + | + * count</a> of the credential used for the assertion. |
+ +199 + | ++ + + + + + | + * |
+ +200 + | ++ + + + + + | + * <p>You should update this value in your database. |
+ +201 + | ++ + + + + + | + * |
+ +202 + | ++ + + + + + | + * @see AuthenticatorData#getSignatureCounter() |
+ +203 + | ++ + + + + + | + */ |
+ +204 + | ++ + + + + + | + @JsonIgnore |
+ +205 + | ++ + + + + + | + public long getSignatureCount() { |
+ +206 + | +
+
+1
+
+1. getSignatureCount : replaced long return with 0 for com/yubico/webauthn/AssertionResultV2::getSignatureCount → KILLED + + + + |
+ return credentialResponse.getResponse().getParsedAuthenticatorData().getSignatureCounter(); |
+ +207 + | ++ + + + + + | + } |
+ +208 + | ++ + + + + + | +|
+ +209 + | ++ + + + + + | + /** |
+ +210 + | ++ + + + + + | + * The <a |
+ +211 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client |
+ +212 + | ++ + + + + + | + * extension outputs</a>, if any. |
+ +213 + | ++ + + + + + | + * |
+ +214 + | ++ + + + + + | + * <p>This is present if and only if at least one extension output is present in the return value. |
+ +215 + | ++ + + + + + | + * |
+ +216 + | ++ + + + + + | + * @see <a |
+ +217 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-client-extension-processing">§9.4. |
+ +218 + | ++ + + + + + | + * Client Extension Processing</a> |
+ +219 + | ++ + + + + + | + * @see ClientAssertionExtensionOutputs |
+ +220 + | ++ + + + + + | + * @see #getAuthenticatorExtensionOutputs() () |
+ +221 + | ++ + + + + + | + */ |
+ +222 + | ++ + + + + + | + @JsonIgnore |
+ +223 + | ++ + + + + + | + public Optional<ClientAssertionExtensionOutputs> getClientExtensionOutputs() { |
+ +224 + | +
+
+1
+
+1. getClientExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResultV2::getClientExtensionOutputs → KILLED + + + + |
+ return Optional.of(credentialResponse.getClientExtensionResults()) |
+ +225 + | +
+
+2
+
+1. lambda$getClientExtensionOutputs$0 : replaced boolean return with true for com/yubico/webauthn/AssertionResultV2::lambda$getClientExtensionOutputs$0 → SURVIVED +2. lambda$getClientExtensionOutputs$0 : negated conditional → KILLED + + + + |
+ .filter(ceo -> !ceo.getExtensionIds().isEmpty()); |
+ +226 + | ++ + + + + + | + } |
+ +227 + | ++ + + + + + | +|
+ +228 + | ++ + + + + + | + /** |
+ +229 + | ++ + + + + + | + * The <a |
+ +230 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +231 + | ++ + + + + + | + * extension outputs</a>, if any. |
+ +232 + | ++ + + + + + | + * |
+ +233 + | ++ + + + + + | + * <p>This is present if and only if at least one extension output is present in the return value. |
+ +234 + | ++ + + + + + | + * |
+ +235 + | ++ + + + + + | + * @see <a |
+ +236 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-extension-processing">§9.5. |
+ +237 + | ++ + + + + + | + * Authenticator Extension Processing</a> |
+ +238 + | ++ + + + + + | + * @see AuthenticatorAssertionExtensionOutputs |
+ +239 + | ++ + + + + + | + * @see #getClientExtensionOutputs() |
+ +240 + | ++ + + + + + | + */ |
+ +241 + | ++ + + + + + | + @JsonIgnore |
+ +242 + | ++ + + + + + | + public Optional<AuthenticatorAssertionExtensionOutputs> getAuthenticatorExtensionOutputs() { |
+ +243 + | +
+
+1
+
+1. getAuthenticatorExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/AssertionResultV2::getAuthenticatorExtensionOutputs → KILLED + + + + |
+ return AuthenticatorAssertionExtensionOutputs.fromAuthenticatorData( |
+ +244 + | ++ + + + + + | + credentialResponse.getResponse().getParsedAuthenticatorData()); |
+ +245 + | ++ + + + + + | + } |
+ +246 + | ++ + + + + + | +} |
Mutations | ||
103 | ++ |
+
+
+
+ 1.1 |
+
106 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 2.2 |
+
155 | ++ |
+
+
+
+ 1.1 2.2 |
+
179 | ++ |
+
+
+
+ 1.1 2.2 |
+
193 | ++ |
+
+
+
+ 1.1 |
+
206 | ++ |
+
+
+
+ 1.1 |
+
224 | ++ |
+
+
+
+ 1.1 |
+
225 | ++ |
+
+
+
+ 1.1 2.2 |
+
243 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestedCredentialData; |
+ +4 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAssertionResponse; |
+ +5 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAttestationResponse; |
+ +6 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorData; |
+ +7 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorTransport; |
+ +8 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +9 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +10 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialDescriptor; |
+ +11 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; |
+ +12 + | ++ + + + + + | +import com.yubico.webauthn.data.UserIdentity; |
+ +13 + | ++ + + + + + | +import java.util.Optional; |
+ +14 + | ++ + + + + + | +import java.util.Set; |
+ +15 + | ++ + + + + + | +import lombok.NonNull; |
+ +16 + | ++ + + + + + | +|
+ +17 + | ++ + + + + + | +/** |
+ +18 + | ++ + + + + + | + * An abstraction of properties of a stored WebAuthn credential. |
+ +19 + | ++ + + + + + | + * |
+ +20 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#credential-record">Credential Record</a> in Web |
+ +21 + | ++ + + + + + | + * Authentication Level 3 (Editor's Draft) |
+ +22 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +23 + | ++ + + + + + | + * before reaching a mature release. |
+ +24 + | ++ + + + + + | + */ |
+ +25 + | ++ + + + + + | +@Deprecated |
+ +26 + | ++ + + + + + | +public interface CredentialRecord extends ToPublicKeyCredentialDescriptor { |
+ +27 + | ++ + + + + + | +|
+ +28 + | ++ + + + + + | + /** |
+ +29 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">credential |
+ +30 + | ++ + + + + + | + * ID</a> of the credential. |
+ +31 + | ++ + + + + + | + * |
+ +32 + | ++ + + + + + | + * <p>Implementations MUST NOT return null. |
+ +33 + | ++ + + + + + | + * |
+ +34 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">Credential |
+ +35 + | ++ + + + + + | + * ID</a> |
+ +36 + | ++ + + + + + | + * @see RegistrationResult#getKeyId() |
+ +37 + | ++ + + + + + | + * @see PublicKeyCredentialDescriptor#getId() |
+ +38 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +39 + | ++ + + + + + | + * before reaching a mature release. |
+ +40 + | ++ + + + + + | + */ |
+ +41 + | ++ + + + + + | + @Deprecated |
+ +42 + | ++ + + + + + | + @NonNull |
+ +43 + | ++ + + + + + | + ByteArray getCredentialId(); |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** |
+ +46 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">user handle</a> |
+ +47 + | ++ + + + + + | + * of the user the credential is registered to. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * <p>Implementations MUST NOT return null. |
+ +50 + | ++ + + + + + | + * |
+ +51 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User Handle</a> |
+ +52 + | ++ + + + + + | + * @see UserIdentity#getId() |
+ +53 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +54 + | ++ + + + + + | + * before reaching a mature release. |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | + @Deprecated |
+ +57 + | ++ + + + + + | + @NonNull |
+ +58 + | ++ + + + + + | + ByteArray getUserHandle(); |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | + /** |
+ +61 + | ++ + + + + + | + * The credential public key encoded in COSE_Key format, as defined in Section 7 of <a |
+ +62 + | ++ + + + + + | + * href="https://tools.ietf.org/html/rfc8152">RFC 8152</a>. |
+ +63 + | ++ + + + + + | + * |
+ +64 + | ++ + + + + + | + * <p>This is used to verify the {@link AuthenticatorAssertionResponse#getSignature() signature} |
+ +65 + | ++ + + + + + | + * in authentication assertions. |
+ +66 + | ++ + + + + + | + * |
+ +67 + | ++ + + + + + | + * <p>If your database has credentials encoded in U2F (raw) format, you may need to use {@link |
+ +68 + | ++ + + + + + | + * #cosePublicKeyFromEs256Raw(ByteArray)} to convert them before returning them in this method. |
+ +69 + | ++ + + + + + | + * |
+ +70 + | ++ + + + + + | + * <p>Implementations MUST NOT return null. |
+ +71 + | ++ + + + + + | + * |
+ +72 + | ++ + + + + + | + * @see AttestedCredentialData#getCredentialPublicKey() |
+ +73 + | ++ + + + + + | + * @see RegistrationResult#getPublicKeyCose() |
+ +74 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +75 + | ++ + + + + + | + * before reaching a mature release. |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + @Deprecated |
+ +78 + | ++ + + + + + | + @NonNull |
+ +79 + | ++ + + + + + | + ByteArray getPublicKeyCose(); |
+ +80 + | ++ + + + + + | +|
+ +81 + | ++ + + + + + | + /** |
+ +82 + | ++ + + + + + | + * The stored <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature |
+ +83 + | ++ + + + + + | + * count</a> of the credential. |
+ +84 + | ++ + + + + + | + * |
+ +85 + | ++ + + + + + | + * <p>This is used to validate the {@link AuthenticatorData#getSignatureCounter() signature |
+ +86 + | ++ + + + + + | + * counter} in authentication assertions. |
+ +87 + | ++ + + + + + | + * |
+ +88 + | ++ + + + + + | + * @see <a |
+ +89 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1. |
+ +90 + | ++ + + + + + | + * Authenticator Data</a> |
+ +91 + | ++ + + + + + | + * @see AuthenticatorData#getSignatureCounter() |
+ +92 + | ++ + + + + + | + * @see AssertionResult#getSignatureCount() |
+ +93 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +94 + | ++ + + + + + | + * before reaching a mature release. |
+ +95 + | ++ + + + + + | + */ |
+ +96 + | ++ + + + + + | + @Deprecated |
+ +97 + | ++ + + + + + | + long getSignatureCount(); |
+ +98 + | ++ + + + + + | +|
+ +99 + | ++ + + + + + | + /** |
+ +100 + | ++ + + + + + | + * Transport hints as to how the client might communicate with the authenticator this credential |
+ +101 + | ++ + + + + + | + * is bound to. |
+ +102 + | ++ + + + + + | + * |
+ +103 + | ++ + + + + + | + * <p>Implementations SHOULD return the value returned by {@link |
+ +104 + | ++ + + + + + | + * AuthenticatorAttestationResponse#getTransports()} when the credential was created. That value |
+ +105 + | ++ + + + + + | + * SHOULD NOT be modified. |
+ +106 + | ++ + + + + + | + * |
+ +107 + | ++ + + + + + | + * <p>Implementations MUST NOT return null. |
+ +108 + | ++ + + + + + | + * |
+ +109 + | ++ + + + + + | + * <p>This is used to set {@link PublicKeyCredentialDescriptor#getTransports()} in {@link |
+ +110 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getExcludeCredentials() excludeCredentials} in {@link |
+ +111 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)} and and {@link |
+ +112 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials() allowCredentials} in {@link |
+ +113 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)}. |
+ +114 + | ++ + + + + + | + * |
+ +115 + | ++ + + + + + | + * @see <a |
+ +116 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatorattestationresponse-gettransports">getTransports() |
+ +117 + | ++ + + + + + | + * in 5.2.1. Information About Public Key Credential (interface |
+ +118 + | ++ + + + + + | + * AuthenticatorAttestationResponse)</a> |
+ +119 + | ++ + + + + + | + * @see <a |
+ +120 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-publickeycredentialdescriptor-transports">transports |
+ +121 + | ++ + + + + + | + * in 5.8.3. Credential Descriptor (dictionary PublicKeyCredentialDescriptor)</a> |
+ +122 + | ++ + + + + + | + * @see AuthenticatorAttestationResponse#getTransports() |
+ +123 + | ++ + + + + + | + * @see PublicKeyCredentialDescriptor#getTransports() |
+ +124 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +125 + | ++ + + + + + | + * before reaching a mature release. |
+ +126 + | ++ + + + + + | + */ |
+ +127 + | ++ + + + + + | + @Deprecated |
+ +128 + | ++ + + + + + | + Optional<Set<AuthenticatorTransport>> getTransports(); |
+ +129 + | ++ + + + + + | +|
+ +130 + | ++ + + + + + | + // boolean isUvInitialized(); |
+ +131 + | ++ + + + + + | +|
+ +132 + | ++ + + + + + | + /** |
+ +133 + | ++ + + + + + | + * The state of the <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> when |
+ +134 + | ++ + + + + + | + * this credential was registered, if known. |
+ +135 + | ++ + + + + + | + * |
+ +136 + | ++ + + + + + | + * <p>If absent, it is not known whether or not this credential is backup eligible. |
+ +137 + | ++ + + + + + | + * |
+ +138 + | ++ + + + + + | + * <p>If present and <code>true</code>, the credential is backup eligible: it can be backed up in |
+ +139 + | ++ + + + + + | + * some way, most commonly by syncing the private key to a cloud account. |
+ +140 + | ++ + + + + + | + * |
+ +141 + | ++ + + + + + | + * <p>If present and <code>false</code>, the credential is not backup eligible: it cannot be |
+ +142 + | ++ + + + + + | + * backed up in any way. |
+ +143 + | ++ + + + + + | + * |
+ +144 + | ++ + + + + + | + * <p>{@link CredentialRecord} implementations SHOULD return the first known value returned by |
+ +145 + | ++ + + + + + | + * {@link RegistrationResult#isBackupEligible()} or {@link AssertionResult#isBackupEligible()}, if |
+ +146 + | ++ + + + + + | + * known. If unknown, {@link CredentialRecord} implementations SHOULD return <code> |
+ +147 + | ++ + + + + + | + * Optional.empty()</code>. |
+ +148 + | ++ + + + + + | + * |
+ +149 + | ++ + + + + + | + * <p>Implementations MUST NOT return null. |
+ +150 + | ++ + + + + + | + * |
+ +151 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +152 + | ++ + + + + + | + * before reaching a mature release. EXPERIMENTAL: This feature is from a not yet mature |
+ +153 + | ++ + + + + + | + * standard; it could change as the standard matures. |
+ +154 + | ++ + + + + + | + */ |
+ +155 + | ++ + + + + + | + @Deprecated |
+ +156 + | ++ + + + + + | + Optional<Boolean> isBackupEligible(); |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + /** |
+ +159 + | ++ + + + + + | + * The last known state of the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS |
+ +160 + | ++ + + + + + | + * flag</a> for this credential, if known. |
+ +161 + | ++ + + + + + | + * |
+ +162 + | ++ + + + + + | + * <p>If absent, the backup state of the credential is not known. |
+ +163 + | ++ + + + + + | + * |
+ +164 + | ++ + + + + + | + * <p>If present and <code>true</code>, the credential is believed to be currently backed up. |
+ +165 + | ++ + + + + + | + * |
+ +166 + | ++ + + + + + | + * <p>If present and <code>false</code>, the credential is believed to not be currently backed up. |
+ +167 + | ++ + + + + + | + * |
+ +168 + | ++ + + + + + | + * <p>{@link CredentialRecord} implementations SHOULD return the most recent value returned by |
+ +169 + | ++ + + + + + | + * {@link AssertionResult#isBackedUp()} or {@link RegistrationResult#isBackedUp()}, if known. If |
+ +170 + | ++ + + + + + | + * unknown, {@link CredentialRecord} implementations SHOULD return <code>Optional.empty()</code>. |
+ +171 + | ++ + + + + + | + * |
+ +172 + | ++ + + + + + | + * <p>Implementations MUST NOT return null. |
+ +173 + | ++ + + + + + | + * |
+ +174 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +175 + | ++ + + + + + | + * before reaching a mature release. EXPERIMENTAL: This feature is from a not yet mature |
+ +176 + | ++ + + + + + | + * standard; it could change as the standard matures. |
+ +177 + | ++ + + + + + | + */ |
+ +178 + | ++ + + + + + | + @Deprecated |
+ +179 + | ++ + + + + + | + Optional<Boolean> isBackedUp(); |
+ +180 + | ++ + + + + + | +|
+ +181 + | ++ + + + + + | + /** |
+ +182 + | ++ + + + + + | + * This default implementation of {@link |
+ +183 + | ++ + + + + + | + * ToPublicKeyCredentialDescriptor#toPublicKeyCredentialDescriptor()} sets the {@link |
+ +184 + | ++ + + + + + | + * PublicKeyCredentialDescriptor.PublicKeyCredentialDescriptorBuilder#id(ByteArray) id} field to |
+ +185 + | ++ + + + + + | + * the return value of {@link #getCredentialId()} and the {@link |
+ +186 + | ++ + + + + + | + * PublicKeyCredentialDescriptor.PublicKeyCredentialDescriptorBuilder#transports(Optional) |
+ +187 + | ++ + + + + + | + * transports} field to the return value of {@link #getTransports()}. |
+ +188 + | ++ + + + + + | + * |
+ +189 + | ++ + + + + + | + * @see <a |
+ +190 + | ++ + + + + + | + * href="https://w3c.github.io/webauthn/#credential-descriptor-for-a-credential-record">credential |
+ +191 + | ++ + + + + + | + * descriptor for a credential record</a> in Web Authentication Level 3 (Editor's Draft) |
+ +192 + | ++ + + + + + | + */ |
+ +193 + | ++ + + + + + | + @Override |
+ +194 + | ++ + + + + + | + default PublicKeyCredentialDescriptor toPublicKeyCredentialDescriptor() { |
+ +195 + | +
+
+1
+
+1. toPublicKeyCredentialDescriptor : replaced return value with null for com/yubico/webauthn/CredentialRecord::toPublicKeyCredentialDescriptor → KILLED + + + + |
+ return PublicKeyCredentialDescriptor.builder() |
+ +196 + | ++ + + + + + | + .id(getCredentialId()) |
+ +197 + | ++ + + + + + | + .transports(getTransports()) |
+ +198 + | ++ + + + + + | + .build(); |
+ +199 + | ++ + + + + + | + } |
+ +200 + | ++ + + + + + | +|
+ +201 + | ++ + + + + + | + /** |
+ +202 + | ++ + + + + + | + * Convert a credential public key from U2F format to COSE_Key format. |
+ +203 + | ++ + + + + + | + * |
+ +204 + | ++ + + + + + | + * <p>The U2F JavaScript API encoded credential public keys in <code>ALG_KEY_ECC_X962_RAW</code> |
+ +205 + | ++ + + + + + | + * format as specified in <a |
+ +206 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +207 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a>. If your database has credential public |
+ +208 + | ++ + + + + + | + * keys stored in this format, those public keys need to be converted to COSE_Key format before |
+ +209 + | ++ + + + + + | + * they can be used by a {@link CredentialRecord} instance. This function performs the conversion. |
+ +210 + | ++ + + + + + | + * |
+ +211 + | ++ + + + + + | + * <p>If your application has only used the <code>navigator.credentials.create()</code> API to |
+ +212 + | ++ + + + + + | + * register credentials, you likely do not need this function. |
+ +213 + | ++ + + + + + | + * |
+ +214 + | ++ + + + + + | + * @param es256RawKey a credential public key in <code>ALG_KEY_ECC_X962_RAW</code> format as |
+ +215 + | ++ + + + + + | + * specified in <a |
+ +216 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +217 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a>. |
+ +218 + | ++ + + + + + | + * @return a credential public key in COSE_Key format, suitable to be returned by {@link |
+ +219 + | ++ + + + + + | + * CredentialRecord#getPublicKeyCose()}. |
+ +220 + | ++ + + + + + | + * @see RegisteredCredential.RegisteredCredentialBuilder#publicKeyEs256Raw(ByteArray) |
+ +221 + | ++ + + + + + | + */ |
+ +222 + | ++ + + + + + | + static ByteArray cosePublicKeyFromEs256Raw(final ByteArray es256RawKey) { |
+ +223 + | +
+
+1
+
+1. cosePublicKeyFromEs256Raw : replaced return value with null for com/yubico/webauthn/CredentialRecord::cosePublicKeyFromEs256Raw → KILLED + + + + |
+ return WebAuthnCodecs.rawEcKeyToCose(es256RawKey); |
+ +224 + | ++ + + + + + | + } |
+ +225 + | ++ + + + + + | +} |
Mutations | ||
195 | ++ |
+
+
+
+ 1.1 |
+
223 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +4 + | ++ + + + + + | +import java.util.Collections; |
+ +5 + | ++ + + + + + | +import java.util.Optional; |
+ +6 + | ++ + + + + + | +import java.util.Set; |
+ +7 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +8 + | ++ + + + + + | +|
+ +9 + | ++ + + + + + | +@AllArgsConstructor |
+ +10 + | ++ + + + + + | +class CredentialRepositoryV1ToV2Adapter |
+ +11 + | ++ + + + + + | + implements CredentialRepositoryV2<RegisteredCredential>, UsernameRepository { |
+ +12 + | ++ + + + + + | +|
+ +13 + | ++ + + + + + | + private final CredentialRepository inner; |
+ +14 + | ++ + + + + + | +|
+ +15 + | ++ + + + + + | + @Override |
+ +16 + | ++ + + + + + | + public Set<? extends ToPublicKeyCredentialDescriptor> getCredentialDescriptorsForUserHandle( |
+ +17 + | ++ + + + + + | + ByteArray userHandle) { |
+ +18 + | +
+
+1
+
+1. getCredentialDescriptorsForUserHandle : replaced return value with Collections.emptySet for com/yubico/webauthn/CredentialRepositoryV1ToV2Adapter::getCredentialDescriptorsForUserHandle → NO_COVERAGE + + + + |
+ return inner |
+ +19 + | ++ + + + + + | + .getUsernameForUserHandle(userHandle) |
+ +20 + | ++ + + + + + | + .map(inner::getCredentialIdsForUsername) |
+ +21 + | ++ + + + + + | + .orElseGet(Collections::emptySet); |
+ +22 + | ++ + + + + + | + } |
+ +23 + | ++ + + + + + | +|
+ +24 + | ++ + + + + + | + @Override |
+ +25 + | ++ + + + + + | + public Optional<RegisteredCredential> lookup(ByteArray credentialId, ByteArray userHandle) { |
+ +26 + | +
+
+1
+
+1. lookup : replaced return value with Optional.empty for com/yubico/webauthn/CredentialRepositoryV1ToV2Adapter::lookup → KILLED + + + + |
+ return inner.lookup(credentialId, userHandle); |
+ +27 + | ++ + + + + + | + } |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | + @Override |
+ +30 + | ++ + + + + + | + public boolean credentialIdExists(ByteArray credentialId) { |
+ +31 + | +
+
+2
+
+1. credentialIdExists : replaced boolean return with true for com/yubico/webauthn/CredentialRepositoryV1ToV2Adapter::credentialIdExists → KILLED +2. credentialIdExists : negated conditional → KILLED + + + + |
+ return !inner.lookupAll(credentialId).isEmpty(); |
+ +32 + | ++ + + + + + | + } |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | + @Override |
+ +35 + | ++ + + + + + | + public Optional<ByteArray> getUserHandleForUsername(String username) { |
+ +36 + | +
+
+1
+
+1. getUserHandleForUsername : replaced return value with Optional.empty for com/yubico/webauthn/CredentialRepositoryV1ToV2Adapter::getUserHandleForUsername → KILLED + + + + |
+ return inner.getUserHandleForUsername(username); |
+ +37 + | ++ + + + + + | + } |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | + @Override |
+ +40 + | ++ + + + + + | + public Optional<String> getUsernameForUserHandle(ByteArray userHandle) { |
+ +41 + | +
+
+1
+
+1. getUsernameForUserHandle : replaced return value with Optional.empty for com/yubico/webauthn/CredentialRepositoryV1ToV2Adapter::getUsernameForUserHandle → KILLED + + + + |
+ return inner.getUsernameForUserHandle(userHandle); |
+ +42 + | ++ + + + + + | + } |
+ +43 + | ++ + + + + + | +} |
Mutations | ||
18 | ++ |
+
+
+
+ 1.1 |
+
26 | ++ |
+
+
+
+ 1.1 |
+
31 | ++ |
+
+
+
+ 1.1 2.2 |
+
36 | ++ |
+
+
+
+ 1.1 |
+
41 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +// Copyright (c) 2014-2018, Yubico AB |
+ +2 + | ++ + + + + + | +// Copyright (c) 2014, Google Inc. |
+ +3 + | ++ + + + + + | +// All rights reserved. |
+ +4 + | ++ + + + + + | +// |
+ +5 + | ++ + + + + + | +// Redistribution and use in source and binary forms, with or without |
+ +6 + | ++ + + + + + | +// modification, are permitted provided that the following conditions are met: |
+ +7 + | ++ + + + + + | +// |
+ +8 + | ++ + + + + + | +// 1. Redistributions of source code must retain the above copyright notice, this |
+ +9 + | ++ + + + + + | +// list of conditions and the following disclaimer. |
+ +10 + | ++ + + + + + | +// |
+ +11 + | ++ + + + + + | +// 2. Redistributions in binary form must reproduce the above copyright notice, |
+ +12 + | ++ + + + + + | +// this list of conditions and the following disclaimer in the documentation |
+ +13 + | ++ + + + + + | +// and/or other materials provided with the distribution. |
+ +14 + | ++ + + + + + | +// |
+ +15 + | ++ + + + + + | +// 3. Neither the name of Google Inc. nor the names of its contributors may be |
+ +16 + | ++ + + + + + | +// used to endorse or promote products derived from this software without |
+ +17 + | ++ + + + + + | +// specific prior written permission. |
+ +18 + | ++ + + + + + | +// |
+ +19 + | ++ + + + + + | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
+ +20 + | ++ + + + + + | +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
+ +21 + | ++ + + + + + | +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
+ +22 + | ++ + + + + + | +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
+ +23 + | ++ + + + + + | +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
+ +24 + | ++ + + + + + | +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
+ +25 + | ++ + + + + + | +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
+ +26 + | ++ + + + + + | +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
+ +27 + | ++ + + + + + | +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ +28 + | ++ + + + + + | +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ +29 + | ++ + + + + + | +|
+ +30 + | ++ + + + + + | +package com.yubico.webauthn; |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | +import com.google.common.hash.Hashing; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +35 + | ++ + + + + + | +import java.math.BigInteger; |
+ +36 + | ++ + + + + + | +import java.nio.charset.StandardCharsets; |
+ +37 + | ++ + + + + + | +import java.security.GeneralSecurityException; |
+ +38 + | ++ + + + + + | +import java.security.MessageDigest; |
+ +39 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +40 + | ++ + + + + + | +import java.security.PublicKey; |
+ +41 + | ++ + + + + + | +import java.security.Signature; |
+ +42 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +43 + | ++ + + + + + | +import java.security.spec.ECFieldFp; |
+ +44 + | ++ + + + + + | +import java.security.spec.ECParameterSpec; |
+ +45 + | ++ + + + + + | +import java.security.spec.EllipticCurve; |
+ +46 + | ++ + + + + + | +import lombok.experimental.UtilityClass; |
+ +47 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | +@UtilityClass |
+ +50 + | ++ + + + + + | +@Slf4j |
+ +51 + | ++ + + + + + | +final class Crypto { |
+ +52 + | ++ + + + + + | + // Values from |
+ +53 + | ++ + + + + + | + // https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/mathematical-routines-for-the-nist-prime-elliptic-curves.cfm |
+ +54 + | ++ + + + + + | + // cross-referenced with "secp256r1" in https://www.secg.org/sec2-v2.pdf |
+ +55 + | ++ + + + + + | + private static final EllipticCurve P256 = |
+ +56 + | ++ + + + + + | + new EllipticCurve( |
+ +57 + | ++ + + + + + | + new ECFieldFp( |
+ +58 + | ++ + + + + + | + new BigInteger( |
+ +59 + | ++ + + + + + | + "115792089210356248762697446949407573530086143415290314195533631308867097853951", |
+ +60 + | ++ + + + + + | + 10)), |
+ +61 + | ++ + + + + + | + new BigInteger( |
+ +62 + | ++ + + + + + | + "115792089210356248762697446949407573530086143415290314195533631308867097853948", 10), |
+ +63 + | ++ + + + + + | + new BigInteger( |
+ +64 + | ++ + + + + + | + "41058363725152142129326129780047268409114441015993725554835256314039467401291", 10)); |
+ +65 + | ++ + + + + + | +|
+ +66 + | ++ + + + + + | + static boolean isP256(ECParameterSpec params) { |
+ +67 + | +
+
+2
+
+1. isP256 : replaced boolean return with true for com/yubico/webauthn/Crypto::isP256 → KILLED +2. isP256 : replaced boolean return with false for com/yubico/webauthn/Crypto::isP256 → KILLED + + + + |
+ return P256.equals(params.getCurve()); |
+ +68 + | ++ + + + + + | + } |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + public static boolean verifySignature( |
+ +71 + | ++ + + + + + | + X509Certificate attestationCertificate, |
+ +72 + | ++ + + + + + | + ByteArray signedBytes, |
+ +73 + | ++ + + + + + | + ByteArray signature, |
+ +74 + | ++ + + + + + | + COSEAlgorithmIdentifier alg) { |
+ +75 + | +
+
+2
+
+1. verifySignature : replaced boolean return with true for com/yubico/webauthn/Crypto::verifySignature → KILLED +2. verifySignature : replaced boolean return with false for com/yubico/webauthn/Crypto::verifySignature → KILLED + + + + |
+ return verifySignature(attestationCertificate.getPublicKey(), signedBytes, signature, alg); |
+ +76 + | ++ + + + + + | + } |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + public static boolean verifySignature( |
+ +79 + | ++ + + + + + | + PublicKey publicKey, |
+ +80 + | ++ + + + + + | + ByteArray signedBytes, |
+ +81 + | ++ + + + + + | + ByteArray signatureBytes, |
+ +82 + | ++ + + + + + | + COSEAlgorithmIdentifier alg) { |
+ +83 + | ++ + + + + + | + try { |
+ +84 + | ++ + + + + + | + Signature signature = Signature.getInstance(WebAuthnCodecs.getJavaAlgorithmName(alg)); |
+ +85 + | +
+
+1
+
+1. verifySignature : removed call to java/security/Signature::initVerify → KILLED + + + + |
+ signature.initVerify(publicKey); |
+ +86 + | +
+
+1
+
+1. verifySignature : removed call to java/security/Signature::update → KILLED + + + + |
+ signature.update(signedBytes.getBytes()); |
+ +87 + | +
+
+2
+
+1. verifySignature : replaced boolean return with true for com/yubico/webauthn/Crypto::verifySignature → KILLED +2. verifySignature : replaced boolean return with false for com/yubico/webauthn/Crypto::verifySignature → KILLED + + + + |
+ return signature.verify(signatureBytes.getBytes()); |
+ +88 + | ++ + + + + + | + } catch (GeneralSecurityException | IllegalArgumentException e) { |
+ +89 + | ++ + + + + + | + throw new RuntimeException( |
+ +90 + | ++ + + + + + | + String.format( |
+ +91 + | ++ + + + + + | + "Failed to verify signature. This could be a problem with your JVM environment, or a bug in webauthn-server-core. Public key: %s, signed data: %s , signature: %s", |
+ +92 + | ++ + + + + + | + publicKey, signedBytes.getBase64Url(), signatureBytes.getBase64Url()), |
+ +93 + | ++ + + + + + | + e); |
+ +94 + | ++ + + + + + | + } |
+ +95 + | ++ + + + + + | + } |
+ +96 + | ++ + + + + + | +|
+ +97 + | ++ + + + + + | + public static ByteArray sha256(ByteArray bytes) { |
+ +98 + | +
+
+1
+
+1. sha256 : replaced return value with null for com/yubico/webauthn/Crypto::sha256 → KILLED + + + + |
+ return new ByteArray(Hashing.sha256().hashBytes(bytes.getBytes()).asBytes()); |
+ +99 + | ++ + + + + + | + } |
+ +100 + | ++ + + + + + | +|
+ +101 + | ++ + + + + + | + public static ByteArray sha384(ByteArray bytes) { |
+ +102 + | +
+
+1
+
+1. sha384 : replaced return value with null for com/yubico/webauthn/Crypto::sha384 → KILLED + + + + |
+ return new ByteArray(Hashing.sha384().hashBytes(bytes.getBytes()).asBytes()); |
+ +103 + | ++ + + + + + | + } |
+ +104 + | ++ + + + + + | +|
+ +105 + | ++ + + + + + | + public static ByteArray sha512(ByteArray bytes) { |
+ +106 + | +
+
+1
+
+1. sha512 : replaced return value with null for com/yubico/webauthn/Crypto::sha512 → KILLED + + + + |
+ return new ByteArray(Hashing.sha512().hashBytes(bytes.getBytes()).asBytes()); |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | +|
+ +109 + | ++ + + + + + | + public static ByteArray sha256(String str) { |
+ +110 + | +
+
+1
+
+1. sha256 : replaced return value with null for com/yubico/webauthn/Crypto::sha256 → KILLED + + + + |
+ return sha256(new ByteArray(str.getBytes(StandardCharsets.UTF_8))); |
+ +111 + | ++ + + + + + | + } |
+ +112 + | ++ + + + + + | +|
+ +113 + | ++ + + + + + | + public static ByteArray sha1(ByteArray bytes) throws NoSuchAlgorithmException { |
+ +114 + | +
+
+1
+
+1. sha1 : replaced return value with null for com/yubico/webauthn/Crypto::sha1 → KILLED + + + + |
+ return new ByteArray(MessageDigest.getInstance("SHA-1").digest(bytes.getBytes())); |
+ +115 + | ++ + + + + + | + } |
+ +116 + | ++ + + + + + | +} |
Mutations | ||
67 | ++ |
+
+
+
+ 1.1 2.2 |
+
75 | ++ |
+
+
+
+ 1.1 2.2 |
+
85 | ++ |
+
+
+
+ 1.1 |
+
86 | ++ |
+
+
+
+ 1.1 |
+
87 | ++ |
+
+
+
+ 1.1 2.2 |
+
98 | ++ |
+
+
+
+ 1.1 |
+
102 | ++ |
+
+
+
+ 1.1 |
+
106 | ++ |
+
+
+
+ 1.1 |
+
110 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
+ +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 static com.yubico.webauthn.Crypto.isP256; |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | +import COSE.CoseException; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonNode; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestedCredentialData; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +36 + | ++ + + + + + | +import java.io.IOException; |
+ +37 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +38 + | ++ + + + + + | +import java.security.PublicKey; |
+ +39 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +40 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +41 + | ++ + + + + + | +import java.security.interfaces.ECPublicKey; |
+ +42 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +43 + | ++ + + + + + | +import java.util.Optional; |
+ +44 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | +@Slf4j |
+ +47 + | ++ + + + + + | +final class FidoU2fAttestationStatementVerifier |
+ +48 + | ++ + + + + + | + implements AttestationStatementVerifier, X5cAttestationStatementVerifier { |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + private X509Certificate getAttestationCertificate(AttestationObject attestationObject) |
+ +51 + | ++ + + + + + | + throws CertificateException { |
+ +52 + | +
+
+1
+
+1. getAttestationCertificate : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::getAttestationCertificate → KILLED + + + + |
+ return getX5cAttestationCertificate(attestationObject) |
+ +53 + | ++ + + + + + | + .map( |
+ +54 + | ++ + + + + + | + attestationCertificate -> { |
+ +55 + | +
+
+1
+
+1. lambda$getAttestationCertificate$0 : negated conditional → KILLED + + + + |
+ if ("EC".equals(attestationCertificate.getPublicKey().getAlgorithm()) |
+ +56 + | +
+
+1
+
+1. lambda$getAttestationCertificate$0 : negated conditional → KILLED + + + + |
+ && isP256(((ECPublicKey) attestationCertificate.getPublicKey()).getParams())) { |
+ +57 + | +
+
+1
+
+1. lambda$getAttestationCertificate$0 : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::lambda$getAttestationCertificate$0 → KILLED + + + + |
+ return attestationCertificate; |
+ +58 + | ++ + + + + + | + } else { |
+ +59 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +60 + | ++ + + + + + | + "Attestation certificate for fido-u2f must have an ECDSA P-256 public key."); |
+ +61 + | ++ + + + + + | + } |
+ +62 + | ++ + + + + + | + }) |
+ +63 + | ++ + + + + + | + .orElseThrow( |
+ +64 + | ++ + + + + + | + () -> |
+ +65 + | +
+
+1
+
+1. lambda$getAttestationCertificate$1 : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::lambda$getAttestationCertificate$1 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +66 + | ++ + + + + + | + "fido-u2f attestation statement must have an \"x5c\" property set to an array of at least one DER encoded X.509 certificate.")); |
+ +67 + | ++ + + + + + | + } |
+ +68 + | ++ + + + + + | +|
+ +69 + | ++ + + + + + | + private static boolean validSelfSignature(X509Certificate cert) { |
+ +70 + | ++ + + + + + | + try { |
+ +71 + | +
+
+1
+
+1. validSelfSignature : removed call to java/security/cert/X509Certificate::verify → SURVIVED + + + + |
+ cert.verify(cert.getPublicKey()); |
+ +72 + | +
+
+1
+
+1. validSelfSignature : replaced boolean return with false for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::validSelfSignature → KILLED + + + + |
+ return true; |
+ +73 + | ++ + + + + + | + } catch (Exception e) { |
+ +74 + | +
+
+1
+
+1. validSelfSignature : replaced boolean return with true for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::validSelfSignature → SURVIVED + + + + |
+ return false; |
+ +75 + | ++ + + + + + | + } |
+ +76 + | ++ + + + + + | + } |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + private static ByteArray getRawUserPublicKey(AttestationObject attestationObject) |
+ +79 + | ++ + + + + + | + throws IOException, CoseException { |
+ +80 + | ++ + + + + + | + final ByteArray pubkeyCose = |
+ +81 + | ++ + + + + + | + attestationObject |
+ +82 + | ++ + + + + + | + .getAuthenticatorData() |
+ +83 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +84 + | ++ + + + + + | + .get() |
+ +85 + | ++ + + + + + | + .getCredentialPublicKey(); |
+ +86 + | ++ + + + + + | + final PublicKey pubkey; |
+ +87 + | ++ + + + + + | + try { |
+ +88 + | ++ + + + + + | + pubkey = WebAuthnCodecs.importCosePublicKey(pubkeyCose); |
+ +89 + | ++ + + + + + | + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { |
+ +90 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog(log, "Failed to decode public key: " + pubkeyCose, e); |
+ +91 + | ++ + + + + + | + } |
+ +92 + | ++ + + + + + | +|
+ +93 + | ++ + + + + + | + final ECPublicKey ecPubkey; |
+ +94 + | ++ + + + + + | + try { |
+ +95 + | ++ + + + + + | + ecPubkey = (ECPublicKey) pubkey; |
+ +96 + | ++ + + + + + | + } catch (ClassCastException e) { |
+ +97 + | ++ + + + + + | + throw new RuntimeException("U2F supports only EC keys, was: " + pubkey); |
+ +98 + | ++ + + + + + | + } |
+ +99 + | ++ + + + + + | +|
+ +100 + | +
+
+1
+
+1. getRawUserPublicKey : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::getRawUserPublicKey → KILLED + + + + |
+ return WebAuthnCodecs.ecPublicKeyToRaw(ecPubkey); |
+ +101 + | ++ + + + + + | + } |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + @Override |
+ +104 + | ++ + + + + + | + public AttestationType getAttestationType(AttestationObject attestationObject) |
+ +105 + | ++ + + + + + | + throws CoseException, IOException, CertificateException { |
+ +106 + | ++ + + + + + | + X509Certificate attestationCertificate = getAttestationCertificate(attestationObject); |
+ +107 + | ++ + + + + + | +|
+ +108 + | +
+
+1
+
+1. getAttestationType : negated conditional → KILLED + + + + |
+ if (attestationCertificate.getPublicKey() instanceof ECPublicKey |
+ +109 + | +
+
+1
+
+1. getAttestationType : negated conditional → KILLED + + + + |
+ && validSelfSignature(attestationCertificate) |
+ +110 + | ++ + + + + + | + && getRawUserPublicKey(attestationObject) |
+ +111 + | +
+
+1
+
+1. getAttestationType : negated conditional → KILLED + + + + |
+ .equals( |
+ +112 + | ++ + + + + + | + WebAuthnCodecs.ecPublicKeyToRaw( |
+ +113 + | ++ + + + + + | + (ECPublicKey) attestationCertificate.getPublicKey()))) { |
+ +114 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.SELF_ATTESTATION; |
+ +115 + | ++ + + + + + | + } else { |
+ +116 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.BASIC; |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | + } |
+ +119 + | ++ + + + + + | +|
+ +120 + | ++ + + + + + | + @Override |
+ +121 + | ++ + + + + + | + public boolean verifyAttestationSignature( |
+ +122 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +123 + | ++ + + + + + | + final X509Certificate attestationCertificate; |
+ +124 + | ++ + + + + + | + try { |
+ +125 + | ++ + + + + + | + attestationCertificate = getAttestationCertificate(attestationObject); |
+ +126 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +127 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +128 + | ++ + + + + + | + String.format( |
+ +129 + | ++ + + + + + | + "Failed to parse X.509 certificate from attestation object: %s", attestationObject)); |
+ +130 + | ++ + + + + + | + } |
+ +131 + | ++ + + + + + | +|
+ +132 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ if (!("EC".equals(attestationCertificate.getPublicKey().getAlgorithm()) |
+ +133 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ && isP256(((ECPublicKey) attestationCertificate.getPublicKey()).getParams()))) { |
+ +134 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +135 + | ++ + + + + + | + "Attestation certificate for fido-u2f must have an ECDSA P-256 public key."); |
+ +136 + | ++ + + + + + | + } |
+ +137 + | ++ + + + + + | +|
+ +138 + | ++ + + + + + | + final Optional<AttestedCredentialData> attData = |
+ +139 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getAttestedCredentialData(); |
+ +140 + | ++ + + + + + | +|
+ +141 + | +
+
+2
+
+1. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::verifyAttestationSignature → KILLED +2. verifyAttestationSignature : replaced boolean return with true for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return attData |
+ +142 + | ++ + + + + + | + .map( |
+ +143 + | ++ + + + + + | + attestedCredentialData -> { |
+ +144 + | ++ + + + + + | + JsonNode signature = attestationObject.getAttestationStatement().get("sig"); |
+ +145 + | ++ + + + + + | +|
+ +146 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$2 : negated conditional → KILLED + + + + |
+ if (signature == null) { |
+ +147 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +148 + | ++ + + + + + | + "fido-u2f attestation statement must have a \"sig\" property set to a DER encoded signature."); |
+ +149 + | ++ + + + + + | + } |
+ +150 + | ++ + + + + + | +|
+ +151 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$2 : negated conditional → KILLED + + + + |
+ if (signature.isBinary()) { |
+ +152 + | ++ + + + + + | + final ByteArray userPublicKey; |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + try { |
+ +155 + | ++ + + + + + | + userPublicKey = getRawUserPublicKey(attestationObject); |
+ +156 + | ++ + + + + + | + } catch (IOException | CoseException e) { |
+ +157 + | ++ + + + + + | + RuntimeException err = |
+ +158 + | ++ + + + + + | + new RuntimeException( |
+ +159 + | ++ + + + + + | + String.format( |
+ +160 + | ++ + + + + + | + "Failed to parse public key from attestation data %s", |
+ +161 + | ++ + + + + + | + attestedCredentialData), |
+ +162 + | ++ + + + + + | + e); |
+ +163 + | ++ + + + + + | + log.error(err.getMessage(), err); |
+ +164 + | ++ + + + + + | + throw err; |
+ +165 + | ++ + + + + + | + } |
+ +166 + | ++ + + + + + | +|
+ +167 + | ++ + + + + + | + ByteArray keyHandle = attestedCredentialData.getCredentialId(); |
+ +168 + | ++ + + + + + | +|
+ +169 + | ++ + + + + + | + U2fRawRegisterResponse u2fRegisterResponse; |
+ +170 + | ++ + + + + + | + try { |
+ +171 + | ++ + + + + + | + u2fRegisterResponse = |
+ +172 + | ++ + + + + + | + new U2fRawRegisterResponse( |
+ +173 + | ++ + + + + + | + userPublicKey, |
+ +174 + | ++ + + + + + | + keyHandle, |
+ +175 + | ++ + + + + + | + attestationCertificate, |
+ +176 + | ++ + + + + + | + new ByteArray(signature.binaryValue())); |
+ +177 + | ++ + + + + + | + } catch (IOException e) { |
+ +178 + | ++ + + + + + | + RuntimeException err = |
+ +179 + | ++ + + + + + | + new RuntimeException( |
+ +180 + | ++ + + + + + | + "signature.isBinary() was true but signature.binaryValue() failed", e); |
+ +181 + | ++ + + + + + | + log.error(err.getMessage(), err); |
+ +182 + | ++ + + + + + | + throw err; |
+ +183 + | ++ + + + + + | + } |
+ +184 + | ++ + + + + + | +|
+ +185 + | +
+
+2
+
+1. lambda$verifyAttestationSignature$2 : replaced Boolean return with False for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::lambda$verifyAttestationSignature$2 → KILLED +2. lambda$verifyAttestationSignature$2 : replaced Boolean return with True for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::lambda$verifyAttestationSignature$2 → KILLED + + + + |
+ return u2fRegisterResponse.verifySignature( |
+ +186 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getRpIdHash(), clientDataJsonHash); |
+ +187 + | ++ + + + + + | + } else { |
+ +188 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +189 + | ++ + + + + + | + "\"sig\" property of fido-u2f attestation statement must be a CBOR byte array value."); |
+ +190 + | ++ + + + + + | + } |
+ +191 + | ++ + + + + + | + }) |
+ +192 + | ++ + + + + + | + .orElseThrow( |
+ +193 + | ++ + + + + + | + () -> |
+ +194 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$3 : replaced return value with null for com/yubico/webauthn/FidoU2fAttestationStatementVerifier::lambda$verifyAttestationSignature$3 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +195 + | ++ + + + + + | + "Attestation object for credential creation must have attestation data.")); |
+ +196 + | ++ + + + + + | + } |
+ +197 + | ++ + + + + + | +} |
Mutations | ||
52 | ++ |
+
+
+
+ 1.1 |
+
55 | ++ |
+
+
+
+ 1.1 |
+
56 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 |
+
65 | ++ |
+
+
+
+ 1.1 |
+
71 | ++ |
+
+
+
+ 1.1 |
+
72 | ++ |
+
+
+
+ 1.1 |
+
74 | ++ |
+
+
+
+ 1.1 |
+
100 | ++ |
+
+
+
+ 1.1 |
+
108 | ++ |
+
+
+
+ 1.1 |
+
109 | ++ |
+
+
+
+ 1.1 |
+
111 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 |
+
132 | ++ |
+
+
+
+ 1.1 |
+
133 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 2.2 |
+
146 | ++ |
+
+
+
+ 1.1 |
+
151 | ++ |
+
+
+
+ 1.1 |
+
185 | ++ |
+
+
+
+ 1.1 2.2 |
+
194 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.webauthn.data.AuthenticatorAssertionResponse; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.ClientAssertionExtensionOutputs; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.CollectedClientData; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredential; |
+ +32 + | ++ + + + + + | +import java.util.Optional; |
+ +33 + | ++ + + + + + | +import java.util.Set; |
+ +34 + | ++ + + + + + | +import lombok.Builder; |
+ +35 + | ++ + + + + + | +import lombok.NonNull; |
+ +36 + | ++ + + + + + | +import lombok.Value; |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | +/** Parameters for {@link RelyingParty#finishAssertion(FinishAssertionOptions)}. */ |
+ +39 + | ++ + + + + + | +@Value |
+ +40 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +41 + | ++ + + + + + | +public class FinishAssertionOptions { |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + /** The request that the {@link #getResponse() response} is a response to. */ |
+ +44 + | ++ + + + + + | + @NonNull private final AssertionRequest request; |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | + /** |
+ +47 + | ++ + + + + + | + * The client's response to the {@link #getRequest() request}. |
+ +48 + | ++ + + + + + | + * |
+ +49 + | ++ + + + + + | + * @see <a |
+ +50 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-getAssertion">navigator.credentials.get()</a> |
+ +51 + | ++ + + + + + | + */ |
+ +52 + | ++ + + + + + | + @NonNull |
+ +53 + | ++ + + + + + | + private final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> |
+ +54 + | ++ + + + + + | + response; |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** |
+ +57 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +58 + | ++ + + + + + | + * connection to the client, if any. |
+ +59 + | ++ + + + + + | + * |
+ +60 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +61 + | ++ + + + + + | + */ |
+ +62 + | ++ + + + + + | + private final ByteArray callerTokenBindingId; |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + /** |
+ +65 + | ++ + + + + + | + * EXPERIMENTAL FEATURE: |
+ +66 + | ++ + + + + + | + * |
+ +67 + | ++ + + + + + | + * <p>If set to <code>false</code> (the default), the <code>"type"</code> property in the <a |
+ +68 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictionary-client-data">collected |
+ +69 + | ++ + + + + + | + * client data</a> of the assertion will be verified to equal <code>"webauthn.get"</code>. |
+ +70 + | ++ + + + + + | + * |
+ +71 + | ++ + + + + + | + * <p>If set to <code>true</code>, it will instead be verified to equal <code>"payment.get"</code> |
+ +72 + | ++ + + + + + | + * . |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * <p>NOTE: If you're using <a |
+ +75 + | ++ + + + + + | + * href="https://www.w3.org/TR/2023/CR-secure-payment-confirmation-20230615/">Secure Payment |
+ +76 + | ++ + + + + + | + * Confirmation</a> (SPC), you likely also need to relax the origin validation logic. Right now |
+ +77 + | ++ + + + + + | + * this library only supports matching against a finite {@link Set} of acceptable origins. If |
+ +78 + | ++ + + + + + | + * necessary, your application may validate the origin externally (see {@link |
+ +79 + | ++ + + + + + | + * PublicKeyCredential#getResponse()}, {@link AuthenticatorAssertionResponse#getClientData()} and |
+ +80 + | ++ + + + + + | + * {@link CollectedClientData#getOrigin()}) and construct a new {@link RelyingParty} instance for |
+ +81 + | ++ + + + + + | + * each SPC response, setting the {@link RelyingParty.RelyingPartyBuilder#origins(Set) origins} |
+ +82 + | ++ + + + + + | + * setting on that instance to contain the pre-validated origin value. |
+ +83 + | ++ + + + + + | + * |
+ +84 + | ++ + + + + + | + * <p>Better support for relaxing origin validation may be added as the feature matures. |
+ +85 + | ++ + + + + + | + * |
+ +86 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +87 + | ++ + + + + + | + * before reaching a mature release. |
+ +88 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2023/CR-secure-payment-confirmation-20230615/">Secure |
+ +89 + | ++ + + + + + | + * Payment Confirmation</a> |
+ +90 + | ++ + + + + + | + * @see <a |
+ +91 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictionary-client-data">5.8.1. |
+ +92 + | ++ + + + + + | + * Client Data Used in WebAuthn Signatures (dictionary CollectedClientData)</a> |
+ +93 + | ++ + + + + + | + * @see RelyingParty.RelyingPartyBuilder#origins(Set) |
+ +94 + | ++ + + + + + | + * @see CollectedClientData |
+ +95 + | ++ + + + + + | + * @see CollectedClientData#getOrigin() |
+ +96 + | ++ + + + + + | + */ |
+ +97 + | ++ + + + + + | + @Deprecated @Builder.Default private final boolean isSecurePaymentConfirmation = false; |
+ +98 + | ++ + + + + + | +|
+ +99 + | ++ + + + + + | + /** |
+ +100 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +101 + | ++ + + + + + | + * connection to the client, if any. |
+ +102 + | ++ + + + + + | + * |
+ +103 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +104 + | ++ + + + + + | + */ |
+ +105 + | ++ + + + + + | + public Optional<ByteArray> getCallerTokenBindingId() { |
+ +106 + | +
+
+1
+
+1. getCallerTokenBindingId : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionOptions::getCallerTokenBindingId → KILLED + + + + |
+ return Optional.ofNullable(callerTokenBindingId); |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | +|
+ +109 + | ++ + + + + + | + public static FinishAssertionOptionsBuilder.MandatoryStages builder() { |
+ +110 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/FinishAssertionOptions::builder → KILLED + + + + |
+ return new FinishAssertionOptionsBuilder.MandatoryStages(); |
+ +111 + | ++ + + + + + | + } |
+ +112 + | ++ + + + + + | +|
+ +113 + | ++ + + + + + | + public static class FinishAssertionOptionsBuilder { |
+ +114 + | ++ + + + + + | + private ByteArray callerTokenBindingId = null; |
+ +115 + | ++ + + + + + | +|
+ +116 + | ++ + + + + + | + public static class MandatoryStages { |
+ +117 + | ++ + + + + + | + private final FinishAssertionOptionsBuilder builder = new FinishAssertionOptionsBuilder(); |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * {@link FinishAssertionOptionsBuilder#request(AssertionRequest) request} is a required |
+ +121 + | ++ + + + + + | + * parameter. |
+ +122 + | ++ + + + + + | + * |
+ +123 + | ++ + + + + + | + * @see FinishAssertionOptionsBuilder#request(AssertionRequest) |
+ +124 + | ++ + + + + + | + */ |
+ +125 + | ++ + + + + + | + public Step2 request(AssertionRequest request) { |
+ +126 + | ++ + + + + + | + builder.request(request); |
+ +127 + | +
+
+1
+
+1. request : replaced return value with null for com/yubico/webauthn/FinishAssertionOptions$FinishAssertionOptionsBuilder$MandatoryStages::request → KILLED + + + + |
+ return new Step2(); |
+ +128 + | ++ + + + + + | + } |
+ +129 + | ++ + + + + + | +|
+ +130 + | ++ + + + + + | + public class Step2 { |
+ +131 + | ++ + + + + + | + /** |
+ +132 + | ++ + + + + + | + * {@link FinishAssertionOptionsBuilder#response(PublicKeyCredential) response} is a |
+ +133 + | ++ + + + + + | + * required parameter. |
+ +134 + | ++ + + + + + | + * |
+ +135 + | ++ + + + + + | + * @see FinishAssertionOptionsBuilder#response(PublicKeyCredential) |
+ +136 + | ++ + + + + + | + */ |
+ +137 + | ++ + + + + + | + public FinishAssertionOptionsBuilder response( |
+ +138 + | ++ + + + + + | + PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> |
+ +139 + | ++ + + + + + | + response) { |
+ +140 + | +
+
+1
+
+1. response : replaced return value with null for com/yubico/webauthn/FinishAssertionOptions$FinishAssertionOptionsBuilder$MandatoryStages$Step2::response → KILLED + + + + |
+ return builder.response(response); |
+ +141 + | ++ + + + + + | + } |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | + } |
+ +144 + | ++ + + + + + | +|
+ +145 + | ++ + + + + + | + /** |
+ +146 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +147 + | ++ + + + + + | + * connection to the client, if any. |
+ +148 + | ++ + + + + + | + * |
+ +149 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +150 + | ++ + + + + + | + */ |
+ +151 + | ++ + + + + + | + public FinishAssertionOptionsBuilder callerTokenBindingId( |
+ +152 + | +
+
+1
+
+1. callerTokenBindingId : negated conditional → KILLED + + + + |
+ @NonNull Optional<ByteArray> callerTokenBindingId) { |
+ +153 + | ++ + + + + + | + this.callerTokenBindingId = callerTokenBindingId.orElse(null); |
+ +154 + | +
+
+1
+
+1. callerTokenBindingId : replaced return value with null for com/yubico/webauthn/FinishAssertionOptions$FinishAssertionOptionsBuilder::callerTokenBindingId → KILLED + + + + |
+ return this; |
+ +155 + | ++ + + + + + | + } |
+ +156 + | ++ + + + + + | +|
+ +157 + | ++ + + + + + | + /** |
+ +158 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +159 + | ++ + + + + + | + * connection to the client, if any. |
+ +160 + | ++ + + + + + | + * |
+ +161 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +162 + | ++ + + + + + | + */ |
+ +163 + | ++ + + + + + | + public FinishAssertionOptionsBuilder callerTokenBindingId( |
+ +164 + | +
+
+1
+
+1. callerTokenBindingId : negated conditional → NO_COVERAGE + + + + |
+ @NonNull ByteArray callerTokenBindingId) { |
+ +165 + | +
+
+1
+
+1. callerTokenBindingId : replaced return value with null for com/yubico/webauthn/FinishAssertionOptions$FinishAssertionOptionsBuilder::callerTokenBindingId → NO_COVERAGE + + + + |
+ return this.callerTokenBindingId(Optional.of(callerTokenBindingId)); |
+ +166 + | ++ + + + + + | + } |
+ +167 + | ++ + + + + + | + } |
+ +168 + | ++ + + + + + | +} |
Mutations | ||
106 | ++ |
+
+
+
+ 1.1 |
+
110 | ++ |
+
+
+
+ 1.1 |
+
127 | ++ |
+
+
+
+ 1.1 |
+
140 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 |
+
154 | ++ |
+
+
+
+ 1.1 |
+
164 | ++ |
+
+
+
+ 1.1 |
+
165 | ++ |
+
+
+
+ 1.1 |
+
+ +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 static com.yubico.internal.util.ExceptionUtil.assertTrue; |
+ +28 + | ++ + + + + + | +|
+ +29 + | ++ + + + + + | +import COSE.CoseException; |
+ +30 + | ++ + + + + + | +import com.yubico.internal.util.OptionalUtil; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAssertionResponse; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.ClientAssertionExtensionOutputs; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.CollectedClientData; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredential; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.UserVerificationRequirement; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.exception.InvalidSignatureCountException; |
+ +39 + | ++ + + + + + | +import com.yubico.webauthn.extension.appid.AppId; |
+ +40 + | ++ + + + + + | +import java.io.IOException; |
+ +41 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +42 + | ++ + + + + + | +import java.security.PublicKey; |
+ +43 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +44 + | ++ + + + + + | +import java.util.Optional; |
+ +45 + | ++ + + + + + | +import java.util.Set; |
+ +46 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +47 + | ++ + + + + + | +import lombok.Value; |
+ +48 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | +@Slf4j |
+ +51 + | ++ + + + + + | +@AllArgsConstructor |
+ +52 + | ++ + + + + + | +final class FinishAssertionSteps<C extends CredentialRecord> { |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + private static final String CLIENT_DATA_TYPE = "webauthn.get"; |
+ +55 + | ++ + + + + + | + private static final String SPC_CLIENT_DATA_TYPE = "payment.get"; |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + private final AssertionRequest request; |
+ +58 + | ++ + + + + + | + private final PublicKeyCredential<AuthenticatorAssertionResponse, ClientAssertionExtensionOutputs> |
+ +59 + | ++ + + + + + | + response; |
+ +60 + | ++ + + + + + | + private final Optional<ByteArray> callerTokenBindingId; |
+ +61 + | ++ + + + + + | + private final Set<String> origins; |
+ +62 + | ++ + + + + + | + private final String rpId; |
+ +63 + | ++ + + + + + | + private final CredentialRepositoryV2<C> credentialRepositoryV2; |
+ +64 + | ++ + + + + + | + private final Optional<UsernameRepository> usernameRepository; |
+ +65 + | ++ + + + + + | + private final boolean allowOriginPort; |
+ +66 + | ++ + + + + + | + private final boolean allowOriginSubdomain; |
+ +67 + | ++ + + + + + | + private final boolean validateSignatureCounter; |
+ +68 + | ++ + + + + + | + private final boolean isSecurePaymentConfirmation; |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + static FinishAssertionSteps<RegisteredCredential> fromV1( |
+ +71 + | ++ + + + + + | + RelyingParty rp, FinishAssertionOptions options) { |
+ +72 + | ++ + + + + + | + final CredentialRepository credRepo = rp.getCredentialRepository(); |
+ +73 + | ++ + + + + + | + final CredentialRepositoryV1ToV2Adapter credRepoV2 = |
+ +74 + | ++ + + + + + | + new CredentialRepositoryV1ToV2Adapter(credRepo); |
+ +75 + | +
+
+1
+
+1. fromV1 : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps::fromV1 → KILLED + + + + |
+ return new FinishAssertionSteps<>( |
+ +76 + | ++ + + + + + | + options.getRequest(), |
+ +77 + | ++ + + + + + | + options.getResponse(), |
+ +78 + | ++ + + + + + | + options.getCallerTokenBindingId(), |
+ +79 + | ++ + + + + + | + rp.getOrigins(), |
+ +80 + | ++ + + + + + | + rp.getIdentity().getId(), |
+ +81 + | ++ + + + + + | + credRepoV2, |
+ +82 + | ++ + + + + + | + Optional.of(credRepoV2), |
+ +83 + | ++ + + + + + | + rp.isAllowOriginPort(), |
+ +84 + | ++ + + + + + | + rp.isAllowOriginSubdomain(), |
+ +85 + | ++ + + + + + | + rp.isValidateSignatureCounter(), |
+ +86 + | ++ + + + + + | + options.isSecurePaymentConfirmation()); |
+ +87 + | ++ + + + + + | + } |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + FinishAssertionSteps(RelyingPartyV2<C> rp, FinishAssertionOptions options) { |
+ +90 + | ++ + + + + + | + this( |
+ +91 + | ++ + + + + + | + options.getRequest(), |
+ +92 + | ++ + + + + + | + options.getResponse(), |
+ +93 + | ++ + + + + + | + options.getCallerTokenBindingId(), |
+ +94 + | ++ + + + + + | + rp.getOrigins(), |
+ +95 + | ++ + + + + + | + rp.getIdentity().getId(), |
+ +96 + | ++ + + + + + | + rp.getCredentialRepository(), |
+ +97 + | ++ + + + + + | + Optional.ofNullable(rp.getUsernameRepository()), |
+ +98 + | ++ + + + + + | + rp.isAllowOriginPort(), |
+ +99 + | ++ + + + + + | + rp.isAllowOriginSubdomain(), |
+ +100 + | ++ + + + + + | + rp.isValidateSignatureCounter(), |
+ +101 + | ++ + + + + + | + options.isSecurePaymentConfirmation()); |
+ +102 + | ++ + + + + + | + } |
+ +103 + | ++ + + + + + | +|
+ +104 + | ++ + + + + + | + private Optional<String> getUsernameForUserHandle(final ByteArray userHandle) { |
+ +105 + | +
+
+2
+
+1. getUsernameForUserHandle : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps::getUsernameForUserHandle → KILLED +2. lambda$getUsernameForUserHandle$0 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps::lambda$getUsernameForUserHandle$0 → KILLED + + + + |
+ return usernameRepository.flatMap(unameRepo -> unameRepo.getUsernameForUserHandle(userHandle)); |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + public Step5 begin() { |
+ +109 + | +
+
+1
+
+1. begin : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps::begin → KILLED + + + + |
+ return new Step5(); |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + public AssertionResult run() throws InvalidSignatureCountException { |
+ +113 + | +
+
+1
+
+1. run : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps::run → KILLED + + + + |
+ return begin().run(); |
+ +114 + | ++ + + + + + | + } |
+ +115 + | ++ + + + + + | +|
+ +116 + | ++ + + + + + | + public AssertionResultV2<C> runV2() throws InvalidSignatureCountException { |
+ +117 + | +
+
+1
+
+1. runV2 : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps::runV2 → KILLED + + + + |
+ return begin().runV2(); |
+ +118 + | ++ + + + + + | + } |
+ +119 + | ++ + + + + + | +|
+ +120 + | ++ + + + + + | + interface Step<C extends CredentialRecord, Next extends Step<C, ?>> { |
+ +121 + | ++ + + + + + | + Next nextStep(); |
+ +122 + | ++ + + + + + | +|
+ +123 + | ++ + + + + + | + void validate() throws InvalidSignatureCountException; |
+ +124 + | ++ + + + + + | +|
+ +125 + | ++ + + + + + | + default Optional<AssertionResult> result() { |
+ +126 + | ++ + + + + + | + return Optional.empty(); |
+ +127 + | ++ + + + + + | + } |
+ +128 + | ++ + + + + + | +|
+ +129 + | ++ + + + + + | + default Optional<AssertionResultV2<C>> resultV2() { |
+ +130 + | ++ + + + + + | + return Optional.empty(); |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | +|
+ +133 + | ++ + + + + + | + default Next next() throws InvalidSignatureCountException { |
+ +134 + | +
+
+1
+
+1. next : removed call to com/yubico/webauthn/FinishAssertionSteps$Step::validate → KILLED + + + + |
+ validate(); |
+ +135 + | +
+
+1
+
+1. next : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step::next → KILLED + + + + |
+ return nextStep(); |
+ +136 + | ++ + + + + + | + } |
+ +137 + | ++ + + + + + | +|
+ +138 + | ++ + + + + + | + default AssertionResult run() throws InvalidSignatureCountException { |
+ +139 + | +
+
+1
+
+1. run : negated conditional → KILLED + + + + |
+ if (result().isPresent()) { |
+ +140 + | +
+
+1
+
+1. run : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step::run → KILLED + + + + |
+ return result().get(); |
+ +141 + | ++ + + + + + | + } else { |
+ +142 + | +
+
+1
+
+1. run : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step::run → KILLED + + + + |
+ return next().run(); |
+ +143 + | ++ + + + + + | + } |
+ +144 + | ++ + + + + + | + } |
+ +145 + | ++ + + + + + | +|
+ +146 + | ++ + + + + + | + default AssertionResultV2<C> runV2() throws InvalidSignatureCountException { |
+ +147 + | +
+
+1
+
+1. runV2 : negated conditional → KILLED + + + + |
+ if (resultV2().isPresent()) { |
+ +148 + | +
+
+1
+
+1. runV2 : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step::runV2 → KILLED + + + + |
+ return resultV2().get(); |
+ +149 + | ++ + + + + + | + } else { |
+ +150 + | +
+
+1
+
+1. runV2 : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step::runV2 → KILLED + + + + |
+ return next().runV2(); |
+ +151 + | ++ + + + + + | + } |
+ +152 + | ++ + + + + + | + } |
+ +153 + | ++ + + + + + | + } |
+ +154 + | ++ + + + + + | +|
+ +155 + | ++ + + + + + | + // Steps 1 through 4 are to create the request and run the client-side part |
+ +156 + | ++ + + + + + | +|
+ +157 + | ++ + + + + + | + @Value |
+ +158 + | ++ + + + + + | + class Step5 implements Step<C, Step6> { |
+ +159 + | ++ + + + + + | + @Override |
+ +160 + | ++ + + + + + | + public Step6 nextStep() { |
+ +161 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step5::nextStep → KILLED + + + + |
+ return new Step6(); |
+ +162 + | ++ + + + + + | + } |
+ +163 + | ++ + + + + + | +|
+ +164 + | ++ + + + + + | + @Override |
+ +165 + | ++ + + + + + | + public void validate() { |
+ +166 + | ++ + + + + + | + request |
+ +167 + | ++ + + + + + | + .getPublicKeyCredentialRequestOptions() |
+ +168 + | ++ + + + + + | + .getAllowCredentials() |
+ +169 + | +
+
+2
+
+1. lambda$validate$0 : negated conditional → KILLED +2. lambda$validate$0 : replaced boolean return with true for com/yubico/webauthn/FinishAssertionSteps$Step5::lambda$validate$0 → KILLED + + + + |
+ .filter(allowCredentials -> !allowCredentials.isEmpty()) |
+ +170 + | +
+
+1
+
+1. validate : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ .ifPresent( |
+ +171 + | ++ + + + + + | + allowed -> { |
+ +172 + | +
+
+1
+
+1. lambda$validate$2 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +173 + | +
+
+2
+
+1. lambda$validate$1 : replaced boolean return with false for com/yubico/webauthn/FinishAssertionSteps$Step5::lambda$validate$1 → KILLED +2. lambda$validate$1 : replaced boolean return with true for com/yubico/webauthn/FinishAssertionSteps$Step5::lambda$validate$1 → KILLED + + + + |
+ allowed.stream().anyMatch(allow -> allow.getId().equals(response.getId())), |
+ +174 + | ++ + + + + + | + "Unrequested credential ID: %s", |
+ +175 + | ++ + + + + + | + response.getId()); |
+ +176 + | ++ + + + + + | + }); |
+ +177 + | ++ + + + + + | + } |
+ +178 + | ++ + + + + + | + } |
+ +179 + | ++ + + + + + | +|
+ +180 + | ++ + + + + + | + @Value |
+ +181 + | ++ + + + + + | + class Step6 implements Step<C, Step7> { |
+ +182 + | ++ + + + + + | +|
+ +183 + | ++ + + + + + | + private final Optional<ByteArray> requestedUserHandle; |
+ +184 + | ++ + + + + + | + private final Optional<String> requestedUsername; |
+ +185 + | ++ + + + + + | + private final Optional<ByteArray> responseUserHandle; |
+ +186 + | ++ + + + + + | +|
+ +187 + | ++ + + + + + | + private final Optional<ByteArray> effectiveRequestUserHandle; |
+ +188 + | ++ + + + + + | + private final Optional<String> effectiveRequestUsername; |
+ +189 + | ++ + + + + + | + private final boolean userHandleDerivedFromUsername; |
+ +190 + | ++ + + + + + | +|
+ +191 + | ++ + + + + + | + private final Optional<ByteArray> finalUserHandle; |
+ +192 + | ++ + + + + + | + private final Optional<String> finalUsername; |
+ +193 + | ++ + + + + + | + private final Optional<C> registration; |
+ +194 + | ++ + + + + + | +|
+ +195 + | ++ + + + + + | + public Step6() { |
+ +196 + | ++ + + + + + | + requestedUserHandle = request.getUserHandle(); |
+ +197 + | ++ + + + + + | + requestedUsername = request.getUsername(); |
+ +198 + | ++ + + + + + | + responseUserHandle = response.getResponse().getUserHandle(); |
+ +199 + | ++ + + + + + | +|
+ +200 + | ++ + + + + + | + effectiveRequestUserHandle = |
+ +201 + | ++ + + + + + | + OptionalUtil.orElseOptional( |
+ +202 + | ++ + + + + + | + requestedUserHandle, |
+ +203 + | ++ + + + + + | + () -> |
+ +204 + | +
+
+1
+
+1. lambda$new$1 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$1 → KILLED + + + + |
+ usernameRepository.flatMap( |
+ +205 + | +
+
+1
+
+1. lambda$new$0 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$0 → KILLED + + + + |
+ unr -> requestedUsername.flatMap(unr::getUserHandleForUsername))); |
+ +206 + | ++ + + + + + | +|
+ +207 + | ++ + + + + + | + effectiveRequestUsername = |
+ +208 + | ++ + + + + + | + OptionalUtil.orElseOptional( |
+ +209 + | ++ + + + + + | + requestedUsername, |
+ +210 + | ++ + + + + + | + () -> |
+ +211 + | +
+
+2
+
+1. lambda$new$3 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$3 → SURVIVED +2. lambda$new$2 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$2 → SURVIVED + + + + |
+ requestedUserHandle.flatMap(FinishAssertionSteps.this::getUsernameForUserHandle)); |
+ +212 + | ++ + + + + + | +|
+ +213 + | ++ + + + + + | + userHandleDerivedFromUsername = |
+ +214 + | +
+
+2
+
+1. <init> : negated conditional → KILLED +2. <init> : negated conditional → KILLED + + + + |
+ !requestedUserHandle.isPresent() && effectiveRequestUserHandle.isPresent(); |
+ +215 + | ++ + + + + + | +|
+ +216 + | ++ + + + + + | + finalUserHandle = OptionalUtil.orOptional(effectiveRequestUserHandle, responseUserHandle); |
+ +217 + | ++ + + + + + | + finalUsername = |
+ +218 + | ++ + + + + + | + OptionalUtil.orElseOptional( |
+ +219 + | ++ + + + + + | + effectiveRequestUsername, |
+ +220 + | +
+
+2
+
+1. lambda$new$5 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$5 → KILLED +2. lambda$new$4 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$4 → KILLED + + + + |
+ () -> finalUserHandle.flatMap(FinishAssertionSteps.this::getUsernameForUserHandle)); |
+ +221 + | ++ + + + + + | +|
+ +222 + | ++ + + + + + | + registration = |
+ +223 + | +
+
+1
+
+1. lambda$new$6 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Step6::lambda$new$6 → KILLED + + + + |
+ finalUserHandle.flatMap(uh -> credentialRepositoryV2.lookup(response.getId(), uh)); |
+ +224 + | ++ + + + + + | + } |
+ +225 + | ++ + + + + + | +|
+ +226 + | ++ + + + + + | + @Override |
+ +227 + | ++ + + + + + | + public Step7 nextStep() { |
+ +228 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step6::nextStep → KILLED + + + + |
+ return new Step7(finalUsername, finalUserHandle.get(), registration); |
+ +229 + | ++ + + + + + | + } |
+ +230 + | ++ + + + + + | +|
+ +231 + | ++ + + + + + | + @Override |
+ +232 + | ++ + + + + + | + public void validate() { |
+ +233 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ assertTrue( |
+ +234 + | +
+
+2
+
+1. validate : negated conditional → KILLED +2. validate : negated conditional → KILLED + + + + |
+ !(request.getUsername().isPresent() && !usernameRepository.isPresent()), |
+ +235 + | ++ + + + + + | + "Cannot set request username when usernameRepository is not configured."); |
+ +236 + | ++ + + + + + | +|
+ +237 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ assertTrue( |
+ +238 + | ++ + + + + + | + finalUserHandle.isPresent(), |
+ +239 + | ++ + + + + + | + "Could not identify user to authenticate: none of requested username, requested user handle or response user handle are set."); |
+ +240 + | ++ + + + + + | +|
+ +241 + | +
+
+2
+
+1. validate : negated conditional → KILLED +2. validate : negated conditional → KILLED + + + + |
+ if (requestedUserHandle.isPresent() && responseUserHandle.isPresent()) { |
+ +242 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +243 + | ++ + + + + + | + requestedUserHandle.get().equals(responseUserHandle.get()), |
+ +244 + | ++ + + + + + | + "User handle set in request (%s) does not match user handle in response (%s).", |
+ +245 + | ++ + + + + + | + requestedUserHandle.get(), |
+ +246 + | ++ + + + + + | + responseUserHandle.get()); |
+ +247 + | ++ + + + + + | + } |
+ +248 + | ++ + + + + + | +|
+ +249 + | +
+
+2
+
+1. validate : negated conditional → KILLED +2. validate : negated conditional → KILLED + + + + |
+ if (userHandleDerivedFromUsername && responseUserHandle.isPresent()) { |
+ +250 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +251 + | ++ + + + + + | + effectiveRequestUserHandle.get().equals(responseUserHandle.get()), |
+ +252 + | ++ + + + + + | + "User handle in request (%s) (derived from username: %s) does not match user handle in response (%s).", |
+ +253 + | ++ + + + + + | + effectiveRequestUserHandle.get(), |
+ +254 + | ++ + + + + + | + requestedUsername.get(), |
+ +255 + | ++ + + + + + | + responseUserHandle.get()); |
+ +256 + | ++ + + + + + | + } |
+ +257 + | ++ + + + + + | +|
+ +258 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue(registration.isPresent(), "Unknown credential: %s", response.getId()); |
+ +259 + | ++ + + + + + | +|
+ +260 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +261 + | ++ + + + + + | + finalUserHandle.get().equals(registration.get().getUserHandle()), |
+ +262 + | ++ + + + + + | + "User handle %s does not own credential %s", |
+ +263 + | ++ + + + + + | + finalUserHandle.get(), |
+ +264 + | ++ + + + + + | + response.getId()); |
+ +265 + | ++ + + + + + | +|
+ +266 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ if (usernameRepository.isPresent()) { |
+ +267 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ assertTrue( |
+ +268 + | ++ + + + + + | + finalUsername.isPresent(), |
+ +269 + | ++ + + + + + | + "Unknown username for user handle: %s", |
+ +270 + | ++ + + + + + | + finalUserHandle.get()); |
+ +271 + | ++ + + + + + | + } |
+ +272 + | ++ + + + + + | + } |
+ +273 + | ++ + + + + + | + } |
+ +274 + | ++ + + + + + | +|
+ +275 + | ++ + + + + + | + @Value |
+ +276 + | ++ + + + + + | + class Step7 implements Step<C, Step8> { |
+ +277 + | ++ + + + + + | + private final Optional<String> username; |
+ +278 + | ++ + + + + + | + private final ByteArray userHandle; |
+ +279 + | ++ + + + + + | + private final Optional<C> credential; |
+ +280 + | ++ + + + + + | +|
+ +281 + | ++ + + + + + | + @Override |
+ +282 + | ++ + + + + + | + public Step8 nextStep() { |
+ +283 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step7::nextStep → KILLED + + + + |
+ return new Step8(username, credential.get()); |
+ +284 + | ++ + + + + + | + } |
+ +285 + | ++ + + + + + | +|
+ +286 + | ++ + + + + + | + @Override |
+ +287 + | ++ + + + + + | + public void validate() { |
+ +288 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +289 + | ++ + + + + + | + credential.isPresent(), |
+ +290 + | ++ + + + + + | + "Unknown credential. Credential ID: %s, user handle: %s", |
+ +291 + | ++ + + + + + | + response.getId(), |
+ +292 + | ++ + + + + + | + userHandle); |
+ +293 + | ++ + + + + + | + } |
+ +294 + | ++ + + + + + | + } |
+ +295 + | ++ + + + + + | +|
+ +296 + | ++ + + + + + | + @Value |
+ +297 + | ++ + + + + + | + class Step8 implements Step<C, Step10> { |
+ +298 + | ++ + + + + + | +|
+ +299 + | ++ + + + + + | + private final Optional<String> username; |
+ +300 + | ++ + + + + + | + private final C credential; |
+ +301 + | ++ + + + + + | +|
+ +302 + | ++ + + + + + | + @Override |
+ +303 + | ++ + + + + + | + public void validate() { |
+ +304 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(clientData() != null, "Missing client data."); |
+ +305 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(authenticatorData() != null, "Missing authenticator data."); |
+ +306 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(signature() != null, "Missing signature."); |
+ +307 + | ++ + + + + + | + } |
+ +308 + | ++ + + + + + | +|
+ +309 + | ++ + + + + + | + @Override |
+ +310 + | ++ + + + + + | + public Step10 nextStep() { |
+ +311 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step8::nextStep → KILLED + + + + |
+ return new Step10(username, credential); |
+ +312 + | ++ + + + + + | + } |
+ +313 + | ++ + + + + + | +|
+ +314 + | ++ + + + + + | + public ByteArray authenticatorData() { |
+ +315 + | +
+
+1
+
+1. authenticatorData : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step8::authenticatorData → KILLED + + + + |
+ return response.getResponse().getAuthenticatorData(); |
+ +316 + | ++ + + + + + | + } |
+ +317 + | ++ + + + + + | +|
+ +318 + | ++ + + + + + | + public ByteArray clientData() { |
+ +319 + | +
+
+1
+
+1. clientData : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step8::clientData → KILLED + + + + |
+ return response.getResponse().getClientDataJSON(); |
+ +320 + | ++ + + + + + | + } |
+ +321 + | ++ + + + + + | +|
+ +322 + | ++ + + + + + | + public ByteArray signature() { |
+ +323 + | +
+
+1
+
+1. signature : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step8::signature → KILLED + + + + |
+ return response.getResponse().getSignature(); |
+ +324 + | ++ + + + + + | + } |
+ +325 + | ++ + + + + + | + } |
+ +326 + | ++ + + + + + | +|
+ +327 + | ++ + + + + + | + // Nothing to do for step 9 |
+ +328 + | ++ + + + + + | +|
+ +329 + | ++ + + + + + | + @Value |
+ +330 + | ++ + + + + + | + class Step10 implements Step<C, Step11> { |
+ +331 + | ++ + + + + + | + private final Optional<String> username; |
+ +332 + | ++ + + + + + | + private final C credential; |
+ +333 + | ++ + + + + + | +|
+ +334 + | ++ + + + + + | + @Override |
+ +335 + | ++ + + + + + | + public void validate() { |
+ +336 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(clientData() != null, "Missing client data."); |
+ +337 + | ++ + + + + + | + } |
+ +338 + | ++ + + + + + | +|
+ +339 + | ++ + + + + + | + @Override |
+ +340 + | ++ + + + + + | + public Step11 nextStep() { |
+ +341 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step10::nextStep → KILLED + + + + |
+ return new Step11(username, credential, clientData()); |
+ +342 + | ++ + + + + + | + } |
+ +343 + | ++ + + + + + | +|
+ +344 + | ++ + + + + + | + public CollectedClientData clientData() { |
+ +345 + | +
+
+1
+
+1. clientData : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step10::clientData → KILLED + + + + |
+ return response.getResponse().getClientData(); |
+ +346 + | ++ + + + + + | + } |
+ +347 + | ++ + + + + + | + } |
+ +348 + | ++ + + + + + | +|
+ +349 + | ++ + + + + + | + @Value |
+ +350 + | ++ + + + + + | + class Step11 implements Step<C, Step12> { |
+ +351 + | ++ + + + + + | + private final Optional<String> username; |
+ +352 + | ++ + + + + + | + private final C credential; |
+ +353 + | ++ + + + + + | + private final CollectedClientData clientData; |
+ +354 + | ++ + + + + + | +|
+ +355 + | ++ + + + + + | + @Override |
+ +356 + | ++ + + + + + | + public void validate() { |
+ +357 + | ++ + + + + + | + final String expectedType = |
+ +358 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ isSecurePaymentConfirmation ? SPC_CLIENT_DATA_TYPE : CLIENT_DATA_TYPE; |
+ +359 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +360 + | ++ + + + + + | + expectedType.equals(clientData.getType()), |
+ +361 + | ++ + + + + + | + "The \"type\" in the client data must be exactly \"%s\", was: %s", |
+ +362 + | ++ + + + + + | + expectedType, |
+ +363 + | ++ + + + + + | + clientData.getType()); |
+ +364 + | ++ + + + + + | + } |
+ +365 + | ++ + + + + + | +|
+ +366 + | ++ + + + + + | + @Override |
+ +367 + | ++ + + + + + | + public Step12 nextStep() { |
+ +368 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step11::nextStep → KILLED + + + + |
+ return new Step12(username, credential); |
+ +369 + | ++ + + + + + | + } |
+ +370 + | ++ + + + + + | + } |
+ +371 + | ++ + + + + + | +|
+ +372 + | ++ + + + + + | + @Value |
+ +373 + | ++ + + + + + | + class Step12 implements Step<C, Step13> { |
+ +374 + | ++ + + + + + | + private final Optional<String> username; |
+ +375 + | ++ + + + + + | + private final C credential; |
+ +376 + | ++ + + + + + | +|
+ +377 + | ++ + + + + + | + @Override |
+ +378 + | ++ + + + + + | + public void validate() { |
+ +379 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +380 + | ++ + + + + + | + request |
+ +381 + | ++ + + + + + | + .getPublicKeyCredentialRequestOptions() |
+ +382 + | ++ + + + + + | + .getChallenge() |
+ +383 + | ++ + + + + + | + .equals(response.getResponse().getClientData().getChallenge()), |
+ +384 + | ++ + + + + + | + "Incorrect challenge."); |
+ +385 + | ++ + + + + + | + } |
+ +386 + | ++ + + + + + | +|
+ +387 + | ++ + + + + + | + @Override |
+ +388 + | ++ + + + + + | + public Step13 nextStep() { |
+ +389 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step12::nextStep → KILLED + + + + |
+ return new Step13(username, credential); |
+ +390 + | ++ + + + + + | + } |
+ +391 + | ++ + + + + + | + } |
+ +392 + | ++ + + + + + | +|
+ +393 + | ++ + + + + + | + @Value |
+ +394 + | ++ + + + + + | + class Step13 implements Step<C, Step14> { |
+ +395 + | ++ + + + + + | + private final Optional<String> username; |
+ +396 + | ++ + + + + + | + private final C credential; |
+ +397 + | ++ + + + + + | +|
+ +398 + | ++ + + + + + | + @Override |
+ +399 + | ++ + + + + + | + public void validate() { |
+ +400 + | ++ + + + + + | + final String responseOrigin = response.getResponse().getClientData().getOrigin(); |
+ +401 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +402 + | ++ + + + + + | + OriginMatcher.isAllowed(responseOrigin, origins, allowOriginPort, allowOriginSubdomain), |
+ +403 + | ++ + + + + + | + "Incorrect origin, please see the RelyingParty.origins setting: %s", |
+ +404 + | ++ + + + + + | + responseOrigin); |
+ +405 + | ++ + + + + + | + } |
+ +406 + | ++ + + + + + | +|
+ +407 + | ++ + + + + + | + @Override |
+ +408 + | ++ + + + + + | + public Step14 nextStep() { |
+ +409 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step13::nextStep → KILLED + + + + |
+ return new Step14(username, credential); |
+ +410 + | ++ + + + + + | + } |
+ +411 + | ++ + + + + + | + } |
+ +412 + | ++ + + + + + | +|
+ +413 + | ++ + + + + + | + @Value |
+ +414 + | ++ + + + + + | + class Step14 implements Step<C, Step15> { |
+ +415 + | ++ + + + + + | + private final Optional<String> username; |
+ +416 + | ++ + + + + + | + private final C credential; |
+ +417 + | ++ + + + + + | +|
+ +418 + | ++ + + + + + | + @Override |
+ +419 + | ++ + + + + + | + public void validate() { |
+ +420 + | ++ + + + + + | + TokenBindingValidator.validate( |
+ +421 + | ++ + + + + + | + response.getResponse().getClientData().getTokenBinding(), callerTokenBindingId); |
+ +422 + | ++ + + + + + | + } |
+ +423 + | ++ + + + + + | +|
+ +424 + | ++ + + + + + | + @Override |
+ +425 + | ++ + + + + + | + public Step15 nextStep() { |
+ +426 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step14::nextStep → KILLED + + + + |
+ return new Step15(username, credential); |
+ +427 + | ++ + + + + + | + } |
+ +428 + | ++ + + + + + | + } |
+ +429 + | ++ + + + + + | +|
+ +430 + | ++ + + + + + | + @Value |
+ +431 + | ++ + + + + + | + class Step15 implements Step<C, Step16> { |
+ +432 + | ++ + + + + + | + private final Optional<String> username; |
+ +433 + | ++ + + + + + | + private final C credential; |
+ +434 + | ++ + + + + + | +|
+ +435 + | ++ + + + + + | + @Override |
+ +436 + | ++ + + + + + | + public void validate() { |
+ +437 + | ++ + + + + + | + try { |
+ +438 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +439 + | ++ + + + + + | + Crypto.sha256(rpId) |
+ +440 + | ++ + + + + + | + .equals(response.getResponse().getParsedAuthenticatorData().getRpIdHash()), |
+ +441 + | ++ + + + + + | + "Wrong RP ID hash."); |
+ +442 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +443 + | ++ + + + + + | + Optional<AppId> appid = |
+ +444 + | ++ + + + + + | + request.getPublicKeyCredentialRequestOptions().getExtensions().getAppid(); |
+ +445 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ if (appid.isPresent()) { |
+ +446 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +447 + | ++ + + + + + | + Crypto.sha256(appid.get().getId()) |
+ +448 + | ++ + + + + + | + .equals(response.getResponse().getParsedAuthenticatorData().getRpIdHash()), |
+ +449 + | ++ + + + + + | + "Wrong RP ID hash."); |
+ +450 + | ++ + + + + + | + } else { |
+ +451 + | ++ + + + + + | + throw e; |
+ +452 + | ++ + + + + + | + } |
+ +453 + | ++ + + + + + | + } |
+ +454 + | ++ + + + + + | + } |
+ +455 + | ++ + + + + + | +|
+ +456 + | ++ + + + + + | + @Override |
+ +457 + | ++ + + + + + | + public Step16 nextStep() { |
+ +458 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step15::nextStep → KILLED + + + + |
+ return new Step16(username, credential); |
+ +459 + | ++ + + + + + | + } |
+ +460 + | ++ + + + + + | + } |
+ +461 + | ++ + + + + + | +|
+ +462 + | ++ + + + + + | + @Value |
+ +463 + | ++ + + + + + | + class Step16 implements Step<C, Step17> { |
+ +464 + | ++ + + + + + | + private final Optional<String> username; |
+ +465 + | ++ + + + + + | + private final C credential; |
+ +466 + | ++ + + + + + | +|
+ +467 + | ++ + + + + + | + @Override |
+ +468 + | ++ + + + + + | + public void validate() { |
+ +469 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +470 + | ++ + + + + + | + response.getResponse().getParsedAuthenticatorData().getFlags().UP, |
+ +471 + | ++ + + + + + | + "User Presence is required."); |
+ +472 + | ++ + + + + + | + } |
+ +473 + | ++ + + + + + | +|
+ +474 + | ++ + + + + + | + @Override |
+ +475 + | ++ + + + + + | + public Step17 nextStep() { |
+ +476 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step16::nextStep → KILLED + + + + |
+ return new Step17(username, credential); |
+ +477 + | ++ + + + + + | + } |
+ +478 + | ++ + + + + + | + } |
+ +479 + | ++ + + + + + | +|
+ +480 + | ++ + + + + + | + @Value |
+ +481 + | ++ + + + + + | + class Step17 implements Step<C, PendingStep16> { |
+ +482 + | ++ + + + + + | + private final Optional<String> username; |
+ +483 + | ++ + + + + + | + private final C credential; |
+ +484 + | ++ + + + + + | +|
+ +485 + | ++ + + + + + | + @Override |
+ +486 + | ++ + + + + + | + public void validate() { |
+ +487 + | ++ + + + + + | + if (request |
+ +488 + | ++ + + + + + | + .getPublicKeyCredentialRequestOptions() |
+ +489 + | ++ + + + + + | + .getUserVerification() |
+ +490 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ .equals(Optional.of(UserVerificationRequirement.REQUIRED))) { |
+ +491 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +492 + | ++ + + + + + | + response.getResponse().getParsedAuthenticatorData().getFlags().UV, |
+ +493 + | ++ + + + + + | + "User Verification is required."); |
+ +494 + | ++ + + + + + | + } |
+ +495 + | ++ + + + + + | + } |
+ +496 + | ++ + + + + + | +|
+ +497 + | ++ + + + + + | + @Override |
+ +498 + | ++ + + + + + | + public PendingStep16 nextStep() { |
+ +499 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step17::nextStep → KILLED + + + + |
+ return new PendingStep16(username, credential); |
+ +500 + | ++ + + + + + | + } |
+ +501 + | ++ + + + + + | + } |
+ +502 + | ++ + + + + + | +|
+ +503 + | ++ + + + + + | + @Value |
+ +504 + | ++ + + + + + | + // Step 16 in editor's draft as of 2022-11-09 https://w3c.github.io/webauthn/ |
+ +505 + | ++ + + + + + | + // TODO: Finalize this when spec matures |
+ +506 + | ++ + + + + + | + class PendingStep16 implements Step<C, Step18> { |
+ +507 + | ++ + + + + + | + private final Optional<String> username; |
+ +508 + | ++ + + + + + | + private final C credential; |
+ +509 + | ++ + + + + + | +|
+ +510 + | ++ + + + + + | + @Override |
+ +511 + | ++ + + + + + | + public void validate() { |
+ +512 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +513 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ !credential.isBackupEligible().isPresent() |
+ +514 + | ++ + + + + + | + || response.getResponse().getParsedAuthenticatorData().getFlags().BE |
+ +515 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ == credential.isBackupEligible().get(), |
+ +516 + | ++ + + + + + | + "Backup eligibility must not change; Stored: BE=%s, received: BE=%s for credential: %s", |
+ +517 + | ++ + + + + + | + credential.isBackupEligible(), |
+ +518 + | ++ + + + + + | + response.getResponse().getParsedAuthenticatorData().getFlags().BE, |
+ +519 + | ++ + + + + + | + credential.getCredentialId()); |
+ +520 + | ++ + + + + + | + } |
+ +521 + | ++ + + + + + | +|
+ +522 + | ++ + + + + + | + @Override |
+ +523 + | ++ + + + + + | + public Step18 nextStep() { |
+ +524 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$PendingStep16::nextStep → KILLED + + + + |
+ return new Step18(username, credential); |
+ +525 + | ++ + + + + + | + } |
+ +526 + | ++ + + + + + | + } |
+ +527 + | ++ + + + + + | +|
+ +528 + | ++ + + + + + | + @Value |
+ +529 + | ++ + + + + + | + class Step18 implements Step<C, Step19> { |
+ +530 + | ++ + + + + + | + private final Optional<String> username; |
+ +531 + | ++ + + + + + | + private final C credential; |
+ +532 + | ++ + + + + + | +|
+ +533 + | ++ + + + + + | + @Override |
+ +534 + | ++ + + + + + | + public void validate() {} |
+ +535 + | ++ + + + + + | +|
+ +536 + | ++ + + + + + | + @Override |
+ +537 + | ++ + + + + + | + public Step19 nextStep() { |
+ +538 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step18::nextStep → KILLED + + + + |
+ return new Step19(username, credential); |
+ +539 + | ++ + + + + + | + } |
+ +540 + | ++ + + + + + | + } |
+ +541 + | ++ + + + + + | +|
+ +542 + | ++ + + + + + | + @Value |
+ +543 + | ++ + + + + + | + class Step19 implements Step<C, Step20> { |
+ +544 + | ++ + + + + + | + private final Optional<String> username; |
+ +545 + | ++ + + + + + | + private final C credential; |
+ +546 + | ++ + + + + + | +|
+ +547 + | ++ + + + + + | + @Override |
+ +548 + | ++ + + + + + | + public void validate() { |
+ +549 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(clientDataJsonHash().size() == 32, "Failed to compute hash of client data"); |
+ +550 + | ++ + + + + + | + } |
+ +551 + | ++ + + + + + | +|
+ +552 + | ++ + + + + + | + @Override |
+ +553 + | ++ + + + + + | + public Step20 nextStep() { |
+ +554 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step19::nextStep → KILLED + + + + |
+ return new Step20(username, credential, clientDataJsonHash()); |
+ +555 + | ++ + + + + + | + } |
+ +556 + | ++ + + + + + | +|
+ +557 + | ++ + + + + + | + public ByteArray clientDataJsonHash() { |
+ +558 + | +
+
+1
+
+1. clientDataJsonHash : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step19::clientDataJsonHash → KILLED + + + + |
+ return Crypto.sha256(response.getResponse().getClientDataJSON()); |
+ +559 + | ++ + + + + + | + } |
+ +560 + | ++ + + + + + | + } |
+ +561 + | ++ + + + + + | +|
+ +562 + | ++ + + + + + | + @Value |
+ +563 + | ++ + + + + + | + class Step20 implements Step<C, Step21> { |
+ +564 + | ++ + + + + + | + private final Optional<String> username; |
+ +565 + | ++ + + + + + | + private final C credential; |
+ +566 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +567 + | ++ + + + + + | +|
+ +568 + | ++ + + + + + | + @Override |
+ +569 + | ++ + + + + + | + public void validate() { |
+ +570 + | ++ + + + + + | + final ByteArray cose = credential.getPublicKeyCose(); |
+ +571 + | ++ + + + + + | + final PublicKey key; |
+ +572 + | ++ + + + + + | +|
+ +573 + | ++ + + + + + | + try { |
+ +574 + | ++ + + + + + | + key = WebAuthnCodecs.importCosePublicKey(cose); |
+ +575 + | ++ + + + + + | + } catch (CoseException | IOException | InvalidKeySpecException e) { |
+ +576 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +577 + | ++ + + + + + | + String.format( |
+ +578 + | ++ + + + + + | + "Failed to decode public key: Credential ID: %s COSE: %s", |
+ +579 + | ++ + + + + + | + credential.getCredentialId().getBase64Url(), cose.getBase64Url()), |
+ +580 + | ++ + + + + + | + e); |
+ +581 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +582 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +583 + | ++ + + + + + | + } |
+ +584 + | ++ + + + + + | +|
+ +585 + | ++ + + + + + | + final COSEAlgorithmIdentifier alg = |
+ +586 + | ++ + + + + + | + COSEAlgorithmIdentifier.fromPublicKey(cose) |
+ +587 + | ++ + + + + + | + .orElseThrow( |
+ +588 + | ++ + + + + + | + () -> |
+ +589 + | +
+
+1
+
+1. lambda$validate$0 : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step20::lambda$validate$0 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +590 + | ++ + + + + + | + String.format("Failed to decode \"alg\" from COSE key: %s", cose))); |
+ +591 + | ++ + + + + + | +|
+ +592 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ if (!Crypto.verifySignature(key, signedBytes(), response.getResponse().getSignature(), alg)) { |
+ +593 + | ++ + + + + + | + throw new IllegalArgumentException("Invalid assertion signature."); |
+ +594 + | ++ + + + + + | + } |
+ +595 + | ++ + + + + + | + } |
+ +596 + | ++ + + + + + | +|
+ +597 + | ++ + + + + + | + @Override |
+ +598 + | ++ + + + + + | + public Step21 nextStep() { |
+ +599 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step20::nextStep → KILLED + + + + |
+ return new Step21(username, credential); |
+ +600 + | ++ + + + + + | + } |
+ +601 + | ++ + + + + + | +|
+ +602 + | ++ + + + + + | + public ByteArray signedBytes() { |
+ +603 + | +
+
+1
+
+1. signedBytes : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step20::signedBytes → KILLED + + + + |
+ return response.getResponse().getAuthenticatorData().concat(clientDataJsonHash); |
+ +604 + | ++ + + + + + | + } |
+ +605 + | ++ + + + + + | + } |
+ +606 + | ++ + + + + + | +|
+ +607 + | ++ + + + + + | + @Value |
+ +608 + | ++ + + + + + | + class Step21 implements Step<C, Finished> { |
+ +609 + | ++ + + + + + | + private final Optional<String> username; |
+ +610 + | ++ + + + + + | + private final C credential; |
+ +611 + | ++ + + + + + | + private final long assertionSignatureCount; |
+ +612 + | ++ + + + + + | + private final long storedSignatureCountBefore; |
+ +613 + | ++ + + + + + | +|
+ +614 + | ++ + + + + + | + public Step21(Optional<String> username, C credential) { |
+ +615 + | ++ + + + + + | + this.username = username; |
+ +616 + | ++ + + + + + | + this.credential = credential; |
+ +617 + | ++ + + + + + | + this.assertionSignatureCount = |
+ +618 + | ++ + + + + + | + response.getResponse().getParsedAuthenticatorData().getSignatureCounter(); |
+ +619 + | ++ + + + + + | + this.storedSignatureCountBefore = credential.getSignatureCount(); |
+ +620 + | ++ + + + + + | + } |
+ +621 + | ++ + + + + + | +|
+ +622 + | ++ + + + + + | + @Override |
+ +623 + | ++ + + + + + | + public void validate() throws InvalidSignatureCountException { |
+ +624 + | +
+
+2
+
+1. validate : negated conditional → KILLED +2. validate : negated conditional → KILLED + + + + |
+ if (validateSignatureCounter && !signatureCounterValid()) { |
+ +625 + | ++ + + + + + | + throw new InvalidSignatureCountException( |
+ +626 + | +
+
+1
+
+1. validate : Replaced long addition with subtraction → KILLED + + + + |
+ response.getId(), storedSignatureCountBefore + 1, assertionSignatureCount); |
+ +627 + | ++ + + + + + | + } |
+ +628 + | ++ + + + + + | + } |
+ +629 + | ++ + + + + + | +|
+ +630 + | ++ + + + + + | + private boolean signatureCounterValid() { |
+ +631 + | +
+
+5
+
+1. signatureCounterValid : negated conditional → KILLED +2. signatureCounterValid : negated conditional → KILLED +3. signatureCounterValid : changed conditional boundary → KILLED +4. signatureCounterValid : replaced boolean return with true for com/yubico/webauthn/FinishAssertionSteps$Step21::signatureCounterValid → KILLED +5. signatureCounterValid : negated conditional → KILLED + + + + |
+ return (assertionSignatureCount == 0 && storedSignatureCountBefore == 0) |
+ +632 + | ++ + + + + + | + || assertionSignatureCount > storedSignatureCountBefore; |
+ +633 + | ++ + + + + + | + } |
+ +634 + | ++ + + + + + | +|
+ +635 + | ++ + + + + + | + @Override |
+ +636 + | ++ + + + + + | + public Finished nextStep() { |
+ +637 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Step21::nextStep → KILLED + + + + |
+ return new Finished(credential, username, assertionSignatureCount, signatureCounterValid()); |
+ +638 + | ++ + + + + + | + } |
+ +639 + | ++ + + + + + | + } |
+ +640 + | ++ + + + + + | +|
+ +641 + | ++ + + + + + | + @Value |
+ +642 + | ++ + + + + + | + class Finished implements Step<C, Finished> { |
+ +643 + | ++ + + + + + | + private final C credential; |
+ +644 + | ++ + + + + + | + private final Optional<String> username; |
+ +645 + | ++ + + + + + | + private final long assertionSignatureCount; |
+ +646 + | ++ + + + + + | + private final boolean signatureCounterValid; |
+ +647 + | ++ + + + + + | +|
+ +648 + | ++ + + + + + | + @Override |
+ +649 + | ++ + + + + + | + public void validate() { |
+ +650 + | ++ + + + + + | + /* No-op */ |
+ +651 + | ++ + + + + + | + } |
+ +652 + | ++ + + + + + | +|
+ +653 + | ++ + + + + + | + @Override |
+ +654 + | ++ + + + + + | + public Finished nextStep() { |
+ +655 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishAssertionSteps$Finished::nextStep → NO_COVERAGE + + + + |
+ return this; |
+ +656 + | ++ + + + + + | + } |
+ +657 + | ++ + + + + + | +|
+ +658 + | ++ + + + + + | + @Override |
+ +659 + | ++ + + + + + | + public Optional<AssertionResult> result() { |
+ +660 + | +
+
+1
+
+1. result : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Finished::result → KILLED + + + + |
+ return Optional.of( |
+ +661 + | ++ + + + + + | + new AssertionResult( |
+ +662 + | ++ + + + + + | + true, |
+ +663 + | ++ + + + + + | + response, |
+ +664 + | ++ + + + + + | + (RegisteredCredential) credential, |
+ +665 + | ++ + + + + + | + username.get(), |
+ +666 + | ++ + + + + + | + signatureCounterValid)); |
+ +667 + | ++ + + + + + | + } |
+ +668 + | ++ + + + + + | +|
+ +669 + | ++ + + + + + | + public Optional<AssertionResultV2<C>> resultV2() { |
+ +670 + | +
+
+1
+
+1. resultV2 : replaced return value with Optional.empty for com/yubico/webauthn/FinishAssertionSteps$Finished::resultV2 → KILLED + + + + |
+ return Optional.of( |
+ +671 + | ++ + + + + + | + new AssertionResultV2<C>(true, response, credential, signatureCounterValid)); |
+ +672 + | ++ + + + + + | + } |
+ +673 + | ++ + + + + + | + } |
+ +674 + | ++ + + + + + | +} |
Mutations | ||
75 | ++ |
+
+
+
+ 1.1 |
+
105 | ++ |
+
+
+
+ 1.1 2.2 |
+
109 | ++ |
+
+
+
+ 1.1 |
+
113 | ++ |
+
+
+
+ 1.1 |
+
117 | ++ |
+
+
+
+ 1.1 |
+
134 | ++ |
+
+
+
+ 1.1 |
+
135 | ++ |
+
+
+
+ 1.1 |
+
139 | ++ |
+
+
+
+ 1.1 |
+
140 | ++ |
+
+
+
+ 1.1 |
+
142 | ++ |
+
+
+
+ 1.1 |
+
147 | ++ |
+
+
+
+ 1.1 |
+
148 | ++ |
+
+
+
+ 1.1 |
+
150 | ++ |
+
+
+
+ 1.1 |
+
161 | ++ |
+
+
+
+ 1.1 |
+
169 | ++ |
+
+
+
+ 1.1 2.2 |
+
170 | ++ |
+
+
+
+ 1.1 |
+
172 | ++ |
+
+
+
+ 1.1 |
+
173 | ++ |
+
+
+
+ 1.1 2.2 |
+
204 | ++ |
+
+
+
+ 1.1 |
+
205 | ++ |
+
+
+
+ 1.1 |
+
211 | ++ |
+
+
+
+ 1.1 2.2 |
+
214 | ++ |
+
+
+
+ 1.1 2.2 |
+
220 | ++ |
+
+
+
+ 1.1 2.2 |
+
223 | ++ |
+
+
+
+ 1.1 |
+
228 | ++ |
+
+
+
+ 1.1 |
+
233 | ++ |
+
+
+
+ 1.1 |
+
234 | ++ |
+
+
+
+ 1.1 2.2 |
+
237 | ++ |
+
+
+
+ 1.1 |
+
241 | ++ |
+
+
+
+ 1.1 2.2 |
+
242 | ++ |
+
+
+
+ 1.1 |
+
249 | ++ |
+
+
+
+ 1.1 2.2 |
+
250 | ++ |
+
+
+
+ 1.1 |
+
258 | ++ |
+
+
+
+ 1.1 |
+
260 | ++ |
+
+
+
+ 1.1 |
+
266 | ++ |
+
+
+
+ 1.1 |
+
267 | ++ |
+
+
+
+ 1.1 |
+
283 | ++ |
+
+
+
+ 1.1 |
+
288 | ++ |
+
+
+
+ 1.1 |
+
304 | ++ |
+
+
+
+ 1.1 2.2 |
+
305 | ++ |
+
+
+
+ 1.1 2.2 |
+
306 | ++ |
+
+
+
+ 1.1 2.2 |
+
311 | ++ |
+
+
+
+ 1.1 |
+
315 | ++ |
+
+
+
+ 1.1 |
+
319 | ++ |
+
+
+
+ 1.1 |
+
323 | ++ |
+
+
+
+ 1.1 |
+
336 | ++ |
+
+
+
+ 1.1 2.2 |
+
341 | ++ |
+
+
+
+ 1.1 |
+
345 | ++ |
+
+
+
+ 1.1 |
+
358 | ++ |
+
+
+
+ 1.1 |
+
359 | ++ |
+
+
+
+ 1.1 |
+
368 | ++ |
+
+
+
+ 1.1 |
+
379 | ++ |
+
+
+
+ 1.1 |
+
389 | ++ |
+
+
+
+ 1.1 |
+
401 | ++ |
+
+
+
+ 1.1 |
+
409 | ++ |
+
+
+
+ 1.1 |
+
426 | ++ |
+
+
+
+ 1.1 |
+
438 | ++ |
+
+
+
+ 1.1 |
+
445 | ++ |
+
+
+
+ 1.1 |
+
446 | ++ |
+
+
+
+ 1.1 |
+
458 | ++ |
+
+
+
+ 1.1 |
+
469 | ++ |
+
+
+
+ 1.1 |
+
476 | ++ |
+
+
+
+ 1.1 |
+
490 | ++ |
+
+
+
+ 1.1 |
+
491 | ++ |
+
+
+
+ 1.1 |
+
499 | ++ |
+
+
+
+ 1.1 |
+
512 | ++ |
+
+
+
+ 1.1 |
+
513 | ++ |
+
+
+
+ 1.1 |
+
515 | ++ |
+
+
+
+ 1.1 |
+
524 | ++ |
+
+
+
+ 1.1 |
+
538 | ++ |
+
+
+
+ 1.1 |
+
549 | ++ |
+
+
+
+ 1.1 2.2 |
+
554 | ++ |
+
+
+
+ 1.1 |
+
558 | ++ |
+
+
+
+ 1.1 |
+
589 | ++ |
+
+
+
+ 1.1 |
+
592 | ++ |
+
+
+
+ 1.1 |
+
599 | ++ |
+
+
+
+ 1.1 |
+
603 | ++ |
+
+
+
+ 1.1 |
+
624 | ++ |
+
+
+
+ 1.1 2.2 |
+
626 | ++ |
+
+
+
+ 1.1 |
+
631 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 5.5 |
+
637 | ++ |
+
+
+
+ 1.1 |
+
655 | ++ |
+
+
+
+ 1.1 |
+
660 | ++ |
+
+
+
+ 1.1 |
+
670 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.webauthn.data.AuthenticatorAttestationResponse; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredential; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +32 + | ++ + + + + + | +import java.util.Optional; |
+ +33 + | ++ + + + + + | +import lombok.Builder; |
+ +34 + | ++ + + + + + | +import lombok.NonNull; |
+ +35 + | ++ + + + + + | +import lombok.Value; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | +/** Parameters for {@link RelyingParty#finishRegistration(FinishRegistrationOptions)}. */ |
+ +38 + | ++ + + + + + | +@Value |
+ +39 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +40 + | ++ + + + + + | +public class FinishRegistrationOptions { |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | + /** The request that the {@link #getResponse() response} is a response to. */ |
+ +43 + | ++ + + + + + | + @NonNull private final PublicKeyCredentialCreationOptions request; |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** |
+ +46 + | ++ + + + + + | + * The client's response to the {@link #getRequest() request}. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * <p><a |
+ +49 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-createCredential">navigator.credentials.create()</a> |
+ +50 + | ++ + + + + + | + */ |
+ +51 + | ++ + + + + + | + @NonNull |
+ +52 + | ++ + + + + + | + private final PublicKeyCredential< |
+ +53 + | ++ + + + + + | + AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +54 + | ++ + + + + + | + response; |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + /** |
+ +57 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +58 + | ++ + + + + + | + * connection to the client, if any. |
+ +59 + | ++ + + + + + | + * |
+ +60 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +61 + | ++ + + + + + | + */ |
+ +62 + | ++ + + + + + | + private final ByteArray callerTokenBindingId; |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + /** |
+ +65 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +66 + | ++ + + + + + | + * connection to the client, if any. |
+ +67 + | ++ + + + + + | + * |
+ +68 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +69 + | ++ + + + + + | + */ |
+ +70 + | ++ + + + + + | + public Optional<ByteArray> getCallerTokenBindingId() { |
+ +71 + | +
+
+1
+
+1. getCallerTokenBindingId : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationOptions::getCallerTokenBindingId → KILLED + + + + |
+ return Optional.ofNullable(callerTokenBindingId); |
+ +72 + | ++ + + + + + | + } |
+ +73 + | ++ + + + + + | +|
+ +74 + | ++ + + + + + | + public static FinishRegistrationOptionsBuilder.MandatoryStages builder() { |
+ +75 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/FinishRegistrationOptions::builder → KILLED + + + + |
+ return new FinishRegistrationOptionsBuilder.MandatoryStages(); |
+ +76 + | ++ + + + + + | + } |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + public static class FinishRegistrationOptionsBuilder { |
+ +79 + | ++ + + + + + | + private ByteArray callerTokenBindingId = null; |
+ +80 + | ++ + + + + + | +|
+ +81 + | ++ + + + + + | + public static class MandatoryStages { |
+ +82 + | ++ + + + + + | + private final FinishRegistrationOptionsBuilder builder = |
+ +83 + | ++ + + + + + | + new FinishRegistrationOptionsBuilder(); |
+ +84 + | ++ + + + + + | +|
+ +85 + | ++ + + + + + | + /** |
+ +86 + | ++ + + + + + | + * {@link FinishRegistrationOptionsBuilder#request(PublicKeyCredentialCreationOptions) |
+ +87 + | ++ + + + + + | + * request} is a required parameter. |
+ +88 + | ++ + + + + + | + * |
+ +89 + | ++ + + + + + | + * @see FinishRegistrationOptionsBuilder#request(PublicKeyCredentialCreationOptions) |
+ +90 + | ++ + + + + + | + */ |
+ +91 + | ++ + + + + + | + public Step2 request(PublicKeyCredentialCreationOptions request) { |
+ +92 + | ++ + + + + + | + builder.request(request); |
+ +93 + | +
+
+1
+
+1. request : replaced return value with null for com/yubico/webauthn/FinishRegistrationOptions$FinishRegistrationOptionsBuilder$MandatoryStages::request → KILLED + + + + |
+ return new Step2(); |
+ +94 + | ++ + + + + + | + } |
+ +95 + | ++ + + + + + | +|
+ +96 + | ++ + + + + + | + public class Step2 { |
+ +97 + | ++ + + + + + | + /** |
+ +98 + | ++ + + + + + | + * {@link FinishRegistrationOptionsBuilder#response(PublicKeyCredential) response} is a |
+ +99 + | ++ + + + + + | + * required parameter. |
+ +100 + | ++ + + + + + | + * |
+ +101 + | ++ + + + + + | + * @see FinishRegistrationOptionsBuilder#response(PublicKeyCredential) |
+ +102 + | ++ + + + + + | + */ |
+ +103 + | ++ + + + + + | + public FinishRegistrationOptionsBuilder response( |
+ +104 + | ++ + + + + + | + PublicKeyCredential< |
+ +105 + | ++ + + + + + | + AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +106 + | ++ + + + + + | + response) { |
+ +107 + | +
+
+1
+
+1. response : replaced return value with null for com/yubico/webauthn/FinishRegistrationOptions$FinishRegistrationOptionsBuilder$MandatoryStages$Step2::response → KILLED + + + + |
+ return builder.response(response); |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | + } |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + /** |
+ +113 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +114 + | ++ + + + + + | + * connection to the client, if any. |
+ +115 + | ++ + + + + + | + * |
+ +116 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +117 + | ++ + + + + + | + */ |
+ +118 + | ++ + + + + + | + public FinishRegistrationOptionsBuilder callerTokenBindingId( |
+ +119 + | +
+
+1
+
+1. callerTokenBindingId : negated conditional → KILLED + + + + |
+ @NonNull Optional<ByteArray> callerTokenBindingId) { |
+ +120 + | ++ + + + + + | + this.callerTokenBindingId = callerTokenBindingId.orElse(null); |
+ +121 + | +
+
+1
+
+1. callerTokenBindingId : replaced return value with null for com/yubico/webauthn/FinishRegistrationOptions$FinishRegistrationOptionsBuilder::callerTokenBindingId → KILLED + + + + |
+ return this; |
+ +122 + | ++ + + + + + | + } |
+ +123 + | ++ + + + + + | +|
+ +124 + | ++ + + + + + | + /** |
+ +125 + | ++ + + + + + | + * The <a href="https://tools.ietf.org/html/rfc8471#section-3.2">token binding ID</a> of the |
+ +126 + | ++ + + + + + | + * connection to the client, if any. |
+ +127 + | ++ + + + + + | + * |
+ +128 + | ++ + + + + + | + * @see <a href="https://tools.ietf.org/html/rfc8471">The Token Binding Protocol Version 1.0</a> |
+ +129 + | ++ + + + + + | + */ |
+ +130 + | ++ + + + + + | + public FinishRegistrationOptionsBuilder callerTokenBindingId( |
+ +131 + | +
+
+1
+
+1. callerTokenBindingId : negated conditional → NO_COVERAGE + + + + |
+ @NonNull ByteArray callerTokenBindingId) { |
+ +132 + | +
+
+1
+
+1. callerTokenBindingId : replaced return value with null for com/yubico/webauthn/FinishRegistrationOptions$FinishRegistrationOptionsBuilder::callerTokenBindingId → NO_COVERAGE + + + + |
+ return this.callerTokenBindingId(Optional.of(callerTokenBindingId)); |
+ +133 + | ++ + + + + + | + } |
+ +134 + | ++ + + + + + | + } |
+ +135 + | ++ + + + + + | +} |
Mutations | ||
71 | ++ |
+
+
+
+ 1.1 |
+
75 | ++ |
+
+
+
+ 1.1 |
+
93 | ++ |
+
+
+
+ 1.1 |
+
107 | ++ |
+
+
+
+ 1.1 |
+
119 | ++ |
+
+
+
+ 1.1 |
+
121 | ++ |
+
+
+
+ 1.1 |
+
131 | ++ |
+
+
+
+ 1.1 |
+
132 | ++ |
+
+
+
+ 1.1 |
+
+ +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 static com.yubico.internal.util.ExceptionUtil.assertTrue; |
+ +28 + | ++ + + + + + | +import static com.yubico.internal.util.ExceptionUtil.wrapAndLog; |
+ +29 + | ++ + + + + + | +|
+ +30 + | ++ + + + + + | +import COSE.CoseException; |
+ +31 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +32 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +33 + | ++ + + + + + | +import com.yubico.internal.util.OptionalUtil; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.attestation.AttestationTrustSource; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.attestation.AttestationTrustSource.TrustRootsResult; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAttestationResponse; |
+ +39 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorSelectionCriteria; |
+ +40 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +41 + | ++ + + + + + | +import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs; |
+ +42 + | ++ + + + + + | +import com.yubico.webauthn.data.CollectedClientData; |
+ +43 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredential; |
+ +44 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +45 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialParameters; |
+ +46 + | ++ + + + + + | +import com.yubico.webauthn.data.UserVerificationRequirement; |
+ +47 + | ++ + + + + + | +import java.io.IOException; |
+ +48 + | ++ + + + + + | +import java.security.InvalidAlgorithmParameterException; |
+ +49 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +50 + | ++ + + + + + | +import java.security.cert.CertPath; |
+ +51 + | ++ + + + + + | +import java.security.cert.CertPathValidator; |
+ +52 + | ++ + + + + + | +import java.security.cert.CertPathValidatorException; |
+ +53 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +54 + | ++ + + + + + | +import java.security.cert.CertificateFactory; |
+ +55 + | ++ + + + + + | +import java.security.cert.PKIXCertPathValidatorResult; |
+ +56 + | ++ + + + + + | +import java.security.cert.PKIXParameters; |
+ +57 + | ++ + + + + + | +import java.security.cert.PKIXReason; |
+ +58 + | ++ + + + + + | +import java.security.cert.TrustAnchor; |
+ +59 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +60 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +61 + | ++ + + + + + | +import java.sql.Date; |
+ +62 + | ++ + + + + + | +import java.time.Clock; |
+ +63 + | ++ + + + + + | +import java.util.List; |
+ +64 + | ++ + + + + + | +import java.util.Optional; |
+ +65 + | ++ + + + + + | +import java.util.Set; |
+ +66 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +67 + | ++ + + + + + | +import lombok.AllArgsConstructor; |
+ +68 + | ++ + + + + + | +import lombok.Value; |
+ +69 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | +@Slf4j |
+ +72 + | ++ + + + + + | +@AllArgsConstructor |
+ +73 + | ++ + + + + + | +final class FinishRegistrationSteps { |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + private static final String CLIENT_DATA_TYPE = "webauthn.create"; |
+ +76 + | ++ + + + + + | + private static final ByteArray ZERO_AAGUID = |
+ +77 + | ++ + + + + + | + new ByteArray(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); |
+ +78 + | ++ + + + + + | +|
+ +79 + | ++ + + + + + | + private final PublicKeyCredentialCreationOptions request; |
+ +80 + | ++ + + + + + | + private final PublicKeyCredential< |
+ +81 + | ++ + + + + + | + AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +82 + | ++ + + + + + | + response; |
+ +83 + | ++ + + + + + | + private final Optional<ByteArray> callerTokenBindingId; |
+ +84 + | ++ + + + + + | + private final Set<String> origins; |
+ +85 + | ++ + + + + + | + private final String rpId; |
+ +86 + | ++ + + + + + | + private final boolean allowUntrustedAttestation; |
+ +87 + | ++ + + + + + | + private final Optional<AttestationTrustSource> attestationTrustSource; |
+ +88 + | ++ + + + + + | + private final CredentialRepositoryV2<?> credentialRepositoryV2; |
+ +89 + | ++ + + + + + | + private final Clock clock; |
+ +90 + | ++ + + + + + | + private final boolean allowOriginPort; |
+ +91 + | ++ + + + + + | + private final boolean allowOriginSubdomain; |
+ +92 + | ++ + + + + + | +|
+ +93 + | ++ + + + + + | + static FinishRegistrationSteps fromV1(RelyingParty rp, FinishRegistrationOptions options) { |
+ +94 + | +
+
+1
+
+1. fromV1 : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps::fromV1 → KILLED + + + + |
+ return new FinishRegistrationSteps( |
+ +95 + | ++ + + + + + | + options.getRequest(), |
+ +96 + | ++ + + + + + | + options.getResponse(), |
+ +97 + | ++ + + + + + | + options.getCallerTokenBindingId(), |
+ +98 + | ++ + + + + + | + rp.getOrigins(), |
+ +99 + | ++ + + + + + | + rp.getIdentity().getId(), |
+ +100 + | ++ + + + + + | + rp.isAllowUntrustedAttestation(), |
+ +101 + | ++ + + + + + | + rp.getAttestationTrustSource(), |
+ +102 + | ++ + + + + + | + new CredentialRepositoryV1ToV2Adapter(rp.getCredentialRepository()), |
+ +103 + | ++ + + + + + | + rp.getClock(), |
+ +104 + | ++ + + + + + | + rp.isAllowOriginPort(), |
+ +105 + | ++ + + + + + | + rp.isAllowOriginSubdomain()); |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + FinishRegistrationSteps(RelyingPartyV2<?> rp, FinishRegistrationOptions options) { |
+ +109 + | ++ + + + + + | + this( |
+ +110 + | ++ + + + + + | + options.getRequest(), |
+ +111 + | ++ + + + + + | + options.getResponse(), |
+ +112 + | ++ + + + + + | + options.getCallerTokenBindingId(), |
+ +113 + | ++ + + + + + | + rp.getOrigins(), |
+ +114 + | ++ + + + + + | + rp.getIdentity().getId(), |
+ +115 + | ++ + + + + + | + rp.isAllowUntrustedAttestation(), |
+ +116 + | ++ + + + + + | + rp.getAttestationTrustSource(), |
+ +117 + | ++ + + + + + | + rp.getCredentialRepository(), |
+ +118 + | ++ + + + + + | + rp.getClock(), |
+ +119 + | ++ + + + + + | + rp.isAllowOriginPort(), |
+ +120 + | ++ + + + + + | + rp.isAllowOriginSubdomain()); |
+ +121 + | ++ + + + + + | + } |
+ +122 + | ++ + + + + + | +|
+ +123 + | ++ + + + + + | + public Step6 begin() { |
+ +124 + | +
+
+1
+
+1. begin : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps::begin → KILLED + + + + |
+ return new Step6(); |
+ +125 + | ++ + + + + + | + } |
+ +126 + | ++ + + + + + | +|
+ +127 + | ++ + + + + + | + public RegistrationResult run() { |
+ +128 + | +
+
+1
+
+1. run : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps::run → KILLED + + + + |
+ return begin().run(); |
+ +129 + | ++ + + + + + | + } |
+ +130 + | ++ + + + + + | +|
+ +131 + | ++ + + + + + | + interface Step<Next extends Step<?>> { |
+ +132 + | ++ + + + + + | + Next nextStep(); |
+ +133 + | ++ + + + + + | +|
+ +134 + | ++ + + + + + | + void validate(); |
+ +135 + | ++ + + + + + | +|
+ +136 + | ++ + + + + + | + default Optional<RegistrationResult> result() { |
+ +137 + | ++ + + + + + | + return Optional.empty(); |
+ +138 + | ++ + + + + + | + } |
+ +139 + | ++ + + + + + | +|
+ +140 + | ++ + + + + + | + default Next next() { |
+ +141 + | +
+
+1
+
+1. next : removed call to com/yubico/webauthn/FinishRegistrationSteps$Step::validate → KILLED + + + + |
+ validate(); |
+ +142 + | +
+
+1
+
+1. next : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step::next → KILLED + + + + |
+ return nextStep(); |
+ +143 + | ++ + + + + + | + } |
+ +144 + | ++ + + + + + | +|
+ +145 + | ++ + + + + + | + default RegistrationResult run() { |
+ +146 + | +
+
+1
+
+1. run : negated conditional → KILLED + + + + |
+ if (result().isPresent()) { |
+ +147 + | +
+
+1
+
+1. run : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step::run → KILLED + + + + |
+ return result().get(); |
+ +148 + | ++ + + + + + | + } else { |
+ +149 + | +
+
+1
+
+1. run : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step::run → KILLED + + + + |
+ return next().run(); |
+ +150 + | ++ + + + + + | + } |
+ +151 + | ++ + + + + + | + } |
+ +152 + | ++ + + + + + | + } |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + // Steps 1 through 4 are to create the request and run the client-side part |
+ +155 + | ++ + + + + + | +|
+ +156 + | ++ + + + + + | + // Step 5 is integrated into step 6 here |
+ +157 + | ++ + + + + + | +|
+ +158 + | ++ + + + + + | + @Value |
+ +159 + | ++ + + + + + | + class Step6 implements Step<Step7> { |
+ +160 + | ++ + + + + + | + @Override |
+ +161 + | ++ + + + + + | + public void validate() { |
+ +162 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(clientData() != null, "Client data must not be null."); |
+ +163 + | ++ + + + + + | + } |
+ +164 + | ++ + + + + + | +|
+ +165 + | ++ + + + + + | + @Override |
+ +166 + | ++ + + + + + | + public Step7 nextStep() { |
+ +167 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step6::nextStep → KILLED + + + + |
+ return new Step7(clientData()); |
+ +168 + | ++ + + + + + | + } |
+ +169 + | ++ + + + + + | +|
+ +170 + | ++ + + + + + | + public CollectedClientData clientData() { |
+ +171 + | +
+
+1
+
+1. clientData : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step6::clientData → KILLED + + + + |
+ return response.getResponse().getClientData(); |
+ +172 + | ++ + + + + + | + } |
+ +173 + | ++ + + + + + | + } |
+ +174 + | ++ + + + + + | +|
+ +175 + | ++ + + + + + | + @Value |
+ +176 + | ++ + + + + + | + class Step7 implements Step<Step8> { |
+ +177 + | ++ + + + + + | + private final CollectedClientData clientData; |
+ +178 + | ++ + + + + + | +|
+ +179 + | ++ + + + + + | + @Override |
+ +180 + | ++ + + + + + | + public void validate() { |
+ +181 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +182 + | ++ + + + + + | + CLIENT_DATA_TYPE.equals(clientData.getType()), |
+ +183 + | ++ + + + + + | + "The \"type\" in the client data must be exactly \"%s\", was: %s", |
+ +184 + | ++ + + + + + | + CLIENT_DATA_TYPE, |
+ +185 + | ++ + + + + + | + clientData.getType()); |
+ +186 + | ++ + + + + + | + } |
+ +187 + | ++ + + + + + | +|
+ +188 + | ++ + + + + + | + @Override |
+ +189 + | ++ + + + + + | + public Step8 nextStep() { |
+ +190 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step7::nextStep → KILLED + + + + |
+ return new Step8(clientData); |
+ +191 + | ++ + + + + + | + } |
+ +192 + | ++ + + + + + | + } |
+ +193 + | ++ + + + + + | +|
+ +194 + | ++ + + + + + | + @Value |
+ +195 + | ++ + + + + + | + class Step8 implements Step<Step9> { |
+ +196 + | ++ + + + + + | + private final CollectedClientData clientData; |
+ +197 + | ++ + + + + + | +|
+ +198 + | ++ + + + + + | + @Override |
+ +199 + | ++ + + + + + | + public void validate() { |
+ +200 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue(request.getChallenge().equals(clientData.getChallenge()), "Incorrect challenge."); |
+ +201 + | ++ + + + + + | + } |
+ +202 + | ++ + + + + + | +|
+ +203 + | ++ + + + + + | + @Override |
+ +204 + | ++ + + + + + | + public Step9 nextStep() { |
+ +205 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step8::nextStep → KILLED + + + + |
+ return new Step9(clientData); |
+ +206 + | ++ + + + + + | + } |
+ +207 + | ++ + + + + + | + } |
+ +208 + | ++ + + + + + | +|
+ +209 + | ++ + + + + + | + @Value |
+ +210 + | ++ + + + + + | + class Step9 implements Step<Step10> { |
+ +211 + | ++ + + + + + | + private final CollectedClientData clientData; |
+ +212 + | ++ + + + + + | +|
+ +213 + | ++ + + + + + | + @Override |
+ +214 + | ++ + + + + + | + public void validate() { |
+ +215 + | ++ + + + + + | + final String responseOrigin = clientData.getOrigin(); |
+ +216 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +217 + | ++ + + + + + | + OriginMatcher.isAllowed(responseOrigin, origins, allowOriginPort, allowOriginSubdomain), |
+ +218 + | ++ + + + + + | + "Incorrect origin, please see the RelyingParty.origins setting: %s", |
+ +219 + | ++ + + + + + | + responseOrigin); |
+ +220 + | ++ + + + + + | + } |
+ +221 + | ++ + + + + + | +|
+ +222 + | ++ + + + + + | + @Override |
+ +223 + | ++ + + + + + | + public Step10 nextStep() { |
+ +224 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step9::nextStep → KILLED + + + + |
+ return new Step10(clientData); |
+ +225 + | ++ + + + + + | + } |
+ +226 + | ++ + + + + + | + } |
+ +227 + | ++ + + + + + | +|
+ +228 + | ++ + + + + + | + @Value |
+ +229 + | ++ + + + + + | + class Step10 implements Step<Step11> { |
+ +230 + | ++ + + + + + | + private final CollectedClientData clientData; |
+ +231 + | ++ + + + + + | +|
+ +232 + | ++ + + + + + | + @Override |
+ +233 + | ++ + + + + + | + public void validate() { |
+ +234 + | ++ + + + + + | + TokenBindingValidator.validate(clientData.getTokenBinding(), callerTokenBindingId); |
+ +235 + | ++ + + + + + | + } |
+ +236 + | ++ + + + + + | +|
+ +237 + | ++ + + + + + | + @Override |
+ +238 + | ++ + + + + + | + public Step11 nextStep() { |
+ +239 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step10::nextStep → KILLED + + + + |
+ return new Step11(); |
+ +240 + | ++ + + + + + | + } |
+ +241 + | ++ + + + + + | + } |
+ +242 + | ++ + + + + + | +|
+ +243 + | ++ + + + + + | + @Value |
+ +244 + | ++ + + + + + | + class Step11 implements Step<Step12> { |
+ +245 + | ++ + + + + + | + @Override |
+ +246 + | ++ + + + + + | + public void validate() { |
+ +247 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(clientDataJsonHash().size() == 32, "Failed to compute hash of client data"); |
+ +248 + | ++ + + + + + | + } |
+ +249 + | ++ + + + + + | +|
+ +250 + | ++ + + + + + | + @Override |
+ +251 + | ++ + + + + + | + public Step12 nextStep() { |
+ +252 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step11::nextStep → KILLED + + + + |
+ return new Step12(clientDataJsonHash()); |
+ +253 + | ++ + + + + + | + } |
+ +254 + | ++ + + + + + | +|
+ +255 + | ++ + + + + + | + public ByteArray clientDataJsonHash() { |
+ +256 + | +
+
+1
+
+1. clientDataJsonHash : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step11::clientDataJsonHash → KILLED + + + + |
+ return Crypto.sha256(response.getResponse().getClientDataJSON()); |
+ +257 + | ++ + + + + + | + } |
+ +258 + | ++ + + + + + | + } |
+ +259 + | ++ + + + + + | +|
+ +260 + | ++ + + + + + | + @Value |
+ +261 + | ++ + + + + + | + class Step12 implements Step<Step13> { |
+ +262 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +263 + | ++ + + + + + | +|
+ +264 + | ++ + + + + + | + @Override |
+ +265 + | ++ + + + + + | + public void validate() { |
+ +266 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(attestation() != null, "Malformed attestation object."); |
+ +267 + | ++ + + + + + | + } |
+ +268 + | ++ + + + + + | +|
+ +269 + | ++ + + + + + | + @Override |
+ +270 + | ++ + + + + + | + public Step13 nextStep() { |
+ +271 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step12::nextStep → KILLED + + + + |
+ return new Step13(clientDataJsonHash, attestation()); |
+ +272 + | ++ + + + + + | + } |
+ +273 + | ++ + + + + + | +|
+ +274 + | ++ + + + + + | + public AttestationObject attestation() { |
+ +275 + | +
+
+1
+
+1. attestation : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step12::attestation → KILLED + + + + |
+ return response.getResponse().getAttestation(); |
+ +276 + | ++ + + + + + | + } |
+ +277 + | ++ + + + + + | + } |
+ +278 + | ++ + + + + + | +|
+ +279 + | ++ + + + + + | + @Value |
+ +280 + | ++ + + + + + | + class Step13 implements Step<Step14> { |
+ +281 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +282 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +283 + | ++ + + + + + | +|
+ +284 + | ++ + + + + + | + @Override |
+ +285 + | ++ + + + + + | + public void validate() { |
+ +286 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +287 + | ++ + + + + + | + Crypto.sha256(rpId) |
+ +288 + | ++ + + + + + | + .equals(response.getResponse().getAttestation().getAuthenticatorData().getRpIdHash()), |
+ +289 + | ++ + + + + + | + "Wrong RP ID hash."); |
+ +290 + | ++ + + + + + | + } |
+ +291 + | ++ + + + + + | +|
+ +292 + | ++ + + + + + | + @Override |
+ +293 + | ++ + + + + + | + public Step14 nextStep() { |
+ +294 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step13::nextStep → KILLED + + + + |
+ return new Step14(clientDataJsonHash, attestation); |
+ +295 + | ++ + + + + + | + } |
+ +296 + | ++ + + + + + | + } |
+ +297 + | ++ + + + + + | +|
+ +298 + | ++ + + + + + | + @Value |
+ +299 + | ++ + + + + + | + class Step14 implements Step<Step15> { |
+ +300 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +301 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +302 + | ++ + + + + + | +|
+ +303 + | ++ + + + + + | + @Override |
+ +304 + | ++ + + + + + | + public void validate() { |
+ +305 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +306 + | ++ + + + + + | + response.getResponse().getParsedAuthenticatorData().getFlags().UP, |
+ +307 + | ++ + + + + + | + "User Presence is required."); |
+ +308 + | ++ + + + + + | + } |
+ +309 + | ++ + + + + + | +|
+ +310 + | ++ + + + + + | + @Override |
+ +311 + | ++ + + + + + | + public Step15 nextStep() { |
+ +312 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step14::nextStep → KILLED + + + + |
+ return new Step15(clientDataJsonHash, attestation); |
+ +313 + | ++ + + + + + | + } |
+ +314 + | ++ + + + + + | + } |
+ +315 + | ++ + + + + + | +|
+ +316 + | ++ + + + + + | + @Value |
+ +317 + | ++ + + + + + | + class Step15 implements Step<Step16> { |
+ +318 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +319 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +320 + | ++ + + + + + | +|
+ +321 + | ++ + + + + + | + @Override |
+ +322 + | ++ + + + + + | + public void validate() { |
+ +323 + | ++ + + + + + | + if (request |
+ +324 + | ++ + + + + + | + .getAuthenticatorSelection() |
+ +325 + | ++ + + + + + | + .flatMap(AuthenticatorSelectionCriteria::getUserVerification) |
+ +326 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ .orElse(UserVerificationRequirement.PREFERRED) |
+ +327 + | ++ + + + + + | + == UserVerificationRequirement.REQUIRED) { |
+ +328 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +329 + | ++ + + + + + | + response.getResponse().getParsedAuthenticatorData().getFlags().UV, |
+ +330 + | ++ + + + + + | + "User Verification is required."); |
+ +331 + | ++ + + + + + | + } |
+ +332 + | ++ + + + + + | + } |
+ +333 + | ++ + + + + + | +|
+ +334 + | ++ + + + + + | + @Override |
+ +335 + | ++ + + + + + | + public Step16 nextStep() { |
+ +336 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step15::nextStep → KILLED + + + + |
+ return new Step16(clientDataJsonHash, attestation); |
+ +337 + | ++ + + + + + | + } |
+ +338 + | ++ + + + + + | + } |
+ +339 + | ++ + + + + + | +|
+ +340 + | ++ + + + + + | + @Value |
+ +341 + | ++ + + + + + | + class Step16 implements Step<Step18> { |
+ +342 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +343 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +344 + | ++ + + + + + | +|
+ +345 + | ++ + + + + + | + @Override |
+ +346 + | ++ + + + + + | + public void validate() { |
+ +347 + | ++ + + + + + | + final ByteArray publicKeyCose = |
+ +348 + | ++ + + + + + | + response |
+ +349 + | ++ + + + + + | + .getResponse() |
+ +350 + | ++ + + + + + | + .getAttestation() |
+ +351 + | ++ + + + + + | + .getAuthenticatorData() |
+ +352 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +353 + | ++ + + + + + | + .get() |
+ +354 + | ++ + + + + + | + .getCredentialPublicKey(); |
+ +355 + | ++ + + + + + | + CBORObject publicKeyCbor = CBORObject.DecodeFromBytes(publicKeyCose.getBytes()); |
+ +356 + | ++ + + + + + | + final int alg = publicKeyCbor.get(CBORObject.FromObject(3)).AsInt32(); |
+ +357 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +358 + | ++ + + + + + | + request.getPubKeyCredParams().stream() |
+ +359 + | +
+
+2
+
+1. lambda$validate$0 : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step16::lambda$validate$0 → KILLED +2. lambda$validate$0 : negated conditional → KILLED + + + + |
+ .anyMatch(pkcparam -> pkcparam.getAlg().getId() == alg), |
+ +360 + | ++ + + + + + | + "Unrequested credential key algorithm: got %d, expected one of: %s", |
+ +361 + | ++ + + + + + | + alg, |
+ +362 + | ++ + + + + + | + request.getPubKeyCredParams().stream() |
+ +363 + | ++ + + + + + | + .map(PublicKeyCredentialParameters::getAlg) |
+ +364 + | ++ + + + + + | + .collect(Collectors.toList())); |
+ +365 + | ++ + + + + + | + try { |
+ +366 + | ++ + + + + + | + WebAuthnCodecs.importCosePublicKey(publicKeyCose); |
+ +367 + | ++ + + + + + | + } catch (CoseException | IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { |
+ +368 + | ++ + + + + + | + throw wrapAndLog(log, "Failed to parse credential public key", e); |
+ +369 + | ++ + + + + + | + } |
+ +370 + | ++ + + + + + | + } |
+ +371 + | ++ + + + + + | +|
+ +372 + | ++ + + + + + | + @Override |
+ +373 + | ++ + + + + + | + public Step18 nextStep() { |
+ +374 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step16::nextStep → KILLED + + + + |
+ return new Step18(clientDataJsonHash, attestation); |
+ +375 + | ++ + + + + + | + } |
+ +376 + | ++ + + + + + | + } |
+ +377 + | ++ + + + + + | +|
+ +378 + | ++ + + + + + | + // Nothing to do for step 17 |
+ +379 + | ++ + + + + + | +|
+ +380 + | ++ + + + + + | + @Value |
+ +381 + | ++ + + + + + | + class Step18 implements Step<Step19> { |
+ +382 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +383 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +384 + | ++ + + + + + | +|
+ +385 + | ++ + + + + + | + @Override |
+ +386 + | ++ + + + + + | + public void validate() {} |
+ +387 + | ++ + + + + + | +|
+ +388 + | ++ + + + + + | + @Override |
+ +389 + | ++ + + + + + | + public Step19 nextStep() { |
+ +390 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step18::nextStep → KILLED + + + + |
+ return new Step19(clientDataJsonHash, attestation, attestationStatementVerifier()); |
+ +391 + | ++ + + + + + | + } |
+ +392 + | ++ + + + + + | +|
+ +393 + | ++ + + + + + | + public String format() { |
+ +394 + | +
+
+1
+
+1. format : replaced return value with "" for com/yubico/webauthn/FinishRegistrationSteps$Step18::format → KILLED + + + + |
+ return attestation.getFormat(); |
+ +395 + | ++ + + + + + | + } |
+ +396 + | ++ + + + + + | +|
+ +397 + | ++ + + + + + | + public Optional<AttestationStatementVerifier> attestationStatementVerifier() { |
+ +398 + | ++ + + + + + | + switch (format()) { |
+ +399 + | ++ + + + + + | + case "fido-u2f": |
+ +400 + | +
+
+1
+
+1. attestationStatementVerifier : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step18::attestationStatementVerifier → KILLED + + + + |
+ return Optional.of(new FidoU2fAttestationStatementVerifier()); |
+ +401 + | ++ + + + + + | + case "none": |
+ +402 + | +
+
+1
+
+1. attestationStatementVerifier : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step18::attestationStatementVerifier → KILLED + + + + |
+ return Optional.of(new NoneAttestationStatementVerifier()); |
+ +403 + | ++ + + + + + | + case "packed": |
+ +404 + | +
+
+1
+
+1. attestationStatementVerifier : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step18::attestationStatementVerifier → KILLED + + + + |
+ return Optional.of(new PackedAttestationStatementVerifier()); |
+ +405 + | ++ + + + + + | + case "android-safetynet": |
+ +406 + | +
+
+1
+
+1. attestationStatementVerifier : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step18::attestationStatementVerifier → KILLED + + + + |
+ return Optional.of(new AndroidSafetynetAttestationStatementVerifier()); |
+ +407 + | ++ + + + + + | + case "apple": |
+ +408 + | +
+
+1
+
+1. attestationStatementVerifier : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step18::attestationStatementVerifier → KILLED + + + + |
+ return Optional.of(new AppleAttestationStatementVerifier()); |
+ +409 + | ++ + + + + + | + case "tpm": |
+ +410 + | +
+
+1
+
+1. attestationStatementVerifier : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step18::attestationStatementVerifier → KILLED + + + + |
+ return Optional.of(new TpmAttestationStatementVerifier()); |
+ +411 + | ++ + + + + + | + default: |
+ +412 + | ++ + + + + + | + return Optional.empty(); |
+ +413 + | ++ + + + + + | + } |
+ +414 + | ++ + + + + + | + } |
+ +415 + | ++ + + + + + | + } |
+ +416 + | ++ + + + + + | +|
+ +417 + | ++ + + + + + | + @Value |
+ +418 + | ++ + + + + + | + class Step19 implements Step<Step20> { |
+ +419 + | ++ + + + + + | + private final ByteArray clientDataJsonHash; |
+ +420 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +421 + | ++ + + + + + | + private final Optional<AttestationStatementVerifier> attestationStatementVerifier; |
+ +422 + | ++ + + + + + | +|
+ +423 + | ++ + + + + + | + @Override |
+ +424 + | ++ + + + + + | + public void validate() { |
+ +425 + | +
+
+1
+
+1. validate : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ attestationStatementVerifier.ifPresent( |
+ +426 + | ++ + + + + + | + verifier -> { |
+ +427 + | +
+
+1
+
+1. lambda$validate$0 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +428 + | ++ + + + + + | + verifier.verifyAttestationSignature(attestation, clientDataJsonHash), |
+ +429 + | ++ + + + + + | + "Invalid attestation signature."); |
+ +430 + | ++ + + + + + | + }); |
+ +431 + | ++ + + + + + | +|
+ +432 + | +
+
+2
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. validate : negated conditional → KILLED + + + + |
+ assertTrue(attestationType() != null, "Failed to determine attestation type"); |
+ +433 + | ++ + + + + + | + } |
+ +434 + | ++ + + + + + | +|
+ +435 + | ++ + + + + + | + @Override |
+ +436 + | ++ + + + + + | + public Step20 nextStep() { |
+ +437 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step19::nextStep → KILLED + + + + |
+ return new Step20(attestation, attestationType(), attestationTrustPath()); |
+ +438 + | ++ + + + + + | + } |
+ +439 + | ++ + + + + + | +|
+ +440 + | ++ + + + + + | + public AttestationType attestationType() { |
+ +441 + | ++ + + + + + | + try { |
+ +442 + | +
+
+1
+
+1. attestationType : negated conditional → KILLED + + + + |
+ if (attestationStatementVerifier.isPresent()) { |
+ +443 + | +
+
+1
+
+1. attestationType : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step19::attestationType → KILLED + + + + |
+ return attestationStatementVerifier.get().getAttestationType(attestation); |
+ +444 + | ++ + + + + + | + } else { |
+ +445 + | +
+
+1
+
+1. attestationType : negated conditional → SURVIVED + + + + |
+ switch (attestation.getFormat()) { |
+ +446 + | ++ + + + + + | + case "android-key": |
+ +447 + | ++ + + + + + | + // TODO delete this once android-key attestation verification is implemented |
+ +448 + | +
+
+1
+
+1. attestationType : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step19::attestationType → KILLED + + + + |
+ return AttestationType.BASIC; |
+ +449 + | ++ + + + + + | + default: |
+ +450 + | +
+
+1
+
+1. attestationType : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step19::attestationType → KILLED + + + + |
+ return AttestationType.UNKNOWN; |
+ +451 + | ++ + + + + + | + } |
+ +452 + | ++ + + + + + | + } |
+ +453 + | ++ + + + + + | + } catch (IOException | CoseException | CertificateException e) { |
+ +454 + | ++ + + + + + | + throw new IllegalArgumentException("Failed to resolve attestation type.", e); |
+ +455 + | ++ + + + + + | + } |
+ +456 + | ++ + + + + + | + } |
+ +457 + | ++ + + + + + | +|
+ +458 + | ++ + + + + + | + public Optional<List<X509Certificate>> attestationTrustPath() { |
+ +459 + | +
+
+1
+
+1. attestationTrustPath : negated conditional → KILLED + + + + |
+ if (attestationStatementVerifier.isPresent()) { |
+ +460 + | ++ + + + + + | + AttestationStatementVerifier verifier = attestationStatementVerifier.get(); |
+ +461 + | +
+
+1
+
+1. attestationTrustPath : negated conditional → KILLED + + + + |
+ if (verifier instanceof X5cAttestationStatementVerifier) { |
+ +462 + | ++ + + + + + | + try { |
+ +463 + | +
+
+1
+
+1. attestationTrustPath : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step19::attestationTrustPath → KILLED + + + + |
+ return ((X5cAttestationStatementVerifier) verifier) |
+ +464 + | ++ + + + + + | + .getAttestationTrustPath(attestation); |
+ +465 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +466 + | ++ + + + + + | + throw new IllegalArgumentException("Failed to resolve attestation trust path.", e); |
+ +467 + | ++ + + + + + | + } |
+ +468 + | ++ + + + + + | + } else { |
+ +469 + | ++ + + + + + | + return Optional.empty(); |
+ +470 + | ++ + + + + + | + } |
+ +471 + | ++ + + + + + | + } else { |
+ +472 + | ++ + + + + + | + return Optional.empty(); |
+ +473 + | ++ + + + + + | + } |
+ +474 + | ++ + + + + + | + } |
+ +475 + | ++ + + + + + | + } |
+ +476 + | ++ + + + + + | +|
+ +477 + | ++ + + + + + | + @Value |
+ +478 + | ++ + + + + + | + class Step20 implements Step<Step21> { |
+ +479 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +480 + | ++ + + + + + | + private final AttestationType attestationType; |
+ +481 + | ++ + + + + + | + private final Optional<List<X509Certificate>> attestationTrustPath; |
+ +482 + | ++ + + + + + | +|
+ +483 + | ++ + + + + + | + private final Optional<AttestationTrustSource.TrustRootsResult> trustRoots; |
+ +484 + | ++ + + + + + | +|
+ +485 + | ++ + + + + + | + public Step20( |
+ +486 + | ++ + + + + + | + AttestationObject attestation, |
+ +487 + | ++ + + + + + | + AttestationType attestationType, |
+ +488 + | ++ + + + + + | + Optional<List<X509Certificate>> attestationTrustPath) { |
+ +489 + | ++ + + + + + | + this.attestation = attestation; |
+ +490 + | ++ + + + + + | + this.attestationType = attestationType; |
+ +491 + | ++ + + + + + | + this.attestationTrustPath = attestationTrustPath; |
+ +492 + | ++ + + + + + | + this.trustRoots = findTrustRoots(); |
+ +493 + | ++ + + + + + | + } |
+ +494 + | ++ + + + + + | +|
+ +495 + | ++ + + + + + | + @Override |
+ +496 + | ++ + + + + + | + public void validate() {} |
+ +497 + | ++ + + + + + | +|
+ +498 + | ++ + + + + + | + @Override |
+ +499 + | ++ + + + + + | + public Step21 nextStep() { |
+ +500 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step20::nextStep → KILLED + + + + |
+ return new Step21(attestation, attestationType, attestationTrustPath, trustRoots); |
+ +501 + | ++ + + + + + | + } |
+ +502 + | ++ + + + + + | +|
+ +503 + | ++ + + + + + | + private Optional<AttestationTrustSource.TrustRootsResult> findTrustRoots() { |
+ +504 + | +
+
+1
+
+1. findTrustRoots : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step20::findTrustRoots → KILLED + + + + |
+ return attestationTrustSource.flatMap( |
+ +505 + | ++ + + + + + | + attestationTrustSource -> |
+ +506 + | +
+
+1
+
+1. lambda$findTrustRoots$3 : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step20::lambda$findTrustRoots$3 → KILLED + + + + |
+ attestationTrustPath.map( |
+ +507 + | ++ + + + + + | + atp -> |
+ +508 + | +
+
+1
+
+1. lambda$findTrustRoots$2 : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step20::lambda$findTrustRoots$2 → KILLED + + + + |
+ attestationTrustSource.findTrustRoots( |
+ +509 + | ++ + + + + + | + atp, |
+ +510 + | ++ + + + + + | + OptionalUtil.orElseOptional( |
+ +511 + | ++ + + + + + | + Optional.of( |
+ +512 + | ++ + + + + + | + attestation |
+ +513 + | ++ + + + + + | + .getAuthenticatorData() |
+ +514 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +515 + | ++ + + + + + | + .get() |
+ +516 + | ++ + + + + + | + .getAaguid()) |
+ +517 + | +
+
+2
+
+1. lambda$findTrustRoots$0 : negated conditional → KILLED +2. lambda$findTrustRoots$0 : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step20::lambda$findTrustRoots$0 → KILLED + + + + |
+ .filter(aaguid -> !aaguid.equals(ZERO_AAGUID)), |
+ +518 + | ++ + + + + + | + () -> { |
+ +519 + | +
+
+1
+
+1. lambda$findTrustRoots$1 : negated conditional → KILLED + + + + |
+ if (!atp.isEmpty()) { |
+ +520 + | +
+
+1
+
+1. lambda$findTrustRoots$1 : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Step20::lambda$findTrustRoots$1 → KILLED + + + + |
+ return CertificateParser.parseFidoAaguidExtension(atp.get(0)) |
+ +521 + | ++ + + + + + | + .map(ByteArray::new); |
+ +522 + | ++ + + + + + | + } else { |
+ +523 + | ++ + + + + + | + return Optional.empty(); |
+ +524 + | ++ + + + + + | + } |
+ +525 + | ++ + + + + + | + })))); |
+ +526 + | ++ + + + + + | + } |
+ +527 + | ++ + + + + + | + } |
+ +528 + | ++ + + + + + | +|
+ +529 + | ++ + + + + + | + @Value |
+ +530 + | ++ + + + + + | + class Step21 implements Step<Step22> { |
+ +531 + | ++ + + + + + | + private final AttestationObject attestation; |
+ +532 + | ++ + + + + + | + private final AttestationType attestationType; |
+ +533 + | ++ + + + + + | + private final Optional<List<X509Certificate>> attestationTrustPath; |
+ +534 + | ++ + + + + + | + private final Optional<AttestationTrustSource.TrustRootsResult> trustRoots; |
+ +535 + | ++ + + + + + | +|
+ +536 + | ++ + + + + + | + private final boolean attestationTrusted; |
+ +537 + | ++ + + + + + | +|
+ +538 + | ++ + + + + + | + public Step21( |
+ +539 + | ++ + + + + + | + AttestationObject attestation, |
+ +540 + | ++ + + + + + | + AttestationType attestationType, |
+ +541 + | ++ + + + + + | + Optional<List<X509Certificate>> attestationTrustPath, |
+ +542 + | ++ + + + + + | + Optional<AttestationTrustSource.TrustRootsResult> trustRoots) { |
+ +543 + | ++ + + + + + | + this.attestation = attestation; |
+ +544 + | ++ + + + + + | + this.attestationType = attestationType; |
+ +545 + | ++ + + + + + | + this.attestationTrustPath = attestationTrustPath; |
+ +546 + | ++ + + + + + | + this.trustRoots = trustRoots; |
+ +547 + | ++ + + + + + | +|
+ +548 + | ++ + + + + + | + this.attestationTrusted = attestationTrusted(); |
+ +549 + | ++ + + + + + | + } |
+ +550 + | ++ + + + + + | +|
+ +551 + | ++ + + + + + | + @Override |
+ +552 + | ++ + + + + + | + public void validate() { |
+ +553 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +554 + | +
+
+2
+
+1. validate : negated conditional → KILLED +2. validate : negated conditional → KILLED + + + + |
+ allowUntrustedAttestation || attestationTrusted, |
+ +555 + | ++ + + + + + | + "Failed to derive trust for attestation key."); |
+ +556 + | ++ + + + + + | + } |
+ +557 + | ++ + + + + + | +|
+ +558 + | ++ + + + + + | + @Override |
+ +559 + | ++ + + + + + | + public Step22 nextStep() { |
+ +560 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step21::nextStep → KILLED + + + + |
+ return new Step22(attestationType, attestationTrusted, attestationTrustPath); |
+ +561 + | ++ + + + + + | + } |
+ +562 + | ++ + + + + + | +|
+ +563 + | ++ + + + + + | + public boolean attestationTrusted() { |
+ +564 + | +
+
+2
+
+1. attestationTrusted : negated conditional → KILLED +2. attestationTrusted : negated conditional → KILLED + + + + |
+ if (attestationTrustPath.isPresent() && attestationTrustSource.isPresent()) { |
+ +565 + | ++ + + + + + | + try { |
+ +566 + | +
+
+2
+
+1. attestationTrusted : negated conditional → KILLED +2. attestationTrusted : negated conditional → KILLED + + + + |
+ if (!trustRoots.isPresent() || trustRoots.get().getTrustRoots().isEmpty()) { |
+ +567 + | +
+
+1
+
+1. attestationTrusted : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step21::attestationTrusted → KILLED + + + + |
+ return false; |
+ +568 + | ++ + + + + + | +|
+ +569 + | ++ + + + + + | + } else { |
+ +570 + | ++ + + + + + | + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); |
+ +571 + | ++ + + + + + | + final CertPathValidator cpv = CertPathValidator.getInstance("PKIX"); |
+ +572 + | ++ + + + + + | + final CertPath certPath = certFactory.generateCertPath(attestationTrustPath.get()); |
+ +573 + | ++ + + + + + | + final PKIXParameters pathParams = |
+ +574 + | ++ + + + + + | + new PKIXParameters( |
+ +575 + | ++ + + + + + | + trustRoots.get().getTrustRoots().stream() |
+ +576 + | +
+
+1
+
+1. lambda$attestationTrusted$0 : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step21::lambda$attestationTrusted$0 → KILLED + + + + |
+ .map(rootCert -> new TrustAnchor(rootCert, null)) |
+ +577 + | ++ + + + + + | + .collect(Collectors.toSet())); |
+ +578 + | +
+
+1
+
+1. attestationTrusted : removed call to java/security/cert/PKIXParameters::setDate → KILLED + + + + |
+ pathParams.setDate(Date.from(clock.instant())); |
+ +579 + | +
+
+1
+
+1. attestationTrusted : removed call to java/security/cert/PKIXParameters::setRevocationEnabled → KILLED + + + + |
+ pathParams.setRevocationEnabled(trustRoots.get().isEnableRevocationChecking()); |
+ +580 + | +
+
+1
+
+1. attestationTrusted : removed call to java/security/cert/PKIXParameters::setPolicyQualifiersRejected → SURVIVED + + + + |
+ pathParams.setPolicyQualifiersRejected( |
+ +581 + | +
+
+1
+
+1. attestationTrusted : negated conditional → SURVIVED + + + + |
+ !trustRoots.get().getPolicyTreeValidator().isPresent()); |
+ +582 + | +
+
+1
+
+1. attestationTrusted : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ trustRoots.get().getCertStore().ifPresent(pathParams::addCertStore); |
+ +583 + | ++ + + + + + | + final PKIXCertPathValidatorResult result = |
+ +584 + | ++ + + + + + | + (PKIXCertPathValidatorResult) cpv.validate(certPath, pathParams); |
+ +585 + | +
+
+2
+
+1. attestationTrusted : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step21::attestationTrusted → KILLED +2. attestationTrusted : replaced boolean return with false for com/yubico/webauthn/FinishRegistrationSteps$Step21::attestationTrusted → KILLED + + + + |
+ return trustRoots |
+ +586 + | ++ + + + + + | + .get() |
+ +587 + | ++ + + + + + | + .getPolicyTreeValidator() |
+ +588 + | ++ + + + + + | + .map( |
+ +589 + | ++ + + + + + | + policyNodePredicate -> { |
+ +590 + | +
+
+1
+
+1. lambda$attestationTrusted$1 : negated conditional → KILLED + + + + |
+ if (policyNodePredicate.test(result.getPolicyTree())) { |
+ +591 + | +
+
+1
+
+1. lambda$attestationTrusted$1 : replaced Boolean return with False for com/yubico/webauthn/FinishRegistrationSteps$Step21::lambda$attestationTrusted$1 → KILLED + + + + |
+ return true; |
+ +592 + | ++ + + + + + | + } else { |
+ +593 + | ++ + + + + + | + log.info( |
+ +594 + | ++ + + + + + | + "Failed to derive trust in attestation statement: Certificate path policy tree does not satisfy policy tree validator. Attestation object: {}", |
+ +595 + | ++ + + + + + | + response.getResponse().getAttestationObject()); |
+ +596 + | +
+
+1
+
+1. lambda$attestationTrusted$1 : replaced Boolean return with True for com/yubico/webauthn/FinishRegistrationSteps$Step21::lambda$attestationTrusted$1 → KILLED + + + + |
+ return false; |
+ +597 + | ++ + + + + + | + } |
+ +598 + | ++ + + + + + | + }) |
+ +599 + | ++ + + + + + | + .orElse(true); |
+ +600 + | ++ + + + + + | + } |
+ +601 + | ++ + + + + + | +|
+ +602 + | ++ + + + + + | + } catch (CertPathValidatorException e) { |
+ +603 + | ++ + + + + + | + log.info( |
+ +604 + | ++ + + + + + | + "Failed to derive trust in attestation statement: {} at cert index {}: {}. Attestation object: {}", |
+ +605 + | ++ + + + + + | + e.getReason(), |
+ +606 + | ++ + + + + + | + e.getIndex(), |
+ +607 + | ++ + + + + + | + e.getMessage(), |
+ +608 + | ++ + + + + + | + response.getResponse().getAttestationObject()); |
+ +609 + | +
+
+1
+
+1. attestationTrusted : negated conditional → SURVIVED + + + + |
+ if (PKIXReason.INVALID_POLICY.equals(e.getReason())) { |
+ +610 + | ++ + + + + + | + log.info( |
+ +611 + | ++ + + + + + | + "You may need to set the policyTreeValidator property on the {} returned by your {}.", |
+ +612 + | ++ + + + + + | + TrustRootsResult.class.getSimpleName(), |
+ +613 + | ++ + + + + + | + AttestationTrustSource.class.getSimpleName()); |
+ +614 + | ++ + + + + + | + } |
+ +615 + | +
+
+1
+
+1. attestationTrusted : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step21::attestationTrusted → SURVIVED + + + + |
+ return false; |
+ +616 + | ++ + + + + + | +|
+ +617 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +618 + | ++ + + + + + | + log.warn( |
+ +619 + | ++ + + + + + | + "Failed to build attestation certificate path. Attestation object: {}", |
+ +620 + | ++ + + + + + | + response.getResponse().getAttestationObject(), |
+ +621 + | ++ + + + + + | + e); |
+ +622 + | +
+
+1
+
+1. attestationTrusted : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step21::attestationTrusted → NO_COVERAGE + + + + |
+ return false; |
+ +623 + | ++ + + + + + | +|
+ +624 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +625 + | ++ + + + + + | + throw new RuntimeException( |
+ +626 + | ++ + + + + + | + "Failed to check attestation trust path. A JCA provider is likely missing in the runtime environment.", |
+ +627 + | ++ + + + + + | + e); |
+ +628 + | ++ + + + + + | +|
+ +629 + | ++ + + + + + | + } catch (InvalidAlgorithmParameterException e) { |
+ +630 + | ++ + + + + + | + throw new RuntimeException( |
+ +631 + | ++ + + + + + | + "Failed to initialize attestation trust path validator. This is likely a bug, please file a bug report.", |
+ +632 + | ++ + + + + + | + e); |
+ +633 + | ++ + + + + + | + } |
+ +634 + | ++ + + + + + | + } else { |
+ +635 + | +
+
+1
+
+1. attestationTrusted : replaced boolean return with true for com/yubico/webauthn/FinishRegistrationSteps$Step21::attestationTrusted → KILLED + + + + |
+ return false; |
+ +636 + | ++ + + + + + | + } |
+ +637 + | ++ + + + + + | + } |
+ +638 + | ++ + + + + + | + } |
+ +639 + | ++ + + + + + | +|
+ +640 + | ++ + + + + + | + @Value |
+ +641 + | ++ + + + + + | + class Step22 implements Step<Finished> { |
+ +642 + | ++ + + + + + | + private final AttestationType attestationType; |
+ +643 + | ++ + + + + + | + private final boolean attestationTrusted; |
+ +644 + | ++ + + + + + | + private final Optional<List<X509Certificate>> attestationTrustPath; |
+ +645 + | ++ + + + + + | +|
+ +646 + | ++ + + + + + | + @Override |
+ +647 + | ++ + + + + + | + public void validate() { |
+ +648 + | +
+
+1
+
+1. validate : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ assertTrue( |
+ +649 + | +
+
+1
+
+1. validate : negated conditional → KILLED + + + + |
+ !credentialRepositoryV2.credentialIdExists(response.getId()), |
+ +650 + | ++ + + + + + | + "Credential ID is already registered: %s", |
+ +651 + | ++ + + + + + | + response.getId()); |
+ +652 + | ++ + + + + + | + } |
+ +653 + | ++ + + + + + | +|
+ +654 + | ++ + + + + + | + @Override |
+ +655 + | ++ + + + + + | + public Finished nextStep() { |
+ +656 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Step22::nextStep → KILLED + + + + |
+ return new Finished(attestationType, attestationTrusted, attestationTrustPath); |
+ +657 + | ++ + + + + + | + } |
+ +658 + | ++ + + + + + | + } |
+ +659 + | ++ + + + + + | +|
+ +660 + | ++ + + + + + | + // Step 23 will be performed externally by library user |
+ +661 + | ++ + + + + + | + // Nothing to do for step 24 |
+ +662 + | ++ + + + + + | +|
+ +663 + | ++ + + + + + | + @Value |
+ +664 + | ++ + + + + + | + class Finished implements Step<Finished> { |
+ +665 + | ++ + + + + + | + private final AttestationType attestationType; |
+ +666 + | ++ + + + + + | + private final boolean attestationTrusted; |
+ +667 + | ++ + + + + + | + private final Optional<List<X509Certificate>> attestationTrustPath; |
+ +668 + | ++ + + + + + | +|
+ +669 + | ++ + + + + + | + @Override |
+ +670 + | ++ + + + + + | + public void validate() { |
+ +671 + | ++ + + + + + | + /* No-op */ |
+ +672 + | ++ + + + + + | + } |
+ +673 + | ++ + + + + + | +|
+ +674 + | ++ + + + + + | + @Override |
+ +675 + | ++ + + + + + | + public Finished nextStep() { |
+ +676 + | +
+
+1
+
+1. nextStep : replaced return value with null for com/yubico/webauthn/FinishRegistrationSteps$Finished::nextStep → NO_COVERAGE + + + + |
+ return this; |
+ +677 + | ++ + + + + + | + } |
+ +678 + | ++ + + + + + | +|
+ +679 + | ++ + + + + + | + @Override |
+ +680 + | ++ + + + + + | + public Optional<RegistrationResult> result() { |
+ +681 + | +
+
+1
+
+1. result : replaced return value with Optional.empty for com/yubico/webauthn/FinishRegistrationSteps$Finished::result → KILLED + + + + |
+ return Optional.of( |
+ +682 + | ++ + + + + + | + new RegistrationResult( |
+ +683 + | ++ + + + + + | + response, attestationTrusted, attestationType, attestationTrustPath)); |
+ +684 + | ++ + + + + + | + } |
+ +685 + | ++ + + + + + | + } |
+ +686 + | ++ + + + + + | +} |
Mutations | ||
94 | ++ |
+
+
+
+ 1.1 |
+
124 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
141 | ++ |
+
+
+
+ 1.1 |
+
142 | ++ |
+
+
+
+ 1.1 |
+
146 | ++ |
+
+
+
+ 1.1 |
+
147 | ++ |
+
+
+
+ 1.1 |
+
149 | ++ |
+
+
+
+ 1.1 |
+
162 | ++ |
+
+
+
+ 1.1 2.2 |
+
167 | ++ |
+
+
+
+ 1.1 |
+
171 | ++ |
+
+
+
+ 1.1 |
+
181 | ++ |
+
+
+
+ 1.1 |
+
190 | ++ |
+
+
+
+ 1.1 |
+
200 | ++ |
+
+
+
+ 1.1 |
+
205 | ++ |
+
+
+
+ 1.1 |
+
216 | ++ |
+
+
+
+ 1.1 |
+
224 | ++ |
+
+
+
+ 1.1 |
+
239 | ++ |
+
+
+
+ 1.1 |
+
247 | ++ |
+
+
+
+ 1.1 2.2 |
+
252 | ++ |
+
+
+
+ 1.1 |
+
256 | ++ |
+
+
+
+ 1.1 |
+
266 | ++ |
+
+
+
+ 1.1 2.2 |
+
271 | ++ |
+
+
+
+ 1.1 |
+
275 | ++ |
+
+
+
+ 1.1 |
+
286 | ++ |
+
+
+
+ 1.1 |
+
294 | ++ |
+
+
+
+ 1.1 |
+
305 | ++ |
+
+
+
+ 1.1 |
+
312 | ++ |
+
+
+
+ 1.1 |
+
326 | ++ |
+
+
+
+ 1.1 |
+
328 | ++ |
+
+
+
+ 1.1 |
+
336 | ++ |
+
+
+
+ 1.1 |
+
357 | ++ |
+
+
+
+ 1.1 |
+
359 | ++ |
+
+
+
+ 1.1 2.2 |
+
374 | ++ |
+
+
+
+ 1.1 |
+
390 | ++ |
+
+
+
+ 1.1 |
+
394 | ++ |
+
+
+
+ 1.1 |
+
400 | ++ |
+
+
+
+ 1.1 |
+
402 | ++ |
+
+
+
+ 1.1 |
+
404 | ++ |
+
+
+
+ 1.1 |
+
406 | ++ |
+
+
+
+ 1.1 |
+
408 | ++ |
+
+
+
+ 1.1 |
+
410 | ++ |
+
+
+
+ 1.1 |
+
425 | ++ |
+
+
+
+ 1.1 |
+
427 | ++ |
+
+
+
+ 1.1 |
+
432 | ++ |
+
+
+
+ 1.1 2.2 |
+
437 | ++ |
+
+
+
+ 1.1 |
+
442 | ++ |
+
+
+
+ 1.1 |
+
443 | ++ |
+
+
+
+ 1.1 |
+
445 | ++ |
+
+
+
+ 1.1 |
+
448 | ++ |
+
+
+
+ 1.1 |
+
450 | ++ |
+
+
+
+ 1.1 |
+
459 | ++ |
+
+
+
+ 1.1 |
+
461 | ++ |
+
+
+
+ 1.1 |
+
463 | ++ |
+
+
+
+ 1.1 |
+
500 | ++ |
+
+
+
+ 1.1 |
+
504 | ++ |
+
+
+
+ 1.1 |
+
506 | ++ |
+
+
+
+ 1.1 |
+
508 | ++ |
+
+
+
+ 1.1 |
+
517 | ++ |
+
+
+
+ 1.1 2.2 |
+
519 | ++ |
+
+
+
+ 1.1 |
+
520 | ++ |
+
+
+
+ 1.1 |
+
553 | ++ |
+
+
+
+ 1.1 |
+
554 | ++ |
+
+
+
+ 1.1 2.2 |
+
560 | ++ |
+
+
+
+ 1.1 |
+
564 | ++ |
+
+
+
+ 1.1 2.2 |
+
566 | ++ |
+
+
+
+ 1.1 2.2 |
+
567 | ++ |
+
+
+
+ 1.1 |
+
576 | ++ |
+
+
+
+ 1.1 |
+
578 | ++ |
+
+
+
+ 1.1 |
+
579 | ++ |
+
+
+
+ 1.1 |
+
580 | ++ |
+
+
+
+ 1.1 |
+
581 | ++ |
+
+
+
+ 1.1 |
+
582 | ++ |
+
+
+
+ 1.1 |
+
585 | ++ |
+
+
+
+ 1.1 2.2 |
+
590 | ++ |
+
+
+
+ 1.1 |
+
591 | ++ |
+
+
+
+ 1.1 |
+
596 | ++ |
+
+
+
+ 1.1 |
+
609 | ++ |
+
+
+
+ 1.1 |
+
615 | ++ |
+
+
+
+ 1.1 |
+
622 | ++ |
+
+
+
+ 1.1 |
+
635 | ++ |
+
+
+
+ 1.1 |
+
648 | ++ |
+
+
+
+ 1.1 |
+
649 | ++ |
+
+
+
+ 1.1 |
+
656 | ++ |
+
+
+
+ 1.1 |
+
676 | ++ |
+
+
+
+ 1.1 |
+
681 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.webauthn.data.AttestationObject; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +30 + | ++ + + + + + | +|
+ +31 + | ++ + + + + + | +final class NoneAttestationStatementVerifier implements AttestationStatementVerifier { |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | + @Override |
+ +34 + | ++ + + + + + | + public AttestationType getAttestationType(AttestationObject attestation) { |
+ +35 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/NoneAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.NONE; |
+ +36 + | ++ + + + + + | + } |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + @Override |
+ +39 + | ++ + + + + + | + public boolean verifyAttestationSignature( |
+ +40 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +41 + | +
+
+1
+
+1. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/NoneAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return true; |
+ +42 + | ++ + + + + + | + } |
+ +43 + | ++ + + + + + | +} |
Mutations | ||
35 | ++ |
+
+
+
+ 1.1 |
+
41 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.webauthn; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.net.MalformedURLException; |
+ +4 + | ++ + + + + + | +import java.net.URL; |
+ +5 + | ++ + + + + + | +import java.util.Set; |
+ +6 + | ++ + + + + + | +import lombok.experimental.UtilityClass; |
+ +7 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +8 + | ++ + + + + + | +|
+ +9 + | ++ + + + + + | +@Slf4j |
+ +10 + | ++ + + + + + | +@UtilityClass |
+ +11 + | ++ + + + + + | +class OriginMatcher { |
+ +12 + | ++ + + + + + | +|
+ +13 + | ++ + + + + + | + static boolean isAllowed( |
+ +14 + | ++ + + + + + | + String origin, Set<String> allowedOrigins, boolean allowPort, boolean allowSubdomain) { |
+ +15 + | ++ + + + + + | + log.trace("isAllowed({}, {}, {}, {})", origin, allowedOrigins, allowPort, allowSubdomain); |
+ +16 + | ++ + + + + + | +|
+ +17 + | ++ + + + + + | + URL tmpOriginUrl; |
+ +18 + | ++ + + + + + | + try { |
+ +19 + | ++ + + + + + | + tmpOriginUrl = new URL(origin); |
+ +20 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +21 + | ++ + + + + + | + log.debug("Origin in client data is not a valid URL; will only match exactly: {}", origin); |
+ +22 + | ++ + + + + + | + tmpOriginUrl = null; |
+ +23 + | ++ + + + + + | + } |
+ +24 + | ++ + + + + + | + final URL originUrl = tmpOriginUrl; |
+ +25 + | ++ + + + + + | +|
+ +26 + | +
+
+2
+
+1. isAllowed : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::isAllowed → KILLED +2. isAllowed : replaced boolean return with false for com/yubico/webauthn/OriginMatcher::isAllowed → KILLED + + + + |
+ return allowedOrigins.stream() |
+ +27 + | ++ + + + + + | + .anyMatch( |
+ +28 + | ++ + + + + + | + allowedOriginString -> { |
+ +29 + | +
+
+1
+
+1. lambda$isAllowed$0 : negated conditional → KILLED + + + + |
+ if (allowedOriginString.equals(origin)) { |
+ +30 + | ++ + + + + + | + log.debug("Exact match: {} == {}", origin, allowedOriginString); |
+ +31 + | +
+
+1
+
+1. lambda$isAllowed$0 : replaced boolean return with false for com/yubico/webauthn/OriginMatcher::lambda$isAllowed$0 → KILLED + + + + |
+ return true; |
+ +32 + | +
+
+3
+
+1. lambda$isAllowed$0 : negated conditional → KILLED +2. lambda$isAllowed$0 : negated conditional → KILLED +3. lambda$isAllowed$0 : negated conditional → KILLED + + + + |
+ } else if (originUrl != null && (allowPort || allowSubdomain)) { |
+ +33 + | ++ + + + + + | + final URL allowedOrigin; |
+ +34 + | ++ + + + + + | + try { |
+ +35 + | ++ + + + + + | + allowedOrigin = new URL(allowedOriginString); |
+ +36 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +37 + | ++ + + + + + | + log.error( |
+ +38 + | ++ + + + + + | + "Allowed origin is not a valid URL; skipping port/subdomain matching: {}", |
+ +39 + | ++ + + + + + | + allowedOriginString); |
+ +40 + | +
+
+1
+
+1. lambda$isAllowed$0 : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::lambda$isAllowed$0 → KILLED + + + + |
+ return false; |
+ +41 + | ++ + + + + + | + } |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + final boolean portAccepted = isPortAccepted(allowPort, allowedOrigin, originUrl); |
+ +44 + | ++ + + + + + | + final boolean domainAccepted = |
+ +45 + | ++ + + + + + | + isDomainAccepted(allowSubdomain, allowedOrigin, originUrl); |
+ +46 + | ++ + + + + + | +|
+ +47 + | ++ + + + + + | + log.debug("portAccepted: {}, domainAccepted: {}", portAccepted, domainAccepted); |
+ +48 + | +
+
+3
+
+1. lambda$isAllowed$0 : negated conditional → KILLED +2. lambda$isAllowed$0 : negated conditional → KILLED +3. lambda$isAllowed$0 : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::lambda$isAllowed$0 → KILLED + + + + |
+ return portAccepted && domainAccepted; |
+ +49 + | ++ + + + + + | + } else { |
+ +50 + | ++ + + + + + | + log.debug("No match: {} != {}", origin, allowedOriginString); |
+ +51 + | +
+
+1
+
+1. lambda$isAllowed$0 : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::lambda$isAllowed$0 → KILLED + + + + |
+ return false; |
+ +52 + | ++ + + + + + | + } |
+ +53 + | ++ + + + + + | + }); |
+ +54 + | ++ + + + + + | + } |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + private static boolean isPortAccepted(boolean allowAnyPort, URL allowedOrigin, URL origin) { |
+ +57 + | +
+
+1
+
+1. isPortAccepted : negated conditional → KILLED + + + + |
+ if (allowAnyPort) { |
+ +58 + | +
+
+1
+
+1. isPortAccepted : replaced boolean return with false for com/yubico/webauthn/OriginMatcher::isPortAccepted → KILLED + + + + |
+ return true; |
+ +59 + | ++ + + + + + | + } else { |
+ +60 + | +
+
+2
+
+1. isPortAccepted : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::isPortAccepted → KILLED +2. isPortAccepted : negated conditional → KILLED + + + + |
+ return origin.getPort() == allowedOrigin.getPort(); |
+ +61 + | ++ + + + + + | + } |
+ +62 + | ++ + + + + + | + } |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + private static boolean isDomainAccepted(boolean allowSubdomain, URL allowedOrigin, URL origin) { |
+ +65 + | ++ + + + + + | + final String allowedDomain = allowedOrigin.getHost(); |
+ +66 + | ++ + + + + + | + final String originDomain = origin.getHost(); |
+ +67 + | ++ + + + + + | +|
+ +68 + | +
+
+1
+
+1. isDomainAccepted : negated conditional → KILLED + + + + |
+ if (allowSubdomain) { |
+ +69 + | +
+
+3
+
+1. isDomainAccepted : negated conditional → KILLED +2. isDomainAccepted : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::isDomainAccepted → KILLED +3. isDomainAccepted : negated conditional → KILLED + + + + |
+ return originDomain.equals(allowedDomain) || originDomain.endsWith("." + allowedDomain); |
+ +70 + | ++ + + + + + | + } else { |
+ +71 + | +
+
+2
+
+1. isDomainAccepted : replaced boolean return with false for com/yubico/webauthn/OriginMatcher::isDomainAccepted → KILLED +2. isDomainAccepted : replaced boolean return with true for com/yubico/webauthn/OriginMatcher::isDomainAccepted → KILLED + + + + |
+ return originDomain.equals(allowedDomain); |
+ +72 + | ++ + + + + + | + } |
+ +73 + | ++ + + + + + | + } |
+ +74 + | ++ + + + + + | +} |
Mutations | ||
26 | ++ |
+
+
+
+ 1.1 2.2 |
+
29 | ++ |
+
+
+
+ 1.1 |
+
31 | ++ |
+
+
+
+ 1.1 |
+
32 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
40 | ++ |
+
+
+
+ 1.1 |
+
48 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
51 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 |
+
58 | ++ |
+
+
+
+ 1.1 |
+
60 | ++ |
+
+
+
+ 1.1 2.2 |
+
68 | ++ |
+
+
+
+ 1.1 |
+
69 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
71 | ++ |
+
+
+
+ 1.1 2.2 |
+
+ +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 COSE.CoseException; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonNode; |
+ +29 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +30 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.CollectionUtil; |
+ +32 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +37 + | ++ + + + + + | +import java.io.IOException; |
+ +38 + | ++ + + + + + | +import java.security.InvalidKeyException; |
+ +39 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +40 + | ++ + + + + + | +import java.security.PublicKey; |
+ +41 + | ++ + + + + + | +import java.security.Signature; |
+ +42 + | ++ + + + + + | +import java.security.SignatureException; |
+ +43 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +44 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +45 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +46 + | ++ + + + + + | +import java.util.Arrays; |
+ +47 + | ++ + + + + + | +import java.util.HashSet; |
+ +48 + | ++ + + + + + | +import java.util.Locale; |
+ +49 + | ++ + + + + + | +import java.util.Objects; |
+ +50 + | ++ + + + + + | +import java.util.Optional; |
+ +51 + | ++ + + + + + | +import java.util.Set; |
+ +52 + | ++ + + + + + | +import javax.naming.InvalidNameException; |
+ +53 + | ++ + + + + + | +import javax.naming.ldap.LdapName; |
+ +54 + | ++ + + + + + | +import javax.naming.ldap.Rdn; |
+ +55 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +56 + | ++ + + + + + | +import lombok.val; |
+ +57 + | ++ + + + + + | +|
+ +58 + | ++ + + + + + | +@Slf4j |
+ +59 + | ++ + + + + + | +final class PackedAttestationStatementVerifier |
+ +60 + | ++ + + + + + | + implements AttestationStatementVerifier, X5cAttestationStatementVerifier { |
+ +61 + | ++ + + + + + | +|
+ +62 + | ++ + + + + + | + @Override |
+ +63 + | ++ + + + + + | + public AttestationType getAttestationType(AttestationObject attestation) { |
+ +64 + | +
+
+1
+
+1. getAttestationType : negated conditional → KILLED + + + + |
+ if (attestation.getAttestationStatement().hasNonNull("x5c")) { |
+ +65 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/PackedAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.BASIC; |
+ +66 + | ++ + + + + + | + } else { |
+ +67 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/PackedAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.SELF_ATTESTATION; |
+ +68 + | ++ + + + + + | + } |
+ +69 + | ++ + + + + + | + } |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | + @Override |
+ +72 + | ++ + + + + + | + public boolean verifyAttestationSignature( |
+ +73 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +74 + | ++ + + + + + | + val signatureNode = attestationObject.getAttestationStatement().get("sig"); |
+ +75 + | ++ + + + + + | +|
+ +76 + | +
+
+2
+
+1. verifyAttestationSignature : negated conditional → KILLED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ if (signatureNode == null || !signatureNode.isBinary()) { |
+ +77 + | ++ + + + + + | + throw new IllegalArgumentException("attStmt.sig must be set to a binary value."); |
+ +78 + | ++ + + + + + | + } |
+ +79 + | ++ + + + + + | +|
+ +80 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ if (attestationObject.getAttestationStatement().has("x5c")) { |
+ +81 + | +
+
+2
+
+1. verifyAttestationSignature : replaced boolean return with true for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyAttestationSignature → KILLED +2. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return verifyX5cSignature(attestationObject, clientDataJsonHash); |
+ +82 + | ++ + + + + + | + } else { |
+ +83 + | +
+
+2
+
+1. verifyAttestationSignature : replaced boolean return with true for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyAttestationSignature → KILLED +2. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return verifySelfAttestationSignature(attestationObject, clientDataJsonHash); |
+ +84 + | ++ + + + + + | + } |
+ +85 + | ++ + + + + + | + } |
+ +86 + | ++ + + + + + | +|
+ +87 + | ++ + + + + + | + private boolean verifySelfAttestationSignature( |
+ +88 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +89 + | ++ + + + + + | + final PublicKey pubkey; |
+ +90 + | ++ + + + + + | + try { |
+ +91 + | ++ + + + + + | + pubkey = |
+ +92 + | ++ + + + + + | + WebAuthnCodecs.importCosePublicKey( |
+ +93 + | ++ + + + + + | + attestationObject |
+ +94 + | ++ + + + + + | + .getAuthenticatorData() |
+ +95 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +96 + | ++ + + + + + | + .get() |
+ +97 + | ++ + + + + + | + .getCredentialPublicKey()); |
+ +98 + | ++ + + + + + | + } catch (IOException | CoseException | InvalidKeySpecException e) { |
+ +99 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +100 + | ++ + + + + + | + log, |
+ +101 + | ++ + + + + + | + String.format( |
+ +102 + | ++ + + + + + | + "Failed to parse public key from attestation data %s", |
+ +103 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getAttestedCredentialData()), |
+ +104 + | ++ + + + + + | + e); |
+ +105 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +106 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | +|
+ +109 + | ++ + + + + + | + final long keyAlgId = |
+ +110 + | ++ + + + + + | + CBORObject.DecodeFromBytes( |
+ +111 + | ++ + + + + + | + attestationObject |
+ +112 + | ++ + + + + + | + .getAuthenticatorData() |
+ +113 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +114 + | ++ + + + + + | + .get() |
+ +115 + | ++ + + + + + | + .getCredentialPublicKey() |
+ +116 + | ++ + + + + + | + .getBytes()) |
+ +117 + | ++ + + + + + | + .get(CBORObject.FromObject(3)) |
+ +118 + | ++ + + + + + | + .AsNumber() |
+ +119 + | ++ + + + + + | + .ToInt64IfExact(); |
+ +120 + | ++ + + + + + | + final COSEAlgorithmIdentifier keyAlg = |
+ +121 + | ++ + + + + + | + COSEAlgorithmIdentifier.fromId(keyAlgId) |
+ +122 + | ++ + + + + + | + .orElseThrow( |
+ +123 + | ++ + + + + + | + () -> |
+ +124 + | +
+
+1
+
+1. lambda$verifySelfAttestationSignature$0 : replaced return value with null for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$verifySelfAttestationSignature$0 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +125 + | ++ + + + + + | + "Unsupported COSE algorithm identifier: " + keyAlgId)); |
+ +126 + | ++ + + + + + | +|
+ +127 + | ++ + + + + + | + final long sigAlgId = attestationObject.getAttestationStatement().get("alg").asLong(); |
+ +128 + | ++ + + + + + | + final COSEAlgorithmIdentifier sigAlg = |
+ +129 + | ++ + + + + + | + COSEAlgorithmIdentifier.fromId(sigAlgId) |
+ +130 + | ++ + + + + + | + .orElseThrow( |
+ +131 + | ++ + + + + + | + () -> |
+ +132 + | +
+
+1
+
+1. lambda$verifySelfAttestationSignature$1 : replaced return value with null for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$verifySelfAttestationSignature$1 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +133 + | ++ + + + + + | + "Unsupported COSE algorithm identifier: " + sigAlgId)); |
+ +134 + | ++ + + + + + | +|
+ +135 + | +
+
+1
+
+1. verifySelfAttestationSignature : negated conditional → KILLED + + + + |
+ if (!Objects.equals(keyAlg, sigAlg)) { |
+ +136 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +137 + | ++ + + + + + | + String.format( |
+ +138 + | ++ + + + + + | + "Key algorithm and signature algorithm must be equal, was: Key: %s, Sig: %s", |
+ +139 + | ++ + + + + + | + keyAlg, sigAlg)); |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + ByteArray signedData = |
+ +143 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getBytes().concat(clientDataJsonHash); |
+ +144 + | ++ + + + + + | + ByteArray signature; |
+ +145 + | ++ + + + + + | + try { |
+ +146 + | ++ + + + + + | + signature = |
+ +147 + | ++ + + + + + | + new ByteArray(attestationObject.getAttestationStatement().get("sig").binaryValue()); |
+ +148 + | ++ + + + + + | + } catch (IOException e) { |
+ +149 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog(log, ".binaryValue() of \"sig\" failed", e); |
+ +150 + | ++ + + + + + | + } |
+ +151 + | ++ + + + + + | +|
+ +152 + | +
+
+2
+
+1. verifySelfAttestationSignature : replaced boolean return with false for com/yubico/webauthn/PackedAttestationStatementVerifier::verifySelfAttestationSignature → KILLED +2. verifySelfAttestationSignature : replaced boolean return with true for com/yubico/webauthn/PackedAttestationStatementVerifier::verifySelfAttestationSignature → KILLED + + + + |
+ return Crypto.verifySignature(pubkey, signedData, signature, keyAlg); |
+ +153 + | ++ + + + + + | + } |
+ +154 + | ++ + + + + + | +|
+ +155 + | ++ + + + + + | + private boolean verifyX5cSignature( |
+ +156 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataHash) { |
+ +157 + | ++ + + + + + | + final Optional<X509Certificate> attestationCert; |
+ +158 + | ++ + + + + + | + try { |
+ +159 + | ++ + + + + + | + attestationCert = getX5cAttestationCertificate(attestationObject); |
+ +160 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +161 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +162 + | ++ + + + + + | + log, |
+ +163 + | ++ + + + + + | + String.format( |
+ +164 + | ++ + + + + + | + "Failed to parse X.509 certificate from attestation object: %s", attestationObject), |
+ +165 + | ++ + + + + + | + e); |
+ +166 + | ++ + + + + + | + } |
+ +167 + | +
+
+2
+
+1. verifyX5cSignature : replaced boolean return with true for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyX5cSignature → KILLED +2. verifyX5cSignature : replaced boolean return with false for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyX5cSignature → KILLED + + + + |
+ return attestationCert |
+ +168 + | ++ + + + + + | + .map( |
+ +169 + | ++ + + + + + | + attestationCertificate -> { |
+ +170 + | ++ + + + + + | + JsonNode signatureNode = attestationObject.getAttestationStatement().get("sig"); |
+ +171 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : negated conditional → KILLED + + + + |
+ if (signatureNode == null) { |
+ +172 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +173 + | ++ + + + + + | + "Packed attestation statement must have field \"sig\"."); |
+ +174 + | ++ + + + + + | + } |
+ +175 + | ++ + + + + + | +|
+ +176 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : negated conditional → KILLED + + + + |
+ if (signatureNode.isBinary()) { |
+ +177 + | ++ + + + + + | + ByteArray signature; |
+ +178 + | ++ + + + + + | + try { |
+ +179 + | ++ + + + + + | + signature = new ByteArray(signatureNode.binaryValue()); |
+ +180 + | ++ + + + + + | + } catch (IOException e) { |
+ +181 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +182 + | ++ + + + + + | + log, |
+ +183 + | ++ + + + + + | + "signatureNode.isBinary() was true but signatureNode.binaryValue() failed", |
+ +184 + | ++ + + + + + | + e); |
+ +185 + | ++ + + + + + | + } |
+ +186 + | ++ + + + + + | +|
+ +187 + | ++ + + + + + | + JsonNode algNode = attestationObject.getAttestationStatement().get("alg"); |
+ +188 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : negated conditional → KILLED + + + + |
+ if (algNode == null) { |
+ +189 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +190 + | ++ + + + + + | + "Packed attestation statement must have field \"alg\"."); |
+ +191 + | ++ + + + + + | + } |
+ +192 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ ExceptionUtil.assertTrue( |
+ +193 + | ++ + + + + + | + algNode.isIntegralNumber(), |
+ +194 + | ++ + + + + + | + "Field \"alg\" in packed attestation statement must be a COSEAlgorithmIdentifier."); |
+ +195 + | ++ + + + + + | + final Long sigAlgId = algNode.asLong(); |
+ +196 + | ++ + + + + + | + final COSEAlgorithmIdentifier sigAlg = |
+ +197 + | ++ + + + + + | + COSEAlgorithmIdentifier.fromId(sigAlgId) |
+ +198 + | ++ + + + + + | + .orElseThrow( |
+ +199 + | ++ + + + + + | + () -> |
+ +200 + | +
+
+1
+
+1. lambda$verifyX5cSignature$2 : replaced return value with null for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$verifyX5cSignature$2 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +201 + | ++ + + + + + | + "Unsupported COSE algorithm identifier: " + sigAlgId)); |
+ +202 + | ++ + + + + + | +|
+ +203 + | ++ + + + + + | + ByteArray signedData = |
+ +204 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getBytes().concat(clientDataHash); |
+ +205 + | ++ + + + + + | +|
+ +206 + | ++ + + + + + | + final String signatureAlgorithmName = WebAuthnCodecs.getJavaAlgorithmName(sigAlg); |
+ +207 + | ++ + + + + + | + Signature signatureVerifier; |
+ +208 + | ++ + + + + + | + try { |
+ +209 + | ++ + + + + + | + signatureVerifier = Signature.getInstance(signatureAlgorithmName); |
+ +210 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +211 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +212 + | ++ + + + + + | + log, "Failed to get a Signature instance for " + signatureAlgorithmName, e); |
+ +213 + | ++ + + + + + | + } |
+ +214 + | ++ + + + + + | + try { |
+ +215 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : removed call to java/security/Signature::initVerify → KILLED + + + + |
+ signatureVerifier.initVerify(attestationCertificate.getPublicKey()); |
+ +216 + | ++ + + + + + | + } catch (InvalidKeyException e) { |
+ +217 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +218 + | ++ + + + + + | + log, "Attestation key is invalid: " + attestationCertificate, e); |
+ +219 + | ++ + + + + + | + } |
+ +220 + | ++ + + + + + | + try { |
+ +221 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : removed call to java/security/Signature::update → KILLED + + + + |
+ signatureVerifier.update(signedData.getBytes()); |
+ +222 + | ++ + + + + + | + } catch (SignatureException e) { |
+ +223 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +224 + | ++ + + + + + | + log, "Signature object in invalid state: " + signatureVerifier, e); |
+ +225 + | ++ + + + + + | + } |
+ +226 + | ++ + + + + + | +|
+ +227 + | ++ + + + + + | + try { |
+ +228 + | +
+
+2
+
+1. lambda$verifyX5cSignature$3 : negated conditional → KILLED +2. lambda$verifyX5cSignature$3 : replaced Boolean return with True for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$verifyX5cSignature$3 → KILLED + + + + |
+ return (signatureVerifier.verify(signature.getBytes()) |
+ +229 + | +
+
+1
+
+1. lambda$verifyX5cSignature$3 : negated conditional → KILLED + + + + |
+ && verifyX5cRequirements( |
+ +230 + | ++ + + + + + | + attestationCertificate, |
+ +231 + | ++ + + + + + | + attestationObject |
+ +232 + | ++ + + + + + | + .getAuthenticatorData() |
+ +233 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +234 + | ++ + + + + + | + .get() |
+ +235 + | ++ + + + + + | + .getAaguid())); |
+ +236 + | ++ + + + + + | + } catch (SignatureException e) { |
+ +237 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +238 + | ++ + + + + + | + log, "Failed to verify signature: " + attestationObject, e); |
+ +239 + | ++ + + + + + | + } |
+ +240 + | ++ + + + + + | + } else { |
+ +241 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +242 + | ++ + + + + + | + "Field \"sig\" in packed attestation statement must be a binary value."); |
+ +243 + | ++ + + + + + | + } |
+ +244 + | ++ + + + + + | + }) |
+ +245 + | ++ + + + + + | + .orElseThrow( |
+ +246 + | ++ + + + + + | + () -> |
+ +247 + | +
+
+1
+
+1. lambda$verifyX5cSignature$4 : replaced return value with null for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$verifyX5cSignature$4 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +248 + | ++ + + + + + | + "If \"x5c\" property is present in \"packed\" attestation format it must be an array containing at least one DER encoded X.509 cerficicate.")); |
+ +249 + | ++ + + + + + | + } |
+ +250 + | ++ + + + + + | +|
+ +251 + | ++ + + + + + | + private Optional<Object> getDnField(String field, X509Certificate cert) { |
+ +252 + | ++ + + + + + | + final LdapName ldap; |
+ +253 + | ++ + + + + + | + try { |
+ +254 + | ++ + + + + + | + ldap = new LdapName(cert.getSubjectX500Principal().getName()); |
+ +255 + | ++ + + + + + | + } catch (InvalidNameException e) { |
+ +256 + | ++ + + + + + | + throw ExceptionUtil.wrapAndLog( |
+ +257 + | ++ + + + + + | + log, |
+ +258 + | ++ + + + + + | + "X500Principal name was not accepted as an LdapName: " |
+ +259 + | ++ + + + + + | + + cert.getSubjectX500Principal().getName(), |
+ +260 + | ++ + + + + + | + e); |
+ +261 + | ++ + + + + + | + } |
+ +262 + | +
+
+1
+
+1. getDnField : replaced return value with Optional.empty for com/yubico/webauthn/PackedAttestationStatementVerifier::getDnField → KILLED + + + + |
+ return ldap.getRdns().stream() |
+ +263 + | +
+
+2
+
+1. lambda$getDnField$5 : replaced boolean return with true for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$getDnField$5 → KILLED +2. lambda$getDnField$5 : replaced boolean return with false for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$getDnField$5 → KILLED + + + + |
+ .filter(rdn -> Objects.equals(rdn.getType(), field)) |
+ +264 + | ++ + + + + + | + .findAny() |
+ +265 + | ++ + + + + + | + .map(Rdn::getValue); |
+ +266 + | ++ + + + + + | + } |
+ +267 + | ++ + + + + + | +|
+ +268 + | ++ + + + + + | + public boolean verifyX5cRequirements(X509Certificate cert, ByteArray aaguid) { |
+ +269 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ if (cert.getVersion() != 3) { |
+ +270 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +271 + | ++ + + + + + | + String.format( |
+ +272 + | ++ + + + + + | + "Wrong attestation certificate X509 version: %s, expected: 3", cert.getVersion())); |
+ +273 + | ++ + + + + + | + } |
+ +274 + | ++ + + + + + | +|
+ +275 + | ++ + + + + + | + final String ouValue = "Authenticator Attestation"; |
+ +276 + | ++ + + + + + | + final Set<String> countries = |
+ +277 + | ++ + + + + + | + CollectionUtil.immutableSet(new HashSet<>(Arrays.asList(Locale.getISOCountries()))); |
+ +278 + | ++ + + + + + | +|
+ +279 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +280 + | ++ + + + + + | + getDnField("C", cert).filter(countries::contains).isPresent(), |
+ +281 + | ++ + + + + + | + "Invalid attestation certificate country code: %s", |
+ +282 + | ++ + + + + + | + getDnField("C", cert)); |
+ +283 + | ++ + + + + + | +|
+ +284 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +285 + | +
+
+2
+
+1. lambda$verifyX5cRequirements$6 : replaced boolean return with true for com/yubico/webauthn/PackedAttestationStatementVerifier::lambda$verifyX5cRequirements$6 → SURVIVED +2. lambda$verifyX5cRequirements$6 : negated conditional → KILLED + + + + |
+ getDnField("O", cert).filter(o -> !((String) o).isEmpty()).isPresent(), |
+ +286 + | ++ + + + + + | + "Organization (O) field of attestation certificate DN must be present."); |
+ +287 + | ++ + + + + + | +|
+ +288 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +289 + | ++ + + + + + | + getDnField("OU", cert).filter(ouValue::equals).isPresent(), |
+ +290 + | ++ + + + + + | + "Organization Unit (OU) field of attestation certificate DN must be exactly \"%s\", was: %s", |
+ +291 + | ++ + + + + + | + ouValue, |
+ +292 + | ++ + + + + + | + getDnField("OU", cert)); |
+ +293 + | ++ + + + + + | +|
+ +294 + | ++ + + + + + | + CertificateParser.parseFidoAaguidExtension(cert) |
+ +295 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ .ifPresent( |
+ +296 + | ++ + + + + + | + extensionAaguid -> { |
+ +297 + | +
+
+1
+
+1. lambda$verifyX5cRequirements$7 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +298 + | ++ + + + + + | + Arrays.equals(aaguid.getBytes(), extensionAaguid), |
+ +299 + | ++ + + + + + | + "X.509 extension \"id-fido-gen-ce-aaguid\" is present but does not match the authenticator AAGUID."); |
+ +300 + | ++ + + + + + | + }); |
+ +301 + | ++ + + + + + | +|
+ +302 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +303 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ cert.getBasicConstraints() == -1, "Attestation certificate must not be a CA certificate."); |
+ +304 + | ++ + + + + + | +|
+ +305 + | +
+
+1
+
+1. verifyX5cRequirements : replaced boolean return with false for com/yubico/webauthn/PackedAttestationStatementVerifier::verifyX5cRequirements → KILLED + + + + |
+ return true; |
+ +306 + | ++ + + + + + | + } |
+ +307 + | ++ + + + + + | +} |
Mutations | ||
64 | ++ |
+
+
+
+ 1.1 |
+
65 | ++ |
+
+
+
+ 1.1 |
+
67 | ++ |
+
+
+
+ 1.1 |
+
76 | ++ |
+
+
+
+ 1.1 2.2 |
+
80 | ++ |
+
+
+
+ 1.1 |
+
81 | ++ |
+
+
+
+ 1.1 2.2 |
+
83 | ++ |
+
+
+
+ 1.1 2.2 |
+
124 | ++ |
+
+
+
+ 1.1 |
+
132 | ++ |
+
+
+
+ 1.1 |
+
135 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 2.2 |
+
167 | ++ |
+
+
+
+ 1.1 2.2 |
+
171 | ++ |
+
+
+
+ 1.1 |
+
176 | ++ |
+
+
+
+ 1.1 |
+
188 | ++ |
+
+
+
+ 1.1 |
+
192 | ++ |
+
+
+
+ 1.1 |
+
200 | ++ |
+
+
+
+ 1.1 |
+
215 | ++ |
+
+
+
+ 1.1 |
+
221 | ++ |
+
+
+
+ 1.1 |
+
228 | ++ |
+
+
+
+ 1.1 2.2 |
+
229 | ++ |
+
+
+
+ 1.1 |
+
247 | ++ |
+
+
+
+ 1.1 |
+
262 | ++ |
+
+
+
+ 1.1 |
+
263 | ++ |
+
+
+
+ 1.1 2.2 |
+
269 | ++ |
+
+
+
+ 1.1 |
+
279 | ++ |
+
+
+
+ 1.1 |
+
284 | ++ |
+
+
+
+ 1.1 |
+
285 | ++ |
+
+
+
+ 1.1 2.2 |
+
288 | ++ |
+
+
+
+ 1.1 |
+
295 | ++ |
+
+
+
+ 1.1 |
+
297 | ++ |
+
+
+
+ 1.1 |
+
302 | ++ |
+
+
+
+ 1.1 |
+
303 | ++ |
+
+
+
+ 1.1 |
+
305 | ++ |
+
+
+
+ 1.1 |
+
+ +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 COSE.CoseException; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonAlias; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnore; |
+ +31 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestedCredentialData; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAssertionResponse; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAttestationResponse; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorData; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorTransport; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +39 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +40 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialDescriptor; |
+ +41 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; |
+ +42 + | ++ + + + + + | +import com.yubico.webauthn.data.UserIdentity; |
+ +43 + | ++ + + + + + | +import java.io.IOException; |
+ +44 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +45 + | ++ + + + + + | +import java.security.PublicKey; |
+ +46 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +47 + | ++ + + + + + | +import java.util.Optional; |
+ +48 + | ++ + + + + + | +import java.util.Set; |
+ +49 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +50 + | ++ + + + + + | +import lombok.Builder; |
+ +51 + | ++ + + + + + | +import lombok.Getter; |
+ +52 + | ++ + + + + + | +import lombok.NonNull; |
+ +53 + | ++ + + + + + | +import lombok.Value; |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | +/** |
+ +56 + | ++ + + + + + | + * An abstraction of a credential registered to a particular user. |
+ +57 + | ++ + + + + + | + * |
+ +58 + | ++ + + + + + | + * <p>Instances of this class are not expected to be long-lived, and the library only needs to read |
+ +59 + | ++ + + + + + | + * them, never write them. You may at your discretion store them directly in your database, or |
+ +60 + | ++ + + + + + | + * assemble them from other components. |
+ +61 + | ++ + + + + + | + */ |
+ +62 + | ++ + + + + + | +@Value |
+ +63 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +64 + | ++ + + + + + | +public final class RegisteredCredential implements CredentialRecord { |
+ +65 + | ++ + + + + + | +|
+ +66 + | ++ + + + + + | + /** |
+ +67 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">credential |
+ +68 + | ++ + + + + + | + * ID</a> of the credential. |
+ +69 + | ++ + + + + + | + * |
+ +70 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">Credential |
+ +71 + | ++ + + + + + | + * ID</a> |
+ +72 + | ++ + + + + + | + * @see RegistrationResult#getKeyId() |
+ +73 + | ++ + + + + + | + * @see PublicKeyCredentialDescriptor#getId() |
+ +74 + | ++ + + + + + | + */ |
+ +75 + | ++ + + + + + | + @NonNull private final ByteArray credentialId; |
+ +76 + | ++ + + + + + | +|
+ +77 + | ++ + + + + + | + /** |
+ +78 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">user handle</a> |
+ +79 + | ++ + + + + + | + * of the user the credential is registered to. |
+ +80 + | ++ + + + + + | + * |
+ +81 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User Handle</a> |
+ +82 + | ++ + + + + + | + * @see UserIdentity#getId() |
+ +83 + | ++ + + + + + | + */ |
+ +84 + | ++ + + + + + | + @NonNull private final ByteArray userHandle; |
+ +85 + | ++ + + + + + | +|
+ +86 + | ++ + + + + + | + /** |
+ +87 + | ++ + + + + + | + * The credential public key encoded in COSE_Key format, as defined in Section 7 of <a |
+ +88 + | ++ + + + + + | + * href="https://tools.ietf.org/html/rfc8152">RFC 8152</a>. |
+ +89 + | ++ + + + + + | + * |
+ +90 + | ++ + + + + + | + * <p>This is used to verify the {@link AuthenticatorAssertionResponse#getSignature() signature} |
+ +91 + | ++ + + + + + | + * in authentication assertions. |
+ +92 + | ++ + + + + + | + * |
+ +93 + | ++ + + + + + | + * @see AttestedCredentialData#getCredentialPublicKey() |
+ +94 + | ++ + + + + + | + * @see RegistrationResult#getPublicKeyCose() |
+ +95 + | ++ + + + + + | + */ |
+ +96 + | ++ + + + + + | + @NonNull private final ByteArray publicKeyCose; |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + /** |
+ +99 + | ++ + + + + + | + * The public key of the credential, parsed as a {@link PublicKey} object. |
+ +100 + | ++ + + + + + | + * |
+ +101 + | ++ + + + + + | + * @see #getPublicKeyCose() |
+ +102 + | ++ + + + + + | + * @see RegistrationResult#getParsedPublicKey() |
+ +103 + | ++ + + + + + | + */ |
+ +104 + | ++ + + + + + | + @NonNull |
+ +105 + | ++ + + + + + | + @JsonIgnore |
+ +106 + | ++ + + + + + | + public PublicKey getParsedPublicKey() |
+ +107 + | ++ + + + + + | + throws InvalidKeySpecException, NoSuchAlgorithmException, CoseException, IOException { |
+ +108 + | +
+
+1
+
+1. getParsedPublicKey : replaced return value with null for com/yubico/webauthn/RegisteredCredential::getParsedPublicKey → NO_COVERAGE + + + + |
+ return WebAuthnCodecs.importCosePublicKey(getPublicKeyCose()); |
+ +109 + | ++ + + + + + | + } |
+ +110 + | ++ + + + + + | +|
+ +111 + | ++ + + + + + | + /** |
+ +112 + | ++ + + + + + | + * The stored <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature |
+ +113 + | ++ + + + + + | + * count</a> of the credential. |
+ +114 + | ++ + + + + + | + * |
+ +115 + | ++ + + + + + | + * <p>This is used to validate the {@link AuthenticatorData#getSignatureCounter() signature |
+ +116 + | ++ + + + + + | + * counter} in authentication assertions. |
+ +117 + | ++ + + + + + | + * |
+ +118 + | ++ + + + + + | + * @see <a |
+ +119 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">§6.1. |
+ +120 + | ++ + + + + + | + * Authenticator Data</a> |
+ +121 + | ++ + + + + + | + * @see AuthenticatorData#getSignatureCounter() |
+ +122 + | ++ + + + + + | + * @see AssertionResult#getSignatureCount() |
+ +123 + | ++ + + + + + | + */ |
+ +124 + | ++ + + + + + | + @Builder.Default private final long signatureCount = 0; |
+ +125 + | ++ + + + + + | +|
+ +126 + | ++ + + + + + | + /** |
+ +127 + | ++ + + + + + | + * Transport hints as to how the client might communicate with the authenticator this credential |
+ +128 + | ++ + + + + + | + * is bound to. |
+ +129 + | ++ + + + + + | + * |
+ +130 + | ++ + + + + + | + * <p>This SHOULD be set to the value returned by {@link |
+ +131 + | ++ + + + + + | + * AuthenticatorAttestationResponse#getTransports()} when the credential was created. That value |
+ +132 + | ++ + + + + + | + * SHOULD NOT be modified. |
+ +133 + | ++ + + + + + | + * |
+ +134 + | ++ + + + + + | + * <p>This is only used if the {@link RelyingParty} is configured with a {@link |
+ +135 + | ++ + + + + + | + * CredentialRepositoryV2}, in which case this is used to set {@link |
+ +136 + | ++ + + + + + | + * PublicKeyCredentialDescriptor#getTransports()} in {@link |
+ +137 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getExcludeCredentials() excludeCredentials} in {@link |
+ +138 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)} and {@link |
+ +139 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials() allowCredentials} in {@link |
+ +140 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)}. This is not used if the {@link |
+ +141 + | ++ + + + + + | + * RelyingParty} is configured with a {@link CredentialRepository}. |
+ +142 + | ++ + + + + + | + * |
+ +143 + | ++ + + + + + | + * @see <a |
+ +144 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatorattestationresponse-gettransports">getTransports() |
+ +145 + | ++ + + + + + | + * in 5.2.1. Information About Public Key Credential (interface |
+ +146 + | ++ + + + + + | + * AuthenticatorAttestationResponse)</a> |
+ +147 + | ++ + + + + + | + * @see <a |
+ +148 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-publickeycredentialdescriptor-transports">transports |
+ +149 + | ++ + + + + + | + * in 5.8.3. Credential Descriptor (dictionary PublicKeyCredentialDescriptor)</a> |
+ +150 + | ++ + + + + + | + * @see AuthenticatorAttestationResponse#getTransports() |
+ +151 + | ++ + + + + + | + * @see PublicKeyCredentialDescriptor#getTransports() |
+ +152 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +153 + | ++ + + + + + | + * before reaching a mature release. |
+ +154 + | ++ + + + + + | + */ |
+ +155 + | ++ + + + + + | + @Deprecated @Builder.Default private final Set<AuthenticatorTransport> transports = null; |
+ +156 + | ++ + + + + + | +|
+ +157 + | ++ + + + + + | + /** |
+ +158 + | ++ + + + + + | + * The state of the <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> when |
+ +159 + | ++ + + + + + | + * this credential was registered, if known. |
+ +160 + | ++ + + + + + | + * |
+ +161 + | ++ + + + + + | + * <p>If absent, it is not known whether or not this credential is backup eligible. |
+ +162 + | ++ + + + + + | + * |
+ +163 + | ++ + + + + + | + * <p>If present and <code>true</code>, the credential is backup eligible: it can be backed up in |
+ +164 + | ++ + + + + + | + * some way, most commonly by syncing the private key to a cloud account. |
+ +165 + | ++ + + + + + | + * |
+ +166 + | ++ + + + + + | + * <p>If present and <code>false</code>, the credential is not backup eligible: it cannot be |
+ +167 + | ++ + + + + + | + * backed up in any way. |
+ +168 + | ++ + + + + + | + * |
+ +169 + | ++ + + + + + | + * <p>{@link CredentialRepository} implementations SHOULD set this to the first known value |
+ +170 + | ++ + + + + + | + * returned by {@link RegistrationResult#isBackupEligible()} or {@link |
+ +171 + | ++ + + + + + | + * AssertionResult#isBackupEligible()}, if known. If unknown, {@link CredentialRepository} |
+ +172 + | ++ + + + + + | + * implementations SHOULD set this to <code>null</code> or not set this value. |
+ +173 + | ++ + + + + + | + * |
+ +174 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +175 + | ++ + + + + + | + * the standard matures. |
+ +176 + | ++ + + + + + | + */ |
+ +177 + | ++ + + + + + | + @Deprecated |
+ +178 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +179 + | ++ + + + + + | + @Builder.Default |
+ +180 + | ++ + + + + + | + private final Boolean backupEligible = null; |
+ +181 + | ++ + + + + + | +|
+ +182 + | ++ + + + + + | + /** |
+ +183 + | ++ + + + + + | + * The last known state of the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS |
+ +184 + | ++ + + + + + | + * flag</a> for this credential, if known. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * <p>If absent, the backup state of the credential is not known. |
+ +187 + | ++ + + + + + | + * |
+ +188 + | ++ + + + + + | + * <p>If present and <code>true</code>, the credential is believed to be currently backed up. |
+ +189 + | ++ + + + + + | + * |
+ +190 + | ++ + + + + + | + * <p>If present and <code>false</code>, the credential is believed to not be currently backed up. |
+ +191 + | ++ + + + + + | + * |
+ +192 + | ++ + + + + + | + * <p>{@link CredentialRepository} implementations SHOULD set this to the most recent value |
+ +193 + | ++ + + + + + | + * returned by {@link AssertionResult#isBackedUp()} or {@link RegistrationResult#isBackedUp()}, if |
+ +194 + | ++ + + + + + | + * known. If unknown, {@link CredentialRepository} implementations SHOULD set this to <code>null |
+ +195 + | ++ + + + + + | + * </code> or not set this value. |
+ +196 + | ++ + + + + + | + * |
+ +197 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +198 + | ++ + + + + + | + * the standard matures. |
+ +199 + | ++ + + + + + | + */ |
+ +200 + | ++ + + + + + | + @Deprecated |
+ +201 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +202 + | ++ + + + + + | + @Builder.Default |
+ +203 + | ++ + + + + + | + private final Boolean backupState = null; |
+ +204 + | ++ + + + + + | +|
+ +205 + | ++ + + + + + | + @JsonCreator |
+ +206 + | ++ + + + + + | + private RegisteredCredential( |
+ +207 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("credentialId") ByteArray credentialId, |
+ +208 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("userHandle") ByteArray userHandle, |
+ +209 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("publicKeyCose") ByteArray publicKeyCose, |
+ +210 + | ++ + + + + + | + @JsonProperty("signatureCount") long signatureCount, |
+ +211 + | ++ + + + + + | + @JsonProperty("transports") Set<AuthenticatorTransport> transports, |
+ +212 + | ++ + + + + + | + @JsonProperty("backupEligible") Boolean backupEligible, |
+ +213 + | ++ + + + + + | + @JsonProperty("backupState") @JsonAlias("backedUp") Boolean backupState) { |
+ +214 + | ++ + + + + + | + this.credentialId = credentialId; |
+ +215 + | ++ + + + + + | + this.userHandle = userHandle; |
+ +216 + | ++ + + + + + | + this.publicKeyCose = publicKeyCose; |
+ +217 + | ++ + + + + + | + this.signatureCount = signatureCount; |
+ +218 + | ++ + + + + + | + this.transports = transports; |
+ +219 + | ++ + + + + + | + this.backupEligible = backupEligible; |
+ +220 + | ++ + + + + + | + this.backupState = backupState; |
+ +221 + | ++ + + + + + | + } |
+ +222 + | ++ + + + + + | +|
+ +223 + | ++ + + + + + | + /** |
+ +224 + | ++ + + + + + | + * Transport hints as to how the client might communicate with the authenticator this credential |
+ +225 + | ++ + + + + + | + * is bound to. |
+ +226 + | ++ + + + + + | + * |
+ +227 + | ++ + + + + + | + * <p>This SHOULD be set to the value returned by {@link |
+ +228 + | ++ + + + + + | + * AuthenticatorAttestationResponse#getTransports()} when the credential was created. That value |
+ +229 + | ++ + + + + + | + * SHOULD NOT be modified. |
+ +230 + | ++ + + + + + | + * |
+ +231 + | ++ + + + + + | + * <p>This is only used if the {@link RelyingParty} is configured with a {@link |
+ +232 + | ++ + + + + + | + * CredentialRepositoryV2}, in which case this is used to set {@link |
+ +233 + | ++ + + + + + | + * PublicKeyCredentialDescriptor#getTransports()} in {@link |
+ +234 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getExcludeCredentials() excludeCredentials} in {@link |
+ +235 + | ++ + + + + + | + * RelyingParty#startRegistration(StartRegistrationOptions)} and {@link |
+ +236 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials() allowCredentials} in {@link |
+ +237 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)}. This is not used if the {@link |
+ +238 + | ++ + + + + + | + * RelyingParty} is configured with a {@link CredentialRepository}. |
+ +239 + | ++ + + + + + | + * |
+ +240 + | ++ + + + + + | + * @see <a |
+ +241 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-authenticatorattestationresponse-gettransports">getTransports() |
+ +242 + | ++ + + + + + | + * in 5.2.1. Information About Public Key Credential (interface |
+ +243 + | ++ + + + + + | + * AuthenticatorAttestationResponse)</a> |
+ +244 + | ++ + + + + + | + * @see <a |
+ +245 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-publickeycredentialdescriptor-transports">transports |
+ +246 + | ++ + + + + + | + * in 5.8.3. Credential Descriptor (dictionary PublicKeyCredentialDescriptor)</a> |
+ +247 + | ++ + + + + + | + * @see AuthenticatorAttestationResponse#getTransports() |
+ +248 + | ++ + + + + + | + * @see PublicKeyCredentialDescriptor#getTransports() |
+ +249 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +250 + | ++ + + + + + | + * before reaching a mature release. |
+ +251 + | ++ + + + + + | + */ |
+ +252 + | ++ + + + + + | + @Deprecated |
+ +253 + | ++ + + + + + | + @Override |
+ +254 + | ++ + + + + + | + public Optional<Set<AuthenticatorTransport>> getTransports() { |
+ +255 + | +
+
+1
+
+1. getTransports : replaced return value with Optional.empty for com/yubico/webauthn/RegisteredCredential::getTransports → SURVIVED + + + + |
+ return Optional.ofNullable(transports); |
+ +256 + | ++ + + + + + | + } |
+ +257 + | ++ + + + + + | +|
+ +258 + | ++ + + + + + | + /** |
+ +259 + | ++ + + + + + | + * The state of the <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> when |
+ +260 + | ++ + + + + + | + * this credential was registered, if known. |
+ +261 + | ++ + + + + + | + * |
+ +262 + | ++ + + + + + | + * <p>If absent, it is not known whether or not this credential is backup eligible. |
+ +263 + | ++ + + + + + | + * |
+ +264 + | ++ + + + + + | + * <p>If present and <code>true</code>, the credential is backup eligible: it can be backed up in |
+ +265 + | ++ + + + + + | + * some way, most commonly by syncing the private key to a cloud account. |
+ +266 + | ++ + + + + + | + * |
+ +267 + | ++ + + + + + | + * <p>If present and <code>false</code>, the credential is not backup eligible: it cannot be |
+ +268 + | ++ + + + + + | + * backed up in any way. |
+ +269 + | ++ + + + + + | + * |
+ +270 + | ++ + + + + + | + * <p>{@link CredentialRepository} implementations SHOULD set this to the first known value |
+ +271 + | ++ + + + + + | + * returned by {@link RegistrationResult#isBackupEligible()} or {@link |
+ +272 + | ++ + + + + + | + * AssertionResult#isBackupEligible()}, if known. If unknown, {@link CredentialRepository} |
+ +273 + | ++ + + + + + | + * implementations SHOULD set this to <code>null</code> or not set this value. |
+ +274 + | ++ + + + + + | + * |
+ +275 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +276 + | ++ + + + + + | + * the standard matures. |
+ +277 + | ++ + + + + + | + */ |
+ +278 + | ++ + + + + + | + @Deprecated |
+ +279 + | ++ + + + + + | + @JsonProperty("backupEligible") |
+ +280 + | ++ + + + + + | + public Optional<Boolean> isBackupEligible() { |
+ +281 + | +
+
+1
+
+1. isBackupEligible : replaced return value with Optional.empty for com/yubico/webauthn/RegisteredCredential::isBackupEligible → KILLED + + + + |
+ return Optional.ofNullable(backupEligible); |
+ +282 + | ++ + + + + + | + } |
+ +283 + | ++ + + + + + | +|
+ +284 + | ++ + + + + + | + /** |
+ +285 + | ++ + + + + + | + * The last known state of the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS |
+ +286 + | ++ + + + + + | + * flag</a> for this credential, if known. |
+ +287 + | ++ + + + + + | + * |
+ +288 + | ++ + + + + + | + * <p>If absent, the backup state of the credential is not known. |
+ +289 + | ++ + + + + + | + * |
+ +290 + | ++ + + + + + | + * <p>If present and <code>true</code>, the credential is believed to be currently backed up. |
+ +291 + | ++ + + + + + | + * |
+ +292 + | ++ + + + + + | + * <p>If present and <code>false</code>, the credential is believed to not be currently backed up. |
+ +293 + | ++ + + + + + | + * |
+ +294 + | ++ + + + + + | + * <p>{@link CredentialRepository} implementations SHOULD set this to the most recent value |
+ +295 + | ++ + + + + + | + * returned by {@link AssertionResult#isBackedUp()} or {@link RegistrationResult#isBackedUp()}, if |
+ +296 + | ++ + + + + + | + * known. If unknown, {@link CredentialRepository} implementations SHOULD set this to <code>null |
+ +297 + | ++ + + + + + | + * </code> or not set this value. |
+ +298 + | ++ + + + + + | + * |
+ +299 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +300 + | ++ + + + + + | + * the standard matures. |
+ +301 + | ++ + + + + + | + */ |
+ +302 + | ++ + + + + + | + @Deprecated |
+ +303 + | ++ + + + + + | + @JsonProperty("backupState") |
+ +304 + | ++ + + + + + | + public Optional<Boolean> isBackedUp() { |
+ +305 + | +
+
+1
+
+1. isBackedUp : replaced return value with Optional.empty for com/yubico/webauthn/RegisteredCredential::isBackedUp → KILLED + + + + |
+ return Optional.ofNullable(backupState); |
+ +306 + | ++ + + + + + | + } |
+ +307 + | ++ + + + + + | +|
+ +308 + | ++ + + + + + | + public static RegisteredCredentialBuilder.MandatoryStages builder() { |
+ +309 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/RegisteredCredential::builder → KILLED + + + + |
+ return new RegisteredCredentialBuilder.MandatoryStages(); |
+ +310 + | ++ + + + + + | + } |
+ +311 + | ++ + + + + + | +|
+ +312 + | ++ + + + + + | + public static class RegisteredCredentialBuilder { |
+ +313 + | ++ + + + + + | + public static class MandatoryStages { |
+ +314 + | ++ + + + + + | + private final RegisteredCredentialBuilder builder = new RegisteredCredentialBuilder(); |
+ +315 + | ++ + + + + + | +|
+ +316 + | ++ + + + + + | + /** |
+ +317 + | ++ + + + + + | + * {@link RegisteredCredentialBuilder#credentialId(ByteArray) credentialId} is a required |
+ +318 + | ++ + + + + + | + * parameter. |
+ +319 + | ++ + + + + + | + * |
+ +320 + | ++ + + + + + | + * @see RegisteredCredentialBuilder#credentialId(ByteArray) |
+ +321 + | ++ + + + + + | + */ |
+ +322 + | ++ + + + + + | + public Step2 credentialId(ByteArray credentialId) { |
+ +323 + | ++ + + + + + | + builder.credentialId(credentialId); |
+ +324 + | +
+
+1
+
+1. credentialId : replaced return value with null for com/yubico/webauthn/RegisteredCredential$RegisteredCredentialBuilder$MandatoryStages::credentialId → KILLED + + + + |
+ return new Step2(); |
+ +325 + | ++ + + + + + | + } |
+ +326 + | ++ + + + + + | +|
+ +327 + | ++ + + + + + | + public class Step2 { |
+ +328 + | ++ + + + + + | + /** |
+ +329 + | ++ + + + + + | + * {@link RegisteredCredentialBuilder#userHandle(ByteArray) userHandle} is a required |
+ +330 + | ++ + + + + + | + * parameter. |
+ +331 + | ++ + + + + + | + * |
+ +332 + | ++ + + + + + | + * @see RegisteredCredentialBuilder#userHandle(ByteArray) |
+ +333 + | ++ + + + + + | + */ |
+ +334 + | ++ + + + + + | + public Step3 userHandle(ByteArray userHandle) { |
+ +335 + | ++ + + + + + | + builder.userHandle(userHandle); |
+ +336 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/RegisteredCredential$RegisteredCredentialBuilder$MandatoryStages$Step2::userHandle → KILLED + + + + |
+ return new Step3(); |
+ +337 + | ++ + + + + + | + } |
+ +338 + | ++ + + + + + | + } |
+ +339 + | ++ + + + + + | +|
+ +340 + | ++ + + + + + | + public class Step3 { |
+ +341 + | ++ + + + + + | + /** |
+ +342 + | ++ + + + + + | + * {@link RegisteredCredentialBuilder#publicKeyCose(ByteArray) publicKeyCose} is a required |
+ +343 + | ++ + + + + + | + * parameter. |
+ +344 + | ++ + + + + + | + * |
+ +345 + | ++ + + + + + | + * <p>The return value of {@link RegistrationResult#getPublicKeyCose()} is a suitable |
+ +346 + | ++ + + + + + | + * argument for this method. |
+ +347 + | ++ + + + + + | + * |
+ +348 + | ++ + + + + + | + * <p>Alternatively, the public key can be specified using the {@link |
+ +349 + | ++ + + + + + | + * #publicKeyEs256Raw(ByteArray)} method if the key is stored in the U2F format (<code> |
+ +350 + | ++ + + + + + | + * ALG_KEY_ECC_X962_RAW</code> as specified in <a |
+ +351 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +352 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a>). This is mostly useful for public |
+ +353 + | ++ + + + + + | + * keys registered via the U2F JavaScript API. |
+ +354 + | ++ + + + + + | + * |
+ +355 + | ++ + + + + + | + * @see #publicKeyEs256Raw(ByteArray) |
+ +356 + | ++ + + + + + | + * @see RegisteredCredentialBuilder#publicKeyCose(ByteArray) |
+ +357 + | ++ + + + + + | + * @see <a |
+ +358 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +359 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a> |
+ +360 + | ++ + + + + + | + */ |
+ +361 + | ++ + + + + + | + public RegisteredCredentialBuilder publicKeyCose(ByteArray publicKeyCose) { |
+ +362 + | +
+
+1
+
+1. publicKeyCose : replaced return value with null for com/yubico/webauthn/RegisteredCredential$RegisteredCredentialBuilder$MandatoryStages$Step3::publicKeyCose → KILLED + + + + |
+ return builder.publicKeyCose(publicKeyCose); |
+ +363 + | ++ + + + + + | + } |
+ +364 + | ++ + + + + + | +|
+ +365 + | ++ + + + + + | + /** |
+ +366 + | ++ + + + + + | + * Specify the credential public key in U2F format. |
+ +367 + | ++ + + + + + | + * |
+ +368 + | ++ + + + + + | + * <p>An alternative to {@link #publicKeyCose(ByteArray)}, this method expects an {@link |
+ +369 + | ++ + + + + + | + * COSEAlgorithmIdentifier#ES256 ES256} public key in <code>ALG_KEY_ECC_X962_RAW</code> |
+ +370 + | ++ + + + + + | + * format as specified in <a |
+ +371 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +372 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a>. |
+ +373 + | ++ + + + + + | + * |
+ +374 + | ++ + + + + + | + * <p>This is primarily intended for public keys registered via the U2F JavaScript API. If |
+ +375 + | ++ + + + + + | + * your application has only used the <code>navigator.credentials.create()</code> API to |
+ +376 + | ++ + + + + + | + * register credentials, you should use {@link #publicKeyCose(ByteArray)} instead. |
+ +377 + | ++ + + + + + | + * |
+ +378 + | ++ + + + + + | + * @see RegisteredCredentialBuilder#publicKeyCose(ByteArray) |
+ +379 + | ++ + + + + + | + */ |
+ +380 + | ++ + + + + + | + public RegisteredCredentialBuilder publicKeyEs256Raw(ByteArray publicKeyEs256Raw) { |
+ +381 + | +
+
+1
+
+1. publicKeyEs256Raw : replaced return value with null for com/yubico/webauthn/RegisteredCredential$RegisteredCredentialBuilder$MandatoryStages$Step3::publicKeyEs256Raw → KILLED + + + + |
+ return builder.publicKeyCose(WebAuthnCodecs.rawEcKeyToCose(publicKeyEs256Raw)); |
+ +382 + | ++ + + + + + | + } |
+ +383 + | ++ + + + + + | + } |
+ +384 + | ++ + + + + + | + } |
+ +385 + | ++ + + + + + | +|
+ +386 + | ++ + + + + + | + /** |
+ +387 + | ++ + + + + + | + * The credential public key encoded in COSE_Key format, as defined in Section 7 of <a |
+ +388 + | ++ + + + + + | + * href="https://tools.ietf.org/html/rfc8152">RFC 8152</a>. This method overwrites {@link |
+ +389 + | ++ + + + + + | + * #publicKeyEs256Raw(ByteArray)}. |
+ +390 + | ++ + + + + + | + * |
+ +391 + | ++ + + + + + | + * <p>The return value of {@link RegistrationResult#getPublicKeyCose()} is a suitable argument |
+ +392 + | ++ + + + + + | + * for this method. |
+ +393 + | ++ + + + + + | + * |
+ +394 + | ++ + + + + + | + * <p>This is used to verify the {@link AuthenticatorAssertionResponse#getSignature() signature} |
+ +395 + | ++ + + + + + | + * in authentication assertions. |
+ +396 + | ++ + + + + + | + * |
+ +397 + | ++ + + + + + | + * <p>Alternatively, the public key can be specified using the {@link |
+ +398 + | ++ + + + + + | + * #publicKeyEs256Raw(ByteArray)} method if the key is stored in the U2F format (<code> |
+ +399 + | ++ + + + + + | + * ALG_KEY_ECC_X962_RAW</code> as specified in <a |
+ +400 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +401 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a>). This is mostly useful for public keys |
+ +402 + | ++ + + + + + | + * registered via the U2F JavaScript API. |
+ +403 + | ++ + + + + + | + * |
+ +404 + | ++ + + + + + | + * @see AttestedCredentialData#getCredentialPublicKey() |
+ +405 + | ++ + + + + + | + * @see RegistrationResult#getPublicKeyCose() |
+ +406 + | ++ + + + + + | + */ |
+ +407 + | +
+
+1
+
+1. publicKeyCose : negated conditional → KILLED + + + + |
+ public RegisteredCredentialBuilder publicKeyCose(@NonNull ByteArray publicKeyCose) { |
+ +408 + | ++ + + + + + | + this.publicKeyCose = publicKeyCose; |
+ +409 + | +
+
+1
+
+1. publicKeyCose : replaced return value with null for com/yubico/webauthn/RegisteredCredential$RegisteredCredentialBuilder::publicKeyCose → KILLED + + + + |
+ return this; |
+ +410 + | ++ + + + + + | + } |
+ +411 + | ++ + + + + + | +|
+ +412 + | ++ + + + + + | + /** |
+ +413 + | ++ + + + + + | + * Specify the credential public key in U2F format. This method overwrites {@link |
+ +414 + | ++ + + + + + | + * #publicKeyCose(ByteArray)}. |
+ +415 + | ++ + + + + + | + * |
+ +416 + | ++ + + + + + | + * <p>An alternative to {@link #publicKeyCose(ByteArray)}, this method expects an {@link |
+ +417 + | ++ + + + + + | + * COSEAlgorithmIdentifier#ES256 ES256} public key in <code>ALG_KEY_ECC_X962_RAW</code> format |
+ +418 + | ++ + + + + + | + * as specified in <a |
+ +419 + | ++ + + + + + | + * href="https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-registry-v2.0-id-20180227.html#public-key-representation-formats">FIDO |
+ +420 + | ++ + + + + + | + * Registry §3.6.2 Public Key Representation Formats</a>. |
+ +421 + | ++ + + + + + | + * |
+ +422 + | ++ + + + + + | + * <p>This is primarily intended for public keys registered via the U2F JavaScript API. If your |
+ +423 + | ++ + + + + + | + * application has only used the <code>navigator.credentials.create()</code> API to register |
+ +424 + | ++ + + + + + | + * credentials, you should use {@link #publicKeyCose(ByteArray)} instead. |
+ +425 + | ++ + + + + + | + * |
+ +426 + | ++ + + + + + | + * @see RegisteredCredentialBuilder#publicKeyCose(ByteArray) |
+ +427 + | ++ + + + + + | + */ |
+ +428 + | ++ + + + + + | + public RegisteredCredentialBuilder publicKeyEs256Raw(ByteArray publicKeyEs256Raw) { |
+ +429 + | +
+
+1
+
+1. publicKeyEs256Raw : replaced return value with null for com/yubico/webauthn/RegisteredCredential$RegisteredCredentialBuilder::publicKeyEs256Raw → KILLED + + + + |
+ return publicKeyCose(WebAuthnCodecs.rawEcKeyToCose(publicKeyEs256Raw)); |
+ +430 + | ++ + + + + + | + } |
+ +431 + | ++ + + + + + | + } |
+ +432 + | ++ + + + + + | +} |
Mutations | ||
108 | ++ |
+
+
+
+ 1.1 |
+
207 | ++ |
+
+
+
+ 1.1 |
+
208 | ++ |
+
+
+
+ 1.1 |
+
209 | ++ |
+
+
+
+ 1.1 |
+
255 | ++ |
+
+
+
+ 1.1 |
+
281 | ++ |
+
+
+
+ 1.1 |
+
305 | ++ |
+
+
+
+ 1.1 |
+
309 | ++ |
+
+
+
+ 1.1 |
+
324 | ++ |
+
+
+
+ 1.1 |
+
336 | ++ |
+
+
+
+ 1.1 |
+
362 | ++ |
+
+
+
+ 1.1 |
+
381 | ++ |
+
+
+
+ 1.1 |
+
407 | ++ |
+
+
+
+ 1.1 |
+
409 | ++ |
+
+
+
+ 1.1 |
+
429 | ++ |
+
+
+
+ 1.1 |
+
+ +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 COSE.CoseException; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonCreator; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonIgnore; |
+ +30 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonProperty; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.RelyingParty.RelyingPartyBuilder; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.attestation.AttestationTrustSource; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAttachment; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorAttestationResponse; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorData; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorDataFlags; |
+ +39 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorRegistrationExtensionOutputs; |
+ +40 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorResponse; |
+ +41 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +42 + | ++ + + + + + | +import com.yubico.webauthn.data.ClientRegistrationExtensionOutputs; |
+ +43 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredential; |
+ +44 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialDescriptor; |
+ +45 + | ++ + + + + + | +import java.io.IOException; |
+ +46 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +47 + | ++ + + + + + | +import java.security.PublicKey; |
+ +48 + | ++ + + + + + | +import java.security.cert.CertificateEncodingException; |
+ +49 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +50 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +51 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +52 + | ++ + + + + + | +import java.util.List; |
+ +53 + | ++ + + + + + | +import java.util.Optional; |
+ +54 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +55 + | ++ + + + + + | +import lombok.AccessLevel; |
+ +56 + | ++ + + + + + | +import lombok.Getter; |
+ +57 + | ++ + + + + + | +import lombok.NonNull; |
+ +58 + | ++ + + + + + | +import lombok.Value; |
+ +59 + | ++ + + + + + | +|
+ +60 + | ++ + + + + + | +/** The result of a call to {@link RelyingParty#finishRegistration(FinishRegistrationOptions)}. */ |
+ +61 + | ++ + + + + + | +@Value |
+ +62 + | ++ + + + + + | +public class RegistrationResult { |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + @JsonProperty |
+ +65 + | ++ + + + + + | + @Getter(AccessLevel.NONE) |
+ +66 + | ++ + + + + + | + private final PublicKeyCredential< |
+ +67 + | ++ + + + + + | + AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +68 + | ++ + + + + + | + credential; |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * <code>true</code> if and only if the attestation signature was successfully linked to a trusted |
+ +72 + | ++ + + + + + | + * attestation root. |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * <p>This will always be <code>false</code> unless the {@link |
+ +75 + | ++ + + + + + | + * RelyingPartyBuilder#attestationTrustSource(AttestationTrustSource) attestationTrustSource} |
+ +76 + | ++ + + + + + | + * setting was configured on the {@link RelyingParty} instance. |
+ +77 + | ++ + + + + + | + * |
+ +78 + | ++ + + + + + | + * <p>You can ignore this if authenticator attestation is not relevant to your application. |
+ +79 + | ++ + + + + + | + */ |
+ +80 + | ++ + + + + + | + private final boolean attestationTrusted; |
+ +81 + | ++ + + + + + | +|
+ +82 + | ++ + + + + + | + /** |
+ +83 + | ++ + + + + + | + * The <a |
+ +84 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation-types">attestation |
+ +85 + | ++ + + + + + | + * type</a> that was used for the created credential. |
+ +86 + | ++ + + + + + | + * |
+ +87 + | ++ + + + + + | + * <p>You can ignore this if authenticator attestation is not relevant to your application. |
+ +88 + | ++ + + + + + | + * |
+ +89 + | ++ + + + + + | + * @see <a |
+ +90 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation-types">§6.4.3. |
+ +91 + | ++ + + + + + | + * Attestation Types</a> |
+ +92 + | ++ + + + + + | + */ |
+ +93 + | ++ + + + + + | + @NonNull private final AttestationType attestationType; |
+ +94 + | ++ + + + + + | +|
+ +95 + | ++ + + + + + | + // JavaDoc on getter |
+ +96 + | ++ + + + + + | + private final List<X509Certificate> attestationTrustPath; |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + RegistrationResult( |
+ +99 + | ++ + + + + + | + PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +100 + | ++ + + + + + | + credential, |
+ +101 + | ++ + + + + + | + boolean attestationTrusted, |
+ +102 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull AttestationType attestationType, |
+ +103 + | ++ + + + + + | + Optional<List<X509Certificate>> attestationTrustPath) { |
+ +104 + | ++ + + + + + | + this.credential = credential; |
+ +105 + | ++ + + + + + | + this.attestationTrusted = attestationTrusted; |
+ +106 + | ++ + + + + + | + this.attestationType = attestationType; |
+ +107 + | ++ + + + + + | + this.attestationTrustPath = attestationTrustPath.orElse(null); |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + @JsonCreator |
+ +111 + | ++ + + + + + | + private static RegistrationResult fromJson( |
+ +112 + | +
+
+1
+
+1. fromJson : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("credential") |
+ +113 + | ++ + + + + + | + PublicKeyCredential<AuthenticatorAttestationResponse, ClientRegistrationExtensionOutputs> |
+ +114 + | ++ + + + + + | + credential, |
+ +115 + | ++ + + + + + | + @JsonProperty("attestationTrusted") boolean attestationTrusted, |
+ +116 + | +
+
+1
+
+1. fromJson : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("attestationType") AttestationType attestationType, |
+ +117 + | +
+
+1
+
+1. fromJson : negated conditional → KILLED + + + + |
+ @NonNull @JsonProperty("attestationTrustPath") Optional<List<String>> attestationTrustPath) { |
+ +118 + | +
+
+1
+
+1. fromJson : replaced return value with null for com/yubico/webauthn/RegistrationResult::fromJson → KILLED + + + + |
+ return new RegistrationResult( |
+ +119 + | ++ + + + + + | + credential, |
+ +120 + | ++ + + + + + | + attestationTrusted, |
+ +121 + | ++ + + + + + | + attestationType, |
+ +122 + | ++ + + + + + | + attestationTrustPath.map( |
+ +123 + | ++ + + + + + | + atp -> |
+ +124 + | ++ + + + + + | + atp.stream() |
+ +125 + | ++ + + + + + | + .map( |
+ +126 + | ++ + + + + + | + pem -> { |
+ +127 + | ++ + + + + + | + try { |
+ +128 + | +
+
+1
+
+1. lambda$fromJson$0 : replaced return value with null for com/yubico/webauthn/RegistrationResult::lambda$fromJson$0 → KILLED + + + + |
+ return CertificateParser.parsePem(pem); |
+ +129 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +130 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +131 + | ++ + + + + + | + } |
+ +132 + | ++ + + + + + | + }) |
+ +133 + | +
+
+1
+
+1. lambda$fromJson$1 : replaced return value with Collections.emptyList for com/yubico/webauthn/RegistrationResult::lambda$fromJson$1 → KILLED + + + + |
+ .collect(Collectors.toList()))); |
+ +134 + | ++ + + + + + | + } |
+ +135 + | ++ + + + + + | +|
+ +136 + | ++ + + + + + | + /** |
+ +137 + | ++ + + + + + | + * Check whether the <a href="https://www.w3.org/TR/webauthn/#user-verification">user |
+ +138 + | ++ + + + + + | + * verification</a> as performed during the registration ceremony. |
+ +139 + | ++ + + + + + | + * |
+ +140 + | ++ + + + + + | + * <p>This flag is also available via <code> |
+ +141 + | ++ + + + + + | + * {@link PublicKeyCredential}.{@link PublicKeyCredential#getResponse() getResponse()}.{@link AuthenticatorResponse#getParsedAuthenticatorData() getParsedAuthenticatorData()}.{@link AuthenticatorData#getFlags() getFlags()}.{@link AuthenticatorDataFlags#UV UV} |
+ +142 + | ++ + + + + + | + * </code>. |
+ +143 + | ++ + + + + + | + * |
+ +144 + | ++ + + + + + | + * @return <code>true</code> if and only if the authenticator claims to have performed user |
+ +145 + | ++ + + + + + | + * verification during the registration ceremony. |
+ +146 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/webauthn/#user-verification">User Verification</a> |
+ +147 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-uv">UV flag in §6.1. Authenticator |
+ +148 + | ++ + + + + + | + * Data</a> |
+ +149 + | ++ + + + + + | + */ |
+ +150 + | ++ + + + + + | + @JsonIgnore |
+ +151 + | ++ + + + + + | + public boolean isUserVerified() { |
+ +152 + | +
+
+2
+
+1. isUserVerified : replaced boolean return with false for com/yubico/webauthn/RegistrationResult::isUserVerified → KILLED +2. isUserVerified : replaced boolean return with true for com/yubico/webauthn/RegistrationResult::isUserVerified → KILLED + + + + |
+ return credential.getResponse().getParsedAuthenticatorData().getFlags().UV; |
+ +153 + | ++ + + + + + | + } |
+ +154 + | ++ + + + + + | +|
+ +155 + | ++ + + + + + | + /** |
+ +156 + | ++ + + + + + | + * Check whether the created credential is <a |
+ +157 + | ++ + + + + + | + * href="https://w3c.github.io/webauthn/#backup-eligible">backup eligible</a>, using the <a |
+ +158 + | ++ + + + + + | + * href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> in the authenticator data. |
+ +159 + | ++ + + + + + | + * |
+ +160 + | ++ + + + + + | + * <p>You SHOULD store this value in your representation of a {@link RegisteredCredential}. {@link |
+ +161 + | ++ + + + + + | + * CredentialRepository} implementations SHOULD set this value as the {@link |
+ +162 + | ++ + + + + + | + * RegisteredCredential.RegisteredCredentialBuilder#backupEligible(Boolean) |
+ +163 + | ++ + + + + + | + * backupEligible(Boolean)} value when reconstructing that {@link RegisteredCredential}. |
+ +164 + | ++ + + + + + | + * |
+ +165 + | ++ + + + + + | + * @return <code>true</code> if and only if the created credential is backup eligible. NOTE that |
+ +166 + | ++ + + + + + | + * this is only a hint and not a guarantee, unless backed by a trusted authenticator |
+ +167 + | ++ + + + + + | + * attestation. |
+ +168 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#backup-eligible">Backup Eligible in §4. |
+ +169 + | ++ + + + + + | + * Terminology</a> |
+ +170 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag in §6.1. Authenticator |
+ +171 + | ++ + + + + + | + * Data</a> |
+ +172 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +173 + | ++ + + + + + | + * the standard matures. |
+ +174 + | ++ + + + + + | + */ |
+ +175 + | ++ + + + + + | + @Deprecated |
+ +176 + | ++ + + + + + | + @JsonIgnore |
+ +177 + | ++ + + + + + | + public boolean isBackupEligible() { |
+ +178 + | +
+
+2
+
+1. isBackupEligible : replaced boolean return with true for com/yubico/webauthn/RegistrationResult::isBackupEligible → KILLED +2. isBackupEligible : replaced boolean return with false for com/yubico/webauthn/RegistrationResult::isBackupEligible → KILLED + + + + |
+ return credential.getResponse().getParsedAuthenticatorData().getFlags().BE; |
+ +179 + | ++ + + + + + | + } |
+ +180 + | ++ + + + + + | +|
+ +181 + | ++ + + + + + | + /** |
+ +182 + | ++ + + + + + | + * Get the current <a href="https://w3c.github.io/webauthn/#backup-state">backup state</a> of the |
+ +183 + | ++ + + + + + | + * created credential, using the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS |
+ +184 + | ++ + + + + + | + * flag</a> in the authenticator data. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * <p>You SHOULD store this value in your representation of a {@link RegisteredCredential}. {@link |
+ +187 + | ++ + + + + + | + * CredentialRepository} implementations SHOULD set this value as the {@link |
+ +188 + | ++ + + + + + | + * RegisteredCredential.RegisteredCredentialBuilder#backupState(Boolean) backupState(Boolean)} |
+ +189 + | ++ + + + + + | + * value when reconstructing that {@link RegisteredCredential}. |
+ +190 + | ++ + + + + + | + * |
+ +191 + | ++ + + + + + | + * @return <code>true</code> if and only if the created credential is believed to currently be |
+ +192 + | ++ + + + + + | + * backed up. NOTE that this is only a hint and not a guarantee, unless backed by a trusted |
+ +193 + | ++ + + + + + | + * authenticator attestation. |
+ +194 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#backup-state">Backup State in §4. Terminology</a> |
+ +195 + | ++ + + + + + | + * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS flag in §6.1. Authenticator |
+ +196 + | ++ + + + + + | + * Data</a> |
+ +197 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as |
+ +198 + | ++ + + + + + | + * the standard matures. |
+ +199 + | ++ + + + + + | + */ |
+ +200 + | ++ + + + + + | + @Deprecated |
+ +201 + | ++ + + + + + | + @JsonIgnore |
+ +202 + | ++ + + + + + | + public boolean isBackedUp() { |
+ +203 + | +
+
+2
+
+1. isBackedUp : replaced boolean return with true for com/yubico/webauthn/RegistrationResult::isBackedUp → KILLED +2. isBackedUp : replaced boolean return with false for com/yubico/webauthn/RegistrationResult::isBackedUp → KILLED + + + + |
+ return credential.getResponse().getParsedAuthenticatorData().getFlags().BS; |
+ +204 + | ++ + + + + + | + } |
+ +205 + | ++ + + + + + | +|
+ +206 + | ++ + + + + + | + /** |
+ +207 + | ++ + + + + + | + * The <a href="https://w3c.github.io/webauthn/#authenticator-attachment-modality">authenticator |
+ +208 + | ++ + + + + + | + * attachment modality</a> in effect at the time the credential was created. |
+ +209 + | ++ + + + + + | + * |
+ +210 + | ++ + + + + + | + * @see PublicKeyCredential#getAuthenticatorAttachment() |
+ +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 Optional<AuthenticatorAttachment> getAuthenticatorAttachment() { |
+ +217 + | +
+
+1
+
+1. getAuthenticatorAttachment : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::getAuthenticatorAttachment → KILLED + + + + |
+ return credential.getAuthenticatorAttachment(); |
+ +218 + | ++ + + + + + | + } |
+ +219 + | ++ + + + + + | +|
+ +220 + | ++ + + + + + | + /** |
+ +221 + | ++ + + + + + | + * The signature count returned with the created credential. |
+ +222 + | ++ + + + + + | + * |
+ +223 + | ++ + + + + + | + * <p>This is used in {@link RelyingParty#finishAssertion(FinishAssertionOptions)} to verify the |
+ +224 + | ++ + + + + + | + * validity of future signature counter values. |
+ +225 + | ++ + + + + + | + * |
+ +226 + | ++ + + + + + | + * @see RegisteredCredential#getSignatureCount() |
+ +227 + | ++ + + + + + | + */ |
+ +228 + | ++ + + + + + | + @JsonIgnore |
+ +229 + | ++ + + + + + | + public long getSignatureCount() { |
+ +230 + | +
+
+1
+
+1. getSignatureCount : replaced long return with 0 for com/yubico/webauthn/RegistrationResult::getSignatureCount → KILLED + + + + |
+ return credential.getResponse().getParsedAuthenticatorData().getSignatureCounter(); |
+ +231 + | ++ + + + + + | + } |
+ +232 + | ++ + + + + + | +|
+ +233 + | ++ + + + + + | + /** |
+ +234 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">credential |
+ +235 + | ++ + + + + + | + * ID</a> and <a |
+ +236 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-publickeycredentialdescriptor-transports">transports</a> |
+ +237 + | ++ + + + + + | + * of the created credential. |
+ +238 + | ++ + + + + + | + * |
+ +239 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#credential-id">Credential |
+ +240 + | ++ + + + + + | + * ID</a> |
+ +241 + | ++ + + + + + | + * @see <a |
+ +242 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dictionary-credential-descriptor">5.8.3. |
+ +243 + | ++ + + + + + | + * Credential Descriptor (dictionary PublicKeyCredentialDescriptor)</a> |
+ +244 + | ++ + + + + + | + * @see PublicKeyCredential#getId() |
+ +245 + | ++ + + + + + | + */ |
+ +246 + | ++ + + + + + | + @JsonIgnore |
+ +247 + | ++ + + + + + | + public PublicKeyCredentialDescriptor getKeyId() { |
+ +248 + | +
+
+1
+
+1. getKeyId : replaced return value with null for com/yubico/webauthn/RegistrationResult::getKeyId → KILLED + + + + |
+ return PublicKeyCredentialDescriptor.builder() |
+ +249 + | ++ + + + + + | + .id(credential.getId()) |
+ +250 + | ++ + + + + + | + .type(credential.getType()) |
+ +251 + | ++ + + + + + | + .transports(credential.getResponse().getTransports()) |
+ +252 + | ++ + + + + + | + .build(); |
+ +253 + | ++ + + + + + | + } |
+ +254 + | ++ + + + + + | +|
+ +255 + | ++ + + + + + | + /** |
+ +256 + | ++ + + + + + | + * The <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#aaguid"><code>aaguid</code> |
+ +257 + | ++ + + + + + | + * </a> reported in the <a |
+ +258 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-data">of the |
+ +259 + | ++ + + + + + | + * created credential.</a> |
+ +260 + | ++ + + + + + | + * |
+ +261 + | ++ + + + + + | + * <p>This MAY be an AAGUID consisting of only zeroes. |
+ +262 + | ++ + + + + + | + */ |
+ +263 + | ++ + + + + + | + @JsonIgnore |
+ +264 + | ++ + + + + + | + public ByteArray getAaguid() { |
+ +265 + | +
+
+1
+
+1. getAaguid : replaced return value with null for com/yubico/webauthn/RegistrationResult::getAaguid → KILLED + + + + |
+ return credential |
+ +266 + | ++ + + + + + | + .getResponse() |
+ +267 + | ++ + + + + + | + .getAttestation() |
+ +268 + | ++ + + + + + | + .getAuthenticatorData() |
+ +269 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +270 + | ++ + + + + + | + .get() |
+ +271 + | ++ + + + + + | + .getAaguid(); |
+ +272 + | ++ + + + + + | + } |
+ +273 + | ++ + + + + + | +|
+ +274 + | ++ + + + + + | + /** |
+ +275 + | ++ + + + + + | + * The public key of the created credential. |
+ +276 + | ++ + + + + + | + * |
+ +277 + | ++ + + + + + | + * <p>This is used in {@link RelyingParty#finishAssertion(FinishAssertionOptions)} to verify the |
+ +278 + | ++ + + + + + | + * authentication signatures. |
+ +279 + | ++ + + + + + | + * |
+ +280 + | ++ + + + + + | + * @see RegisteredCredential#getPublicKeyCose() |
+ +281 + | ++ + + + + + | + */ |
+ +282 + | ++ + + + + + | + @JsonIgnore |
+ +283 + | ++ + + + + + | + public ByteArray getPublicKeyCose() { |
+ +284 + | +
+
+1
+
+1. getPublicKeyCose : replaced return value with null for com/yubico/webauthn/RegistrationResult::getPublicKeyCose → KILLED + + + + |
+ return credential |
+ +285 + | ++ + + + + + | + .getResponse() |
+ +286 + | ++ + + + + + | + .getAttestation() |
+ +287 + | ++ + + + + + | + .getAuthenticatorData() |
+ +288 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +289 + | ++ + + + + + | + .get() |
+ +290 + | ++ + + + + + | + .getCredentialPublicKey(); |
+ +291 + | ++ + + + + + | + } |
+ +292 + | ++ + + + + + | +|
+ +293 + | ++ + + + + + | + /** |
+ +294 + | ++ + + + + + | + * The public key of the created credential, parsed as a {@link PublicKey} object. |
+ +295 + | ++ + + + + + | + * |
+ +296 + | ++ + + + + + | + * @see #getPublicKeyCose() |
+ +297 + | ++ + + + + + | + * @see RegisteredCredential#getParsedPublicKey() |
+ +298 + | ++ + + + + + | + */ |
+ +299 + | ++ + + + + + | + @NonNull |
+ +300 + | ++ + + + + + | + @JsonIgnore |
+ +301 + | ++ + + + + + | + public PublicKey getParsedPublicKey() |
+ +302 + | ++ + + + + + | + throws InvalidKeySpecException, NoSuchAlgorithmException, CoseException, IOException { |
+ +303 + | +
+
+1
+
+1. getParsedPublicKey : replaced return value with null for com/yubico/webauthn/RegistrationResult::getParsedPublicKey → NO_COVERAGE + + + + |
+ return WebAuthnCodecs.importCosePublicKey(getPublicKeyCose()); |
+ +304 + | ++ + + + + + | + } |
+ +305 + | ++ + + + + + | +|
+ +306 + | ++ + + + + + | + /** |
+ +307 + | ++ + + + + + | + * The <a |
+ +308 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client |
+ +309 + | ++ + + + + + | + * extension outputs</a>, if any. |
+ +310 + | ++ + + + + + | + * |
+ +311 + | ++ + + + + + | + * <p>This is present if and only if at least one extension output is present in the return value. |
+ +312 + | ++ + + + + + | + * |
+ +313 + | ++ + + + + + | + * @see <a |
+ +314 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-client-extension-processing">§9.4. |
+ +315 + | ++ + + + + + | + * Client Extension Processing</a> |
+ +316 + | ++ + + + + + | + * @see ClientRegistrationExtensionOutputs |
+ +317 + | ++ + + + + + | + * @see #getAuthenticatorExtensionOutputs() () |
+ +318 + | ++ + + + + + | + */ |
+ +319 + | ++ + + + + + | + @JsonIgnore |
+ +320 + | ++ + + + + + | + public Optional<ClientRegistrationExtensionOutputs> getClientExtensionOutputs() { |
+ +321 + | +
+
+1
+
+1. getClientExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::getClientExtensionOutputs → KILLED + + + + |
+ return Optional.ofNullable(credential.getClientExtensionResults()) |
+ +322 + | +
+
+2
+
+1. lambda$getClientExtensionOutputs$2 : replaced boolean return with true for com/yubico/webauthn/RegistrationResult::lambda$getClientExtensionOutputs$2 → SURVIVED +2. lambda$getClientExtensionOutputs$2 : negated conditional → KILLED + + + + |
+ .filter(ceo -> !ceo.getExtensionIds().isEmpty()); |
+ +323 + | ++ + + + + + | + } |
+ +324 + | ++ + + + + + | +|
+ +325 + | ++ + + + + + | + /** |
+ +326 + | ++ + + + + + | + * The <a |
+ +327 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#authenticator-extension-output">authenticator |
+ +328 + | ++ + + + + + | + * extension outputs</a>, if any. |
+ +329 + | ++ + + + + + | + * |
+ +330 + | ++ + + + + + | + * <p>This is present if and only if at least one extension output is present in the return value. |
+ +331 + | ++ + + + + + | + * |
+ +332 + | ++ + + + + + | + * @see <a |
+ +333 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-extension-processing">§9.5. |
+ +334 + | ++ + + + + + | + * Authenticator Extension Processing</a> |
+ +335 + | ++ + + + + + | + * @see AuthenticatorRegistrationExtensionOutputs |
+ +336 + | ++ + + + + + | + * @see #getClientExtensionOutputs() |
+ +337 + | ++ + + + + + | + */ |
+ +338 + | ++ + + + + + | + @JsonIgnore |
+ +339 + | ++ + + + + + | + public Optional<AuthenticatorRegistrationExtensionOutputs> getAuthenticatorExtensionOutputs() { |
+ +340 + | +
+
+1
+
+1. getAuthenticatorExtensionOutputs : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::getAuthenticatorExtensionOutputs → KILLED + + + + |
+ return AuthenticatorRegistrationExtensionOutputs.fromAuthenticatorData( |
+ +341 + | ++ + + + + + | + credential.getResponse().getParsedAuthenticatorData()); |
+ +342 + | ++ + + + + + | + } |
+ +343 + | ++ + + + + + | +|
+ +344 + | ++ + + + + + | + /** |
+ +345 + | ++ + + + + + | + * Try to determine whether the created credential is a <a |
+ +346 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#discoverable-credential">discoverable |
+ +347 + | ++ + + + + + | + * credential</a>, also called a <i>passkey</i>, using the output from the <a |
+ +348 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension"> |
+ +349 + | ++ + + + + + | + * <code>credProps</code></a> extension. |
+ +350 + | ++ + + + + + | + * |
+ +351 + | ++ + + + + + | + * @return A present <code>true</code> if the created credential is a passkey (discoverable). A |
+ +352 + | ++ + + + + + | + * present <code> |
+ +353 + | ++ + + + + + | + * false</code> if the created credential is not a passkey. An empty value if it is not known |
+ +354 + | ++ + + + + + | + * whether the created credential is a passkey. |
+ +355 + | ++ + + + + + | + * @see <a |
+ +356 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#dom-credentialpropertiesoutput-rk">§10.4. |
+ +357 + | ++ + + + + + | + * Credential Properties Extension (credProps), "rk" output</a> |
+ +358 + | ++ + + + + + | + * @see <a |
+ +359 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#discoverable-credential">Discoverable |
+ +360 + | ++ + + + + + | + * Credential</a> |
+ +361 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +362 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +363 + | ++ + + + + + | + */ |
+ +364 + | ++ + + + + + | + @JsonIgnore |
+ +365 + | ++ + + + + + | + public Optional<Boolean> isDiscoverable() { |
+ +366 + | +
+
+1
+
+1. isDiscoverable : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::isDiscoverable → KILLED + + + + |
+ return getClientExtensionOutputs() |
+ +367 + | +
+
+1
+
+1. lambda$isDiscoverable$3 : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::lambda$isDiscoverable$3 → KILLED + + + + |
+ .flatMap(outputs -> outputs.getCredProps()) |
+ +368 + | +
+
+1
+
+1. lambda$isDiscoverable$4 : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::lambda$isDiscoverable$4 → KILLED + + + + |
+ .flatMap(credProps -> credProps.getRk()); |
+ +369 + | ++ + + + + + | + } |
+ +370 + | ++ + + + + + | +|
+ +371 + | ++ + + + + + | + /** |
+ +372 + | ++ + + + + + | + * The <a |
+ +373 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-trust-path">attestation |
+ +374 + | ++ + + + + + | + * trust path</a> for the created credential, if any. |
+ +375 + | ++ + + + + + | + * |
+ +376 + | ++ + + + + + | + * <p>If present, this may be useful for looking up attestation metadata from external sources. |
+ +377 + | ++ + + + + + | + * The attestation trust path has been successfully verified as trusted if and only if {@link |
+ +378 + | ++ + + + + + | + * #isAttestationTrusted()} is <code>true</code>. |
+ +379 + | ++ + + + + + | + * |
+ +380 + | ++ + + + + + | + * <p>You can ignore this if authenticator attestation is not relevant to your application. |
+ +381 + | ++ + + + + + | + * |
+ +382 + | ++ + + + + + | + * @see <a |
+ +383 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#attestation-trust-path">Attestation |
+ +384 + | ++ + + + + + | + * trust path</a> |
+ +385 + | ++ + + + + + | + */ |
+ +386 + | ++ + + + + + | + @JsonIgnore |
+ +387 + | ++ + + + + + | + public Optional<List<X509Certificate>> getAttestationTrustPath() { |
+ +388 + | +
+
+1
+
+1. getAttestationTrustPath : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::getAttestationTrustPath → KILLED + + + + |
+ return Optional.ofNullable(attestationTrustPath); |
+ +389 + | ++ + + + + + | + } |
+ +390 + | ++ + + + + + | +|
+ +391 + | ++ + + + + + | + @JsonProperty("attestationTrustPath") |
+ +392 + | ++ + + + + + | + private Optional<List<String>> getAttestationTrustPathJson() { |
+ +393 + | +
+
+1
+
+1. getAttestationTrustPathJson : replaced return value with Optional.empty for com/yubico/webauthn/RegistrationResult::getAttestationTrustPathJson → KILLED + + + + |
+ return getAttestationTrustPath() |
+ +394 + | ++ + + + + + | + .map( |
+ +395 + | ++ + + + + + | + x5c -> |
+ +396 + | ++ + + + + + | + x5c.stream() |
+ +397 + | ++ + + + + + | + .map( |
+ +398 + | ++ + + + + + | + cert -> { |
+ +399 + | ++ + + + + + | + try { |
+ +400 + | +
+
+1
+
+1. lambda$getAttestationTrustPathJson$5 : replaced return value with "" for com/yubico/webauthn/RegistrationResult::lambda$getAttestationTrustPathJson$5 → KILLED + + + + |
+ return new ByteArray(cert.getEncoded()).getBase64(); |
+ +401 + | ++ + + + + + | + } catch (CertificateEncodingException e) { |
+ +402 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +403 + | ++ + + + + + | + } |
+ +404 + | ++ + + + + + | + }) |
+ +405 + | +
+
+1
+
+1. lambda$getAttestationTrustPathJson$6 : replaced return value with Collections.emptyList for com/yubico/webauthn/RegistrationResult::lambda$getAttestationTrustPathJson$6 → KILLED + + + + |
+ .collect(Collectors.toList())); |
+ +406 + | ++ + + + + + | + } |
+ +407 + | ++ + + + + + | +} |
Mutations | ||
102 | ++ |
+
+
+
+ 1.1 |
+
112 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 |
+
117 | ++ |
+
+
+
+ 1.1 |
+
118 | ++ |
+
+
+
+ 1.1 |
+
128 | ++ |
+
+
+
+ 1.1 |
+
133 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 2.2 |
+
178 | ++ |
+
+
+
+ 1.1 2.2 |
+
203 | ++ |
+
+
+
+ 1.1 2.2 |
+
217 | ++ |
+
+
+
+ 1.1 |
+
230 | ++ |
+
+
+
+ 1.1 |
+
248 | ++ |
+
+
+
+ 1.1 |
+
265 | ++ |
+
+
+
+ 1.1 |
+
284 | ++ |
+
+
+
+ 1.1 |
+
303 | ++ |
+
+
+
+ 1.1 |
+
321 | ++ |
+
+
+
+ 1.1 |
+
322 | ++ |
+
+
+
+ 1.1 2.2 |
+
340 | ++ |
+
+
+
+ 1.1 |
+
366 | ++ |
+
+
+
+ 1.1 |
+
367 | ++ |
+
+
+
+ 1.1 |
+
368 | ++ |
+
+
+
+ 1.1 |
+
388 | ++ |
+
+
+
+ 1.1 |
+
393 | ++ |
+
+
+
+ 1.1 |
+
400 | ++ |
+
+
+
+ 1.1 |
+
405 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.internal.util.CollectionUtil; |
+ +28 + | ++ + + + + + | +import com.yubico.internal.util.OptionalUtil; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.attestation.AttestationTrustSource; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.AssertionExtensionInputs; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationConveyancePreference; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorData; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.CollectedClientData; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions.PublicKeyCredentialCreationOptionsBuilder; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialParameters; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; |
+ +39 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions.PublicKeyCredentialRequestOptionsBuilder; |
+ +40 + | ++ + + + + + | +import com.yubico.webauthn.data.RegistrationExtensionInputs; |
+ +41 + | ++ + + + + + | +import com.yubico.webauthn.data.RelyingPartyIdentity; |
+ +42 + | ++ + + + + + | +import com.yubico.webauthn.exception.AssertionFailedException; |
+ +43 + | ++ + + + + + | +import com.yubico.webauthn.exception.InvalidSignatureCountException; |
+ +44 + | ++ + + + + + | +import com.yubico.webauthn.exception.RegistrationFailedException; |
+ +45 + | ++ + + + + + | +import com.yubico.webauthn.extension.appid.AppId; |
+ +46 + | ++ + + + + + | +import java.net.MalformedURLException; |
+ +47 + | ++ + + + + + | +import java.net.URL; |
+ +48 + | ++ + + + + + | +import java.security.KeyFactory; |
+ +49 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +50 + | ++ + + + + + | +import java.security.SecureRandom; |
+ +51 + | ++ + + + + + | +import java.security.Signature; |
+ +52 + | ++ + + + + + | +import java.time.Clock; |
+ +53 + | ++ + + + + + | +import java.util.ArrayList; |
+ +54 + | ++ + + + + + | +import java.util.Arrays; |
+ +55 + | ++ + + + + + | +import java.util.Collections; |
+ +56 + | ++ + + + + + | +import java.util.List; |
+ +57 + | ++ + + + + + | +import java.util.Optional; |
+ +58 + | ++ + + + + + | +import java.util.Set; |
+ +59 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +60 + | ++ + + + + + | +import lombok.Builder; |
+ +61 + | ++ + + + + + | +import lombok.NonNull; |
+ +62 + | ++ + + + + + | +import lombok.Value; |
+ +63 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | +/** |
+ +66 + | ++ + + + + + | + * Encapsulates the four basic Web Authentication operations - start/finish registration, |
+ +67 + | ++ + + + + + | + * start/finish authentication - along with overall operational settings for them. |
+ +68 + | ++ + + + + + | + * |
+ +69 + | ++ + + + + + | + * <p>This class has no mutable state. An instance of this class may therefore be thought of as a |
+ +70 + | ++ + + + + + | + * container for specialized versions (function closures) of these four operations rather than a |
+ +71 + | ++ + + + + + | + * stateful object. |
+ +72 + | ++ + + + + + | + */ |
+ +73 + | ++ + + + + + | +@Slf4j |
+ +74 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +75 + | ++ + + + + + | +@Value |
+ +76 + | ++ + + + + + | +public class RelyingParty { |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + private static final SecureRandom random = new SecureRandom(); |
+ +79 + | ++ + + + + + | +|
+ +80 + | ++ + + + + + | + /** |
+ +81 + | ++ + + + + + | + * The {@link RelyingPartyIdentity} that will be set as the {@link |
+ +82 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getRp() rp} parameter when initiating registration |
+ +83 + | ++ + + + + + | + * operations, and which {@link AuthenticatorData#getRpIdHash()} will be compared against. This is |
+ +84 + | ++ + + + + + | + * a required parameter. |
+ +85 + | ++ + + + + + | + * |
+ +86 + | ++ + + + + + | + * <p>A successful registration or authentication operation requires {@link |
+ +87 + | ++ + + + + + | + * AuthenticatorData#getRpIdHash()} to exactly equal the SHA-256 hash of this member's {@link |
+ +88 + | ++ + + + + + | + * RelyingPartyIdentity#getId() id} member. Alternatively, it may instead equal the SHA-256 hash |
+ +89 + | ++ + + + + + | + * of {@link #getAppId() appId} if the latter is present. |
+ +90 + | ++ + + + + + | + * |
+ +91 + | ++ + + + + + | + * @see #startRegistration(StartRegistrationOptions) |
+ +92 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions |
+ +93 + | ++ + + + + + | + */ |
+ +94 + | ++ + + + + + | + @NonNull private final RelyingPartyIdentity identity; |
+ +95 + | ++ + + + + + | +|
+ +96 + | ++ + + + + + | + /** |
+ +97 + | ++ + + + + + | + * The allowed origins that returned authenticator responses will be compared against. |
+ +98 + | ++ + + + + + | + * |
+ +99 + | ++ + + + + + | + * <p>The default is the set containing only the string <code> |
+ +100 + | ++ + + + + + | + * "https://" + {@link #getIdentity()}.getId()</code>. |
+ +101 + | ++ + + + + + | + * |
+ +102 + | ++ + + + + + | + * <p>If {@link RelyingPartyBuilder#allowOriginPort(boolean) allowOriginPort} and {@link |
+ +103 + | ++ + + + + + | + * RelyingPartyBuilder#allowOriginSubdomain(boolean) allowOriginSubdomain} are both <code>false |
+ +104 + | ++ + + + + + | + * </code> (the default), then a successful registration or authentication operation requires |
+ +105 + | ++ + + + + + | + * {@link CollectedClientData#getOrigin()} to exactly equal one of these values. |
+ +106 + | ++ + + + + + | + * |
+ +107 + | ++ + + + + + | + * <p>If {@link RelyingPartyBuilder#allowOriginPort(boolean) allowOriginPort} is <code>true</code> |
+ +108 + | ++ + + + + + | + * , then the above rule is relaxed to allow any port number in {@link |
+ +109 + | ++ + + + + + | + * CollectedClientData#getOrigin()}, regardless of any port specified. |
+ +110 + | ++ + + + + + | + * |
+ +111 + | ++ + + + + + | + * <p>If {@link RelyingPartyBuilder#allowOriginSubdomain(boolean) allowOriginSubdomain} is <code> |
+ +112 + | ++ + + + + + | + * true</code>, then the above rule is relaxed to allow any subdomain, of any depth, of any of |
+ +113 + | ++ + + + + + | + * these values. |
+ +114 + | ++ + + + + + | + * |
+ +115 + | ++ + + + + + | + * <p>For either of the above relaxations to take effect, both the allowed origin and the client |
+ +116 + | ++ + + + + + | + * data origin must be valid URLs. Origins that are not valid URLs are matched only by exact |
+ +117 + | ++ + + + + + | + * string equality. |
+ +118 + | ++ + + + + + | + * |
+ +119 + | ++ + + + + + | + * @see #getIdentity() |
+ +120 + | ++ + + + + + | + */ |
+ +121 + | ++ + + + + + | + @NonNull private final Set<String> origins; |
+ +122 + | ++ + + + + + | +|
+ +123 + | ++ + + + + + | + /** |
+ +124 + | ++ + + + + + | + * An abstract database which can look up credentials, usernames and user handles from usernames, |
+ +125 + | ++ + + + + + | + * user handles and credential IDs. This is a required parameter. |
+ +126 + | ++ + + + + + | + * |
+ +127 + | ++ + + + + + | + * <p>This is used to look up: |
+ +128 + | ++ + + + + + | + * |
+ +129 + | ++ + + + + + | + * <ul> |
+ +130 + | ++ + + + + + | + * <li>the user handle for a user logging in via user name |
+ +131 + | ++ + + + + + | + * <li>the user name for a user logging in via user handle |
+ +132 + | ++ + + + + + | + * <li>the credential IDs to include in {@link |
+ +133 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getExcludeCredentials()} |
+ +134 + | ++ + + + + + | + * <li>the credential IDs to include in {@link |
+ +135 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} |
+ +136 + | ++ + + + + + | + * <li>that the correct user owns the credential when verifying an assertion |
+ +137 + | ++ + + + + + | + * <li>the public key to use to verify an assertion |
+ +138 + | ++ + + + + + | + * <li>the stored signature counter when verifying an assertion |
+ +139 + | ++ + + + + + | + * </ul> |
+ +140 + | ++ + + + + + | + */ |
+ +141 + | ++ + + + + + | + @NonNull private final CredentialRepository credentialRepository; |
+ +142 + | ++ + + + + + | +|
+ +143 + | ++ + + + + + | + /** |
+ +144 + | ++ + + + + + | + * The extension input to set for the <code>appid</code> and <code>appidExclude</code> extensions. |
+ +145 + | ++ + + + + + | + * |
+ +146 + | ++ + + + + + | + * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to |
+ +147 + | ++ + + + + + | + * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not |
+ +148 + | ++ + + + + + | + * needed for new registrations, even of U2F authenticators. |
+ +149 + | ++ + + + + + | + * |
+ +150 + | ++ + + + + + | + * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will |
+ +151 + | ++ + + + + + | + * automatically set the <code>appid</code> extension input, and {@link |
+ +152 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic to |
+ +153 + | ++ + + + + + | + * also accept this AppID as an alternative to the RP ID. Likewise, {@link |
+ +154 + | ++ + + + + + | + * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the |
+ +155 + | ++ + + + + + | + * <code>appidExclude</code> extension input. |
+ +156 + | ++ + + + + + | + * |
+ +157 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +158 + | ++ + + + + + | + * |
+ +159 + | ++ + + + + + | + * @see AssertionExtensionInputs#getAppid() |
+ +160 + | ++ + + + + + | + * @see RegistrationExtensionInputs#getAppidExclude() |
+ +161 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +162 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +163 + | ++ + + + + + | + * @see <a |
+ +164 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +165 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +166 + | ++ + + + + + | + */ |
+ +167 + | ++ + + + + + | + @NonNull private final Optional<AppId> appId; |
+ +168 + | ++ + + + + + | +|
+ +169 + | ++ + + + + + | + /** |
+ +170 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} |
+ +171 + | ++ + + + + + | + * parameter in registration operations. |
+ +172 + | ++ + + + + + | + * |
+ +173 + | ++ + + + + + | + * <p>Unless your application has a concrete policy for authenticator attestation, it is |
+ +174 + | ++ + + + + + | + * recommended to leave this parameter undefined. |
+ +175 + | ++ + + + + + | + * |
+ +176 + | ++ + + + + + | + * <p>If you set this, you may want to explicitly set {@link |
+ +177 + | ++ + + + + + | + * RelyingPartyBuilder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and {@link |
+ +178 + | ++ + + + + + | + * RelyingPartyBuilder#attestationTrustSource(AttestationTrustSource) attestationTrustSource} too. |
+ +179 + | ++ + + + + + | + * |
+ +180 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +181 + | ++ + + + + + | + * |
+ +182 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +183 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +184 + | ++ + + + + + | + * Attestation</a> |
+ +185 + | ++ + + + + + | + */ |
+ +186 + | ++ + + + + + | + @NonNull private final Optional<AttestationConveyancePreference> attestationConveyancePreference; |
+ +187 + | ++ + + + + + | +|
+ +188 + | ++ + + + + + | + /** |
+ +189 + | ++ + + + + + | + * An {@link AttestationTrustSource} instance to use for looking up trust roots for authenticator |
+ +190 + | ++ + + + + + | + * attestation. This matters only if {@link #getAttestationConveyancePreference()} is non-empty |
+ +191 + | ++ + + + + + | + * and not set to {@link AttestationConveyancePreference#NONE}. |
+ +192 + | ++ + + + + + | + * |
+ +193 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +194 + | ++ + + + + + | + * |
+ +195 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +196 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +197 + | ++ + + + + + | + * Attestation</a> |
+ +198 + | ++ + + + + + | + */ |
+ +199 + | ++ + + + + + | + @NonNull private final Optional<AttestationTrustSource> attestationTrustSource; |
+ +200 + | ++ + + + + + | +|
+ +201 + | ++ + + + + + | + /** |
+ +202 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getPubKeyCredParams() |
+ +203 + | ++ + + + + + | + * pubKeyCredParams} parameter in registration operations. |
+ +204 + | ++ + + + + + | + * |
+ +205 + | ++ + + + + + | + * <p>This is a list of acceptable public key algorithms and their parameters, ordered from most |
+ +206 + | ++ + + + + + | + * to least preferred. |
+ +207 + | ++ + + + + + | + * |
+ +208 + | ++ + + + + + | + * <p>The default is the following list, in order: |
+ +209 + | ++ + + + + + | + * |
+ +210 + | ++ + + + + + | + * <ol> |
+ +211 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#ES256 ES256} |
+ +212 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#EdDSA EdDSA} |
+ +213 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#ES256 ES384} |
+ +214 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#ES256 ES512} |
+ +215 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#RS256 RS256} |
+ +216 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#RS384 RS384} |
+ +217 + | ++ + + + + + | + * <li>{@link com.yubico.webauthn.data.PublicKeyCredentialParameters#RS512 RS512} |
+ +218 + | ++ + + + + + | + * </ol> |
+ +219 + | ++ + + + + + | + * |
+ +220 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +221 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +222 + | ++ + + + + + | + * Attestation</a> |
+ +223 + | ++ + + + + + | + */ |
+ +224 + | ++ + + + + + | + @Builder.Default @NonNull |
+ +225 + | ++ + + + + + | + private final List<PublicKeyCredentialParameters> preferredPubkeyParams = |
+ +226 + | ++ + + + + + | + Collections.unmodifiableList( |
+ +227 + | ++ + + + + + | + Arrays.asList( |
+ +228 + | ++ + + + + + | + PublicKeyCredentialParameters.ES256, |
+ +229 + | ++ + + + + + | + PublicKeyCredentialParameters.EdDSA, |
+ +230 + | ++ + + + + + | + PublicKeyCredentialParameters.ES384, |
+ +231 + | ++ + + + + + | + PublicKeyCredentialParameters.ES512, |
+ +232 + | ++ + + + + + | + PublicKeyCredentialParameters.RS256, |
+ +233 + | ++ + + + + + | + PublicKeyCredentialParameters.RS384, |
+ +234 + | ++ + + + + + | + PublicKeyCredentialParameters.RS512)); |
+ +235 + | ++ + + + + + | +|
+ +236 + | ++ + + + + + | + /** |
+ +237 + | ++ + + + + + | + * If <code>true</code>, the origin matching rule is relaxed to allow any port number. |
+ +238 + | ++ + + + + + | + * |
+ +239 + | ++ + + + + + | + * <p>The default is <code>false</code>. |
+ +240 + | ++ + + + + + | + * |
+ +241 + | ++ + + + + + | + * <p>Examples with <code> |
+ +242 + | ++ + + + + + | + * origins: ["https://example.org", "https://accounts.example.org", "https://acme.com:8443"] |
+ +243 + | ++ + + + + + | + * </code> |
+ +244 + | ++ + + + + + | + * |
+ +245 + | ++ + + + + + | + * <ul> |
+ +246 + | ++ + + + + + | + * <li> |
+ +247 + | ++ + + + + + | + * <p><code>allowOriginPort: false</code> |
+ +248 + | ++ + + + + + | + * <p>Accepted: |
+ +249 + | ++ + + + + + | + * <ul> |
+ +250 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +251 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +252 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +253 + | ++ + + + + + | + * </ul> |
+ +254 + | ++ + + + + + | + * <p>Rejected: |
+ +255 + | ++ + + + + + | + * <ul> |
+ +256 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +257 + | ++ + + + + + | + * <li><code>https://shop.example.org</code> |
+ +258 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +259 + | ++ + + + + + | + * <li><code>https://acme.com:9000</code> |
+ +260 + | ++ + + + + + | + * </ul> |
+ +261 + | ++ + + + + + | + * <li> |
+ +262 + | ++ + + + + + | + * <p><code>allowOriginPort: true</code> |
+ +263 + | ++ + + + + + | + * <p>Accepted: |
+ +264 + | ++ + + + + + | + * <ul> |
+ +265 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +266 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +267 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +268 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +269 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +270 + | ++ + + + + + | + * <li><code>https://acme.com:9000</code> |
+ +271 + | ++ + + + + + | + * </ul> |
+ +272 + | ++ + + + + + | + * <p>Rejected: |
+ +273 + | ++ + + + + + | + * <ul> |
+ +274 + | ++ + + + + + | + * <li><code>https://shop.example.org</code> |
+ +275 + | ++ + + + + + | + * </ul> |
+ +276 + | ++ + + + + + | + * </ul> |
+ +277 + | ++ + + + + + | + */ |
+ +278 + | ++ + + + + + | + @Builder.Default private final boolean allowOriginPort = false; |
+ +279 + | ++ + + + + + | +|
+ +280 + | ++ + + + + + | + /** |
+ +281 + | ++ + + + + + | + * If <code>true</code>, the origin matching rule is relaxed to allow any subdomain, of any depth, |
+ +282 + | ++ + + + + + | + * of the values of {@link RelyingPartyBuilder#origins(Set) origins}. |
+ +283 + | ++ + + + + + | + * |
+ +284 + | ++ + + + + + | + * <p>Please see <a |
+ +285 + | ++ + + + + + | + * href="https://www.w3.org/TR/2023/WD-webauthn-3-20230927/#sctn-code-injection">Security |
+ +286 + | ++ + + + + + | + * Considerations: Code injection attacks</a> for discussion of the risks in setting this to |
+ +287 + | ++ + + + + + | + * <code>true</code>. |
+ +288 + | ++ + + + + + | + * |
+ +289 + | ++ + + + + + | + * <p>The default is <code>false</code>. |
+ +290 + | ++ + + + + + | + * |
+ +291 + | ++ + + + + + | + * <p>Examples with <code>origins: ["https://example.org", "https://acme.com:8443"]</code> |
+ +292 + | ++ + + + + + | + * |
+ +293 + | ++ + + + + + | + * <ul> |
+ +294 + | ++ + + + + + | + * <li> |
+ +295 + | ++ + + + + + | + * <p><code>allowOriginSubdomain: false</code> |
+ +296 + | ++ + + + + + | + * <p>Accepted: |
+ +297 + | ++ + + + + + | + * <ul> |
+ +298 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +299 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +300 + | ++ + + + + + | + * </ul> |
+ +301 + | ++ + + + + + | + * <p>Rejected: |
+ +302 + | ++ + + + + + | + * <ul> |
+ +303 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +304 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +305 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +306 + | ++ + + + + + | + * <li><code>https://eu.shop.acme.com:8443</code> |
+ +307 + | ++ + + + + + | + * </ul> |
+ +308 + | ++ + + + + + | + * <li> |
+ +309 + | ++ + + + + + | + * <p><code>allowOriginSubdomain: true</code> |
+ +310 + | ++ + + + + + | + * <p>Accepted: |
+ +311 + | ++ + + + + + | + * <ul> |
+ +312 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +313 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +314 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +315 + | ++ + + + + + | + * <li><code>https://eu.shop.acme.com:8443</code> |
+ +316 + | ++ + + + + + | + * </ul> |
+ +317 + | ++ + + + + + | + * <p>Rejected: |
+ +318 + | ++ + + + + + | + * <ul> |
+ +319 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +320 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +321 + | ++ + + + + + | + * </ul> |
+ +322 + | ++ + + + + + | + * </ul> |
+ +323 + | ++ + + + + + | + * |
+ +324 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2023/WD-webauthn-3-20230927/#sctn-code-injection">§13.4.8. |
+ +325 + | ++ + + + + + | + * Code injection attacks</a> |
+ +326 + | ++ + + + + + | + */ |
+ +327 + | ++ + + + + + | + @Builder.Default private final boolean allowOriginSubdomain = false; |
+ +328 + | ++ + + + + + | +|
+ +329 + | ++ + + + + + | + /** |
+ +330 + | ++ + + + + + | + * If <code>false</code>, {@link #finishRegistration(FinishRegistrationOptions) |
+ +331 + | ++ + + + + + | + * finishRegistration} will only allow registrations where the attestation signature can be linked |
+ +332 + | ++ + + + + + | + * to a trusted attestation root. This excludes none attestation, and self attestation unless the |
+ +333 + | ++ + + + + + | + * self attestation key is explicitly trusted. |
+ +334 + | ++ + + + + + | + * |
+ +335 + | ++ + + + + + | + * <p>Regardless of the value of this option, invalid attestation statements of supported formats |
+ +336 + | ++ + + + + + | + * will always be rejected. For example, a "packed" attestation statement with an invalid |
+ +337 + | ++ + + + + + | + * signature will be rejected even if this option is set to <code>true</code>. |
+ +338 + | ++ + + + + + | + * |
+ +339 + | ++ + + + + + | + * <p>The default is <code>true</code>. |
+ +340 + | ++ + + + + + | + */ |
+ +341 + | ++ + + + + + | + @Builder.Default private final boolean allowUntrustedAttestation = true; |
+ +342 + | ++ + + + + + | +|
+ +343 + | ++ + + + + + | + /** |
+ +344 + | ++ + + + + + | + * If <code>true</code>, {@link #finishAssertion(FinishAssertionOptions) finishAssertion} will |
+ +345 + | ++ + + + + + | + * succeed only if the {@link AuthenticatorData#getSignatureCounter() signature counter value} in |
+ +346 + | ++ + + + + + | + * the response is strictly greater than the {@link RegisteredCredential#getSignatureCount() |
+ +347 + | ++ + + + + + | + * stored signature counter value}, or if both counters are exactly zero. |
+ +348 + | ++ + + + + + | + * |
+ +349 + | ++ + + + + + | + * <p>The default is <code>true</code>. |
+ +350 + | ++ + + + + + | + */ |
+ +351 + | ++ + + + + + | + @Builder.Default private final boolean validateSignatureCounter = true; |
+ +352 + | ++ + + + + + | +|
+ +353 + | ++ + + + + + | + /** |
+ +354 + | ++ + + + + + | + * A {@link Clock} which will be used to tell the current time while verifying attestation |
+ +355 + | ++ + + + + + | + * certificate chains. |
+ +356 + | ++ + + + + + | + * |
+ +357 + | ++ + + + + + | + * <p>This is intended primarily for testing, and relevant only if {@link |
+ +358 + | ++ + + + + + | + * RelyingPartyBuilder#attestationTrustSource(AttestationTrustSource)} is set. |
+ +359 + | ++ + + + + + | + * |
+ +360 + | ++ + + + + + | + * <p>The default is <code>Clock.systemUTC()</code>. |
+ +361 + | ++ + + + + + | + */ |
+ +362 + | ++ + + + + + | + @Builder.Default @NonNull private final Clock clock = Clock.systemUTC(); |
+ +363 + | ++ + + + + + | +|
+ +364 + | ++ + + + + + | + @Builder |
+ +365 + | ++ + + + + + | + private RelyingParty( |
+ +366 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull RelyingPartyIdentity identity, |
+ +367 + | ++ + + + + + | + Set<String> origins, |
+ +368 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull CredentialRepository credentialRepository, |
+ +369 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Optional<AppId> appId, |
+ +370 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference, |
+ +371 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationTrustSource> attestationTrustSource, |
+ +372 + | ++ + + + + + | + List<PublicKeyCredentialParameters> preferredPubkeyParams, |
+ +373 + | ++ + + + + + | + boolean allowOriginPort, |
+ +374 + | ++ + + + + + | + boolean allowOriginSubdomain, |
+ +375 + | ++ + + + + + | + boolean allowUntrustedAttestation, |
+ +376 + | ++ + + + + + | + boolean validateSignatureCounter, |
+ +377 + | ++ + + + + + | + Clock clock) { |
+ +378 + | ++ + + + + + | + this.identity = identity; |
+ +379 + | ++ + + + + + | + this.origins = |
+ +380 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ origins != null |
+ +381 + | ++ + + + + + | + ? CollectionUtil.immutableSet(origins) |
+ +382 + | ++ + + + + + | + : Collections.singleton("https://" + identity.getId()); |
+ +383 + | ++ + + + + + | +|
+ +384 + | ++ + + + + + | + for (String origin : this.origins) { |
+ +385 + | ++ + + + + + | + try { |
+ +386 + | ++ + + + + + | + new URL(origin); |
+ +387 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +388 + | ++ + + + + + | + log.warn( |
+ +389 + | ++ + + + + + | + "Allowed origin is not a valid URL, it will match only by exact string equality: {}", |
+ +390 + | ++ + + + + + | + origin); |
+ +391 + | ++ + + + + + | + } |
+ +392 + | ++ + + + + + | + } |
+ +393 + | ++ + + + + + | +|
+ +394 + | ++ + + + + + | + this.credentialRepository = credentialRepository; |
+ +395 + | ++ + + + + + | + this.appId = appId; |
+ +396 + | ++ + + + + + | + this.attestationConveyancePreference = attestationConveyancePreference; |
+ +397 + | ++ + + + + + | + this.attestationTrustSource = attestationTrustSource; |
+ +398 + | ++ + + + + + | + this.preferredPubkeyParams = filterAvailableAlgorithms(preferredPubkeyParams); |
+ +399 + | ++ + + + + + | + this.allowOriginPort = allowOriginPort; |
+ +400 + | ++ + + + + + | + this.allowOriginSubdomain = allowOriginSubdomain; |
+ +401 + | ++ + + + + + | + this.allowUntrustedAttestation = allowUntrustedAttestation; |
+ +402 + | ++ + + + + + | + this.validateSignatureCounter = validateSignatureCounter; |
+ +403 + | ++ + + + + + | + this.clock = clock; |
+ +404 + | ++ + + + + + | + } |
+ +405 + | ++ + + + + + | +|
+ +406 + | ++ + + + + + | + private static ByteArray generateChallenge() { |
+ +407 + | ++ + + + + + | + byte[] bytes = new byte[32]; |
+ +408 + | +
+
+1
+
+1. generateChallenge : removed call to java/security/SecureRandom::nextBytes → KILLED + + + + |
+ random.nextBytes(bytes); |
+ +409 + | +
+
+1
+
+1. generateChallenge : replaced return value with null for com/yubico/webauthn/RelyingParty::generateChallenge → KILLED + + + + |
+ return new ByteArray(bytes); |
+ +410 + | ++ + + + + + | + } |
+ +411 + | ++ + + + + + | +|
+ +412 + | ++ + + + + + | + /** |
+ +413 + | ++ + + + + + | + * Filter <code>pubKeyCredParams</code> to only contain algorithms with a {@link KeyFactory} and a |
+ +414 + | ++ + + + + + | + * {@link Signature} available, and log a warning for every unsupported algorithm. |
+ +415 + | ++ + + + + + | + * |
+ +416 + | ++ + + + + + | + * @return a new {@link List} containing only the algorithms supported in the current JCA context. |
+ +417 + | ++ + + + + + | + */ |
+ +418 + | ++ + + + + + | + static List<PublicKeyCredentialParameters> filterAvailableAlgorithms( |
+ +419 + | ++ + + + + + | + List<PublicKeyCredentialParameters> pubKeyCredParams) { |
+ +420 + | +
+
+1
+
+1. filterAvailableAlgorithms : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingParty::filterAvailableAlgorithms → KILLED + + + + |
+ return Collections.unmodifiableList( |
+ +421 + | ++ + + + + + | + pubKeyCredParams.stream() |
+ +422 + | ++ + + + + + | + .filter( |
+ +423 + | ++ + + + + + | + param -> { |
+ +424 + | ++ + + + + + | + try { |
+ +425 + | ++ + + + + + | + switch (param.getAlg()) { |
+ +426 + | ++ + + + + + | + case EdDSA: |
+ +427 + | ++ + + + + + | + KeyFactory.getInstance("EdDSA"); |
+ +428 + | ++ + + + + + | + break; |
+ +429 + | ++ + + + + + | +|
+ +430 + | ++ + + + + + | + case ES256: |
+ +431 + | ++ + + + + + | + case ES384: |
+ +432 + | ++ + + + + + | + case ES512: |
+ +433 + | ++ + + + + + | + KeyFactory.getInstance("EC"); |
+ +434 + | ++ + + + + + | + break; |
+ +435 + | ++ + + + + + | +|
+ +436 + | ++ + + + + + | + case RS256: |
+ +437 + | ++ + + + + + | + case RS384: |
+ +438 + | ++ + + + + + | + case RS512: |
+ +439 + | ++ + + + + + | + case RS1: |
+ +440 + | ++ + + + + + | + KeyFactory.getInstance("RSA"); |
+ +441 + | ++ + + + + + | + break; |
+ +442 + | ++ + + + + + | +|
+ +443 + | ++ + + + + + | + default: |
+ +444 + | ++ + + + + + | + log.warn( |
+ +445 + | ++ + + + + + | + "Unknown algorithm: {}. Please file a bug report.", param.getAlg()); |
+ +446 + | ++ + + + + + | + } |
+ +447 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +448 + | ++ + + + + + | + log.warn( |
+ +449 + | ++ + + + + + | + "Unsupported algorithm in RelyingParty.preferredPubkeyParams: {}. No KeyFactory available; registrations with this key algorithm will fail. You may need to add a dependency and load a provider using java.security.Security.addProvider().", |
+ +450 + | ++ + + + + + | + param.getAlg()); |
+ +451 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with true for com/yubico/webauthn/RelyingParty::lambda$filterAvailableAlgorithms$0 → KILLED + + + + |
+ return false; |
+ +452 + | ++ + + + + + | + } |
+ +453 + | ++ + + + + + | +|
+ +454 + | ++ + + + + + | + final String signatureAlgName; |
+ +455 + | ++ + + + + + | + try { |
+ +456 + | ++ + + + + + | + signatureAlgName = WebAuthnCodecs.getJavaAlgorithmName(param.getAlg()); |
+ +457 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +458 + | ++ + + + + + | + log.warn("Unknown algorithm: {}. Please file a bug report.", param.getAlg()); |
+ +459 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with true for com/yubico/webauthn/RelyingParty::lambda$filterAvailableAlgorithms$0 → NO_COVERAGE + + + + |
+ return false; |
+ +460 + | ++ + + + + + | + } |
+ +461 + | ++ + + + + + | +|
+ +462 + | ++ + + + + + | + try { |
+ +463 + | ++ + + + + + | + Signature.getInstance(signatureAlgName); |
+ +464 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +465 + | ++ + + + + + | + log.warn( |
+ +466 + | ++ + + + + + | + "Unsupported algorithm in RelyingParty.preferredPubkeyParams: {}. No Signature available; registrations with this key algorithm will fail. You may need to add a dependency and load a provider using java.security.Security.addProvider().", |
+ +467 + | ++ + + + + + | + param.getAlg()); |
+ +468 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with true for com/yubico/webauthn/RelyingParty::lambda$filterAvailableAlgorithms$0 → NO_COVERAGE + + + + |
+ return false; |
+ +469 + | ++ + + + + + | + } |
+ +470 + | ++ + + + + + | +|
+ +471 + | +
+
+1
+
+1. lambda$filterAvailableAlgorithms$0 : replaced boolean return with false for com/yubico/webauthn/RelyingParty::lambda$filterAvailableAlgorithms$0 → KILLED + + + + |
+ return true; |
+ +472 + | ++ + + + + + | + }) |
+ +473 + | ++ + + + + + | + .collect(Collectors.toList())); |
+ +474 + | ++ + + + + + | + } |
+ +475 + | ++ + + + + + | +|
+ +476 + | ++ + + + + + | + public PublicKeyCredentialCreationOptions startRegistration( |
+ +477 + | ++ + + + + + | + StartRegistrationOptions startRegistrationOptions) { |
+ +478 + | ++ + + + + + | + PublicKeyCredentialCreationOptionsBuilder builder = |
+ +479 + | ++ + + + + + | + PublicKeyCredentialCreationOptions.builder() |
+ +480 + | ++ + + + + + | + .rp(identity) |
+ +481 + | ++ + + + + + | + .user(startRegistrationOptions.getUser()) |
+ +482 + | ++ + + + + + | + .challenge(generateChallenge()) |
+ +483 + | ++ + + + + + | + .pubKeyCredParams(preferredPubkeyParams) |
+ +484 + | ++ + + + + + | + .excludeCredentials( |
+ +485 + | ++ + + + + + | + credentialRepository.getCredentialIdsForUsername( |
+ +486 + | ++ + + + + + | + startRegistrationOptions.getUser().getName())) |
+ +487 + | ++ + + + + + | + .authenticatorSelection(startRegistrationOptions.getAuthenticatorSelection()) |
+ +488 + | ++ + + + + + | + .extensions( |
+ +489 + | ++ + + + + + | + startRegistrationOptions |
+ +490 + | ++ + + + + + | + .getExtensions() |
+ +491 + | ++ + + + + + | + .merge( |
+ +492 + | ++ + + + + + | + RegistrationExtensionInputs.builder() |
+ +493 + | ++ + + + + + | + .appidExclude(appId) |
+ +494 + | ++ + + + + + | + .credProps() |
+ +495 + | ++ + + + + + | + .build())) |
+ +496 + | ++ + + + + + | + .timeout(startRegistrationOptions.getTimeout()); |
+ +497 + | +
+
+1
+
+1. startRegistration : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ attestationConveyancePreference.ifPresent(builder::attestation); |
+ +498 + | +
+
+1
+
+1. startRegistration : replaced return value with null for com/yubico/webauthn/RelyingParty::startRegistration → KILLED + + + + |
+ return builder.build(); |
+ +499 + | ++ + + + + + | + } |
+ +500 + | ++ + + + + + | +|
+ +501 + | ++ + + + + + | + public RegistrationResult finishRegistration(FinishRegistrationOptions finishRegistrationOptions) |
+ +502 + | ++ + + + + + | + throws RegistrationFailedException { |
+ +503 + | ++ + + + + + | + try { |
+ +504 + | +
+
+1
+
+1. finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingParty::finishRegistration → KILLED + + + + |
+ return _finishRegistration(finishRegistrationOptions).run(); |
+ +505 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +506 + | ++ + + + + + | + throw new RegistrationFailedException(e); |
+ +507 + | ++ + + + + + | + } |
+ +508 + | ++ + + + + + | + } |
+ +509 + | ++ + + + + + | +|
+ +510 + | ++ + + + + + | + /** |
+ +511 + | ++ + + + + + | + * This method is NOT part of the public API. |
+ +512 + | ++ + + + + + | + * |
+ +513 + | ++ + + + + + | + * <p>This method is called internally by {@link #finishRegistration(FinishRegistrationOptions)}. |
+ +514 + | ++ + + + + + | + * It is a separate method to facilitate testing; users should call {@link |
+ +515 + | ++ + + + + + | + * #finishRegistration(FinishRegistrationOptions)} instead of this method. |
+ +516 + | ++ + + + + + | + */ |
+ +517 + | ++ + + + + + | + FinishRegistrationSteps _finishRegistration(FinishRegistrationOptions options) { |
+ +518 + | +
+
+1
+
+1. _finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingParty::_finishRegistration → KILLED + + + + |
+ return FinishRegistrationSteps.fromV1(this, options); |
+ +519 + | ++ + + + + + | + } |
+ +520 + | ++ + + + + + | +|
+ +521 + | ++ + + + + + | + public AssertionRequest startAssertion(StartAssertionOptions startAssertionOptions) { |
+ +522 + | ++ + + + + + | + PublicKeyCredentialRequestOptionsBuilder pkcro = |
+ +523 + | ++ + + + + + | + PublicKeyCredentialRequestOptions.builder() |
+ +524 + | ++ + + + + + | + .challenge(generateChallenge()) |
+ +525 + | ++ + + + + + | + .rpId(identity.getId()) |
+ +526 + | ++ + + + + + | + .allowCredentials( |
+ +527 + | ++ + + + + + | + OptionalUtil.orElseOptional( |
+ +528 + | ++ + + + + + | + startAssertionOptions.getUsername(), |
+ +529 + | ++ + + + + + | + () -> |
+ +530 + | +
+
+1
+
+1. lambda$startAssertion$1 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingParty::lambda$startAssertion$1 → KILLED + + + + |
+ startAssertionOptions |
+ +531 + | ++ + + + + + | + .getUserHandle() |
+ +532 + | ++ + + + + + | + .flatMap(credentialRepository::getUsernameForUserHandle)) |
+ +533 + | ++ + + + + + | + .map( |
+ +534 + | ++ + + + + + | + un -> |
+ +535 + | +
+
+1
+
+1. lambda$startAssertion$2 : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingParty::lambda$startAssertion$2 → KILLED + + + + |
+ new ArrayList<>(credentialRepository.getCredentialIdsForUsername(un)))) |
+ +536 + | ++ + + + + + | + .extensions( |
+ +537 + | ++ + + + + + | + startAssertionOptions |
+ +538 + | ++ + + + + + | + .getExtensions() |
+ +539 + | ++ + + + + + | + .merge(startAssertionOptions.getExtensions().toBuilder().appid(appId).build())) |
+ +540 + | ++ + + + + + | + .timeout(startAssertionOptions.getTimeout()); |
+ +541 + | ++ + + + + + | +|
+ +542 + | +
+
+1
+
+1. startAssertion : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ startAssertionOptions.getUserVerification().ifPresent(pkcro::userVerification); |
+ +543 + | ++ + + + + + | +|
+ +544 + | +
+
+1
+
+1. startAssertion : replaced return value with null for com/yubico/webauthn/RelyingParty::startAssertion → KILLED + + + + |
+ return AssertionRequest.builder() |
+ +545 + | ++ + + + + + | + .publicKeyCredentialRequestOptions(pkcro.build()) |
+ +546 + | ++ + + + + + | + .username(startAssertionOptions.getUsername()) |
+ +547 + | ++ + + + + + | + .userHandle(startAssertionOptions.getUserHandle()) |
+ +548 + | ++ + + + + + | + .build(); |
+ +549 + | ++ + + + + + | + } |
+ +550 + | ++ + + + + + | +|
+ +551 + | ++ + + + + + | + /** |
+ +552 + | ++ + + + + + | + * @throws InvalidSignatureCountException if {@link |
+ +553 + | ++ + + + + + | + * RelyingPartyBuilder#validateSignatureCounter(boolean) validateSignatureCounter} is <code> |
+ +554 + | ++ + + + + + | + * true</code>, the {@link AuthenticatorData#getSignatureCounter() signature count} in the |
+ +555 + | ++ + + + + + | + * response is less than or equal to the {@link RegisteredCredential#getSignatureCount() |
+ +556 + | ++ + + + + + | + * stored signature count}, and at least one of the signature count values is nonzero. |
+ +557 + | ++ + + + + + | + * @throws AssertionFailedException if validation fails for any other reason. |
+ +558 + | ++ + + + + + | + */ |
+ +559 + | ++ + + + + + | + public AssertionResult finishAssertion(FinishAssertionOptions finishAssertionOptions) |
+ +560 + | ++ + + + + + | + throws AssertionFailedException { |
+ +561 + | ++ + + + + + | + try { |
+ +562 + | +
+
+1
+
+1. finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingParty::finishAssertion → KILLED + + + + |
+ return _finishAssertion(finishAssertionOptions).run(); |
+ +563 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +564 + | ++ + + + + + | + throw new AssertionFailedException(e); |
+ +565 + | ++ + + + + + | + } |
+ +566 + | ++ + + + + + | + } |
+ +567 + | ++ + + + + + | +|
+ +568 + | ++ + + + + + | + /** |
+ +569 + | ++ + + + + + | + * This method is NOT part of the public API. |
+ +570 + | ++ + + + + + | + * |
+ +571 + | ++ + + + + + | + * <p>This method is called internally by {@link #finishAssertion(FinishAssertionOptions)}. It is |
+ +572 + | ++ + + + + + | + * a separate method to facilitate testing; users should call {@link |
+ +573 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions)} instead of this method. |
+ +574 + | ++ + + + + + | + */ |
+ +575 + | ++ + + + + + | + FinishAssertionSteps<RegisteredCredential> _finishAssertion(FinishAssertionOptions options) { |
+ +576 + | +
+
+1
+
+1. _finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingParty::_finishAssertion → KILLED + + + + |
+ return FinishAssertionSteps.fromV1(this, options); |
+ +577 + | ++ + + + + + | + } |
+ +578 + | ++ + + + + + | +|
+ +579 + | ++ + + + + + | + public static RelyingPartyBuilder.MandatoryStages builder() { |
+ +580 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/RelyingParty::builder → KILLED + + + + |
+ return new RelyingPartyBuilder.MandatoryStages(); |
+ +581 + | ++ + + + + + | + } |
+ +582 + | ++ + + + + + | +|
+ +583 + | ++ + + + + + | + public static class RelyingPartyBuilder { |
+ +584 + | ++ + + + + + | + private @NonNull Optional<AppId> appId = Optional.empty(); |
+ +585 + | ++ + + + + + | + private @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference = |
+ +586 + | ++ + + + + + | + Optional.empty(); |
+ +587 + | ++ + + + + + | + private @NonNull Optional<AttestationTrustSource> attestationTrustSource = Optional.empty(); |
+ +588 + | ++ + + + + + | +|
+ +589 + | ++ + + + + + | + public static class MandatoryStages { |
+ +590 + | ++ + + + + + | + private final RelyingPartyBuilder builder = new RelyingPartyBuilder(); |
+ +591 + | ++ + + + + + | +|
+ +592 + | ++ + + + + + | + /** |
+ +593 + | ++ + + + + + | + * {@link RelyingPartyBuilder#identity(RelyingPartyIdentity) identity} is a required |
+ +594 + | ++ + + + + + | + * parameter. |
+ +595 + | ++ + + + + + | + * |
+ +596 + | ++ + + + + + | + * @see RelyingPartyBuilder#identity(RelyingPartyIdentity) |
+ +597 + | ++ + + + + + | + */ |
+ +598 + | ++ + + + + + | + public Step2 identity(RelyingPartyIdentity identity) { |
+ +599 + | ++ + + + + + | + builder.identity(identity); |
+ +600 + | +
+
+1
+
+1. identity : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder$MandatoryStages::identity → KILLED + + + + |
+ return new Step2(); |
+ +601 + | ++ + + + + + | + } |
+ +602 + | ++ + + + + + | +|
+ +603 + | ++ + + + + + | + public class Step2 { |
+ +604 + | ++ + + + + + | + /** |
+ +605 + | ++ + + + + + | + * {@link RelyingPartyBuilder#credentialRepository(CredentialRepository) |
+ +606 + | ++ + + + + + | + * credentialRepository} is a required parameter. |
+ +607 + | ++ + + + + + | + * |
+ +608 + | ++ + + + + + | + * @see RelyingPartyBuilder#credentialRepository(CredentialRepository) |
+ +609 + | ++ + + + + + | + * @see #credentialRepositoryV2(CredentialRepositoryV2) |
+ +610 + | ++ + + + + + | + */ |
+ +611 + | ++ + + + + + | + public RelyingPartyBuilder credentialRepository(CredentialRepository credentialRepository) { |
+ +612 + | +
+
+1
+
+1. credentialRepository : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder$MandatoryStages$Step2::credentialRepository → KILLED + + + + |
+ return builder.credentialRepository(credentialRepository); |
+ +613 + | ++ + + + + + | + } |
+ +614 + | ++ + + + + + | +|
+ +615 + | ++ + + + + + | + /** |
+ +616 + | ++ + + + + + | + * {@link RelyingPartyBuilder#credentialRepository(CredentialRepository) |
+ +617 + | ++ + + + + + | + * credentialRepository} is a required parameter. This setter differs from {@link |
+ +618 + | ++ + + + + + | + * #credentialRepository(CredentialRepository)} in that it takes an instance of {@link |
+ +619 + | ++ + + + + + | + * CredentialRepositoryV2} and converts the builder's return type to {@link RelyingPartyV2}. |
+ +620 + | ++ + + + + + | + * {@link CredentialRepositoryV2} does not require the application to support usernames, |
+ +621 + | ++ + + + + + | + * unless {@link RelyingPartyV2.RelyingPartyV2Builder#usernameRepository(UsernameRepository) |
+ +622 + | ++ + + + + + | + * usernameRepository} is also set in a subsequent builder step. |
+ +623 + | ++ + + + + + | + * |
+ +624 + | ++ + + + + + | + * @see #credentialRepository(CredentialRepository) |
+ +625 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be |
+ +626 + | ++ + + + + + | + * deleted before reaching a mature release. |
+ +627 + | ++ + + + + + | + */ |
+ +628 + | ++ + + + + + | + @Deprecated |
+ +629 + | ++ + + + + + | + public <C extends CredentialRecord> |
+ +630 + | ++ + + + + + | + RelyingPartyV2.RelyingPartyV2Builder<C> credentialRepositoryV2( |
+ +631 + | ++ + + + + + | + CredentialRepositoryV2<C> credentialRepository) { |
+ +632 + | +
+
+1
+
+1. credentialRepositoryV2 : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder$MandatoryStages$Step2::credentialRepositoryV2 → KILLED + + + + |
+ return RelyingPartyV2.builder(builder.identity, credentialRepository); |
+ +633 + | ++ + + + + + | + } |
+ +634 + | ++ + + + + + | + } |
+ +635 + | ++ + + + + + | + } |
+ +636 + | ++ + + + + + | +|
+ +637 + | ++ + + + + + | + /** |
+ +638 + | ++ + + + + + | + * The extension input to set for the <code>appid</code> and <code>appidExclude</code> |
+ +639 + | ++ + + + + + | + * extensions. |
+ +640 + | ++ + + + + + | + * |
+ +641 + | ++ + + + + + | + * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to |
+ +642 + | ++ + + + + + | + * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not |
+ +643 + | ++ + + + + + | + * needed for new registrations, even of U2F authenticators. |
+ +644 + | ++ + + + + + | + * |
+ +645 + | ++ + + + + + | + * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will |
+ +646 + | ++ + + + + + | + * automatically set the <code>appid</code> extension input, and {@link |
+ +647 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic |
+ +648 + | ++ + + + + + | + * to also accept this AppID as an alternative to the RP ID. Likewise, {@link |
+ +649 + | ++ + + + + + | + * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the |
+ +650 + | ++ + + + + + | + * <code>appidExclude</code> extension input. |
+ +651 + | ++ + + + + + | + * |
+ +652 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +653 + | ++ + + + + + | + * |
+ +654 + | ++ + + + + + | + * @see AssertionExtensionInputs#getAppid() |
+ +655 + | ++ + + + + + | + * @see RegistrationExtensionInputs#getAppidExclude() |
+ +656 + | ++ + + + + + | + * @see <a |
+ +657 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +658 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +659 + | ++ + + + + + | + * @see <a |
+ +660 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +661 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +662 + | ++ + + + + + | + */ |
+ +663 + | +
+
+1
+
+1. appId : negated conditional → KILLED + + + + |
+ public RelyingPartyBuilder appId(@NonNull Optional<AppId> appId) { |
+ +664 + | ++ + + + + + | + this.appId = appId; |
+ +665 + | +
+
+1
+
+1. appId : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder::appId → KILLED + + + + |
+ return this; |
+ +666 + | ++ + + + + + | + } |
+ +667 + | ++ + + + + + | +|
+ +668 + | ++ + + + + + | + /** |
+ +669 + | ++ + + + + + | + * The extension input to set for the <code>appid</code> and <code>appidExclude</code> |
+ +670 + | ++ + + + + + | + * extensions. |
+ +671 + | ++ + + + + + | + * |
+ +672 + | ++ + + + + + | + * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to |
+ +673 + | ++ + + + + + | + * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not |
+ +674 + | ++ + + + + + | + * needed for new registrations, even of U2F authenticators. |
+ +675 + | ++ + + + + + | + * |
+ +676 + | ++ + + + + + | + * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will |
+ +677 + | ++ + + + + + | + * automatically set the <code>appid</code> extension input, and {@link |
+ +678 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic |
+ +679 + | ++ + + + + + | + * to also accept this AppID as an alternative to the RP ID. Likewise, {@link |
+ +680 + | ++ + + + + + | + * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the |
+ +681 + | ++ + + + + + | + * <code>appidExclude</code> extension input. |
+ +682 + | ++ + + + + + | + * |
+ +683 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +684 + | ++ + + + + + | + * |
+ +685 + | ++ + + + + + | + * @see AssertionExtensionInputs#getAppid() |
+ +686 + | ++ + + + + + | + * @see RegistrationExtensionInputs#getAppidExclude() |
+ +687 + | ++ + + + + + | + * @see <a |
+ +688 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +689 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +690 + | ++ + + + + + | + * @see <a |
+ +691 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +692 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +693 + | ++ + + + + + | + */ |
+ +694 + | +
+
+1
+
+1. appId : negated conditional → KILLED + + + + |
+ public RelyingPartyBuilder appId(@NonNull AppId appId) { |
+ +695 + | +
+
+1
+
+1. appId : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder::appId → KILLED + + + + |
+ return this.appId(Optional.of(appId)); |
+ +696 + | ++ + + + + + | + } |
+ +697 + | ++ + + + + + | +|
+ +698 + | ++ + + + + + | + /** |
+ +699 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} |
+ +700 + | ++ + + + + + | + * parameter in registration operations. |
+ +701 + | ++ + + + + + | + * |
+ +702 + | ++ + + + + + | + * <p>Unless your application has a concrete policy for authenticator attestation, it is |
+ +703 + | ++ + + + + + | + * recommended to leave this parameter undefined. |
+ +704 + | ++ + + + + + | + * |
+ +705 + | ++ + + + + + | + * <p>If you set this, you may want to explicitly set {@link |
+ +706 + | ++ + + + + + | + * RelyingPartyBuilder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and {@link |
+ +707 + | ++ + + + + + | + * RelyingPartyBuilder#attestationTrustSource(AttestationTrustSource) attestationTrustSource} |
+ +708 + | ++ + + + + + | + * too. |
+ +709 + | ++ + + + + + | + * |
+ +710 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +711 + | ++ + + + + + | + * |
+ +712 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +713 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +714 + | ++ + + + + + | + * Attestation</a> |
+ +715 + | ++ + + + + + | + */ |
+ +716 + | ++ + + + + + | + public RelyingPartyBuilder attestationConveyancePreference( |
+ +717 + | +
+
+1
+
+1. attestationConveyancePreference : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference) { |
+ +718 + | ++ + + + + + | + this.attestationConveyancePreference = attestationConveyancePreference; |
+ +719 + | +
+
+1
+
+1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder::attestationConveyancePreference → KILLED + + + + |
+ return this; |
+ +720 + | ++ + + + + + | + } |
+ +721 + | ++ + + + + + | +|
+ +722 + | ++ + + + + + | + /** |
+ +723 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} |
+ +724 + | ++ + + + + + | + * parameter in registration operations. |
+ +725 + | ++ + + + + + | + * |
+ +726 + | ++ + + + + + | + * <p>Unless your application has a concrete policy for authenticator attestation, it is |
+ +727 + | ++ + + + + + | + * recommended to leave this parameter undefined. |
+ +728 + | ++ + + + + + | + * |
+ +729 + | ++ + + + + + | + * <p>If you set this, you may want to explicitly set {@link |
+ +730 + | ++ + + + + + | + * RelyingPartyBuilder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and {@link |
+ +731 + | ++ + + + + + | + * RelyingPartyBuilder#attestationTrustSource(AttestationTrustSource) attestationTrustSource} |
+ +732 + | ++ + + + + + | + * too. |
+ +733 + | ++ + + + + + | + * |
+ +734 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +735 + | ++ + + + + + | + * |
+ +736 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +737 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +738 + | ++ + + + + + | + * Attestation</a> |
+ +739 + | ++ + + + + + | + */ |
+ +740 + | ++ + + + + + | + public RelyingPartyBuilder attestationConveyancePreference( |
+ +741 + | +
+
+1
+
+1. attestationConveyancePreference : negated conditional → KILLED + + + + |
+ @NonNull AttestationConveyancePreference attestationConveyancePreference) { |
+ +742 + | +
+
+1
+
+1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder::attestationConveyancePreference → KILLED + + + + |
+ return this.attestationConveyancePreference(Optional.of(attestationConveyancePreference)); |
+ +743 + | ++ + + + + + | + } |
+ +744 + | ++ + + + + + | +|
+ +745 + | ++ + + + + + | + /** |
+ +746 + | ++ + + + + + | + * An {@link AttestationTrustSource} instance to use for looking up trust roots for |
+ +747 + | ++ + + + + + | + * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()} |
+ +748 + | ++ + + + + + | + * is non-empty and not set to {@link AttestationConveyancePreference#NONE}. |
+ +749 + | ++ + + + + + | + * |
+ +750 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +751 + | ++ + + + + + | + * |
+ +752 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +753 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +754 + | ++ + + + + + | + * Attestation</a> |
+ +755 + | ++ + + + + + | + */ |
+ +756 + | ++ + + + + + | + public RelyingPartyBuilder attestationTrustSource( |
+ +757 + | +
+
+1
+
+1. attestationTrustSource : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationTrustSource> attestationTrustSource) { |
+ +758 + | ++ + + + + + | + this.attestationTrustSource = attestationTrustSource; |
+ +759 + | +
+
+1
+
+1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder::attestationTrustSource → KILLED + + + + |
+ return this; |
+ +760 + | ++ + + + + + | + } |
+ +761 + | ++ + + + + + | +|
+ +762 + | ++ + + + + + | + /** |
+ +763 + | ++ + + + + + | + * An {@link AttestationTrustSource} instance to use for looking up trust roots for |
+ +764 + | ++ + + + + + | + * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()} |
+ +765 + | ++ + + + + + | + * is non-empty and not set to {@link AttestationConveyancePreference#NONE}. |
+ +766 + | ++ + + + + + | + * |
+ +767 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +768 + | ++ + + + + + | + * |
+ +769 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +770 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +771 + | ++ + + + + + | + * Attestation</a> |
+ +772 + | ++ + + + + + | + */ |
+ +773 + | ++ + + + + + | + public RelyingPartyBuilder attestationTrustSource( |
+ +774 + | +
+
+1
+
+1. attestationTrustSource : negated conditional → KILLED + + + + |
+ @NonNull AttestationTrustSource attestationTrustSource) { |
+ +775 + | +
+
+1
+
+1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingParty$RelyingPartyBuilder::attestationTrustSource → KILLED + + + + |
+ return this.attestationTrustSource(Optional.of(attestationTrustSource)); |
+ +776 + | ++ + + + + + | + } |
+ +777 + | ++ + + + + + | + } |
+ +778 + | ++ + + + + + | +} |
Mutations | ||
366 | ++ |
+
+
+
+ 1.1 |
+
368 | ++ |
+
+
+
+ 1.1 |
+
369 | ++ |
+
+
+
+ 1.1 |
+
370 | ++ |
+
+
+
+ 1.1 |
+
371 | ++ |
+
+
+
+ 1.1 |
+
380 | ++ |
+
+
+
+ 1.1 |
+
408 | ++ |
+
+
+
+ 1.1 |
+
409 | ++ |
+
+
+
+ 1.1 |
+
420 | ++ |
+
+
+
+ 1.1 |
+
451 | ++ |
+
+
+
+ 1.1 |
+
459 | ++ |
+
+
+
+ 1.1 |
+
468 | ++ |
+
+
+
+ 1.1 |
+
471 | ++ |
+
+
+
+ 1.1 |
+
497 | ++ |
+
+
+
+ 1.1 |
+
498 | ++ |
+
+
+
+ 1.1 |
+
504 | ++ |
+
+
+
+ 1.1 |
+
518 | ++ |
+
+
+
+ 1.1 |
+
530 | ++ |
+
+
+
+ 1.1 |
+
535 | ++ |
+
+
+
+ 1.1 |
+
542 | ++ |
+
+
+
+ 1.1 |
+
544 | ++ |
+
+
+
+ 1.1 |
+
562 | ++ |
+
+
+
+ 1.1 |
+
576 | ++ |
+
+
+
+ 1.1 |
+
580 | ++ |
+
+
+
+ 1.1 |
+
600 | ++ |
+
+
+
+ 1.1 |
+
612 | ++ |
+
+
+
+ 1.1 |
+
632 | ++ |
+
+
+
+ 1.1 |
+
663 | ++ |
+
+
+
+ 1.1 |
+
665 | ++ |
+
+
+
+ 1.1 |
+
694 | ++ |
+
+
+
+ 1.1 |
+
695 | ++ |
+
+
+
+ 1.1 |
+
717 | ++ |
+
+
+
+ 1.1 |
+
719 | ++ |
+
+
+
+ 1.1 |
+
741 | ++ |
+
+
+
+ 1.1 |
+
742 | ++ |
+
+
+
+ 1.1 |
+
757 | ++ |
+
+
+
+ 1.1 |
+
759 | ++ |
+
+
+
+ 1.1 |
+
774 | ++ |
+
+
+
+ 1.1 |
+
775 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.internal.util.CollectionUtil; |
+ +28 + | ++ + + + + + | +import com.yubico.internal.util.OptionalUtil; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.attestation.AttestationTrustSource; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.AssertionExtensionInputs; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationConveyancePreference; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.AuthenticatorData; |
+ +33 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.CollectedClientData; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions.PublicKeyCredentialCreationOptionsBuilder; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialParameters; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; |
+ +39 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions.PublicKeyCredentialRequestOptionsBuilder; |
+ +40 + | ++ + + + + + | +import com.yubico.webauthn.data.RegistrationExtensionInputs; |
+ +41 + | ++ + + + + + | +import com.yubico.webauthn.data.RelyingPartyIdentity; |
+ +42 + | ++ + + + + + | +import com.yubico.webauthn.exception.AssertionFailedException; |
+ +43 + | ++ + + + + + | +import com.yubico.webauthn.exception.InvalidSignatureCountException; |
+ +44 + | ++ + + + + + | +import com.yubico.webauthn.exception.RegistrationFailedException; |
+ +45 + | ++ + + + + + | +import com.yubico.webauthn.extension.appid.AppId; |
+ +46 + | ++ + + + + + | +import java.net.MalformedURLException; |
+ +47 + | ++ + + + + + | +import java.net.URL; |
+ +48 + | ++ + + + + + | +import java.security.KeyFactory; |
+ +49 + | ++ + + + + + | +import java.security.SecureRandom; |
+ +50 + | ++ + + + + + | +import java.security.Signature; |
+ +51 + | ++ + + + + + | +import java.time.Clock; |
+ +52 + | ++ + + + + + | +import java.util.Arrays; |
+ +53 + | ++ + + + + + | +import java.util.Collections; |
+ +54 + | ++ + + + + + | +import java.util.List; |
+ +55 + | ++ + + + + + | +import java.util.Optional; |
+ +56 + | ++ + + + + + | +import java.util.Set; |
+ +57 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +58 + | ++ + + + + + | +import lombok.Builder; |
+ +59 + | ++ + + + + + | +import lombok.NonNull; |
+ +60 + | ++ + + + + + | +import lombok.Value; |
+ +61 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +62 + | ++ + + + + + | +|
+ +63 + | ++ + + + + + | +/** |
+ +64 + | ++ + + + + + | + * Encapsulates the four basic Web Authentication operations - start/finish registration, |
+ +65 + | ++ + + + + + | + * start/finish authentication - along with overall operational settings for them. |
+ +66 + | ++ + + + + + | + * |
+ +67 + | ++ + + + + + | + * <p>This class has no mutable state. An instance of this class may therefore be thought of as a |
+ +68 + | ++ + + + + + | + * container for specialized versions (function closures) of these four operations rather than a |
+ +69 + | ++ + + + + + | + * stateful object. |
+ +70 + | ++ + + + + + | + */ |
+ +71 + | ++ + + + + + | +@Slf4j |
+ +72 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +73 + | ++ + + + + + | +@Value |
+ +74 + | ++ + + + + + | +public class RelyingPartyV2<C extends CredentialRecord> { |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + private static final SecureRandom random = new SecureRandom(); |
+ +77 + | ++ + + + + + | +|
+ +78 + | ++ + + + + + | + /** |
+ +79 + | ++ + + + + + | + * The {@link RelyingPartyIdentity} that will be set as the {@link |
+ +80 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getRp() rp} parameter when initiating registration |
+ +81 + | ++ + + + + + | + * operations, and which {@link AuthenticatorData#getRpIdHash()} will be compared against. This is |
+ +82 + | ++ + + + + + | + * a required parameter. |
+ +83 + | ++ + + + + + | + * |
+ +84 + | ++ + + + + + | + * <p>A successful registration or authentication operation requires {@link |
+ +85 + | ++ + + + + + | + * AuthenticatorData#getRpIdHash()} to exactly equal the SHA-256 hash of this member's {@link |
+ +86 + | ++ + + + + + | + * RelyingPartyIdentity#getId() id} member. Alternatively, it may instead equal the SHA-256 hash |
+ +87 + | ++ + + + + + | + * of {@link #getAppId() appId} if the latter is present. |
+ +88 + | ++ + + + + + | + * |
+ +89 + | ++ + + + + + | + * @see #startRegistration(StartRegistrationOptions) |
+ +90 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions |
+ +91 + | ++ + + + + + | + */ |
+ +92 + | ++ + + + + + | + @NonNull private final RelyingPartyIdentity identity; |
+ +93 + | ++ + + + + + | +|
+ +94 + | ++ + + + + + | + /** |
+ +95 + | ++ + + + + + | + * The allowed origins that returned authenticator responses will be compared against. |
+ +96 + | ++ + + + + + | + * |
+ +97 + | ++ + + + + + | + * <p>The default is the set containing only the string <code> |
+ +98 + | ++ + + + + + | + * "https://" + {@link #getIdentity()}.getId()</code>. |
+ +99 + | ++ + + + + + | + * |
+ +100 + | ++ + + + + + | + * <p>If {@link RelyingPartyV2Builder#allowOriginPort(boolean) allowOriginPort} and {@link |
+ +101 + | ++ + + + + + | + * RelyingPartyV2Builder#allowOriginSubdomain(boolean) allowOriginSubdomain} are both <code>false |
+ +102 + | ++ + + + + + | + * </code> (the default), then a successful registration or authentication operation requires |
+ +103 + | ++ + + + + + | + * {@link CollectedClientData#getOrigin()} to exactly equal one of these values. |
+ +104 + | ++ + + + + + | + * |
+ +105 + | ++ + + + + + | + * <p>If {@link RelyingPartyV2Builder#allowOriginPort(boolean) allowOriginPort} is <code>true |
+ +106 + | ++ + + + + + | + * </code> , then the above rule is relaxed to allow any port number in {@link |
+ +107 + | ++ + + + + + | + * CollectedClientData#getOrigin()}, regardless of any port specified. |
+ +108 + | ++ + + + + + | + * |
+ +109 + | ++ + + + + + | + * <p>If {@link RelyingPartyV2Builder#allowOriginSubdomain(boolean) allowOriginSubdomain} is |
+ +110 + | ++ + + + + + | + * <code> |
+ +111 + | ++ + + + + + | + * true</code>, then the above rule is relaxed to allow any subdomain, of any depth, of any of |
+ +112 + | ++ + + + + + | + * these values. |
+ +113 + | ++ + + + + + | + * |
+ +114 + | ++ + + + + + | + * <p>For either of the above relaxations to take effect, both the allowed origin and the client |
+ +115 + | ++ + + + + + | + * data origin must be valid URLs. Origins that are not valid URLs are matched only by exact |
+ +116 + | ++ + + + + + | + * string equality. |
+ +117 + | ++ + + + + + | + * |
+ +118 + | ++ + + + + + | + * @see #getIdentity() |
+ +119 + | ++ + + + + + | + */ |
+ +120 + | ++ + + + + + | + @NonNull private final Set<String> origins; |
+ +121 + | ++ + + + + + | +|
+ +122 + | ++ + + + + + | + /** |
+ +123 + | ++ + + + + + | + * An abstract database which can look up credentials, usernames and user handles from usernames, |
+ +124 + | ++ + + + + + | + * user handles and credential IDs. This is a required parameter. |
+ +125 + | ++ + + + + + | + * |
+ +126 + | ++ + + + + + | + * <p>This is used to look up: |
+ +127 + | ++ + + + + + | + * |
+ +128 + | ++ + + + + + | + * <ul> |
+ +129 + | ++ + + + + + | + * <li>the user handle for a user logging in via user name |
+ +130 + | ++ + + + + + | + * <li>the user name for a user logging in via user handle |
+ +131 + | ++ + + + + + | + * <li>the credential IDs to include in {@link |
+ +132 + | ++ + + + + + | + * PublicKeyCredentialCreationOptions#getExcludeCredentials()} |
+ +133 + | ++ + + + + + | + * <li>the credential IDs to include in {@link |
+ +134 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} |
+ +135 + | ++ + + + + + | + * <li>that the correct user owns the credential when verifying an assertion |
+ +136 + | ++ + + + + + | + * <li>the public key to use to verify an assertion |
+ +137 + | ++ + + + + + | + * <li>the stored signature counter when verifying an assertion |
+ +138 + | ++ + + + + + | + * </ul> |
+ +139 + | ++ + + + + + | + */ |
+ +140 + | ++ + + + + + | + @NonNull private final CredentialRepositoryV2<C> credentialRepository; |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + /** |
+ +143 + | ++ + + + + + | + * Enable support for identifying users by username. |
+ +144 + | ++ + + + + + | + * |
+ +145 + | ++ + + + + + | + * <p>If set, then {@link #startAssertion(StartAssertionOptions)} allows setting the {@link |
+ +146 + | ++ + + + + + | + * StartAssertionOptions.StartAssertionOptionsBuilder#username(String) username} parameter when |
+ +147 + | ++ + + + + + | + * starting an assertion. |
+ +148 + | ++ + + + + + | + * |
+ +149 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +150 + | ++ + + + + + | + * |
+ +151 + | ++ + + + + + | + * @deprecated EXPERIMENTAL: This is an experimental feature. It is likely to change or be deleted |
+ +152 + | ++ + + + + + | + * before reaching a mature release. |
+ +153 + | ++ + + + + + | + */ |
+ +154 + | ++ + + + + + | + @Deprecated private final UsernameRepository usernameRepository; |
+ +155 + | ++ + + + + + | +|
+ +156 + | ++ + + + + + | + /** |
+ +157 + | ++ + + + + + | + * The extension input to set for the <code>appid</code> and <code>appidExclude</code> extensions. |
+ +158 + | ++ + + + + + | + * |
+ +159 + | ++ + + + + + | + * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to |
+ +160 + | ++ + + + + + | + * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not |
+ +161 + | ++ + + + + + | + * needed for new registrations, even of U2F authenticators. |
+ +162 + | ++ + + + + + | + * |
+ +163 + | ++ + + + + + | + * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will |
+ +164 + | ++ + + + + + | + * automatically set the <code>appid</code> extension input, and {@link |
+ +165 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic to |
+ +166 + | ++ + + + + + | + * also accept this AppID as an alternative to the RP ID. Likewise, {@link |
+ +167 + | ++ + + + + + | + * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the |
+ +168 + | ++ + + + + + | + * <code>appidExclude</code> extension input. |
+ +169 + | ++ + + + + + | + * |
+ +170 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +171 + | ++ + + + + + | + * |
+ +172 + | ++ + + + + + | + * @see AssertionExtensionInputs#getAppid() |
+ +173 + | ++ + + + + + | + * @see RegistrationExtensionInputs#getAppidExclude() |
+ +174 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +175 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +176 + | ++ + + + + + | + * @see <a |
+ +177 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +178 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +179 + | ++ + + + + + | + */ |
+ +180 + | ++ + + + + + | + @NonNull private final Optional<AppId> appId; |
+ +181 + | ++ + + + + + | +|
+ +182 + | ++ + + + + + | + /** |
+ +183 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} |
+ +184 + | ++ + + + + + | + * parameter in registration operations. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * <p>Unless your application has a concrete policy for authenticator attestation, it is |
+ +187 + | ++ + + + + + | + * recommended to leave this parameter undefined. |
+ +188 + | ++ + + + + + | + * |
+ +189 + | ++ + + + + + | + * <p>If you set this, you may want to explicitly set {@link |
+ +190 + | ++ + + + + + | + * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and {@link |
+ +191 + | ++ + + + + + | + * RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource) attestationTrustSource} |
+ +192 + | ++ + + + + + | + * too. |
+ +193 + | ++ + + + + + | + * |
+ +194 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +195 + | ++ + + + + + | + * |
+ +196 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +197 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +198 + | ++ + + + + + | + * Attestation</a> |
+ +199 + | ++ + + + + + | + */ |
+ +200 + | ++ + + + + + | + @NonNull private final Optional<AttestationConveyancePreference> attestationConveyancePreference; |
+ +201 + | ++ + + + + + | +|
+ +202 + | ++ + + + + + | + /** |
+ +203 + | ++ + + + + + | + * An {@link AttestationTrustSource} instance to use for looking up trust roots for authenticator |
+ +204 + | ++ + + + + + | + * attestation. This matters only if {@link #getAttestationConveyancePreference()} is non-empty |
+ +205 + | ++ + + + + + | + * and not set to {@link AttestationConveyancePreference#NONE}. |
+ +206 + | ++ + + + + + | + * |
+ +207 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +208 + | ++ + + + + + | + * |
+ +209 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +210 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +211 + | ++ + + + + + | + * Attestation</a> |
+ +212 + | ++ + + + + + | + */ |
+ +213 + | ++ + + + + + | + @NonNull private final Optional<AttestationTrustSource> attestationTrustSource; |
+ +214 + | ++ + + + + + | +|
+ +215 + | ++ + + + + + | + /** |
+ +216 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getPubKeyCredParams() |
+ +217 + | ++ + + + + + | + * pubKeyCredParams} parameter in registration operations. |
+ +218 + | ++ + + + + + | + * |
+ +219 + | ++ + + + + + | + * <p>This is a list of acceptable public key algorithms and their parameters, ordered from most |
+ +220 + | ++ + + + + + | + * to least preferred. |
+ +221 + | ++ + + + + + | + * |
+ +222 + | ++ + + + + + | + * <p>The default is the following list, in order: |
+ +223 + | ++ + + + + + | + * |
+ +224 + | ++ + + + + + | + * <ol> |
+ +225 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#ES256 ES256} |
+ +226 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#EdDSA EdDSA} |
+ +227 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#ES256 ES384} |
+ +228 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#ES256 ES512} |
+ +229 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#RS256 RS256} |
+ +230 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#RS384 RS384} |
+ +231 + | ++ + + + + + | + * <li>{@link PublicKeyCredentialParameters#RS512 RS512} |
+ +232 + | ++ + + + + + | + * </ol> |
+ +233 + | ++ + + + + + | + * |
+ +234 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +235 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +236 + | ++ + + + + + | + * Attestation</a> |
+ +237 + | ++ + + + + + | + */ |
+ +238 + | ++ + + + + + | + @Builder.Default @NonNull |
+ +239 + | ++ + + + + + | + private final List<PublicKeyCredentialParameters> preferredPubkeyParams = |
+ +240 + | ++ + + + + + | + Collections.unmodifiableList( |
+ +241 + | ++ + + + + + | + Arrays.asList( |
+ +242 + | ++ + + + + + | + PublicKeyCredentialParameters.ES256, |
+ +243 + | ++ + + + + + | + PublicKeyCredentialParameters.EdDSA, |
+ +244 + | ++ + + + + + | + PublicKeyCredentialParameters.ES384, |
+ +245 + | ++ + + + + + | + PublicKeyCredentialParameters.ES512, |
+ +246 + | ++ + + + + + | + PublicKeyCredentialParameters.RS256, |
+ +247 + | ++ + + + + + | + PublicKeyCredentialParameters.RS384, |
+ +248 + | ++ + + + + + | + PublicKeyCredentialParameters.RS512)); |
+ +249 + | ++ + + + + + | +|
+ +250 + | ++ + + + + + | + /** |
+ +251 + | ++ + + + + + | + * If <code>true</code>, the origin matching rule is relaxed to allow any port number. |
+ +252 + | ++ + + + + + | + * |
+ +253 + | ++ + + + + + | + * <p>The default is <code>false</code>. |
+ +254 + | ++ + + + + + | + * |
+ +255 + | ++ + + + + + | + * <p>Examples with <code> |
+ +256 + | ++ + + + + + | + * origins: ["https://example.org", "https://accounts.example.org", "https://acme.com:8443"] |
+ +257 + | ++ + + + + + | + * </code> |
+ +258 + | ++ + + + + + | + * |
+ +259 + | ++ + + + + + | + * <ul> |
+ +260 + | ++ + + + + + | + * <li> |
+ +261 + | ++ + + + + + | + * <p><code>allowOriginPort: false</code> |
+ +262 + | ++ + + + + + | + * <p>Accepted: |
+ +263 + | ++ + + + + + | + * <ul> |
+ +264 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +265 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +266 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +267 + | ++ + + + + + | + * </ul> |
+ +268 + | ++ + + + + + | + * <p>Rejected: |
+ +269 + | ++ + + + + + | + * <ul> |
+ +270 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +271 + | ++ + + + + + | + * <li><code>https://shop.example.org</code> |
+ +272 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +273 + | ++ + + + + + | + * <li><code>https://acme.com:9000</code> |
+ +274 + | ++ + + + + + | + * </ul> |
+ +275 + | ++ + + + + + | + * <li> |
+ +276 + | ++ + + + + + | + * <p><code>allowOriginPort: true</code> |
+ +277 + | ++ + + + + + | + * <p>Accepted: |
+ +278 + | ++ + + + + + | + * <ul> |
+ +279 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +280 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +281 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +282 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +283 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +284 + | ++ + + + + + | + * <li><code>https://acme.com:9000</code> |
+ +285 + | ++ + + + + + | + * </ul> |
+ +286 + | ++ + + + + + | + * <p>Rejected: |
+ +287 + | ++ + + + + + | + * <ul> |
+ +288 + | ++ + + + + + | + * <li><code>https://shop.example.org</code> |
+ +289 + | ++ + + + + + | + * </ul> |
+ +290 + | ++ + + + + + | + * </ul> |
+ +291 + | ++ + + + + + | + */ |
+ +292 + | ++ + + + + + | + @Builder.Default private final boolean allowOriginPort = false; |
+ +293 + | ++ + + + + + | +|
+ +294 + | ++ + + + + + | + /** |
+ +295 + | ++ + + + + + | + * If <code>true</code>, the origin matching rule is relaxed to allow any subdomain, of any depth, |
+ +296 + | ++ + + + + + | + * of the values of {@link RelyingPartyV2Builder#origins(Set) origins}. |
+ +297 + | ++ + + + + + | + * |
+ +298 + | ++ + + + + + | + * <p>The default is <code>false</code>. |
+ +299 + | ++ + + + + + | + * |
+ +300 + | ++ + + + + + | + * <p>Examples with <code>origins: ["https://example.org", "https://acme.com:8443"]</code> |
+ +301 + | ++ + + + + + | + * |
+ +302 + | ++ + + + + + | + * <ul> |
+ +303 + | ++ + + + + + | + * <li> |
+ +304 + | ++ + + + + + | + * <p><code>allowOriginSubdomain: false</code> |
+ +305 + | ++ + + + + + | + * <p>Accepted: |
+ +306 + | ++ + + + + + | + * <ul> |
+ +307 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +308 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +309 + | ++ + + + + + | + * </ul> |
+ +310 + | ++ + + + + + | + * <p>Rejected: |
+ +311 + | ++ + + + + + | + * <ul> |
+ +312 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +313 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +314 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +315 + | ++ + + + + + | + * <li><code>https://eu.shop.acme.com:8443</code> |
+ +316 + | ++ + + + + + | + * </ul> |
+ +317 + | ++ + + + + + | + * <li> |
+ +318 + | ++ + + + + + | + * <p><code>allowOriginSubdomain: true</code> |
+ +319 + | ++ + + + + + | + * <p>Accepted: |
+ +320 + | ++ + + + + + | + * <ul> |
+ +321 + | ++ + + + + + | + * <li><code>https://example.org</code> |
+ +322 + | ++ + + + + + | + * <li><code>https://accounts.example.org</code> |
+ +323 + | ++ + + + + + | + * <li><code>https://acme.com:8443</code> |
+ +324 + | ++ + + + + + | + * <li><code>https://eu.shop.acme.com:8443</code> |
+ +325 + | ++ + + + + + | + * </ul> |
+ +326 + | ++ + + + + + | + * <p>Rejected: |
+ +327 + | ++ + + + + + | + * <ul> |
+ +328 + | ++ + + + + + | + * <li><code>https://example.org:8443</code> |
+ +329 + | ++ + + + + + | + * <li><code>https://acme.com</code> |
+ +330 + | ++ + + + + + | + * </ul> |
+ +331 + | ++ + + + + + | + * </ul> |
+ +332 + | ++ + + + + + | + */ |
+ +333 + | ++ + + + + + | + @Builder.Default private final boolean allowOriginSubdomain = false; |
+ +334 + | ++ + + + + + | +|
+ +335 + | ++ + + + + + | + /** |
+ +336 + | ++ + + + + + | + * If <code>false</code>, {@link #finishRegistration(FinishRegistrationOptions) |
+ +337 + | ++ + + + + + | + * finishRegistration} will only allow registrations where the attestation signature can be linked |
+ +338 + | ++ + + + + + | + * to a trusted attestation root. This excludes none attestation, and self attestation unless the |
+ +339 + | ++ + + + + + | + * self attestation key is explicitly trusted. |
+ +340 + | ++ + + + + + | + * |
+ +341 + | ++ + + + + + | + * <p>Regardless of the value of this option, invalid attestation statements of supported formats |
+ +342 + | ++ + + + + + | + * will always be rejected. For example, a "packed" attestation statement with an invalid |
+ +343 + | ++ + + + + + | + * signature will be rejected even if this option is set to <code>true</code>. |
+ +344 + | ++ + + + + + | + * |
+ +345 + | ++ + + + + + | + * <p>The default is <code>true</code>. |
+ +346 + | ++ + + + + + | + */ |
+ +347 + | ++ + + + + + | + @Builder.Default private final boolean allowUntrustedAttestation = true; |
+ +348 + | ++ + + + + + | +|
+ +349 + | ++ + + + + + | + /** |
+ +350 + | ++ + + + + + | + * If <code>true</code>, {@link #finishAssertion(FinishAssertionOptions) finishAssertion} will |
+ +351 + | ++ + + + + + | + * succeed only if the {@link AuthenticatorData#getSignatureCounter() signature counter value} in |
+ +352 + | ++ + + + + + | + * the response is strictly greater than the {@link RegisteredCredential#getSignatureCount() |
+ +353 + | ++ + + + + + | + * stored signature counter value}, or if both counters are exactly zero. |
+ +354 + | ++ + + + + + | + * |
+ +355 + | ++ + + + + + | + * <p>The default is <code>true</code>. |
+ +356 + | ++ + + + + + | + */ |
+ +357 + | ++ + + + + + | + @Builder.Default private final boolean validateSignatureCounter = true; |
+ +358 + | ++ + + + + + | +|
+ +359 + | ++ + + + + + | + /** |
+ +360 + | ++ + + + + + | + * A {@link Clock} which will be used to tell the current time while verifying attestation |
+ +361 + | ++ + + + + + | + * certificate chains. |
+ +362 + | ++ + + + + + | + * |
+ +363 + | ++ + + + + + | + * <p>This is intended primarily for testing, and relevant only if {@link |
+ +364 + | ++ + + + + + | + * RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource)} is set. |
+ +365 + | ++ + + + + + | + * |
+ +366 + | ++ + + + + + | + * <p>The default is <code>Clock.systemUTC()</code>. |
+ +367 + | ++ + + + + + | + */ |
+ +368 + | ++ + + + + + | + @Builder.Default @NonNull private final Clock clock = Clock.systemUTC(); |
+ +369 + | ++ + + + + + | +|
+ +370 + | ++ + + + + + | + @Builder |
+ +371 + | ++ + + + + + | + private RelyingPartyV2( |
+ +372 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull RelyingPartyIdentity identity, |
+ +373 + | ++ + + + + + | + Set<String> origins, |
+ +374 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull CredentialRepositoryV2<C> credentialRepository, |
+ +375 + | ++ + + + + + | + UsernameRepository usernameRepository, |
+ +376 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Optional<AppId> appId, |
+ +377 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference, |
+ +378 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationTrustSource> attestationTrustSource, |
+ +379 + | ++ + + + + + | + List<PublicKeyCredentialParameters> preferredPubkeyParams, |
+ +380 + | ++ + + + + + | + boolean allowOriginPort, |
+ +381 + | ++ + + + + + | + boolean allowOriginSubdomain, |
+ +382 + | ++ + + + + + | + boolean allowUntrustedAttestation, |
+ +383 + | ++ + + + + + | + boolean validateSignatureCounter, |
+ +384 + | ++ + + + + + | + Clock clock) { |
+ +385 + | ++ + + + + + | + this.identity = identity; |
+ +386 + | ++ + + + + + | + this.origins = |
+ +387 + | +
+
+1
+
+1. <init> : negated conditional → KILLED + + + + |
+ origins != null |
+ +388 + | ++ + + + + + | + ? CollectionUtil.immutableSet(origins) |
+ +389 + | ++ + + + + + | + : Collections.singleton("https://" + identity.getId()); |
+ +390 + | ++ + + + + + | +|
+ +391 + | ++ + + + + + | + for (String origin : this.origins) { |
+ +392 + | ++ + + + + + | + try { |
+ +393 + | ++ + + + + + | + new URL(origin); |
+ +394 + | ++ + + + + + | + } catch (MalformedURLException e) { |
+ +395 + | ++ + + + + + | + log.warn( |
+ +396 + | ++ + + + + + | + "Allowed origin is not a valid URL, it will match only by exact string equality: {}", |
+ +397 + | ++ + + + + + | + origin); |
+ +398 + | ++ + + + + + | + } |
+ +399 + | ++ + + + + + | + } |
+ +400 + | ++ + + + + + | +|
+ +401 + | ++ + + + + + | + this.credentialRepository = credentialRepository; |
+ +402 + | ++ + + + + + | + this.usernameRepository = usernameRepository; |
+ +403 + | ++ + + + + + | + this.appId = appId; |
+ +404 + | ++ + + + + + | + this.attestationConveyancePreference = attestationConveyancePreference; |
+ +405 + | ++ + + + + + | + this.attestationTrustSource = attestationTrustSource; |
+ +406 + | ++ + + + + + | + this.preferredPubkeyParams = filterAvailableAlgorithms(preferredPubkeyParams); |
+ +407 + | ++ + + + + + | + this.allowOriginPort = allowOriginPort; |
+ +408 + | ++ + + + + + | + this.allowOriginSubdomain = allowOriginSubdomain; |
+ +409 + | ++ + + + + + | + this.allowUntrustedAttestation = allowUntrustedAttestation; |
+ +410 + | ++ + + + + + | + this.validateSignatureCounter = validateSignatureCounter; |
+ +411 + | ++ + + + + + | + this.clock = clock; |
+ +412 + | ++ + + + + + | + } |
+ +413 + | ++ + + + + + | +|
+ +414 + | ++ + + + + + | + private static ByteArray generateChallenge() { |
+ +415 + | ++ + + + + + | + byte[] bytes = new byte[32]; |
+ +416 + | +
+
+1
+
+1. generateChallenge : removed call to java/security/SecureRandom::nextBytes → KILLED + + + + |
+ random.nextBytes(bytes); |
+ +417 + | +
+
+1
+
+1. generateChallenge : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::generateChallenge → KILLED + + + + |
+ return new ByteArray(bytes); |
+ +418 + | ++ + + + + + | + } |
+ +419 + | ++ + + + + + | +|
+ +420 + | ++ + + + + + | + /** |
+ +421 + | ++ + + + + + | + * Filter <code>pubKeyCredParams</code> to only contain algorithms with a {@link KeyFactory} and a |
+ +422 + | ++ + + + + + | + * {@link Signature} available, and log a warning for every unsupported algorithm. |
+ +423 + | ++ + + + + + | + * |
+ +424 + | ++ + + + + + | + * @return a new {@link List} containing only the algorithms supported in the current JCA context. |
+ +425 + | ++ + + + + + | + */ |
+ +426 + | ++ + + + + + | + private static List<PublicKeyCredentialParameters> filterAvailableAlgorithms( |
+ +427 + | ++ + + + + + | + List<PublicKeyCredentialParameters> pubKeyCredParams) { |
+ +428 + | +
+
+1
+
+1. filterAvailableAlgorithms : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::filterAvailableAlgorithms → KILLED + + + + |
+ return RelyingParty.filterAvailableAlgorithms(pubKeyCredParams); |
+ +429 + | ++ + + + + + | + } |
+ +430 + | ++ + + + + + | +|
+ +431 + | ++ + + + + + | + public PublicKeyCredentialCreationOptions startRegistration( |
+ +432 + | ++ + + + + + | + StartRegistrationOptions startRegistrationOptions) { |
+ +433 + | ++ + + + + + | + PublicKeyCredentialCreationOptionsBuilder builder = |
+ +434 + | ++ + + + + + | + PublicKeyCredentialCreationOptions.builder() |
+ +435 + | ++ + + + + + | + .rp(identity) |
+ +436 + | ++ + + + + + | + .user(startRegistrationOptions.getUser()) |
+ +437 + | ++ + + + + + | + .challenge(generateChallenge()) |
+ +438 + | ++ + + + + + | + .pubKeyCredParams(preferredPubkeyParams) |
+ +439 + | ++ + + + + + | + .excludeCredentials( |
+ +440 + | ++ + + + + + | + credentialRepository |
+ +441 + | ++ + + + + + | + .getCredentialDescriptorsForUserHandle( |
+ +442 + | ++ + + + + + | + startRegistrationOptions.getUser().getId()) |
+ +443 + | ++ + + + + + | + .stream() |
+ +444 + | ++ + + + + + | + .map(ToPublicKeyCredentialDescriptor::toPublicKeyCredentialDescriptor) |
+ +445 + | ++ + + + + + | + .collect(Collectors.toSet())) |
+ +446 + | ++ + + + + + | + .authenticatorSelection(startRegistrationOptions.getAuthenticatorSelection()) |
+ +447 + | ++ + + + + + | + .extensions( |
+ +448 + | ++ + + + + + | + startRegistrationOptions |
+ +449 + | ++ + + + + + | + .getExtensions() |
+ +450 + | ++ + + + + + | + .merge( |
+ +451 + | ++ + + + + + | + RegistrationExtensionInputs.builder() |
+ +452 + | ++ + + + + + | + .appidExclude(appId) |
+ +453 + | ++ + + + + + | + .credProps() |
+ +454 + | ++ + + + + + | + .build())) |
+ +455 + | ++ + + + + + | + .timeout(startRegistrationOptions.getTimeout()); |
+ +456 + | +
+
+1
+
+1. startRegistration : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ attestationConveyancePreference.ifPresent(builder::attestation); |
+ +457 + | +
+
+1
+
+1. startRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startRegistration → KILLED + + + + |
+ return builder.build(); |
+ +458 + | ++ + + + + + | + } |
+ +459 + | ++ + + + + + | +|
+ +460 + | ++ + + + + + | + public RegistrationResult finishRegistration(FinishRegistrationOptions finishRegistrationOptions) |
+ +461 + | ++ + + + + + | + throws RegistrationFailedException { |
+ +462 + | ++ + + + + + | + try { |
+ +463 + | +
+
+1
+
+1. finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishRegistration → KILLED + + + + |
+ return _finishRegistration(finishRegistrationOptions).run(); |
+ +464 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +465 + | ++ + + + + + | + throw new RegistrationFailedException(e); |
+ +466 + | ++ + + + + + | + } |
+ +467 + | ++ + + + + + | + } |
+ +468 + | ++ + + + + + | +|
+ +469 + | ++ + + + + + | + /** |
+ +470 + | ++ + + + + + | + * This method is NOT part of the public API. |
+ +471 + | ++ + + + + + | + * |
+ +472 + | ++ + + + + + | + * <p>This method is called internally by {@link #finishRegistration(FinishRegistrationOptions)}. |
+ +473 + | ++ + + + + + | + * It is a separate method to facilitate testing; users should call {@link |
+ +474 + | ++ + + + + + | + * #finishRegistration(FinishRegistrationOptions)} instead of this method. |
+ +475 + | ++ + + + + + | + */ |
+ +476 + | ++ + + + + + | + FinishRegistrationSteps _finishRegistration(FinishRegistrationOptions options) { |
+ +477 + | +
+
+1
+
+1. _finishRegistration : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishRegistration → KILLED + + + + |
+ return new FinishRegistrationSteps(this, options); |
+ +478 + | ++ + + + + + | + } |
+ +479 + | ++ + + + + + | +|
+ +480 + | ++ + + + + + | + public AssertionRequest startAssertion(StartAssertionOptions startAssertionOptions) { |
+ +481 + | +
+
+2
+
+1. startAssertion : negated conditional → KILLED +2. startAssertion : negated conditional → KILLED + + + + |
+ if (startAssertionOptions.getUsername().isPresent() && usernameRepository == null) { |
+ +482 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +483 + | ++ + + + + + | + "StartAssertionOptions.username must not be set when usernameRepository is not configured."); |
+ +484 + | ++ + + + + + | + } |
+ +485 + | ++ + + + + + | +|
+ +486 + | ++ + + + + + | + PublicKeyCredentialRequestOptionsBuilder pkcro = |
+ +487 + | ++ + + + + + | + PublicKeyCredentialRequestOptions.builder() |
+ +488 + | ++ + + + + + | + .challenge(generateChallenge()) |
+ +489 + | ++ + + + + + | + .rpId(identity.getId()) |
+ +490 + | ++ + + + + + | + .allowCredentials( |
+ +491 + | ++ + + + + + | + OptionalUtil.orElseOptional( |
+ +492 + | ++ + + + + + | + startAssertionOptions.getUserHandle(), |
+ +493 + | ++ + + + + + | + () -> |
+ +494 + | +
+
+1
+
+1. lambda$startAssertion$1 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$1 → KILLED + + + + |
+ Optional.ofNullable(usernameRepository) |
+ +495 + | ++ + + + + + | + .flatMap( |
+ +496 + | ++ + + + + + | + unr -> |
+ +497 + | +
+
+1
+
+1. lambda$startAssertion$0 : replaced return value with Optional.empty for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$0 → KILLED + + + + |
+ startAssertionOptions |
+ +498 + | ++ + + + + + | + .getUsername() |
+ +499 + | ++ + + + + + | + .flatMap(unr::getUserHandleForUsername))) |
+ +500 + | ++ + + + + + | + .map(credentialRepository::getCredentialDescriptorsForUserHandle) |
+ +501 + | ++ + + + + + | + .map( |
+ +502 + | ++ + + + + + | + descriptors -> |
+ +503 + | ++ + + + + + | + descriptors.stream() |
+ +504 + | ++ + + + + + | + .map( |
+ +505 + | ++ + + + + + | + ToPublicKeyCredentialDescriptor |
+ +506 + | ++ + + + + + | + ::toPublicKeyCredentialDescriptor) |
+ +507 + | +
+
+1
+
+1. lambda$startAssertion$2 : replaced return value with Collections.emptyList for com/yubico/webauthn/RelyingPartyV2::lambda$startAssertion$2 → KILLED + + + + |
+ .collect(Collectors.toList()))) |
+ +508 + | ++ + + + + + | + .extensions( |
+ +509 + | ++ + + + + + | + startAssertionOptions |
+ +510 + | ++ + + + + + | + .getExtensions() |
+ +511 + | ++ + + + + + | + .merge(startAssertionOptions.getExtensions().toBuilder().appid(appId).build())) |
+ +512 + | ++ + + + + + | + .timeout(startAssertionOptions.getTimeout()); |
+ +513 + | ++ + + + + + | +|
+ +514 + | +
+
+1
+
+1. startAssertion : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ startAssertionOptions.getUserVerification().ifPresent(pkcro::userVerification); |
+ +515 + | ++ + + + + + | +|
+ +516 + | +
+
+1
+
+1. startAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::startAssertion → KILLED + + + + |
+ return AssertionRequest.builder() |
+ +517 + | ++ + + + + + | + .publicKeyCredentialRequestOptions(pkcro.build()) |
+ +518 + | ++ + + + + + | + .username(startAssertionOptions.getUsername()) |
+ +519 + | ++ + + + + + | + .userHandle(startAssertionOptions.getUserHandle()) |
+ +520 + | ++ + + + + + | + .build(); |
+ +521 + | ++ + + + + + | + } |
+ +522 + | ++ + + + + + | +|
+ +523 + | ++ + + + + + | + /** |
+ +524 + | ++ + + + + + | + * @throws InvalidSignatureCountException if {@link |
+ +525 + | ++ + + + + + | + * RelyingPartyV2Builder#validateSignatureCounter(boolean) validateSignatureCounter} is <code> |
+ +526 + | ++ + + + + + | + * true</code>, the {@link AuthenticatorData#getSignatureCounter() signature count} in the |
+ +527 + | ++ + + + + + | + * response is less than or equal to the {@link RegisteredCredential#getSignatureCount() |
+ +528 + | ++ + + + + + | + * stored signature count}, and at least one of the signature count values is nonzero. |
+ +529 + | ++ + + + + + | + * @throws AssertionFailedException if validation fails for any other reason. |
+ +530 + | ++ + + + + + | + */ |
+ +531 + | ++ + + + + + | + public AssertionResultV2<C> finishAssertion(FinishAssertionOptions finishAssertionOptions) |
+ +532 + | ++ + + + + + | + throws AssertionFailedException { |
+ +533 + | ++ + + + + + | + try { |
+ +534 + | +
+
+1
+
+1. finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::finishAssertion → KILLED + + + + |
+ return _finishAssertion(finishAssertionOptions).runV2(); |
+ +535 + | ++ + + + + + | + } catch (IllegalArgumentException e) { |
+ +536 + | ++ + + + + + | + throw new AssertionFailedException(e); |
+ +537 + | ++ + + + + + | + } |
+ +538 + | ++ + + + + + | + } |
+ +539 + | ++ + + + + + | +|
+ +540 + | ++ + + + + + | + /** |
+ +541 + | ++ + + + + + | + * This method is NOT part of the public API. |
+ +542 + | ++ + + + + + | + * |
+ +543 + | ++ + + + + + | + * <p>This method is called internally by {@link #finishAssertion(FinishAssertionOptions)}. It is |
+ +544 + | ++ + + + + + | + * a separate method to facilitate testing; users should call {@link |
+ +545 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions)} instead of this method. |
+ +546 + | ++ + + + + + | + */ |
+ +547 + | ++ + + + + + | + FinishAssertionSteps<C> _finishAssertion(FinishAssertionOptions options) { |
+ +548 + | +
+
+1
+
+1. _finishAssertion : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::_finishAssertion → KILLED + + + + |
+ return new FinishAssertionSteps<C>(this, options); |
+ +549 + | ++ + + + + + | + } |
+ +550 + | ++ + + + + + | +|
+ +551 + | ++ + + + + + | + static <C extends CredentialRecord> RelyingPartyV2Builder<C> builder( |
+ +552 + | ++ + + + + + | + RelyingPartyIdentity identity, CredentialRepositoryV2<C> credentialRepository) { |
+ +553 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/RelyingPartyV2::builder → KILLED + + + + |
+ return new RelyingPartyV2Builder<C>() |
+ +554 + | ++ + + + + + | + .identity(identity) |
+ +555 + | ++ + + + + + | + .credentialRepository(credentialRepository); |
+ +556 + | ++ + + + + + | + } |
+ +557 + | ++ + + + + + | +|
+ +558 + | ++ + + + + + | + public static class RelyingPartyV2Builder<C extends CredentialRecord> { |
+ +559 + | ++ + + + + + | + private @NonNull Optional<AppId> appId = Optional.empty(); |
+ +560 + | ++ + + + + + | + private @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference = |
+ +561 + | ++ + + + + + | + Optional.empty(); |
+ +562 + | ++ + + + + + | + private @NonNull Optional<AttestationTrustSource> attestationTrustSource = Optional.empty(); |
+ +563 + | ++ + + + + + | +|
+ +564 + | ++ + + + + + | + /** |
+ +565 + | ++ + + + + + | + * The extension input to set for the <code>appid</code> and <code>appidExclude</code> |
+ +566 + | ++ + + + + + | + * extensions. |
+ +567 + | ++ + + + + + | + * |
+ +568 + | ++ + + + + + | + * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to |
+ +569 + | ++ + + + + + | + * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not |
+ +570 + | ++ + + + + + | + * needed for new registrations, even of U2F authenticators. |
+ +571 + | ++ + + + + + | + * |
+ +572 + | ++ + + + + + | + * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will |
+ +573 + | ++ + + + + + | + * automatically set the <code>appid</code> extension input, and {@link |
+ +574 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic |
+ +575 + | ++ + + + + + | + * to also accept this AppID as an alternative to the RP ID. Likewise, {@link |
+ +576 + | ++ + + + + + | + * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the |
+ +577 + | ++ + + + + + | + * <code>appidExclude</code> extension input. |
+ +578 + | ++ + + + + + | + * |
+ +579 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +580 + | ++ + + + + + | + * |
+ +581 + | ++ + + + + + | + * @see AssertionExtensionInputs#getAppid() |
+ +582 + | ++ + + + + + | + * @see RegistrationExtensionInputs#getAppidExclude() |
+ +583 + | ++ + + + + + | + * @see <a |
+ +584 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +585 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +586 + | ++ + + + + + | + * @see <a |
+ +587 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +588 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +589 + | ++ + + + + + | + */ |
+ +590 + | +
+
+1
+
+1. appId : negated conditional → KILLED + + + + |
+ public RelyingPartyV2Builder<C> appId(@NonNull Optional<AppId> appId) { |
+ +591 + | ++ + + + + + | + this.appId = appId; |
+ +592 + | +
+
+1
+
+1. appId : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED + + + + |
+ return this; |
+ +593 + | ++ + + + + + | + } |
+ +594 + | ++ + + + + + | +|
+ +595 + | ++ + + + + + | + /** |
+ +596 + | ++ + + + + + | + * The extension input to set for the <code>appid</code> and <code>appidExclude</code> |
+ +597 + | ++ + + + + + | + * extensions. |
+ +598 + | ++ + + + + + | + * |
+ +599 + | ++ + + + + + | + * <p>You do not need this extension if you have not previously supported U2F. Its purpose is to |
+ +600 + | ++ + + + + + | + * make already-registered U2F credentials forward-compatible with the WebAuthn API. It is not |
+ +601 + | ++ + + + + + | + * needed for new registrations, even of U2F authenticators. |
+ +602 + | ++ + + + + + | + * |
+ +603 + | ++ + + + + + | + * <p>If this member is set, {@link #startAssertion(StartAssertionOptions) startAssertion} will |
+ +604 + | ++ + + + + + | + * automatically set the <code>appid</code> extension input, and {@link |
+ +605 + | ++ + + + + + | + * #finishAssertion(FinishAssertionOptions) finishAssertion} will adjust its verification logic |
+ +606 + | ++ + + + + + | + * to also accept this AppID as an alternative to the RP ID. Likewise, {@link |
+ +607 + | ++ + + + + + | + * #startRegistration(StartRegistrationOptions)} startRegistration} will automatically set the |
+ +608 + | ++ + + + + + | + * <code>appidExclude</code> extension input. |
+ +609 + | ++ + + + + + | + * |
+ +610 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +611 + | ++ + + + + + | + * |
+ +612 + | ++ + + + + + | + * @see AssertionExtensionInputs#getAppid() |
+ +613 + | ++ + + + + + | + * @see RegistrationExtensionInputs#getAppidExclude() |
+ +614 + | ++ + + + + + | + * @see <a |
+ +615 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-extension">§10.1. |
+ +616 + | ++ + + + + + | + * FIDO AppID Extension (appid)</a> |
+ +617 + | ++ + + + + + | + * @see <a |
+ +618 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-appid-exclude-extension">§10.2. |
+ +619 + | ++ + + + + + | + * FIDO AppID Exclusion Extension (appidExclude)</a> |
+ +620 + | ++ + + + + + | + */ |
+ +621 + | +
+
+1
+
+1. appId : negated conditional → KILLED + + + + |
+ public RelyingPartyV2Builder<C> appId(@NonNull AppId appId) { |
+ +622 + | +
+
+1
+
+1. appId : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::appId → KILLED + + + + |
+ return this.appId(Optional.of(appId)); |
+ +623 + | ++ + + + + + | + } |
+ +624 + | ++ + + + + + | +|
+ +625 + | ++ + + + + + | + /** |
+ +626 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} |
+ +627 + | ++ + + + + + | + * parameter in registration operations. |
+ +628 + | ++ + + + + + | + * |
+ +629 + | ++ + + + + + | + * <p>Unless your application has a concrete policy for authenticator attestation, it is |
+ +630 + | ++ + + + + + | + * recommended to leave this parameter undefined. |
+ +631 + | ++ + + + + + | + * |
+ +632 + | ++ + + + + + | + * <p>If you set this, you may want to explicitly set {@link |
+ +633 + | ++ + + + + + | + * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and |
+ +634 + | ++ + + + + + | + * {@link RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource) |
+ +635 + | ++ + + + + + | + * attestationTrustSource} too. |
+ +636 + | ++ + + + + + | + * |
+ +637 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +638 + | ++ + + + + + | + * |
+ +639 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +640 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +641 + | ++ + + + + + | + * Attestation</a> |
+ +642 + | ++ + + + + + | + */ |
+ +643 + | ++ + + + + + | + public RelyingPartyV2Builder<C> attestationConveyancePreference( |
+ +644 + | +
+
+1
+
+1. attestationConveyancePreference : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationConveyancePreference> attestationConveyancePreference) { |
+ +645 + | ++ + + + + + | + this.attestationConveyancePreference = attestationConveyancePreference; |
+ +646 + | +
+
+1
+
+1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED + + + + |
+ return this; |
+ +647 + | ++ + + + + + | + } |
+ +648 + | ++ + + + + + | +|
+ +649 + | ++ + + + + + | + /** |
+ +650 + | ++ + + + + + | + * The argument for the {@link PublicKeyCredentialCreationOptions#getAttestation() attestation} |
+ +651 + | ++ + + + + + | + * parameter in registration operations. |
+ +652 + | ++ + + + + + | + * |
+ +653 + | ++ + + + + + | + * <p>Unless your application has a concrete policy for authenticator attestation, it is |
+ +654 + | ++ + + + + + | + * recommended to leave this parameter undefined. |
+ +655 + | ++ + + + + + | + * |
+ +656 + | ++ + + + + + | + * <p>If you set this, you may want to explicitly set {@link |
+ +657 + | ++ + + + + + | + * RelyingPartyV2Builder#allowUntrustedAttestation(boolean) allowUntrustedAttestation} and |
+ +658 + | ++ + + + + + | + * {@link RelyingPartyV2Builder#attestationTrustSource(AttestationTrustSource) |
+ +659 + | ++ + + + + + | + * attestationTrustSource} too. |
+ +660 + | ++ + + + + + | + * |
+ +661 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +662 + | ++ + + + + + | + * |
+ +663 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +664 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +665 + | ++ + + + + + | + * Attestation</a> |
+ +666 + | ++ + + + + + | + */ |
+ +667 + | ++ + + + + + | + public RelyingPartyV2Builder<C> attestationConveyancePreference( |
+ +668 + | +
+
+1
+
+1. attestationConveyancePreference : negated conditional → KILLED + + + + |
+ @NonNull AttestationConveyancePreference attestationConveyancePreference) { |
+ +669 + | +
+
+1
+
+1. attestationConveyancePreference : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationConveyancePreference → KILLED + + + + |
+ return this.attestationConveyancePreference(Optional.of(attestationConveyancePreference)); |
+ +670 + | ++ + + + + + | + } |
+ +671 + | ++ + + + + + | +|
+ +672 + | ++ + + + + + | + /** |
+ +673 + | ++ + + + + + | + * An {@link AttestationTrustSource} instance to use for looking up trust roots for |
+ +674 + | ++ + + + + + | + * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()} |
+ +675 + | ++ + + + + + | + * is non-empty and not set to {@link AttestationConveyancePreference#NONE}. |
+ +676 + | ++ + + + + + | + * |
+ +677 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +678 + | ++ + + + + + | + * |
+ +679 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +680 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +681 + | ++ + + + + + | + * Attestation</a> |
+ +682 + | ++ + + + + + | + */ |
+ +683 + | ++ + + + + + | + public RelyingPartyV2Builder<C> attestationTrustSource( |
+ +684 + | +
+
+1
+
+1. attestationTrustSource : negated conditional → KILLED + + + + |
+ @NonNull Optional<AttestationTrustSource> attestationTrustSource) { |
+ +685 + | ++ + + + + + | + this.attestationTrustSource = attestationTrustSource; |
+ +686 + | +
+
+1
+
+1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED + + + + |
+ return this; |
+ +687 + | ++ + + + + + | + } |
+ +688 + | ++ + + + + + | +|
+ +689 + | ++ + + + + + | + /** |
+ +690 + | ++ + + + + + | + * An {@link AttestationTrustSource} instance to use for looking up trust roots for |
+ +691 + | ++ + + + + + | + * authenticator attestation. This matters only if {@link #getAttestationConveyancePreference()} |
+ +692 + | ++ + + + + + | + * is non-empty and not set to {@link AttestationConveyancePreference#NONE}. |
+ +693 + | ++ + + + + + | + * |
+ +694 + | ++ + + + + + | + * <p>By default, this is not set. |
+ +695 + | ++ + + + + + | + * |
+ +696 + | ++ + + + + + | + * @see PublicKeyCredentialCreationOptions#getAttestation() |
+ +697 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-attestation">§6.4. |
+ +698 + | ++ + + + + + | + * Attestation</a> |
+ +699 + | ++ + + + + + | + */ |
+ +700 + | ++ + + + + + | + public RelyingPartyV2Builder<C> attestationTrustSource( |
+ +701 + | +
+
+1
+
+1. attestationTrustSource : negated conditional → KILLED + + + + |
+ @NonNull AttestationTrustSource attestationTrustSource) { |
+ +702 + | +
+
+1
+
+1. attestationTrustSource : replaced return value with null for com/yubico/webauthn/RelyingPartyV2$RelyingPartyV2Builder::attestationTrustSource → KILLED + + + + |
+ return this.attestationTrustSource(Optional.of(attestationTrustSource)); |
+ +703 + | ++ + + + + + | + } |
+ +704 + | ++ + + + + + | + } |
+ +705 + | ++ + + + + + | +} |
Mutations | ||
372 | ++ |
+
+
+
+ 1.1 |
+
374 | ++ |
+
+
+
+ 1.1 |
+
376 | ++ |
+
+
+
+ 1.1 |
+
377 | ++ |
+
+
+
+ 1.1 |
+
378 | ++ |
+
+
+
+ 1.1 |
+
387 | ++ |
+
+
+
+ 1.1 |
+
416 | ++ |
+
+
+
+ 1.1 |
+
417 | ++ |
+
+
+
+ 1.1 |
+
428 | ++ |
+
+
+
+ 1.1 |
+
456 | ++ |
+
+
+
+ 1.1 |
+
457 | ++ |
+
+
+
+ 1.1 |
+
463 | ++ |
+
+
+
+ 1.1 |
+
477 | ++ |
+
+
+
+ 1.1 |
+
481 | ++ |
+
+
+
+ 1.1 2.2 |
+
494 | ++ |
+
+
+
+ 1.1 |
+
497 | ++ |
+
+
+
+ 1.1 |
+
507 | ++ |
+
+
+
+ 1.1 |
+
514 | ++ |
+
+
+
+ 1.1 |
+
516 | ++ |
+
+
+
+ 1.1 |
+
534 | ++ |
+
+
+
+ 1.1 |
+
548 | ++ |
+
+
+
+ 1.1 |
+
553 | ++ |
+
+
+
+ 1.1 |
+
590 | ++ |
+
+
+
+ 1.1 |
+
592 | ++ |
+
+
+
+ 1.1 |
+
621 | ++ |
+
+
+
+ 1.1 |
+
622 | ++ |
+
+
+
+ 1.1 |
+
644 | ++ |
+
+
+
+ 1.1 |
+
646 | ++ |
+
+
+
+ 1.1 |
+
668 | ++ |
+
+
+
+ 1.1 |
+
669 | ++ |
+
+
+
+ 1.1 |
+
684 | ++ |
+
+
+
+ 1.1 |
+
686 | ++ |
+
+
+
+ 1.1 |
+
701 | ++ |
+
+
+
+ 1.1 |
+
702 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.webauthn.data.AssertionExtensionInputs; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.UserVerificationRequirement; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import lombok.Builder; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +import lombok.Value; |
+ +35 + | ++ + + + + + | +|
+ +36 + | ++ + + + + + | +/** Parameters for {@link RelyingParty#startAssertion(StartAssertionOptions)}. */ |
+ +37 + | ++ + + + + + | +@Value |
+ +38 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +39 + | ++ + + + + + | +public class StartAssertionOptions { |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | + private final String username; |
+ +42 + | ++ + + + + + | +|
+ +43 + | ++ + + + + + | + private final ByteArray userHandle; |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + /** |
+ +46 + | ++ + + + + + | + * Extension inputs for this authentication operation. |
+ +47 + | ++ + + + + + | + * |
+ +48 + | ++ + + + + + | + * <p>If {@link RelyingParty#getAppId()} is set, {@link |
+ +49 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will overwrite any {@link |
+ +50 + | ++ + + + + + | + * AssertionExtensionInputs#getAppid() appId} extension input set herein. |
+ +51 + | ++ + + + + + | + * |
+ +52 + | ++ + + + + + | + * <p>The default specifies no extension inputs. |
+ +53 + | ++ + + + + + | + */ |
+ +54 + | ++ + + + + + | + @NonNull @Builder.Default |
+ +55 + | ++ + + + + + | + private final AssertionExtensionInputs extensions = AssertionExtensionInputs.builder().build(); |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | + /** |
+ +58 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getUserVerification()} for this |
+ +59 + | ++ + + + + + | + * authentication operation. |
+ +60 + | ++ + + + + + | + * |
+ +61 + | ++ + + + + + | + * <p>If set to {@link UserVerificationRequirement#REQUIRED}, then {@link |
+ +62 + | ++ + + + + + | + * RelyingParty#finishAssertion(FinishAssertionOptions)} will enforce that <a |
+ +63 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408#user-verification">user |
+ +64 + | ++ + + + + + | + * verification</a>was performed in this authentication ceremony. |
+ +65 + | ++ + + + + + | + * |
+ +66 + | ++ + + + + + | + * <p>The default is {@link UserVerificationRequirement#PREFERRED}. |
+ +67 + | ++ + + + + + | + */ |
+ +68 + | ++ + + + + + | + private final UserVerificationRequirement userVerification; |
+ +69 + | ++ + + + + + | +|
+ +70 + | ++ + + + + + | + /** |
+ +71 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication |
+ +72 + | ++ + + + + + | + * operation. |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +75 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialRequestOptions} so it can be used as an argument to |
+ +76 + | ++ + + + + + | + * <code>navigator.credentials.get()</code> on the client side. |
+ +77 + | ++ + + + + + | + * |
+ +78 + | ++ + + + + + | + * <p>The default is empty. |
+ +79 + | ++ + + + + + | + */ |
+ +80 + | ++ + + + + + | + private final Long timeout; |
+ +81 + | ++ + + + + + | +|
+ +82 + | ++ + + + + + | + /** |
+ +83 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +84 + | ++ + + + + + | + * |
+ +85 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #getUserHandle()}. |
+ +86 + | ++ + + + + + | + * |
+ +87 + | ++ + + + + + | + * <p>If this or {@link #getUserHandle()} is present, then {@link |
+ +88 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link |
+ +89 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's |
+ +90 + | ++ + + + + + | + * credentials. |
+ +91 + | ++ + + + + + | + * |
+ +92 + | ++ + + + + + | + * <p>If this and {@link #getUserHandle()} are both absent, that implies authentication with a |
+ +93 + | ++ + + + + + | + * discoverable credential (passkey) - meaning identification of the user is deferred until after |
+ +94 + | ++ + + + + + | + * receiving the response from the client. |
+ +95 + | ++ + + + + + | + * |
+ +96 + | ++ + + + + + | + * <p>The default is empty (absent). |
+ +97 + | ++ + + + + + | + * |
+ +98 + | ++ + + + + + | + * @see <a |
+ +99 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-discoverable |
+ +100 + | ++ + + + + + | + * credential</a> |
+ +101 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +102 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +103 + | ++ + + + + + | + */ |
+ +104 + | ++ + + + + + | + public Optional<String> getUsername() { |
+ +105 + | +
+
+1
+
+1. getUsername : replaced return value with Optional.empty for com/yubico/webauthn/StartAssertionOptions::getUsername → KILLED + + + + |
+ return Optional.ofNullable(username); |
+ +106 + | ++ + + + + + | + } |
+ +107 + | ++ + + + + + | +|
+ +108 + | ++ + + + + + | + /** |
+ +109 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +110 + | ++ + + + + + | + * |
+ +111 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #getUsername()}. |
+ +112 + | ++ + + + + + | + * |
+ +113 + | ++ + + + + + | + * <p>If this or {@link #getUsername()} is present, then {@link |
+ +114 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link |
+ +115 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's |
+ +116 + | ++ + + + + + | + * credentials. |
+ +117 + | ++ + + + + + | + * |
+ +118 + | ++ + + + + + | + * <p>If this and {@link #getUsername()} are both absent, that implies authentication with a |
+ +119 + | ++ + + + + + | + * discoverable credential (passkey) - meaning identification of the user is deferred until after |
+ +120 + | ++ + + + + + | + * receiving the response from the client. |
+ +121 + | ++ + + + + + | + * |
+ +122 + | ++ + + + + + | + * <p>The default is empty (absent). |
+ +123 + | ++ + + + + + | + * |
+ +124 + | ++ + + + + + | + * @see #getUsername() |
+ +125 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User Handle</a> |
+ +126 + | ++ + + + + + | + * @see <a |
+ +127 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-discoverable |
+ +128 + | ++ + + + + + | + * credential</a> |
+ +129 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +130 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +131 + | ++ + + + + + | + */ |
+ +132 + | ++ + + + + + | + public Optional<ByteArray> getUserHandle() { |
+ +133 + | +
+
+1
+
+1. getUserHandle : replaced return value with Optional.empty for com/yubico/webauthn/StartAssertionOptions::getUserHandle → KILLED + + + + |
+ return Optional.ofNullable(userHandle); |
+ +134 + | ++ + + + + + | + } |
+ +135 + | ++ + + + + + | +|
+ +136 + | ++ + + + + + | + /** |
+ +137 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getUserVerification()} for this |
+ +138 + | ++ + + + + + | + * authentication operation. |
+ +139 + | ++ + + + + + | + * |
+ +140 + | ++ + + + + + | + * <p>If set to {@link UserVerificationRequirement#REQUIRED}, then {@link |
+ +141 + | ++ + + + + + | + * RelyingParty#finishAssertion(FinishAssertionOptions)} will enforce that <a |
+ +142 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408#user-verification">user |
+ +143 + | ++ + + + + + | + * verification</a>was performed in this authentication ceremony. |
+ +144 + | ++ + + + + + | + * |
+ +145 + | ++ + + + + + | + * <p>The default is {@link UserVerificationRequirement#PREFERRED}. |
+ +146 + | ++ + + + + + | + */ |
+ +147 + | ++ + + + + + | + public Optional<UserVerificationRequirement> getUserVerification() { |
+ +148 + | +
+
+1
+
+1. getUserVerification : replaced return value with Optional.empty for com/yubico/webauthn/StartAssertionOptions::getUserVerification → KILLED + + + + |
+ return Optional.ofNullable(userVerification); |
+ +149 + | ++ + + + + + | + } |
+ +150 + | ++ + + + + + | +|
+ +151 + | ++ + + + + + | + /** |
+ +152 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication |
+ +153 + | ++ + + + + + | + * operation. |
+ +154 + | ++ + + + + + | + * |
+ +155 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +156 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialRequestOptions} so it can be used as an argument to |
+ +157 + | ++ + + + + + | + * <code>navigator.credentials.get()</code> on the client side. |
+ +158 + | ++ + + + + + | + * |
+ +159 + | ++ + + + + + | + * <p>The default is empty. |
+ +160 + | ++ + + + + + | + */ |
+ +161 + | ++ + + + + + | + public Optional<Long> getTimeout() { |
+ +162 + | +
+
+1
+
+1. getTimeout : replaced return value with Optional.empty for com/yubico/webauthn/StartAssertionOptions::getTimeout → KILLED + + + + |
+ return Optional.ofNullable(timeout); |
+ +163 + | ++ + + + + + | + } |
+ +164 + | ++ + + + + + | +|
+ +165 + | ++ + + + + + | + public static class StartAssertionOptionsBuilder { |
+ +166 + | ++ + + + + + | + private String username = null; |
+ +167 + | ++ + + + + + | + private ByteArray userHandle = null; |
+ +168 + | ++ + + + + + | + private UserVerificationRequirement userVerification = null; |
+ +169 + | ++ + + + + + | + private Long timeout = null; |
+ +170 + | ++ + + + + + | +|
+ +171 + | ++ + + + + + | + /** |
+ +172 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +173 + | ++ + + + + + | + * |
+ +174 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #userHandle(Optional)}. Setting this to a present value |
+ +175 + | ++ + + + + + | + * will set {@link #userHandle(Optional)} to empty. |
+ +176 + | ++ + + + + + | + * |
+ +177 + | ++ + + + + + | + * <p>If this or {@link #userHandle(Optional)} is present, then {@link |
+ +178 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link |
+ +179 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's |
+ +180 + | ++ + + + + + | + * credentials. |
+ +181 + | ++ + + + + + | + * |
+ +182 + | ++ + + + + + | + * <p>If this and {@link #getUserHandle()} are both absent, that implies authentication with a |
+ +183 + | ++ + + + + + | + * discoverable credential (passkey) - meaning identification of the user is deferred until |
+ +184 + | ++ + + + + + | + * after receiving the response from the client. |
+ +185 + | ++ + + + + + | + * |
+ +186 + | ++ + + + + + | + * <p>The default is empty (absent). |
+ +187 + | ++ + + + + + | + * |
+ +188 + | ++ + + + + + | + * @see #username(String) |
+ +189 + | ++ + + + + + | + * @see #userHandle(Optional) |
+ +190 + | ++ + + + + + | + * @see #userHandle(ByteArray) |
+ +191 + | ++ + + + + + | + * @see <a |
+ +192 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-discoverable |
+ +193 + | ++ + + + + + | + * credential</a> |
+ +194 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +195 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +196 + | ++ + + + + + | + */ |
+ +197 + | +
+
+1
+
+1. username : negated conditional → KILLED + + + + |
+ public StartAssertionOptionsBuilder username(@NonNull Optional<String> username) { |
+ +198 + | ++ + + + + + | + this.username = username.orElse(null); |
+ +199 + | +
+
+1
+
+1. username : negated conditional → KILLED + + + + |
+ if (username.isPresent()) { |
+ +200 + | ++ + + + + + | + this.userHandle = null; |
+ +201 + | ++ + + + + + | + } |
+ +202 + | +
+
+1
+
+1. username : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::username → KILLED + + + + |
+ return this; |
+ +203 + | ++ + + + + + | + } |
+ +204 + | ++ + + + + + | +|
+ +205 + | ++ + + + + + | + /** |
+ +206 + | ++ + + + + + | + * The username of the user to authenticate, if the user has already been identified. |
+ +207 + | ++ + + + + + | + * |
+ +208 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #userHandle(Optional)}. Setting this to a non-null value |
+ +209 + | ++ + + + + + | + * will set {@link #userHandle(Optional)} to empty. |
+ +210 + | ++ + + + + + | + * |
+ +211 + | ++ + + + + + | + * <p>If this or {@link #userHandle(Optional)} is present, then {@link |
+ +212 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link |
+ +213 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's |
+ +214 + | ++ + + + + + | + * credentials. |
+ +215 + | ++ + + + + + | + * |
+ +216 + | ++ + + + + + | + * <p>If this and {@link #getUserHandle()} are both absent, that implies authentication with a |
+ +217 + | ++ + + + + + | + * discoverable credential (passkey) - meaning identification of the user is deferred until |
+ +218 + | ++ + + + + + | + * after receiving the response from the client. |
+ +219 + | ++ + + + + + | + * |
+ +220 + | ++ + + + + + | + * <p>The default is empty (absent). |
+ +221 + | ++ + + + + + | + * |
+ +222 + | ++ + + + + + | + * @see #username(Optional) |
+ +223 + | ++ + + + + + | + * @see #userHandle(Optional) |
+ +224 + | ++ + + + + + | + * @see #userHandle(ByteArray) |
+ +225 + | ++ + + + + + | + * @see <a |
+ +226 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-discoverable |
+ +227 + | ++ + + + + + | + * credential</a> |
+ +228 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +229 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +230 + | ++ + + + + + | + */ |
+ +231 + | ++ + + + + + | + public StartAssertionOptionsBuilder username(String username) { |
+ +232 + | +
+
+1
+
+1. username : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::username → KILLED + + + + |
+ return this.username(Optional.ofNullable(username)); |
+ +233 + | ++ + + + + + | + } |
+ +234 + | ++ + + + + + | +|
+ +235 + | ++ + + + + + | + /** |
+ +236 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +237 + | ++ + + + + + | + * |
+ +238 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #username(Optional)}. Setting this to a present value will |
+ +239 + | ++ + + + + + | + * set {@link #username(Optional)} to empty. |
+ +240 + | ++ + + + + + | + * |
+ +241 + | ++ + + + + + | + * <p>If this or {@link #username(Optional)} is present, then {@link |
+ +242 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link |
+ +243 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's |
+ +244 + | ++ + + + + + | + * credentials. |
+ +245 + | ++ + + + + + | + * |
+ +246 + | ++ + + + + + | + * <p>If this and {@link #getUsername()} are both absent, that implies authentication with a |
+ +247 + | ++ + + + + + | + * discoverable credential (passkey) - meaning identification of the user is deferred until |
+ +248 + | ++ + + + + + | + * after receiving the response from the client. |
+ +249 + | ++ + + + + + | + * |
+ +250 + | ++ + + + + + | + * <p>The default is empty (absent). |
+ +251 + | ++ + + + + + | + * |
+ +252 + | ++ + + + + + | + * @see #username(String) |
+ +253 + | ++ + + + + + | + * @see #username(Optional) |
+ +254 + | ++ + + + + + | + * @see #userHandle(ByteArray) |
+ +255 + | ++ + + + + + | + * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User |
+ +256 + | ++ + + + + + | + * Handle</a> |
+ +257 + | ++ + + + + + | + * @see <a |
+ +258 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-discoverable |
+ +259 + | ++ + + + + + | + * credential</a> |
+ +260 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +261 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +262 + | ++ + + + + + | + */ |
+ +263 + | +
+
+1
+
+1. userHandle : negated conditional → KILLED + + + + |
+ public StartAssertionOptionsBuilder userHandle(@NonNull Optional<ByteArray> userHandle) { |
+ +264 + | ++ + + + + + | + this.userHandle = userHandle.orElse(null); |
+ +265 + | +
+
+1
+
+1. userHandle : negated conditional → KILLED + + + + |
+ if (userHandle.isPresent()) { |
+ +266 + | ++ + + + + + | + this.username = null; |
+ +267 + | ++ + + + + + | + } |
+ +268 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::userHandle → KILLED + + + + |
+ return this; |
+ +269 + | ++ + + + + + | + } |
+ +270 + | ++ + + + + + | +|
+ +271 + | ++ + + + + + | + /** |
+ +272 + | ++ + + + + + | + * The user handle of the user to authenticate, if the user has already been identified. |
+ +273 + | ++ + + + + + | + * |
+ +274 + | ++ + + + + + | + * <p>Mutually exclusive with {@link #username(Optional)}. Setting this to a non-null value will |
+ +275 + | ++ + + + + + | + * set {@link #username(Optional)} to empty. |
+ +276 + | ++ + + + + + | + * |
+ +277 + | ++ + + + + + | + * <p>If this or {@link #username(Optional)} is present, then {@link |
+ +278 + | ++ + + + + + | + * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link |
+ +279 + | ++ + + + + + | + * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's |
+ +280 + | ++ + + + + + | + * credentials. |
+ +281 + | ++ + + + + + | + * |
+ +282 + | ++ + + + + + | + * <p>If this and {@link #getUsername()} are both absent, that implies authentication with a |
+ +283 + | ++ + + + + + | + * discoverable credential (passkey) - meaning identification of the user is deferred until |
+ +284 + | ++ + + + + + | + * after receiving the response from the client. |
+ +285 + | ++ + + + + + | + * |
+ +286 + | ++ + + + + + | + * <p>The default is empty (absent). |
+ +287 + | ++ + + + + + | + * |
+ +288 + | ++ + + + + + | + * @see #username(String) |
+ +289 + | ++ + + + + + | + * @see #username(Optional) |
+ +290 + | ++ + + + + + | + * @see #userHandle(Optional) |
+ +291 + | ++ + + + + + | + * @see <a |
+ +292 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-discoverable |
+ +293 + | ++ + + + + + | + * credential</a> |
+ +294 + | ++ + + + + + | + * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a |
+ +295 + | ++ + + + + + | + * href="https://passkeys.dev">passkeys.dev</a> reference |
+ +296 + | ++ + + + + + | + */ |
+ +297 + | ++ + + + + + | + public StartAssertionOptionsBuilder userHandle(ByteArray userHandle) { |
+ +298 + | +
+
+1
+
+1. userHandle : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::userHandle → KILLED + + + + |
+ return this.userHandle(Optional.ofNullable(userHandle)); |
+ +299 + | ++ + + + + + | + } |
+ +300 + | ++ + + + + + | +|
+ +301 + | ++ + + + + + | + /** |
+ +302 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getUserVerification()} for this |
+ +303 + | ++ + + + + + | + * authentication operation. |
+ +304 + | ++ + + + + + | + * |
+ +305 + | ++ + + + + + | + * <p>If set to {@link UserVerificationRequirement#REQUIRED}, then {@link |
+ +306 + | ++ + + + + + | + * RelyingParty#finishAssertion(FinishAssertionOptions)} will enforce that <a |
+ +307 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">user |
+ +308 + | ++ + + + + + | + * verification</a>was performed in this authentication ceremony. |
+ +309 + | ++ + + + + + | + * |
+ +310 + | ++ + + + + + | + * <p>The default is {@link UserVerificationRequirement#PREFERRED}. |
+ +311 + | ++ + + + + + | + */ |
+ +312 + | ++ + + + + + | + public StartAssertionOptionsBuilder userVerification( |
+ +313 + | +
+
+1
+
+1. userVerification : negated conditional → KILLED + + + + |
+ @NonNull Optional<UserVerificationRequirement> userVerification) { |
+ +314 + | ++ + + + + + | + this.userVerification = userVerification.orElse(null); |
+ +315 + | +
+
+1
+
+1. userVerification : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::userVerification → KILLED + + + + |
+ return this; |
+ +316 + | ++ + + + + + | + } |
+ +317 + | ++ + + + + + | +|
+ +318 + | ++ + + + + + | + /** |
+ +319 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getUserVerification()} for this |
+ +320 + | ++ + + + + + | + * authentication operation. |
+ +321 + | ++ + + + + + | + * |
+ +322 + | ++ + + + + + | + * <p>If set to {@link UserVerificationRequirement#REQUIRED}, then {@link |
+ +323 + | ++ + + + + + | + * RelyingParty#finishAssertion(FinishAssertionOptions)} will enforce that <a |
+ +324 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-verification">user |
+ +325 + | ++ + + + + + | + * verification</a>was performed in this authentication ceremony. |
+ +326 + | ++ + + + + + | + * |
+ +327 + | ++ + + + + + | + * <p>The default is {@link UserVerificationRequirement#PREFERRED}. |
+ +328 + | ++ + + + + + | + */ |
+ +329 + | ++ + + + + + | + public StartAssertionOptionsBuilder userVerification( |
+ +330 + | ++ + + + + + | + UserVerificationRequirement userVerification) { |
+ +331 + | +
+
+1
+
+1. userVerification : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::userVerification → KILLED + + + + |
+ return this.userVerification(Optional.ofNullable(userVerification)); |
+ +332 + | ++ + + + + + | + } |
+ +333 + | ++ + + + + + | +|
+ +334 + | ++ + + + + + | + /** |
+ +335 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication |
+ +336 + | ++ + + + + + | + * operation. |
+ +337 + | ++ + + + + + | + * |
+ +338 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +339 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialRequestOptions} so it can be used as an argument to |
+ +340 + | ++ + + + + + | + * <code>navigator.credentials.get()</code> on the client side. |
+ +341 + | ++ + + + + + | + * |
+ +342 + | ++ + + + + + | + * <p>The default is empty. |
+ +343 + | ++ + + + + + | + */ |
+ +344 + | +
+
+1
+
+1. timeout : negated conditional → KILLED + + + + |
+ public StartAssertionOptionsBuilder timeout(@NonNull Optional<Long> timeout) { |
+ +345 + | +
+
+3
+
+1. timeout : negated conditional → KILLED +2. timeout : changed conditional boundary → KILLED +3. timeout : negated conditional → KILLED + + + + |
+ if (timeout.isPresent() && timeout.get() <= 0) { |
+ +346 + | ++ + + + + + | + throw new IllegalArgumentException("timeout must be positive, was: " + timeout.get()); |
+ +347 + | ++ + + + + + | + } |
+ +348 + | ++ + + + + + | + this.timeout = timeout.orElse(null); |
+ +349 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::timeout → KILLED + + + + |
+ return this; |
+ +350 + | ++ + + + + + | + } |
+ +351 + | ++ + + + + + | +|
+ +352 + | ++ + + + + + | + /** |
+ +353 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication |
+ +354 + | ++ + + + + + | + * operation. |
+ +355 + | ++ + + + + + | + * |
+ +356 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +357 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialRequestOptions} so it can be used as an argument to |
+ +358 + | ++ + + + + + | + * <code>navigator.credentials.get()</code> on the client side. |
+ +359 + | ++ + + + + + | + * |
+ +360 + | ++ + + + + + | + * <p>The default is empty. |
+ +361 + | ++ + + + + + | + */ |
+ +362 + | ++ + + + + + | + public StartAssertionOptionsBuilder timeout(long timeout) { |
+ +363 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::timeout → KILLED + + + + |
+ return this.timeout(Optional.of(timeout)); |
+ +364 + | ++ + + + + + | + } |
+ +365 + | ++ + + + + + | +|
+ +366 + | ++ + + + + + | + /* |
+ +367 + | ++ + + + + + | + * Workaround, see: https://github.com/rzwitserloot/lombok/issues/2623#issuecomment-714816001 |
+ +368 + | ++ + + + + + | + * Consider reverting this workaround if Lombok fixes that issue. |
+ +369 + | ++ + + + + + | + */ |
+ +370 + | ++ + + + + + | + private StartAssertionOptionsBuilder timeout(Long timeout) { |
+ +371 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/StartAssertionOptions$StartAssertionOptionsBuilder::timeout → KILLED + + + + |
+ return this.timeout(Optional.ofNullable(timeout)); |
+ +372 + | ++ + + + + + | + } |
+ +373 + | ++ + + + + + | + } |
+ +374 + | ++ + + + + + | +} |
Mutations | ||
105 | ++ |
+
+
+
+ 1.1 |
+
133 | ++ |
+
+
+
+ 1.1 |
+
148 | ++ |
+
+
+
+ 1.1 |
+
162 | ++ |
+
+
+
+ 1.1 |
+
197 | ++ |
+
+
+
+ 1.1 |
+
199 | ++ |
+
+
+
+ 1.1 |
+
202 | ++ |
+
+
+
+ 1.1 |
+
232 | ++ |
+
+
+
+ 1.1 |
+
263 | ++ |
+
+
+
+ 1.1 |
+
265 | ++ |
+
+
+
+ 1.1 |
+
268 | ++ |
+
+
+
+ 1.1 |
+
298 | ++ |
+
+
+
+ 1.1 |
+
313 | ++ |
+
+
+
+ 1.1 |
+
315 | ++ |
+
+
+
+ 1.1 |
+
331 | ++ |
+
+
+
+ 1.1 |
+
344 | ++ |
+
+
+
+ 1.1 |
+
345 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
349 | ++ |
+
+
+
+ 1.1 |
+
363 | ++ |
+
+
+
+ 1.1 |
+
371 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.webauthn.data.AuthenticatorSelectionCriteria; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.RegistrationExtensionInputs; |
+ +30 + | ++ + + + + + | +import com.yubico.webauthn.data.UserIdentity; |
+ +31 + | ++ + + + + + | +import java.util.Optional; |
+ +32 + | ++ + + + + + | +import lombok.Builder; |
+ +33 + | ++ + + + + + | +import lombok.NonNull; |
+ +34 + | ++ + + + + + | +import lombok.Value; |
+ +35 + | ++ + + + + + | +|
+ +36 + | ++ + + + + + | +/** Parameters for {@link RelyingParty#startRegistration(StartRegistrationOptions)}. */ |
+ +37 + | ++ + + + + + | +@Value |
+ +38 + | ++ + + + + + | +@Builder(toBuilder = true) |
+ +39 + | ++ + + + + + | +public class StartRegistrationOptions { |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | + /** Identifiers for the user creating a credential. */ |
+ +42 + | ++ + + + + + | + @NonNull private final UserIdentity user; |
+ +43 + | ++ + + + + + | +|
+ +44 + | ++ + + + + + | + /** |
+ +45 + | ++ + + + + + | + * Constraints on what kind of authenticator the user is allowed to use to create the credential, |
+ +46 + | ++ + + + + + | + * and on features that authenticator must or should support. |
+ +47 + | ++ + + + + + | + */ |
+ +48 + | ++ + + + + + | + private final AuthenticatorSelectionCriteria authenticatorSelection; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + /** Extension inputs for this registration operation. */ |
+ +51 + | ++ + + + + + | + @NonNull @Builder.Default |
+ +52 + | ++ + + + + + | + private final RegistrationExtensionInputs extensions = |
+ +53 + | ++ + + + + + | + RegistrationExtensionInputs.builder().build(); |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | + /** |
+ +56 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration |
+ +57 + | ++ + + + + + | + * operation. |
+ +58 + | ++ + + + + + | + * |
+ +59 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +60 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialCreationOptions} so it can be used as an argument to |
+ +61 + | ++ + + + + + | + * <code>navigator.credentials.create()</code> on the client side. |
+ +62 + | ++ + + + + + | + * |
+ +63 + | ++ + + + + + | + * <p>The default is empty. |
+ +64 + | ++ + + + + + | + */ |
+ +65 + | ++ + + + + + | + private final Long timeout; |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + /** |
+ +68 + | ++ + + + + + | + * Constraints on what kind of authenticator the user is allowed to use to create the credential, |
+ +69 + | ++ + + + + + | + * and on features that authenticator must or should support. |
+ +70 + | ++ + + + + + | + */ |
+ +71 + | ++ + + + + + | + public Optional<AuthenticatorSelectionCriteria> getAuthenticatorSelection() { |
+ +72 + | +
+
+1
+
+1. getAuthenticatorSelection : replaced return value with Optional.empty for com/yubico/webauthn/StartRegistrationOptions::getAuthenticatorSelection → KILLED + + + + |
+ return Optional.ofNullable(authenticatorSelection); |
+ +73 + | ++ + + + + + | + } |
+ +74 + | ++ + + + + + | +|
+ +75 + | ++ + + + + + | + /** |
+ +76 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration |
+ +77 + | ++ + + + + + | + * operation. |
+ +78 + | ++ + + + + + | + * |
+ +79 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +80 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialCreationOptions} so it can be used as an argument to |
+ +81 + | ++ + + + + + | + * <code>navigator.credentials.create()</code> on the client side. |
+ +82 + | ++ + + + + + | + * |
+ +83 + | ++ + + + + + | + * <p>The default is empty. |
+ +84 + | ++ + + + + + | + */ |
+ +85 + | ++ + + + + + | + public Optional<Long> getTimeout() { |
+ +86 + | +
+
+1
+
+1. getTimeout : replaced return value with Optional.empty for com/yubico/webauthn/StartRegistrationOptions::getTimeout → KILLED + + + + |
+ return Optional.ofNullable(timeout); |
+ +87 + | ++ + + + + + | + } |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + public static StartRegistrationOptionsBuilder.MandatoryStages builder() { |
+ +90 + | +
+
+1
+
+1. builder : replaced return value with null for com/yubico/webauthn/StartRegistrationOptions::builder → KILLED + + + + |
+ return new StartRegistrationOptionsBuilder.MandatoryStages(); |
+ +91 + | ++ + + + + + | + } |
+ +92 + | ++ + + + + + | +|
+ +93 + | ++ + + + + + | + public static class StartRegistrationOptionsBuilder { |
+ +94 + | ++ + + + + + | + private AuthenticatorSelectionCriteria authenticatorSelection = null; |
+ +95 + | ++ + + + + + | + private Long timeout = null; |
+ +96 + | ++ + + + + + | +|
+ +97 + | ++ + + + + + | + public static class MandatoryStages { |
+ +98 + | ++ + + + + + | + private final StartRegistrationOptionsBuilder builder = new StartRegistrationOptionsBuilder(); |
+ +99 + | ++ + + + + + | +|
+ +100 + | ++ + + + + + | + /** |
+ +101 + | ++ + + + + + | + * {@link StartRegistrationOptionsBuilder#user(UserIdentity) user} is a required parameter. |
+ +102 + | ++ + + + + + | + * |
+ +103 + | ++ + + + + + | + * @see StartRegistrationOptionsBuilder#user(UserIdentity) |
+ +104 + | ++ + + + + + | + */ |
+ +105 + | ++ + + + + + | + public StartRegistrationOptionsBuilder user(UserIdentity user) { |
+ +106 + | +
+
+1
+
+1. user : replaced return value with null for com/yubico/webauthn/StartRegistrationOptions$StartRegistrationOptionsBuilder$MandatoryStages::user → KILLED + + + + |
+ return builder.user(user); |
+ +107 + | ++ + + + + + | + } |
+ +108 + | ++ + + + + + | + } |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + /** |
+ +111 + | ++ + + + + + | + * Constraints on what kind of authenticator the user is allowed to use to create the |
+ +112 + | ++ + + + + + | + * credential, and on features that authenticator must or should support. |
+ +113 + | ++ + + + + + | + */ |
+ +114 + | ++ + + + + + | + public StartRegistrationOptionsBuilder authenticatorSelection( |
+ +115 + | +
+
+1
+
+1. authenticatorSelection : negated conditional → KILLED + + + + |
+ @NonNull Optional<AuthenticatorSelectionCriteria> authenticatorSelection) { |
+ +116 + | +
+
+1
+
+1. authenticatorSelection : replaced return value with null for com/yubico/webauthn/StartRegistrationOptions$StartRegistrationOptionsBuilder::authenticatorSelection → KILLED + + + + |
+ return this.authenticatorSelection(authenticatorSelection.orElse(null)); |
+ +117 + | ++ + + + + + | + } |
+ +118 + | ++ + + + + + | +|
+ +119 + | ++ + + + + + | + /** |
+ +120 + | ++ + + + + + | + * Constraints on what kind of authenticator the user is allowed to use to create the |
+ +121 + | ++ + + + + + | + * credential, and on features that authenticator must or should support. |
+ +122 + | ++ + + + + + | + */ |
+ +123 + | ++ + + + + + | + public StartRegistrationOptionsBuilder authenticatorSelection( |
+ +124 + | ++ + + + + + | + AuthenticatorSelectionCriteria authenticatorSelection) { |
+ +125 + | ++ + + + + + | + this.authenticatorSelection = authenticatorSelection; |
+ +126 + | +
+
+1
+
+1. authenticatorSelection : replaced return value with null for com/yubico/webauthn/StartRegistrationOptions$StartRegistrationOptionsBuilder::authenticatorSelection → KILLED + + + + |
+ return this; |
+ +127 + | ++ + + + + + | + } |
+ +128 + | ++ + + + + + | +|
+ +129 + | ++ + + + + + | + /** |
+ +130 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration |
+ +131 + | ++ + + + + + | + * operation. |
+ +132 + | ++ + + + + + | + * |
+ +133 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +134 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialCreationOptions} so it can be used as an argument to |
+ +135 + | ++ + + + + + | + * <code>navigator.credentials.create()</code> on the client side. |
+ +136 + | ++ + + + + + | + * |
+ +137 + | ++ + + + + + | + * <p>The default is empty. |
+ +138 + | ++ + + + + + | + */ |
+ +139 + | +
+
+1
+
+1. timeout : negated conditional → KILLED + + + + |
+ public StartRegistrationOptionsBuilder timeout(@NonNull Optional<Long> timeout) { |
+ +140 + | +
+
+3
+
+1. timeout : negated conditional → KILLED +2. timeout : changed conditional boundary → KILLED +3. timeout : negated conditional → KILLED + + + + |
+ if (timeout.isPresent() && timeout.get() <= 0) { |
+ +141 + | ++ + + + + + | + throw new IllegalArgumentException("timeout must be positive, was: " + timeout.get()); |
+ +142 + | ++ + + + + + | + } |
+ +143 + | ++ + + + + + | + this.timeout = timeout.orElse(null); |
+ +144 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/StartRegistrationOptions$StartRegistrationOptionsBuilder::timeout → KILLED + + + + |
+ return this; |
+ +145 + | ++ + + + + + | + } |
+ +146 + | ++ + + + + + | +|
+ +147 + | ++ + + + + + | + /** |
+ +148 + | ++ + + + + + | + * The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration |
+ +149 + | ++ + + + + + | + * operation. |
+ +150 + | ++ + + + + + | + * |
+ +151 + | ++ + + + + + | + * <p>This library does not take the timeout into account in any way, other than passing it |
+ +152 + | ++ + + + + + | + * through to the {@link PublicKeyCredentialCreationOptions} so it can be used as an argument to |
+ +153 + | ++ + + + + + | + * <code>navigator.credentials.create()</code> on the client side. |
+ +154 + | ++ + + + + + | + * |
+ +155 + | ++ + + + + + | + * <p>The default is empty. |
+ +156 + | ++ + + + + + | + */ |
+ +157 + | ++ + + + + + | + public StartRegistrationOptionsBuilder timeout(long timeout) { |
+ +158 + | +
+
+1
+
+1. timeout : replaced return value with null for com/yubico/webauthn/StartRegistrationOptions$StartRegistrationOptionsBuilder::timeout → KILLED + + + + |
+ return this.timeout(Optional.of(timeout)); |
+ +159 + | ++ + + + + + | + } |
+ +160 + | ++ + + + + + | + } |
+ +161 + | ++ + + + + + | +} |
Mutations | ||
72 | ++ |
+
+
+
+ 1.1 |
+
86 | ++ |
+
+
+
+ 1.1 |
+
90 | ++ |
+
+
+
+ 1.1 |
+
106 | ++ |
+
+
+
+ 1.1 |
+
115 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 |
+
126 | ++ |
+
+
+
+ 1.1 |
+
139 | ++ |
+
+
+
+ 1.1 |
+
140 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
144 | ++ |
+
+
+
+ 1.1 |
+
158 | ++ |
+
+
+
+ 1.1 |
+
+ +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.yubico.webauthn.data.ByteArray; |
+ +28 + | ++ + + + + + | +import com.yubico.webauthn.data.TokenBindingInfo; |
+ +29 + | ++ + + + + + | +import java.util.Optional; |
+ +30 + | ++ + + + + + | +|
+ +31 + | ++ + + + + + | +final class TokenBindingValidator { |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | + static boolean validate( |
+ +34 + | ++ + + + + + | + Optional<TokenBindingInfo> clientTokenBinding, Optional<ByteArray> rpTokenBindingId) { |
+ +35 + | +
+
+2
+
+1. validate : replaced boolean return with false for com/yubico/webauthn/TokenBindingValidator::validate → SURVIVED +2. validate : replaced boolean return with true for com/yubico/webauthn/TokenBindingValidator::validate → SURVIVED + + + + |
+ return rpTokenBindingId |
+ +36 + | ++ + + + + + | + .map( |
+ +37 + | ++ + + + + + | + rpToken -> |
+ +38 + | ++ + + + + + | + clientTokenBinding |
+ +39 + | ++ + + + + + | + .map( |
+ +40 + | ++ + + + + + | + tbi -> { |
+ +41 + | ++ + + + + + | + switch (tbi.getStatus()) { |
+ +42 + | ++ + + + + + | + case SUPPORTED: |
+ +43 + | ++ + + + + + | +|
+ +44 + | ++ + + + + + | + case PRESENT: |
+ +45 + | +
+
+2
+
+1. lambda$validate$2 : replaced Boolean return with False for com/yubico/webauthn/TokenBindingValidator::lambda$validate$2 → SURVIVED +2. lambda$validate$2 : replaced Boolean return with True for com/yubico/webauthn/TokenBindingValidator::lambda$validate$2 → SURVIVED + + + + |
+ return tbi.getId() |
+ +46 + | ++ + + + + + | + .map( |
+ +47 + | ++ + + + + + | + id -> { |
+ +48 + | +
+
+1
+
+1. lambda$validate$0 : negated conditional → KILLED + + + + |
+ if (id.equals(rpToken)) { |
+ +49 + | +
+
+1
+
+1. lambda$validate$0 : replaced Boolean return with False for com/yubico/webauthn/TokenBindingValidator::lambda$validate$0 → SURVIVED + + + + |
+ return true; |
+ +50 + | ++ + + + + + | + } else { |
+ +51 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +52 + | ++ + + + + + | + "Incorrect token binding ID."); |
+ +53 + | ++ + + + + + | + } |
+ +54 + | ++ + + + + + | + }) |
+ +55 + | ++ + + + + + | + .orElseThrow( |
+ +56 + | ++ + + + + + | + () -> |
+ +57 + | +
+
+1
+
+1. lambda$validate$1 : replaced return value with null for com/yubico/webauthn/TokenBindingValidator::lambda$validate$1 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +58 + | ++ + + + + + | + "Property \"id\" missing from \"tokenBinding\" object.")); |
+ +59 + | ++ + + + + + | + } |
+ +60 + | ++ + + + + + | + throw new RuntimeException( |
+ +61 + | ++ + + + + + | + "Unknown token binding status: " + tbi.getStatus()); |
+ +62 + | ++ + + + + + | + }) |
+ +63 + | +
+
+2
+
+1. lambda$validate$4 : replaced Boolean return with True for com/yubico/webauthn/TokenBindingValidator::lambda$validate$4 → SURVIVED +2. lambda$validate$4 : replaced Boolean return with False for com/yubico/webauthn/TokenBindingValidator::lambda$validate$4 → SURVIVED + + + + |
+ .orElseThrow( |
+ +64 + | ++ + + + + + | + () -> |
+ +65 + | +
+
+1
+
+1. lambda$validate$3 : replaced return value with null for com/yubico/webauthn/TokenBindingValidator::lambda$validate$3 → KILLED + + + + |
+ new IllegalArgumentException( |
+ +66 + | ++ + + + + + | + "Token binding ID set by RP but not by client."))) |
+ +67 + | ++ + + + + + | + .orElseGet( |
+ +68 + | ++ + + + + + | + () -> |
+ +69 + | ++ + + + + + | + clientTokenBinding |
+ +70 + | ++ + + + + + | + .map( |
+ +71 + | ++ + + + + + | + tbi -> { |
+ +72 + | ++ + + + + + | + switch (tbi.getStatus()) { |
+ +73 + | ++ + + + + + | + case SUPPORTED: |
+ +74 + | +
+
+1
+
+1. lambda$validate$5 : replaced Boolean return with False for com/yubico/webauthn/TokenBindingValidator::lambda$validate$5 → SURVIVED + + + + |
+ return true; |
+ +75 + | ++ + + + + + | +|
+ +76 + | ++ + + + + + | + case PRESENT: |
+ +77 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +78 + | ++ + + + + + | + "Token binding ID set by client but not by RP."); |
+ +79 + | ++ + + + + + | + } |
+ +80 + | ++ + + + + + | + throw new RuntimeException( |
+ +81 + | ++ + + + + + | + "Unknown token binding status: " + tbi.getStatus()); |
+ +82 + | ++ + + + + + | + }) |
+ +83 + | +
+
+2
+
+1. lambda$validate$6 : replaced Boolean return with False for com/yubico/webauthn/TokenBindingValidator::lambda$validate$6 → SURVIVED +2. lambda$validate$6 : replaced Boolean return with True for com/yubico/webauthn/TokenBindingValidator::lambda$validate$6 → SURVIVED + + + + |
+ .orElse(true)); |
+ +84 + | ++ + + + + + | + } |
+ +85 + | ++ + + + + + | +} |
Mutations | ||
35 | ++ |
+
+
+
+ 1.1 2.2 |
+
45 | ++ |
+
+
+
+ 1.1 2.2 |
+
48 | ++ |
+
+
+
+ 1.1 |
+
49 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 |
+
63 | ++ |
+
+
+
+ 1.1 2.2 |
+
65 | ++ |
+
+
+
+ 1.1 |
+
74 | ++ |
+
+
+
+ 1.1 |
+
83 | ++ |
+
+
+
+ 1.1 2.2 |
+
+ +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 COSE.CoseException; |
+ +28 + | ++ + + + + + | +import com.fasterxml.jackson.databind.JsonNode; |
+ +29 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ObjectNode; |
+ +30 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +31 + | ++ + + + + + | +import com.yubico.internal.util.BinaryUtil; |
+ +32 + | ++ + + + + + | +import com.yubico.internal.util.ByteInputStream; |
+ +33 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +34 + | ++ + + + + + | +import com.yubico.internal.util.ExceptionUtil; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +36 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationType; |
+ +37 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +38 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +39 + | ++ + + + + + | +import java.io.IOException; |
+ +40 + | ++ + + + + + | +import java.math.BigInteger; |
+ +41 + | ++ + + + + + | +import java.security.KeyFactory; |
+ +42 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +43 + | ++ + + + + + | +import java.security.PublicKey; |
+ +44 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +45 + | ++ + + + + + | +import java.security.cert.CertificateParsingException; |
+ +46 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +47 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +48 + | ++ + + + + + | +import java.security.spec.RSAPublicKeySpec; |
+ +49 + | ++ + + + + + | +import java.util.Arrays; |
+ +50 + | ++ + + + + + | +import java.util.List; |
+ +51 + | ++ + + + + + | +import javax.naming.InvalidNameException; |
+ +52 + | ++ + + + + + | +import javax.naming.ldap.LdapName; |
+ +53 + | ++ + + + + + | +import javax.naming.ldap.Rdn; |
+ +54 + | ++ + + + + + | +import lombok.Value; |
+ +55 + | ++ + + + + + | +import lombok.extern.slf4j.Slf4j; |
+ +56 + | ++ + + + + + | +|
+ +57 + | ++ + + + + + | +@Slf4j |
+ +58 + | ++ + + + + + | +final class TpmAttestationStatementVerifier |
+ +59 + | ++ + + + + + | + implements AttestationStatementVerifier, X5cAttestationStatementVerifier { |
+ +60 + | ++ + + + + + | +|
+ +61 + | ++ + + + + + | + private static final String TPM_VER = "2.0"; |
+ +62 + | ++ + + + + + | + static final ByteArray TPM_GENERATED_VALUE = ByteArray.fromBase64("/1RDRw=="); |
+ +63 + | ++ + + + + + | + static final ByteArray TPM_ST_ATTEST_CERTIFY = ByteArray.fromBase64("gBc="); |
+ +64 + | ++ + + + + + | +|
+ +65 + | ++ + + + + + | + static final int TPM_ALG_NULL = 0x0010; |
+ +66 + | ++ + + + + + | +|
+ +67 + | ++ + + + + + | + private static final String OID_TCG_AT_TPM_MANUFACTURER = "2.23.133.2.1"; |
+ +68 + | ++ + + + + + | + private static final String OID_TCG_AT_TPM_MODEL = "2.23.133.2.2"; |
+ +69 + | ++ + + + + + | + private static final String OID_TCG_AT_TPM_VERSION = "2.23.133.2.3"; |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | + /** |
+ +72 + | ++ + + + + + | + * Object attributes |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * <p>see section 8.3 of |
+ +75 + | ++ + + + + + | + * https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf |
+ +76 + | ++ + + + + + | + */ |
+ +77 + | ++ + + + + + | + static final class Attributes { |
+ +78 + | ++ + + + + + | + static final int SIGN_ENCRYPT = 1 << 18; |
+ +79 + | ++ + + + + + | +|
+ +80 + | ++ + + + + + | + private static final int SHALL_BE_ZERO = |
+ +81 + | ++ + + + + + | + (1 << 0) // 0 Reserved |
+ +82 + | ++ + + + + + | + | (1 << 3) // 3 Reserved |
+ +83 + | ++ + + + + + | + | (0x3 << 8) // 9:8 Reserved |
+ +84 + | ++ + + + + + | + | (0xF << 12) // 15:12 Reserved |
+ +85 + | ++ + + + + + | + | ((0xFFFFFFFF << 19) & ((1 << 31) | ((1 << 31) - 1))) // 31:19 Reserved |
+ +86 + | ++ + + + + + | + ; |
+ +87 + | ++ + + + + + | + } |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + @Override |
+ +90 + | ++ + + + + + | + public AttestationType getAttestationType(AttestationObject attestation) { |
+ +91 + | +
+
+1
+
+1. getAttestationType : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier::getAttestationType → KILLED + + + + |
+ return AttestationType.ATTESTATION_CA; |
+ +92 + | ++ + + + + + | + } |
+ +93 + | ++ + + + + + | +|
+ +94 + | ++ + + + + + | + @Override |
+ +95 + | ++ + + + + + | + public boolean verifyAttestationSignature( |
+ +96 + | ++ + + + + + | + AttestationObject attestationObject, ByteArray clientDataJsonHash) { |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + // Step 1: Verify that attStmt is valid CBOR conforming to the syntax defined above and perform |
+ +99 + | ++ + + + + + | + // CBOR decoding on it to extract the contained fields. |
+ +100 + | ++ + + + + + | +|
+ +101 + | ++ + + + + + | + ObjectNode attStmt = attestationObject.getAttestationStatement(); |
+ +102 + | ++ + + + + + | +|
+ +103 + | ++ + + + + + | + JsonNode verNode = attStmt.get("ver"); |
+ +104 + | +
+
+2
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +105 + | +
+
+2
+
+1. verifyAttestationSignature : negated conditional → KILLED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ verNode != null && verNode.isTextual() && verNode.textValue().equals(TPM_VER), |
+ +106 + | ++ + + + + + | + "attStmt.ver must equal \"%s\", was: %s", |
+ +107 + | ++ + + + + + | + TPM_VER, |
+ +108 + | ++ + + + + + | + verNode); |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + JsonNode algNode = attStmt.get("alg"); |
+ +111 + | +
+
+2
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +112 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ algNode != null && algNode.canConvertToLong(), |
+ +113 + | ++ + + + + + | + "attStmt.alg must be set to an integer value, was: %s", |
+ +114 + | ++ + + + + + | + algNode); |
+ +115 + | ++ + + + + + | + final COSEAlgorithmIdentifier alg = |
+ +116 + | ++ + + + + + | + COSEAlgorithmIdentifier.fromId(algNode.longValue()) |
+ +117 + | ++ + + + + + | + .orElseThrow( |
+ +118 + | ++ + + + + + | + () -> |
+ +119 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$0 : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier::lambda$verifyAttestationSignature$0 → NO_COVERAGE + + + + |
+ new IllegalArgumentException("Unknown COSE algorithm identifier: " + algNode)); |
+ +120 + | ++ + + + + + | +|
+ +121 + | ++ + + + + + | + JsonNode x5cNode = attStmt.get("x5c"); |
+ +122 + | +
+
+2
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +123 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ x5cNode != null && x5cNode.isArray(), |
+ +124 + | ++ + + + + + | + "attStmt.x5c must be set to an array value, was: %s", |
+ +125 + | ++ + + + + + | + x5cNode); |
+ +126 + | ++ + + + + + | + final List<X509Certificate> x5c; |
+ +127 + | ++ + + + + + | + try { |
+ +128 + | ++ + + + + + | + x5c = |
+ +129 + | ++ + + + + + | + getAttestationTrustPath(attestationObject) |
+ +130 + | ++ + + + + + | + .orElseThrow( |
+ +131 + | ++ + + + + + | + () -> |
+ +132 + | +
+
+1
+
+1. lambda$verifyAttestationSignature$1 : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier::lambda$verifyAttestationSignature$1 → NO_COVERAGE + + + + |
+ new IllegalArgumentException( |
+ +133 + | ++ + + + + + | + "Failed to parse \"x5c\" attestation certificate chain in \"tpm\" attestation statement.")); |
+ +134 + | ++ + + + + + | + } catch (CertificateException e) { |
+ +135 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +136 + | ++ + + + + + | + } |
+ +137 + | ++ + + + + + | + final X509Certificate aikCert = x5c.get(0); |
+ +138 + | ++ + + + + + | +|
+ +139 + | ++ + + + + + | + JsonNode sigNode = attStmt.get("sig"); |
+ +140 + | +
+
+2
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +141 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ sigNode != null && sigNode.isBinary(), |
+ +142 + | ++ + + + + + | + "attStmt.sig must be set to a binary value, was: %s", |
+ +143 + | ++ + + + + + | + sigNode); |
+ +144 + | ++ + + + + + | + final ByteArray sig; |
+ +145 + | ++ + + + + + | + try { |
+ +146 + | ++ + + + + + | + sig = new ByteArray(sigNode.binaryValue()); |
+ +147 + | ++ + + + + + | + } catch (IOException e) { |
+ +148 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +149 + | ++ + + + + + | + } |
+ +150 + | ++ + + + + + | +|
+ +151 + | ++ + + + + + | + JsonNode certInfoNode = attStmt.get("certInfo"); |
+ +152 + | +
+
+2
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +153 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ certInfoNode != null && certInfoNode.isBinary(), |
+ +154 + | ++ + + + + + | + "attStmt.certInfo must be set to a binary value, was: %s", |
+ +155 + | ++ + + + + + | + certInfoNode); |
+ +156 + | ++ + + + + + | +|
+ +157 + | ++ + + + + + | + JsonNode pubAreaNode = attStmt.get("pubArea"); |
+ +158 + | +
+
+2
+
+1. verifyAttestationSignature : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +159 + | +
+
+1
+
+1. verifyAttestationSignature : negated conditional → KILLED + + + + |
+ pubAreaNode != null && pubAreaNode.isBinary(), |
+ +160 + | ++ + + + + + | + "attStmt.pubArea must be set to a binary value, was: %s", |
+ +161 + | ++ + + + + + | + pubAreaNode); |
+ +162 + | ++ + + + + + | +|
+ +163 + | ++ + + + + + | + final TpmtPublic pubArea; |
+ +164 + | ++ + + + + + | + try { |
+ +165 + | ++ + + + + + | + pubArea = TpmtPublic.parse(pubAreaNode.binaryValue()); |
+ +166 + | ++ + + + + + | + } catch (IOException e) { |
+ +167 + | ++ + + + + + | + throw new RuntimeException("Failed to parse TPMT_PUBLIC data structure.", e); |
+ +168 + | ++ + + + + + | + } |
+ +169 + | ++ + + + + + | +|
+ +170 + | ++ + + + + + | + final TpmsAttest certInfo; |
+ +171 + | ++ + + + + + | + try { |
+ +172 + | ++ + + + + + | + certInfo = TpmsAttest.parse(certInfoNode.binaryValue()); |
+ +173 + | ++ + + + + + | + } catch (IOException e) { |
+ +174 + | ++ + + + + + | + throw new RuntimeException("Failed to parse TPMS_ATTEST data structure.", e); |
+ +175 + | ++ + + + + + | + } |
+ +176 + | ++ + + + + + | +|
+ +177 + | ++ + + + + + | + // Step 2: Verify that the public key specified by the parameters and unique fields of pubArea |
+ +178 + | ++ + + + + + | + // is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData. |
+ +179 + | ++ + + + + + | + try { |
+ +180 + | +
+
+1
+
+1. verifyAttestationSignature : removed call to com/yubico/webauthn/TpmAttestationStatementVerifier::verifyPublicKeysMatch → KILLED + + + + |
+ verifyPublicKeysMatch(attestationObject, pubArea); |
+ +181 + | ++ + + + + + | + } catch (CoseException | IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { |
+ +182 + | ++ + + + + + | + throw new RuntimeException( |
+ +183 + | ++ + + + + + | + "Failed to verify that public key in TPM attestation matches public key in authData.", e); |
+ +184 + | ++ + + + + + | + } |
+ +185 + | ++ + + + + + | +|
+ +186 + | ++ + + + + + | + // Step 3: Concatenate authenticatorData and clientDataHash to form attToBeSigned. |
+ +187 + | ++ + + + + + | + final ByteArray attToBeSigned = |
+ +188 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getBytes().concat(clientDataJsonHash); |
+ +189 + | ++ + + + + + | +|
+ +190 + | ++ + + + + + | + // Step 4: Validate that certInfo is valid: |
+ +191 + | ++ + + + + + | + try { |
+ +192 + | +
+
+1
+
+1. verifyAttestationSignature : removed call to com/yubico/webauthn/TpmAttestationStatementVerifier::validateCertInfo → KILLED + + + + |
+ validateCertInfo(alg, aikCert, sig, pubArea, certInfo, attToBeSigned, attestationObject); |
+ +193 + | ++ + + + + + | + } catch (CertificateParsingException e) { |
+ +194 + | ++ + + + + + | + throw new RuntimeException("Failed to verify TPM attestation.", e); |
+ +195 + | ++ + + + + + | + } |
+ +196 + | ++ + + + + + | +|
+ +197 + | +
+
+1
+
+1. verifyAttestationSignature : replaced boolean return with false for com/yubico/webauthn/TpmAttestationStatementVerifier::verifyAttestationSignature → KILLED + + + + |
+ return true; |
+ +198 + | ++ + + + + + | + } |
+ +199 + | ++ + + + + + | +|
+ +200 + | ++ + + + + + | + private void validateCertInfo( |
+ +201 + | ++ + + + + + | + COSEAlgorithmIdentifier alg, |
+ +202 + | ++ + + + + + | + X509Certificate aikCert, |
+ +203 + | ++ + + + + + | + ByteArray sig, |
+ +204 + | ++ + + + + + | + TpmtPublic pubArea, |
+ +205 + | ++ + + + + + | + TpmsAttest certInfo, |
+ +206 + | ++ + + + + + | + ByteArray attToBeSigned, |
+ +207 + | ++ + + + + + | + AttestationObject attestationObject) |
+ +208 + | ++ + + + + + | + throws CertificateParsingException { |
+ +209 + | ++ + + + + + | + // Sub-steps 1-2 handled in TpmsAttest.parse() |
+ +210 + | ++ + + + + + | + // Sub-step 3: Verify that extraData is set to the hash of attToBeSigned using the hash |
+ +211 + | ++ + + + + + | + // algorithm employed in "alg". |
+ +212 + | ++ + + + + + | + final ByteArray expectedExtraData; |
+ +213 + | ++ + + + + + | + switch (alg) { |
+ +214 + | ++ + + + + + | + case ES256: |
+ +215 + | ++ + + + + + | + case RS256: |
+ +216 + | ++ + + + + + | + expectedExtraData = Crypto.sha256(attToBeSigned); |
+ +217 + | ++ + + + + + | + break; |
+ +218 + | ++ + + + + + | +|
+ +219 + | ++ + + + + + | + case ES384: |
+ +220 + | ++ + + + + + | + case RS384: |
+ +221 + | ++ + + + + + | + expectedExtraData = Crypto.sha384(attToBeSigned); |
+ +222 + | ++ + + + + + | + break; |
+ +223 + | ++ + + + + + | +|
+ +224 + | ++ + + + + + | + case ES512: |
+ +225 + | ++ + + + + + | + case RS512: |
+ +226 + | ++ + + + + + | + expectedExtraData = Crypto.sha512(attToBeSigned); |
+ +227 + | ++ + + + + + | + break; |
+ +228 + | ++ + + + + + | +|
+ +229 + | ++ + + + + + | + case RS1: |
+ +230 + | ++ + + + + + | + try { |
+ +231 + | ++ + + + + + | + expectedExtraData = Crypto.sha1(attToBeSigned); |
+ +232 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +233 + | ++ + + + + + | + throw new RuntimeException("Failed to hash attToBeSigned to verify TPM attestation.", e); |
+ +234 + | ++ + + + + + | + } |
+ +235 + | ++ + + + + + | + break; |
+ +236 + | ++ + + + + + | +|
+ +237 + | ++ + + + + + | + default: |
+ +238 + | ++ + + + + + | + throw new UnsupportedOperationException("Signing algorithm not implemented: " + alg); |
+ +239 + | ++ + + + + + | + } |
+ +240 + | +
+
+1
+
+1. validateCertInfo : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +241 + | ++ + + + + + | + certInfo.extraData.equals(expectedExtraData), "Incorrect certInfo.extraData."); |
+ +242 + | ++ + + + + + | +|
+ +243 + | ++ + + + + + | + // Sub-step 4: Verify that attested contains a TPMS_CERTIFY_INFO structure as specified in |
+ +244 + | ++ + + + + + | + // [TPMv2-Part2] section 10.12.3, whose name field contains a valid Name for pubArea, as |
+ +245 + | ++ + + + + + | + // computed using the algorithm in the nameAlg field of pubArea using the procedure specified in |
+ +246 + | ++ + + + + + | + // [TPMv2-Part1] section 16. |
+ +247 + | +
+
+1
+
+1. validateCertInfo : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +248 + | ++ + + + + + | + certInfo.attestedName.equals(pubArea.name()), "Incorrect certInfo.attestedName."); |
+ +249 + | ++ + + + + + | +|
+ +250 + | ++ + + + + + | + // Sub-step 5 handled by parsing above |
+ +251 + | ++ + + + + + | + // Sub-step 6: Nothing to do |
+ +252 + | ++ + + + + + | +|
+ +253 + | ++ + + + + + | + // Sub-step 7: Verify the sig is a valid signature over certInfo using the attestation public |
+ +254 + | ++ + + + + + | + // key in aikCert with the algorithm specified in alg. |
+ +255 + | +
+
+1
+
+1. validateCertInfo : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +256 + | ++ + + + + + | + Crypto.verifySignature(aikCert, certInfo.getRawBytes(), sig, alg), |
+ +257 + | ++ + + + + + | + "Incorrect TPM attestation signature."); |
+ +258 + | ++ + + + + + | +|
+ +259 + | ++ + + + + + | + // Sub-step 8: Verify that aikCert meets the requirements in § 8.3.1 TPM Attestation Statement |
+ +260 + | ++ + + + + + | + // Certificate Requirements. |
+ +261 + | ++ + + + + + | + // Sub-step 9: If aikCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 |
+ +262 + | ++ + + + + + | + // (id-fido-gen-ce-aaguid) verify that the value of this extension matches the aaguid in |
+ +263 + | ++ + + + + + | + // authenticatorData. |
+ +264 + | +
+
+1
+
+1. validateCertInfo : removed call to com/yubico/webauthn/TpmAttestationStatementVerifier::verifyX5cRequirements → KILLED + + + + |
+ verifyX5cRequirements( |
+ +265 + | ++ + + + + + | + aikCert, |
+ +266 + | ++ + + + + + | + attestationObject.getAuthenticatorData().getAttestedCredentialData().get().getAaguid()); |
+ +267 + | ++ + + + + + | + } |
+ +268 + | ++ + + + + + | +|
+ +269 + | ++ + + + + + | + private void verifyPublicKeysMatch(AttestationObject attestationObject, TpmtPublic pubArea) |
+ +270 + | ++ + + + + + | + throws CoseException, IOException, InvalidKeySpecException, NoSuchAlgorithmException { |
+ +271 + | ++ + + + + + | + final PublicKey credentialPubKey = |
+ +272 + | ++ + + + + + | + WebAuthnCodecs.importCosePublicKey( |
+ +273 + | ++ + + + + + | + attestationObject |
+ +274 + | ++ + + + + + | + .getAuthenticatorData() |
+ +275 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +276 + | ++ + + + + + | + .get() |
+ +277 + | ++ + + + + + | + .getCredentialPublicKey()); |
+ +278 + | ++ + + + + + | +|
+ +279 + | ++ + + + + + | + final PublicKey signedCredentialPublicKey; |
+ +280 + | ++ + + + + + | + switch (pubArea.signAlg) { |
+ +281 + | ++ + + + + + | + case TpmAlgAsym.RSA: |
+ +282 + | ++ + + + + + | + { |
+ +283 + | ++ + + + + + | + TpmsRsaParms params = (TpmsRsaParms) pubArea.parameters; |
+ +284 + | ++ + + + + + | + Tpm2bPublicKeyRsa unique = (Tpm2bPublicKeyRsa) pubArea.unique; |
+ +285 + | ++ + + + + + | + RSAPublicKeySpec spec = |
+ +286 + | ++ + + + + + | + new RSAPublicKeySpec( |
+ +287 + | ++ + + + + + | + new BigInteger(1, unique.bytes.getBytes()), BigInteger.valueOf(params.exponent)); |
+ +288 + | ++ + + + + + | + KeyFactory kf = KeyFactory.getInstance("RSA"); |
+ +289 + | ++ + + + + + | + signedCredentialPublicKey = kf.generatePublic(spec); |
+ +290 + | ++ + + + + + | + } |
+ +291 + | ++ + + + + + | +|
+ +292 + | +
+
+1
+
+1. verifyPublicKeysMatch : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +293 + | ++ + + + + + | + Arrays.equals(credentialPubKey.getEncoded(), signedCredentialPublicKey.getEncoded()), |
+ +294 + | ++ + + + + + | + "Signed public key in TPM attestation is not identical to credential public key in authData."); |
+ +295 + | ++ + + + + + | + break; |
+ +296 + | ++ + + + + + | +|
+ +297 + | ++ + + + + + | + case TpmAlgAsym.ECC: |
+ +298 + | ++ + + + + + | + { |
+ +299 + | ++ + + + + + | + TpmsEccParms params = (TpmsEccParms) pubArea.parameters; |
+ +300 + | ++ + + + + + | + TpmsEccPoint unique = (TpmsEccPoint) pubArea.unique; |
+ +301 + | ++ + + + + + | +|
+ +302 + | ++ + + + + + | + final COSEAlgorithmIdentifier algId = |
+ +303 + | ++ + + + + + | + COSEAlgorithmIdentifier.fromPublicKey( |
+ +304 + | ++ + + + + + | + attestationObject |
+ +305 + | ++ + + + + + | + .getAuthenticatorData() |
+ +306 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +307 + | ++ + + + + + | + .get() |
+ +308 + | ++ + + + + + | + .getCredentialPublicKey()) |
+ +309 + | ++ + + + + + | + .get(); |
+ +310 + | ++ + + + + + | + final COSEAlgorithmIdentifier tpmAlgId; |
+ +311 + | ++ + + + + + | + final CBORObject cosePubkey = |
+ +312 + | ++ + + + + + | + CBORObject.DecodeFromBytes( |
+ +313 + | ++ + + + + + | + attestationObject |
+ +314 + | ++ + + + + + | + .getAuthenticatorData() |
+ +315 + | ++ + + + + + | + .getAttestedCredentialData() |
+ +316 + | ++ + + + + + | + .get() |
+ +317 + | ++ + + + + + | + .getCredentialPublicKey() |
+ +318 + | ++ + + + + + | + .getBytes()); |
+ +319 + | ++ + + + + + | +|
+ +320 + | ++ + + + + + | + switch (params.curve_id) { |
+ +321 + | ++ + + + + + | + case TpmEccCurve.NIST_P256: |
+ +322 + | ++ + + + + + | + tpmAlgId = COSEAlgorithmIdentifier.ES256; |
+ +323 + | ++ + + + + + | + break; |
+ +324 + | ++ + + + + + | +|
+ +325 + | ++ + + + + + | + case TpmEccCurve.NIST_P384: |
+ +326 + | ++ + + + + + | + tpmAlgId = COSEAlgorithmIdentifier.ES384; |
+ +327 + | ++ + + + + + | + break; |
+ +328 + | ++ + + + + + | +|
+ +329 + | ++ + + + + + | + case TpmEccCurve.NIST_P521: |
+ +330 + | ++ + + + + + | + tpmAlgId = COSEAlgorithmIdentifier.ES512; |
+ +331 + | ++ + + + + + | + break; |
+ +332 + | ++ + + + + + | +|
+ +333 + | ++ + + + + + | + default: |
+ +334 + | ++ + + + + + | + throw new UnsupportedOperationException( |
+ +335 + | ++ + + + + + | + "Unsupported elliptic curve: " + params.curve_id); |
+ +336 + | ++ + + + + + | + } |
+ +337 + | ++ + + + + + | +|
+ +338 + | +
+
+1
+
+1. verifyPublicKeysMatch : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +339 + | ++ + + + + + | + algId.equals(tpmAlgId), |
+ +340 + | ++ + + + + + | + "Signed public key in TPM attestation is not identical to credential public key in authData; elliptic curve differs: %s != %s", |
+ +341 + | ++ + + + + + | + tpmAlgId, |
+ +342 + | ++ + + + + + | + algId); |
+ +343 + | ++ + + + + + | + byte[] cosePubkeyX = cosePubkey.get(CBORObject.FromObject(-2)).GetByteString(); |
+ +344 + | ++ + + + + + | + byte[] cosePubkeyY = cosePubkey.get(CBORObject.FromObject(-3)).GetByteString(); |
+ +345 + | +
+
+1
+
+1. verifyPublicKeysMatch : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +346 + | ++ + + + + + | + new BigInteger(1, unique.x.getBytes()).equals(new BigInteger(1, cosePubkeyX)), |
+ +347 + | ++ + + + + + | + "Signed public key in TPM attestation is not identical to credential public key in authData; EC X coordinate differs: %s != %s", |
+ +348 + | ++ + + + + + | + unique.x, |
+ +349 + | ++ + + + + + | + new ByteArray(cosePubkeyX)); |
+ +350 + | +
+
+1
+
+1. verifyPublicKeysMatch : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +351 + | ++ + + + + + | + new BigInteger(1, unique.y.getBytes()).equals(new BigInteger(1, cosePubkeyY)), |
+ +352 + | ++ + + + + + | + "Signed public key in TPM attestation is not identical to credential public key in authData; EC Y coordinate differs: %s != %s", |
+ +353 + | ++ + + + + + | + unique.y, |
+ +354 + | ++ + + + + + | + new ByteArray(cosePubkeyY)); |
+ +355 + | ++ + + + + + | + } |
+ +356 + | ++ + + + + + | + break; |
+ +357 + | ++ + + + + + | +|
+ +358 + | ++ + + + + + | + default: |
+ +359 + | ++ + + + + + | + throw new UnsupportedOperationException( |
+ +360 + | ++ + + + + + | + "Unsupported algorithm for credential public key: " + pubArea.signAlg); |
+ +361 + | ++ + + + + + | + } |
+ +362 + | ++ + + + + + | + } |
+ +363 + | ++ + + + + + | +|
+ +364 + | ++ + + + + + | + static final class TpmAlgAsym { |
+ +365 + | ++ + + + + + | + static final int RSA = 0x0001; |
+ +366 + | ++ + + + + + | + static final int ECC = 0x0023; |
+ +367 + | ++ + + + + + | + } |
+ +368 + | ++ + + + + + | +|
+ +369 + | ++ + + + + + | + private interface Parameters {} |
+ +370 + | ++ + + + + + | +|
+ +371 + | ++ + + + + + | + private interface Unique {} |
+ +372 + | ++ + + + + + | +|
+ +373 + | ++ + + + + + | + @Value |
+ +374 + | ++ + + + + + | + private static class TpmtPublic { |
+ +375 + | ++ + + + + + | + int signAlg; |
+ +376 + | ++ + + + + + | + int nameAlg; |
+ +377 + | ++ + + + + + | + Parameters parameters; |
+ +378 + | ++ + + + + + | + Unique unique; |
+ +379 + | ++ + + + + + | + ByteArray rawBytes; |
+ +380 + | ++ + + + + + | +|
+ +381 + | ++ + + + + + | + private static TpmtPublic parse(byte[] pubArea) throws IOException { |
+ +382 + | ++ + + + + + | + try (ByteInputStream reader = new ByteInputStream(pubArea)) { |
+ +383 + | ++ + + + + + | + final int signAlg = reader.readUnsignedShort(); |
+ +384 + | ++ + + + + + | + final int nameAlg = reader.readUnsignedShort(); |
+ +385 + | ++ + + + + + | +|
+ +386 + | ++ + + + + + | + final int attributes = reader.readInt(); |
+ +387 + | +
+
+3
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. parse : negated conditional → KILLED +3. parse : Replaced bitwise AND with OR → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +388 + | ++ + + + + + | + (attributes & Attributes.SHALL_BE_ZERO) == 0, |
+ +389 + | ++ + + + + + | + "Attributes contains 1 bits in reserved position(s): 0x%08x", |
+ +390 + | ++ + + + + + | + attributes); |
+ +391 + | ++ + + + + + | +|
+ +392 + | ++ + + + + + | + // authPolicy is not used by this implementation |
+ +393 + | ++ + + + + + | + reader.skipBytes(reader.readUnsignedShort()); |
+ +394 + | ++ + + + + + | +|
+ +395 + | ++ + + + + + | + final Parameters parameters; |
+ +396 + | ++ + + + + + | + final Unique unique; |
+ +397 + | ++ + + + + + | +|
+ +398 + | +
+
+3
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. parse : negated conditional → KILLED +3. parse : Replaced bitwise AND with OR → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +399 + | ++ + + + + + | + (attributes & Attributes.SIGN_ENCRYPT) == Attributes.SIGN_ENCRYPT, |
+ +400 + | ++ + + + + + | + "Public key is expected to have the SIGN_ENCRYPT attribute set, attributes were: 0x%08x", |
+ +401 + | ++ + + + + + | + attributes); |
+ +402 + | ++ + + + + + | +|
+ +403 + | +
+
+1
+
+1. parse : negated conditional → KILLED + + + + |
+ if (signAlg == TpmAlgAsym.RSA) { |
+ +404 + | ++ + + + + + | + parameters = TpmsRsaParms.parse(reader); |
+ +405 + | ++ + + + + + | + unique = Tpm2bPublicKeyRsa.parse(reader); |
+ +406 + | +
+
+1
+
+1. parse : negated conditional → KILLED + + + + |
+ } else if (signAlg == TpmAlgAsym.ECC) { |
+ +407 + | ++ + + + + + | + parameters = TpmsEccParms.parse(reader); |
+ +408 + | ++ + + + + + | + unique = TpmsEccPoint.parse(reader); |
+ +409 + | ++ + + + + + | + } else { |
+ +410 + | ++ + + + + + | + throw new UnsupportedOperationException("Signing algorithm not implemented: " + signAlg); |
+ +411 + | ++ + + + + + | + } |
+ +412 + | ++ + + + + + | +|
+ +413 + | +
+
+1
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ ExceptionUtil.assertTrue( |
+ +414 + | +
+
+1
+
+1. parse : negated conditional → KILLED + + + + |
+ reader.available() == 0, |
+ +415 + | ++ + + + + + | + "%d remaining bytes in TPMT_PUBLIC buffer", |
+ +416 + | ++ + + + + + | + reader.available()); |
+ +417 + | ++ + + + + + | +|
+ +418 + | ++ + + + + + | + return new TpmtPublic(signAlg, nameAlg, parameters, unique, new ByteArray(pubArea)); |
+ +419 + | ++ + + + + + | + } |
+ +420 + | ++ + + + + + | + } |
+ +421 + | ++ + + + + + | +|
+ +422 + | ++ + + + + + | + /** |
+ +423 + | ++ + + + + + | + * Computing Entity Names |
+ +424 + | ++ + + + + + | + * |
+ +425 + | ++ + + + + + | + * <p>see: |
+ +426 + | ++ + + + + + | + * https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-1-Architecture-01.38.pdf |
+ +427 + | ++ + + + + + | + * section 16 Names |
+ +428 + | ++ + + + + + | + * |
+ +429 + | ++ + + + + + | + * <pre> |
+ +430 + | ++ + + + + + | + * Name ≔ nameAlg || HnameAlg (handle→nvPublicArea) |
+ +431 + | ++ + + + + + | + * where |
+ +432 + | ++ + + + + + | + * nameAlg algorithm used to compute Name |
+ +433 + | ++ + + + + + | + * HnameAlg hash using the nameAlg parameter in the NV Index location |
+ +434 + | ++ + + + + + | + * associated with handle |
+ +435 + | ++ + + + + + | + * nvPublicArea contents of the TPMS_NV_PUBLIC associated with handle |
+ +436 + | ++ + + + + + | + * </pre> |
+ +437 + | ++ + + + + + | + */ |
+ +438 + | ++ + + + + + | + private ByteArray name() { |
+ +439 + | ++ + + + + + | + final ByteArray hash; |
+ +440 + | ++ + + + + + | + switch (this.nameAlg) { |
+ +441 + | ++ + + + + + | + case TpmAlgHash.SHA1: |
+ +442 + | ++ + + + + + | + try { |
+ +443 + | ++ + + + + + | + hash = Crypto.sha1(this.rawBytes); |
+ +444 + | ++ + + + + + | + } catch (NoSuchAlgorithmException e) { |
+ +445 + | ++ + + + + + | + throw new RuntimeException("Failed to hash TPMU_ATTEST name.", e); |
+ +446 + | ++ + + + + + | + } |
+ +447 + | ++ + + + + + | + break; |
+ +448 + | ++ + + + + + | +|
+ +449 + | ++ + + + + + | + case TpmAlgHash.SHA256: |
+ +450 + | ++ + + + + + | + hash = Crypto.sha256(this.rawBytes); |
+ +451 + | ++ + + + + + | + break; |
+ +452 + | ++ + + + + + | +|
+ +453 + | ++ + + + + + | + case TpmAlgHash.SHA384: |
+ +454 + | ++ + + + + + | + hash = Crypto.sha384(this.rawBytes); |
+ +455 + | ++ + + + + + | + break; |
+ +456 + | ++ + + + + + | +|
+ +457 + | ++ + + + + + | + case TpmAlgHash.SHA512: |
+ +458 + | ++ + + + + + | + hash = Crypto.sha512(this.rawBytes); |
+ +459 + | ++ + + + + + | + break; |
+ +460 + | ++ + + + + + | +|
+ +461 + | ++ + + + + + | + default: |
+ +462 + | ++ + + + + + | + throw new IllegalArgumentException("Unknown hash algorithm identifier: " + this.nameAlg); |
+ +463 + | ++ + + + + + | + } |
+ +464 + | +
+
+1
+
+1. name : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier$TpmtPublic::name → KILLED + + + + |
+ return new ByteArray(BinaryUtil.encodeUint16(this.nameAlg)).concat(hash); |
+ +465 + | ++ + + + + + | + } |
+ +466 + | ++ + + + + + | + } |
+ +467 + | ++ + + + + + | +|
+ +468 + | ++ + + + + + | + static class TpmAlgHash { |
+ +469 + | ++ + + + + + | + static final int SHA1 = 0x0004; |
+ +470 + | ++ + + + + + | + static final int SHA256 = 0x000B; |
+ +471 + | ++ + + + + + | + static final int SHA384 = 0x000C; |
+ +472 + | ++ + + + + + | + static final int SHA512 = 0x000D; |
+ +473 + | ++ + + + + + | + } |
+ +474 + | ++ + + + + + | +|
+ +475 + | ++ + + + + + | + private void verifyX5cRequirements(X509Certificate cert, ByteArray aaguid) |
+ +476 + | ++ + + + + + | + throws CertificateParsingException { |
+ +477 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED + + + + |
+ ExceptionUtil.assertTrue( |
+ +478 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ cert.getVersion() == 3, |
+ +479 + | ++ + + + + + | + "Invalid TPM attestation certificate: Version MUST be 3, but was: %s", |
+ +480 + | ++ + + + + + | + cert.getVersion()); |
+ +481 + | ++ + + + + + | +|
+ +482 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +483 + | ++ + + + + + | + cert.getSubjectX500Principal().getName().isEmpty(), |
+ +484 + | ++ + + + + + | + "Invalid TPM attestation certificate: subject MUST be empty, but was: %s", |
+ +485 + | ++ + + + + + | + cert.getSubjectX500Principal()); |
+ +486 + | ++ + + + + + | +|
+ +487 + | ++ + + + + + | + boolean foundManufacturer = false; |
+ +488 + | ++ + + + + + | + boolean foundModel = false; |
+ +489 + | ++ + + + + + | + boolean foundVersion = false; |
+ +490 + | ++ + + + + + | + for (List<?> n : cert.getSubjectAlternativeNames()) { |
+ +491 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ if ((Integer) n.get(0) == 4) { // GeneralNames CHOICE 4: directoryName |
+ +492 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ if (n.get(1) instanceof String) { |
+ +493 + | ++ + + + + + | + try { |
+ +494 + | ++ + + + + + | + for (final Rdn rdn : new LdapName((String) n.get(1)).getRdns()) { |
+ +495 + | ++ + + + + + | + javax.naming.directory.Attributes attrs = rdn.toAttributes(); |
+ +496 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ foundManufacturer = |
+ +497 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ foundManufacturer || attrs.get(OID_TCG_AT_TPM_MANUFACTURER) != null; |
+ +498 + | +
+
+2
+
+1. verifyX5cRequirements : negated conditional → KILLED +2. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ foundModel = foundModel || attrs.get(OID_TCG_AT_TPM_MODEL) != null; |
+ +499 + | +
+
+2
+
+1. verifyX5cRequirements : negated conditional → KILLED +2. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ foundVersion = foundVersion || attrs.get(OID_TCG_AT_TPM_VERSION) != null; |
+ +500 + | ++ + + + + + | + } |
+ +501 + | ++ + + + + + | + } catch (InvalidNameException e) { |
+ +502 + | ++ + + + + + | + throw new RuntimeException( |
+ +503 + | ++ + + + + + | + "Failed to decode subject alternative name in TPM attestation cert", e); |
+ +504 + | ++ + + + + + | + } |
+ +505 + | ++ + + + + + | + } else { |
+ +506 + | ++ + + + + + | + log.debug("Unknown type of SubjectAlternativeNames entry: {}", n.get(1)); |
+ +507 + | ++ + + + + + | + } |
+ +508 + | ++ + + + + + | + } |
+ +509 + | ++ + + + + + | + } |
+ +510 + | +
+
+4
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. verifyX5cRequirements : negated conditional → KILLED +3. verifyX5cRequirements : negated conditional → KILLED +4. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +511 + | ++ + + + + + | + foundManufacturer && foundModel && foundVersion, |
+ +512 + | ++ + + + + + | + "Invalid TPM attestation certificate: The Subject Alternative Name extension MUST be set as defined in [TPMv2-EK-Profile] section 3.2.9.%s%s%s", |
+ +513 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → SURVIVED + + + + |
+ foundManufacturer ? "" : " Missing TPM manufacturer.", |
+ +514 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → SURVIVED + + + + |
+ foundModel ? "" : " Missing TPM model.", |
+ +515 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → SURVIVED + + + + |
+ foundVersion ? "" : " Missing TPM version."); |
+ +516 + | ++ + + + + + | +|
+ +517 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +518 + | +
+
+2
+
+1. verifyX5cRequirements : negated conditional → KILLED +2. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ cert.getExtendedKeyUsage() != null && cert.getExtendedKeyUsage().contains("2.23.133.8.3"), |
+ +519 + | ++ + + + + + | + "Invalid TPM attestation certificate: extended key usage extension MUST contain the OID 2.23.133.8.3, but was: %s", |
+ +520 + | ++ + + + + + | + cert.getExtendedKeyUsage()); |
+ +521 + | ++ + + + + + | +|
+ +522 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +523 + | +
+
+1
+
+1. verifyX5cRequirements : negated conditional → KILLED + + + + |
+ cert.getBasicConstraints() == -1, |
+ +524 + | ++ + + + + + | + "Invalid TPM attestation certificate: MUST NOT be a CA certificate, but was."); |
+ +525 + | ++ + + + + + | +|
+ +526 + | ++ + + + + + | + CertificateParser.parseFidoAaguidExtension(cert) |
+ +527 + | +
+
+1
+
+1. verifyX5cRequirements : removed call to java/util/Optional::ifPresent → KILLED + + + + |
+ .ifPresent( |
+ +528 + | ++ + + + + + | + extensionAaguid -> { |
+ +529 + | +
+
+1
+
+1. lambda$verifyX5cRequirements$2 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +530 + | ++ + + + + + | + Arrays.equals(aaguid.getBytes(), extensionAaguid), |
+ +531 + | ++ + + + + + | + "Invalid TPM attestation certificate: X.509 extension \"id-fido-gen-ce-aaguid\" is present but does not match the authenticator AAGUID."); |
+ +532 + | ++ + + + + + | + }); |
+ +533 + | ++ + + + + + | + } |
+ +534 + | ++ + + + + + | +|
+ +535 + | ++ + + + + + | + static final class TpmRsaScheme { |
+ +536 + | ++ + + + + + | + static final int RSASSA = 0x0014; |
+ +537 + | ++ + + + + + | + } |
+ +538 + | ++ + + + + + | +|
+ +539 + | ++ + + + + + | + /** |
+ +540 + | ++ + + + + + | + * See: |
+ +541 + | ++ + + + + + | + * https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf |
+ +542 + | ++ + + + + + | + * section 12.2.3.5 |
+ +543 + | ++ + + + + + | + */ |
+ +544 + | ++ + + + + + | + @Value |
+ +545 + | ++ + + + + + | + private static class TpmsRsaParms implements Parameters { |
+ +546 + | ++ + + + + + | +|
+ +547 + | ++ + + + + + | + long exponent; |
+ +548 + | ++ + + + + + | +|
+ +549 + | ++ + + + + + | + private static TpmsRsaParms parse(ByteInputStream reader) throws IOException { |
+ +550 + | ++ + + + + + | + final int symmetric = reader.readUnsignedShort(); |
+ +551 + | +
+
+2
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. parse : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +552 + | ++ + + + + + | + symmetric == TPM_ALG_NULL, |
+ +553 + | ++ + + + + + | + "RSA key is expected to have \"symmetric\" set to TPM_ALG_NULL, was: 0x%04x", |
+ +554 + | ++ + + + + + | + symmetric); |
+ +555 + | ++ + + + + + | +|
+ +556 + | ++ + + + + + | + final int scheme = reader.readUnsignedShort(); |
+ +557 + | +
+
+3
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. parse : negated conditional → KILLED +3. parse : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +558 + | ++ + + + + + | + scheme == TpmRsaScheme.RSASSA || scheme == TPM_ALG_NULL, |
+ +559 + | ++ + + + + + | + "RSA key is expected to have \"scheme\" set to TPM_ALG_RSASSA or TPM_ALG_NULL, was: 0x%04x", |
+ +560 + | ++ + + + + + | + scheme); |
+ +561 + | ++ + + + + + | +|
+ +562 + | ++ + + + + + | + reader.skipBytes(2); // key_bits is not used by this implementation |
+ +563 + | ++ + + + + + | +|
+ +564 + | ++ + + + + + | + int exponent = reader.readInt(); |
+ +565 + | +
+
+3
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → SURVIVED +2. parse : changed conditional boundary → KILLED +3. parse : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +566 + | ++ + + + + + | + exponent >= 0, "Exponent is too large and wrapped around to negative: %d", exponent); |
+ +567 + | +
+
+1
+
+1. parse : negated conditional → KILLED + + + + |
+ if (exponent == 0) { |
+ +568 + | ++ + + + + + | + // When zero, indicates that the exponent is the default of 2^16 + 1 |
+ +569 + | ++ + + + + + | + exponent = (1 << 16) + 1; |
+ +570 + | ++ + + + + + | + } |
+ +571 + | ++ + + + + + | +|
+ +572 + | +
+
+1
+
+1. parse : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier$TpmsRsaParms::parse → KILLED + + + + |
+ return new TpmsRsaParms(exponent); |
+ +573 + | ++ + + + + + | + } |
+ +574 + | ++ + + + + + | + } |
+ +575 + | ++ + + + + + | +|
+ +576 + | ++ + + + + + | + @Value |
+ +577 + | ++ + + + + + | + private static class Tpm2bPublicKeyRsa implements Unique { |
+ +578 + | ++ + + + + + | + ByteArray bytes; |
+ +579 + | ++ + + + + + | +|
+ +580 + | ++ + + + + + | + private static Tpm2bPublicKeyRsa parse(ByteInputStream reader) throws IOException { |
+ +581 + | +
+
+1
+
+1. parse : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier$Tpm2bPublicKeyRsa::parse → KILLED + + + + |
+ return new Tpm2bPublicKeyRsa(new ByteArray(reader.read(reader.readUnsignedShort()))); |
+ +582 + | ++ + + + + + | + } |
+ +583 + | ++ + + + + + | + } |
+ +584 + | ++ + + + + + | +|
+ +585 + | ++ + + + + + | + @Value |
+ +586 + | ++ + + + + + | + private static class TpmsEccParms implements Parameters { |
+ +587 + | ++ + + + + + | + int curve_id; |
+ +588 + | ++ + + + + + | +|
+ +589 + | ++ + + + + + | + private static TpmsEccParms parse(ByteInputStream reader) throws IOException { |
+ +590 + | ++ + + + + + | + final int symmetric = reader.readUnsignedShort(); |
+ +591 + | ++ + + + + + | + final int scheme = reader.readUnsignedShort(); |
+ +592 + | +
+
+2
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. parse : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +593 + | ++ + + + + + | + symmetric == TPM_ALG_NULL, |
+ +594 + | ++ + + + + + | + "ECC key is expected to have \"symmetric\" set to TPM_ALG_NULL, was: 0x%04x", |
+ +595 + | ++ + + + + + | + symmetric); |
+ +596 + | +
+
+2
+
+1. parse : negated conditional → KILLED +2. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +597 + | ++ + + + + + | + scheme == TPM_ALG_NULL, |
+ +598 + | ++ + + + + + | + "ECC key is expected to have \"scheme\" set to TPM_ALG_NULL, was: 0x%04x", |
+ +599 + | ++ + + + + + | + scheme); |
+ +600 + | ++ + + + + + | +|
+ +601 + | ++ + + + + + | + final int curve_id = reader.readUnsignedShort(); |
+ +602 + | ++ + + + + + | + reader.skipBytes(2); // kdf_scheme is not used by this implementation |
+ +603 + | ++ + + + + + | +|
+ +604 + | +
+
+1
+
+1. parse : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier$TpmsEccParms::parse → KILLED + + + + |
+ return new TpmsEccParms(curve_id); |
+ +605 + | ++ + + + + + | + } |
+ +606 + | ++ + + + + + | + } |
+ +607 + | ++ + + + + + | +|
+ +608 + | ++ + + + + + | + /** |
+ +609 + | ++ + + + + + | + * TPMS_ECC_POINT |
+ +610 + | ++ + + + + + | + * |
+ +611 + | ++ + + + + + | + * <p>See |
+ +612 + | ++ + + + + + | + * https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf |
+ +613 + | ++ + + + + + | + * Section 11.2.5.2 |
+ +614 + | ++ + + + + + | + */ |
+ +615 + | ++ + + + + + | + @Value |
+ +616 + | ++ + + + + + | + private static class TpmsEccPoint implements Unique { |
+ +617 + | ++ + + + + + | +|
+ +618 + | ++ + + + + + | + ByteArray x; |
+ +619 + | ++ + + + + + | + ByteArray y; |
+ +620 + | ++ + + + + + | +|
+ +621 + | ++ + + + + + | + private static TpmsEccPoint parse(ByteInputStream reader) throws IOException { |
+ +622 + | ++ + + + + + | + final ByteArray x = new ByteArray(reader.read(reader.readUnsignedShort())); |
+ +623 + | ++ + + + + + | + final ByteArray y = new ByteArray(reader.read(reader.readUnsignedShort())); |
+ +624 + | ++ + + + + + | +|
+ +625 + | +
+
+1
+
+1. parse : replaced return value with null for com/yubico/webauthn/TpmAttestationStatementVerifier$TpmsEccPoint::parse → KILLED + + + + |
+ return new TpmsEccPoint(x, y); |
+ +626 + | ++ + + + + + | + } |
+ +627 + | ++ + + + + + | + } |
+ +628 + | ++ + + + + + | +|
+ +629 + | ++ + + + + + | + /** |
+ +630 + | ++ + + + + + | + * TPM_ECC_CURVE |
+ +631 + | ++ + + + + + | + * |
+ +632 + | ++ + + + + + | + * <p>https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf |
+ +633 + | ++ + + + + + | + * section 6.4 |
+ +634 + | ++ + + + + + | + */ |
+ +635 + | ++ + + + + + | + private static class TpmEccCurve { |
+ +636 + | ++ + + + + + | +|
+ +637 + | ++ + + + + + | + private static final int NONE = 0x0000; |
+ +638 + | ++ + + + + + | + private static final int NIST_P256 = 0x0003; |
+ +639 + | ++ + + + + + | + private static final int NIST_P384 = 0x0004; |
+ +640 + | ++ + + + + + | + private static final int NIST_P521 = 0x0005; |
+ +641 + | ++ + + + + + | + } |
+ +642 + | ++ + + + + + | +|
+ +643 + | ++ + + + + + | + /** |
+ +644 + | ++ + + + + + | + * the signature data is defined by [TPMv2-Part2] Section 10.12.8 (TPMS_ATTEST) as: |
+ +645 + | ++ + + + + + | + * TPM_GENERATED_VALUE (0xff544347 aka "\xffTCG") TPMI_ST_ATTEST - always TPM_ST_ATTEST_CERTIFY |
+ +646 + | ++ + + + + + | + * (0x8017) because signing procedure defines it should call TPM_Certify [TPMv2-Part3] Section |
+ +647 + | ++ + + + + + | + * 18.2 TPM2B_NAME size (uint16) name (size long) TPM2B_DATA size (uint16) name (size long) |
+ +648 + | ++ + + + + + | + * TPMS_CLOCK_INFO clock (uint64) resetCount (uint32) restartCount (uint32) safe (byte) 1 yes, 0 |
+ +649 + | ++ + + + + + | + * no firmwareVersion uint64 attested TPMS_CERTIFY_INFO (because TPM_ST_ATTEST_CERTIFY) name |
+ +650 + | ++ + + + + + | + * TPM2B_NAME qualified_name TPM2B_NAME See: |
+ +651 + | ++ + + + + + | + * https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf |
+ +652 + | ++ + + + + + | + * https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf |
+ +653 + | ++ + + + + + | + */ |
+ +654 + | ++ + + + + + | + @Value |
+ +655 + | ++ + + + + + | + private static class TpmsAttest { |
+ +656 + | ++ + + + + + | +|
+ +657 + | ++ + + + + + | + ByteArray rawBytes; |
+ +658 + | ++ + + + + + | + ByteArray extraData; |
+ +659 + | ++ + + + + + | + ByteArray attestedName; |
+ +660 + | ++ + + + + + | +|
+ +661 + | ++ + + + + + | + private static TpmsAttest parse(byte[] certInfo) throws IOException { |
+ +662 + | ++ + + + + + | + try (ByteInputStream reader = new ByteInputStream(certInfo)) { |
+ +663 + | ++ + + + + + | + final ByteArray magic = new ByteArray(reader.read(4)); |
+ +664 + | ++ + + + + + | +|
+ +665 + | ++ + + + + + | + // Verify that magic is set to TPM_GENERATED_VALUE. |
+ +666 + | ++ + + + + + | + // see https://w3c.github.io/webauthn/#sctn-tpm-attestation |
+ +667 + | ++ + + + + + | + // verification procedure |
+ +668 + | +
+
+1
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +669 + | ++ + + + + + | + magic.equals(TPM_GENERATED_VALUE), "magic field is invalid: %s", magic); |
+ +670 + | ++ + + + + + | +|
+ +671 + | ++ + + + + + | + // Verify that type is set to TPM_ST_ATTEST_CERTIFY. |
+ +672 + | ++ + + + + + | + // see https://w3c.github.io/webauthn/#sctn-tpm-attestation |
+ +673 + | ++ + + + + + | + // verification procedure |
+ +674 + | ++ + + + + + | + final ByteArray type = new ByteArray(reader.read(2)); |
+ +675 + | +
+
+1
+
+1. parse : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +676 + | ++ + + + + + | + type.equals(TPM_ST_ATTEST_CERTIFY), "type field is invalid: %s", type); |
+ +677 + | ++ + + + + + | +|
+ +678 + | ++ + + + + + | + // qualifiedSigner is not used by this implementation |
+ +679 + | ++ + + + + + | + reader.skipBytes(reader.readUnsignedShort()); |
+ +680 + | ++ + + + + + | +|
+ +681 + | ++ + + + + + | + final ByteArray extraData = new ByteArray(reader.read(reader.readUnsignedShort())); |
+ +682 + | ++ + + + + + | +|
+ +683 + | ++ + + + + + | + // clockInfo is not used by this implementation |
+ +684 + | ++ + + + + + | + reader.skipBytes(8 + 4 + 4 + 1); |
+ +685 + | ++ + + + + + | +|
+ +686 + | ++ + + + + + | + // firmwareVersion is not used by this implementation |
+ +687 + | ++ + + + + + | + reader.skipBytes(8); |
+ +688 + | ++ + + + + + | +|
+ +689 + | ++ + + + + + | + final ByteArray attestedName = new ByteArray(reader.read(reader.readUnsignedShort())); |
+ +690 + | ++ + + + + + | +|
+ +691 + | ++ + + + + + | + // attestedQualifiedName is not used by this implementation |
+ +692 + | ++ + + + + + | +|
+ +693 + | ++ + + + + + | + return new TpmsAttest(new ByteArray(certInfo), extraData, attestedName); |
+ +694 + | ++ + + + + + | + } |
+ +695 + | ++ + + + + + | + } |
+ +696 + | ++ + + + + + | + } |
+ +697 + | ++ + + + + + | +} |
Mutations | ||
91 | ++ |
+
+
+
+ 1.1 |
+
104 | ++ |
+
+
+
+ 1.1 2.2 |
+
105 | ++ |
+
+
+
+ 1.1 2.2 |
+
111 | ++ |
+
+
+
+ 1.1 2.2 |
+
112 | ++ |
+
+
+
+ 1.1 |
+
119 | ++ |
+
+
+
+ 1.1 |
+
122 | ++ |
+
+
+
+ 1.1 2.2 |
+
123 | ++ |
+
+
+
+ 1.1 |
+
132 | ++ |
+
+
+
+ 1.1 |
+
140 | ++ |
+
+
+
+ 1.1 2.2 |
+
141 | ++ |
+
+
+
+ 1.1 |
+
152 | ++ |
+
+
+
+ 1.1 2.2 |
+
153 | ++ |
+
+
+
+ 1.1 |
+
158 | ++ |
+
+
+
+ 1.1 2.2 |
+
159 | ++ |
+
+
+
+ 1.1 |
+
180 | ++ |
+
+
+
+ 1.1 |
+
192 | ++ |
+
+
+
+ 1.1 |
+
197 | ++ |
+
+
+
+ 1.1 |
+
240 | ++ |
+
+
+
+ 1.1 |
+
247 | ++ |
+
+
+
+ 1.1 |
+
255 | ++ |
+
+
+
+ 1.1 |
+
264 | ++ |
+
+
+
+ 1.1 |
+
292 | ++ |
+
+
+
+ 1.1 |
+
338 | ++ |
+
+
+
+ 1.1 |
+
345 | ++ |
+
+
+
+ 1.1 |
+
350 | ++ |
+
+
+
+ 1.1 |
+
387 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
398 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
403 | ++ |
+
+
+
+ 1.1 |
+
406 | ++ |
+
+
+
+ 1.1 |
+
413 | ++ |
+
+
+
+ 1.1 |
+
414 | ++ |
+
+
+
+ 1.1 |
+
464 | ++ |
+
+
+
+ 1.1 |
+
477 | ++ |
+
+
+
+ 1.1 |
+
478 | ++ |
+
+
+
+ 1.1 |
+
482 | ++ |
+
+
+
+ 1.1 |
+
491 | ++ |
+
+
+
+ 1.1 |
+
492 | ++ |
+
+
+
+ 1.1 |
+
496 | ++ |
+
+
+
+ 1.1 |
+
497 | ++ |
+
+
+
+ 1.1 |
+
498 | ++ |
+
+
+
+ 1.1 2.2 |
+
499 | ++ |
+
+
+
+ 1.1 2.2 |
+
510 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 |
+
513 | ++ |
+
+
+
+ 1.1 |
+
514 | ++ |
+
+
+
+ 1.1 |
+
515 | ++ |
+
+
+
+ 1.1 |
+
517 | ++ |
+
+
+
+ 1.1 |
+
518 | ++ |
+
+
+
+ 1.1 2.2 |
+
522 | ++ |
+
+
+
+ 1.1 |
+
523 | ++ |
+
+
+
+ 1.1 |
+
527 | ++ |
+
+
+
+ 1.1 |
+
529 | ++ |
+
+
+
+ 1.1 |
+
551 | ++ |
+
+
+
+ 1.1 2.2 |
+
557 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
565 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
567 | ++ |
+
+
+
+ 1.1 |
+
572 | ++ |
+
+
+
+ 1.1 |
+
581 | ++ |
+
+
+
+ 1.1 |
+
592 | ++ |
+
+
+
+ 1.1 2.2 |
+
596 | ++ |
+
+
+
+ 1.1 2.2 |
+
604 | ++ |
+
+
+
+ 1.1 |
+
625 | ++ |
+
+
+
+ 1.1 |
+
668 | ++ |
+
+
+
+ 1.1 |
+
675 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +// Copyright (c) 2014-2018, Yubico AB |
+ +2 + | ++ + + + + + | +// Copyright (c) 2014, Google Inc. |
+ +3 + | ++ + + + + + | +// All rights reserved. |
+ +4 + | ++ + + + + + | +// |
+ +5 + | ++ + + + + + | +// Redistribution and use in source and binary forms, with or without |
+ +6 + | ++ + + + + + | +// modification, are permitted provided that the following conditions are met: |
+ +7 + | ++ + + + + + | +// |
+ +8 + | ++ + + + + + | +// 1. Redistributions of source code must retain the above copyright notice, this |
+ +9 + | ++ + + + + + | +// list of conditions and the following disclaimer. |
+ +10 + | ++ + + + + + | +// |
+ +11 + | ++ + + + + + | +// 2. Redistributions in binary form must reproduce the above copyright notice, |
+ +12 + | ++ + + + + + | +// this list of conditions and the following disclaimer in the documentation |
+ +13 + | ++ + + + + + | +// and/or other materials provided with the distribution. |
+ +14 + | ++ + + + + + | +// |
+ +15 + | ++ + + + + + | +// 3. Neither the name of Google Inc. nor the names of its contributors may be |
+ +16 + | ++ + + + + + | +// used to endorse or promote products derived from this software without |
+ +17 + | ++ + + + + + | +// specific prior written permission. |
+ +18 + | ++ + + + + + | +// |
+ +19 + | ++ + + + + + | +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
+ +20 + | ++ + + + + + | +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
+ +21 + | ++ + + + + + | +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
+ +22 + | ++ + + + + + | +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
+ +23 + | ++ + + + + + | +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
+ +24 + | ++ + + + + + | +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
+ +25 + | ++ + + + + + | +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
+ +26 + | ++ + + + + + | +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
+ +27 + | ++ + + + + + | +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ +28 + | ++ + + + + + | +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ +29 + | ++ + + + + + | +|
+ +30 + | ++ + + + + + | +package com.yubico.webauthn; |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | +import com.google.common.io.ByteArrayDataOutput; |
+ +33 + | ++ + + + + + | +import com.google.common.io.ByteStreams; |
+ +34 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +35 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +36 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +37 + | ++ + + + + + | +import lombok.Value; |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | +/** The register response produced by the token/key */ |
+ +40 + | ++ + + + + + | +@Value |
+ +41 + | ++ + + + + + | +class U2fRawRegisterResponse { |
+ +42 + | ++ + + + + + | + private static final byte REGISTRATION_SIGNED_RESERVED_BYTE_VALUE = (byte) 0x00; |
+ +43 + | ++ + + + + + | +|
+ +44 + | ++ + + + + + | + /** The (uncompressed) x,y-representation of a curve point on the P-256 NIST elliptic curve. */ |
+ +45 + | ++ + + + + + | + private final ByteArray userPublicKey; |
+ +46 + | ++ + + + + + | +|
+ +47 + | ++ + + + + + | + /** A handle that allows the U2F token to identify the generated key pair. */ |
+ +48 + | ++ + + + + + | + private final ByteArray keyHandle; |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + private final X509Certificate attestationCertificate; |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** A ECDSA signature (on P-256) */ |
+ +53 + | ++ + + + + + | + private final ByteArray signature; |
+ +54 + | ++ + + + + + | +|
+ +55 + | ++ + + + + + | + U2fRawRegisterResponse( |
+ +56 + | ++ + + + + + | + ByteArray userPublicKey, |
+ +57 + | ++ + + + + + | + ByteArray keyHandle, |
+ +58 + | ++ + + + + + | + X509Certificate attestationCertificate, |
+ +59 + | ++ + + + + + | + ByteArray signature) { |
+ +60 + | ++ + + + + + | + this.userPublicKey = userPublicKey; |
+ +61 + | ++ + + + + + | + this.keyHandle = keyHandle; |
+ +62 + | ++ + + + + + | + this.attestationCertificate = attestationCertificate; |
+ +63 + | ++ + + + + + | + this.signature = signature; |
+ +64 + | ++ + + + + + | + } |
+ +65 + | ++ + + + + + | +|
+ +66 + | ++ + + + + + | + boolean verifySignature(ByteArray appIdHash, ByteArray clientDataHash) { |
+ +67 + | ++ + + + + + | + ByteArray signedBytes = packBytesToSign(appIdHash, clientDataHash, keyHandle, userPublicKey); |
+ +68 + | +
+
+2
+
+1. verifySignature : replaced boolean return with true for com/yubico/webauthn/U2fRawRegisterResponse::verifySignature → KILLED +2. verifySignature : replaced boolean return with false for com/yubico/webauthn/U2fRawRegisterResponse::verifySignature → KILLED + + + + |
+ return Crypto.verifySignature( |
+ +69 + | ++ + + + + + | + attestationCertificate, signedBytes, signature, COSEAlgorithmIdentifier.ES256); |
+ +70 + | ++ + + + + + | + } |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + private static ByteArray packBytesToSign( |
+ +73 + | ++ + + + + + | + ByteArray appIdHash, ByteArray clientDataHash, ByteArray keyHandle, ByteArray userPublicKey) { |
+ +74 + | ++ + + + + + | + ByteArrayDataOutput encoded = ByteStreams.newDataOutput(); |
+ +75 + | +
+
+1
+
+1. packBytesToSign : removed call to com/google/common/io/ByteArrayDataOutput::write → KILLED + + + + |
+ encoded.write(REGISTRATION_SIGNED_RESERVED_BYTE_VALUE); |
+ +76 + | +
+
+1
+
+1. packBytesToSign : removed call to com/google/common/io/ByteArrayDataOutput::write → KILLED + + + + |
+ encoded.write(appIdHash.getBytes()); |
+ +77 + | +
+
+1
+
+1. packBytesToSign : removed call to com/google/common/io/ByteArrayDataOutput::write → KILLED + + + + |
+ encoded.write(clientDataHash.getBytes()); |
+ +78 + | +
+
+1
+
+1. packBytesToSign : removed call to com/google/common/io/ByteArrayDataOutput::write → KILLED + + + + |
+ encoded.write(keyHandle.getBytes()); |
+ +79 + | +
+
+1
+
+1. packBytesToSign : removed call to com/google/common/io/ByteArrayDataOutput::write → KILLED + + + + |
+ encoded.write(userPublicKey.getBytes()); |
+ +80 + | +
+
+1
+
+1. packBytesToSign : replaced return value with null for com/yubico/webauthn/U2fRawRegisterResponse::packBytesToSign → KILLED + + + + |
+ return new ByteArray(encoded.toByteArray()); |
+ +81 + | ++ + + + + + | + } |
+ +82 + | ++ + + + + + | +} |
Mutations | ||
68 | ++ |
+
+
+
+ 1.1 2.2 |
+
75 | ++ |
+
+
+
+ 1.1 |
+
76 | ++ |
+
+
+
+ 1.1 |
+
77 | ++ |
+
+
+
+ 1.1 |
+
78 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
80 | ++ |
+
+
+
+ 1.1 |
+
+ +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 COSE.CoseException; |
+ +28 + | ++ + + + + + | +import COSE.OneKey; |
+ +29 + | ++ + + + + + | +import com.google.common.primitives.Bytes; |
+ +30 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +31 + | ++ + + + + + | +import com.yubico.webauthn.data.ByteArray; |
+ +32 + | ++ + + + + + | +import com.yubico.webauthn.data.COSEAlgorithmIdentifier; |
+ +33 + | ++ + + + + + | +import java.io.IOException; |
+ +34 + | ++ + + + + + | +import java.math.BigInteger; |
+ +35 + | ++ + + + + + | +import java.security.KeyFactory; |
+ +36 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +37 + | ++ + + + + + | +import java.security.PublicKey; |
+ +38 + | ++ + + + + + | +import java.security.interfaces.ECPublicKey; |
+ +39 + | ++ + + + + + | +import java.security.spec.InvalidKeySpecException; |
+ +40 + | ++ + + + + + | +import java.security.spec.RSAPublicKeySpec; |
+ +41 + | ++ + + + + + | +import java.security.spec.X509EncodedKeySpec; |
+ +42 + | ++ + + + + + | +import java.util.Arrays; |
+ +43 + | ++ + + + + + | +import java.util.HashMap; |
+ +44 + | ++ + + + + + | +import java.util.Map; |
+ +45 + | ++ + + + + + | +|
+ +46 + | ++ + + + + + | +final class WebAuthnCodecs { |
+ +47 + | ++ + + + + + | +|
+ +48 + | ++ + + + + + | + private static final ByteArray ED25519_CURVE_OID = |
+ +49 + | ++ + + + + + | + new ByteArray(new byte[] {0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x70}); |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + static ByteArray ecPublicKeyToRaw(ECPublicKey key) { |
+ +52 + | ++ + + + + + | +|
+ +53 + | ++ + + + + + | + final int fieldSizeBytes = |
+ +54 + | ++ + + + + + | + Math.toIntExact( |
+ +55 + | +
+
+1
+
+1. ecPublicKeyToRaw : Replaced double division with multiplication → KILLED + + + + |
+ Math.round(Math.ceil(key.getParams().getCurve().getField().getFieldSize() / 8.0))); |
+ +56 + | ++ + + + + + | + byte[] x = key.getW().getAffineX().toByteArray(); |
+ +57 + | ++ + + + + + | + byte[] y = key.getW().getAffineY().toByteArray(); |
+ +58 + | +
+
+1
+
+1. ecPublicKeyToRaw : Replaced integer subtraction with addition → KILLED + + + + |
+ byte[] xPadding = new byte[Math.max(0, fieldSizeBytes - x.length)]; |
+ +59 + | +
+
+1
+
+1. ecPublicKeyToRaw : Replaced integer subtraction with addition → KILLED + + + + |
+ byte[] yPadding = new byte[Math.max(0, fieldSizeBytes - y.length)]; |
+ +60 + | ++ + + + + + | +|
+ +61 + | +
+
+1
+
+1. ecPublicKeyToRaw : removed call to java/util/Arrays::fill → SURVIVED + + + + |
+ Arrays.fill(xPadding, (byte) 0); |
+ +62 + | +
+
+1
+
+1. ecPublicKeyToRaw : removed call to java/util/Arrays::fill → SURVIVED + + + + |
+ Arrays.fill(yPadding, (byte) 0); |
+ +63 + | ++ + + + + + | +|
+ +64 + | +
+
+2
+
+1. ecPublicKeyToRaw : Replaced integer subtraction with addition → KILLED +2. ecPublicKeyToRaw : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::ecPublicKeyToRaw → KILLED + + + + |
+ return new ByteArray( |
+ +65 + | ++ + + + + + | + Bytes.concat( |
+ +66 + | ++ + + + + + | + new byte[] {0x04}, |
+ +67 + | ++ + + + + + | + xPadding, |
+ +68 + | +
+
+1
+
+1. ecPublicKeyToRaw : Replaced integer subtraction with addition → KILLED + + + + |
+ Arrays.copyOfRange(x, Math.max(0, x.length - fieldSizeBytes), x.length), |
+ +69 + | ++ + + + + + | + yPadding, |
+ +70 + | ++ + + + + + | + Arrays.copyOfRange(y, Math.max(0, y.length - fieldSizeBytes), y.length))); |
+ +71 + | ++ + + + + + | + } |
+ +72 + | ++ + + + + + | +|
+ +73 + | ++ + + + + + | + static ByteArray rawEcKeyToCose(ByteArray key) { |
+ +74 + | ++ + + + + + | + final byte[] keyBytes = key.getBytes(); |
+ +75 + | ++ + + + + + | + final int len = keyBytes.length; |
+ +76 + | +
+
+1
+
+1. rawEcKeyToCose : Replaced integer subtraction with addition → KILLED + + + + |
+ final int lenSub1 = keyBytes.length - 1; |
+ +77 + | +
+
+7
+
+1. rawEcKeyToCose : negated conditional → SURVIVED +2. rawEcKeyToCose : negated conditional → SURVIVED +3. rawEcKeyToCose : negated conditional → SURVIVED +4. rawEcKeyToCose : negated conditional → NO_COVERAGE +5. rawEcKeyToCose : negated conditional → KILLED +6. rawEcKeyToCose : negated conditional → KILLED +7. rawEcKeyToCose : negated conditional → KILLED + + + + |
+ if (!(len == 64 |
+ +78 + | ++ + + + + + | + || len == 96 |
+ +79 + | ++ + + + + + | + || len == 132 |
+ +80 + | ++ + + + + + | + || (keyBytes[0] == 0x04 && (lenSub1 == 64 || lenSub1 == 96 || lenSub1 == 132)))) { |
+ +81 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +82 + | ++ + + + + + | + String.format( |
+ +83 + | ++ + + + + + | + "Raw key must be 64, 96 or 132 bytes long, or start with 0x04 and be 65, 97 or 133 bytes long; was %d bytes starting with %02x", |
+ +84 + | ++ + + + + + | + keyBytes.length, keyBytes[0])); |
+ +85 + | ++ + + + + + | + } |
+ +86 + | +
+
+3
+
+1. rawEcKeyToCose : negated conditional → KILLED +2. rawEcKeyToCose : negated conditional → KILLED +3. rawEcKeyToCose : negated conditional → KILLED + + + + |
+ final int start = (len == 64 || len == 96 || len == 132) ? 0 : 1; |
+ +87 + | +
+
+2
+
+1. rawEcKeyToCose : Replaced integer subtraction with addition → KILLED +2. rawEcKeyToCose : Replaced integer division with multiplication → KILLED + + + + |
+ final int coordinateLength = (len - start) / 2; |
+ +88 + | ++ + + + + + | +|
+ +89 + | ++ + + + + + | + final Map<Long, Object> coseKey = new HashMap<>(); |
+ +90 + | ++ + + + + + | + coseKey.put(1L, 2L); // Key type: EC |
+ +91 + | ++ + + + + + | +|
+ +92 + | ++ + + + + + | + final COSEAlgorithmIdentifier coseAlg; |
+ +93 + | ++ + + + + + | + final int coseCrv; |
+ +94 + | +
+
+1
+
+1. rawEcKeyToCose : Replaced integer subtraction with addition → KILLED + + + + |
+ switch (len - start) { |
+ +95 + | ++ + + + + + | + case 64: |
+ +96 + | ++ + + + + + | + coseAlg = COSEAlgorithmIdentifier.ES256; |
+ +97 + | ++ + + + + + | + coseCrv = 1; |
+ +98 + | ++ + + + + + | + break; |
+ +99 + | ++ + + + + + | + case 96: |
+ +100 + | ++ + + + + + | + coseAlg = COSEAlgorithmIdentifier.ES384; |
+ +101 + | ++ + + + + + | + coseCrv = 2; |
+ +102 + | ++ + + + + + | + break; |
+ +103 + | ++ + + + + + | + case 132: |
+ +104 + | ++ + + + + + | + coseAlg = COSEAlgorithmIdentifier.ES512; |
+ +105 + | ++ + + + + + | + coseCrv = 3; |
+ +106 + | ++ + + + + + | + break; |
+ +107 + | ++ + + + + + | + default: |
+ +108 + | ++ + + + + + | + throw new RuntimeException( |
+ +109 + | ++ + + + + + | + "Failed to determine COSE EC algorithm. This should not be possible, please file a bug report."); |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | + coseKey.put(3L, coseAlg.getId()); |
+ +112 + | ++ + + + + + | + coseKey.put(-1L, coseCrv); |
+ +113 + | ++ + + + + + | +|
+ +114 + | +
+
+1
+
+1. rawEcKeyToCose : Replaced integer addition with subtraction → KILLED + + + + |
+ coseKey.put(-2L, Arrays.copyOfRange(keyBytes, start, start + coordinateLength)); // x |
+ +115 + | ++ + + + + + | + coseKey.put( |
+ +116 + | +
+
+3
+
+1. rawEcKeyToCose : Replaced integer addition with subtraction → KILLED +2. rawEcKeyToCose : Replaced integer addition with subtraction → KILLED +3. rawEcKeyToCose : Replaced integer multiplication with division → KILLED + + + + |
+ -3L, |
+ +117 + | ++ + + + + + | + Arrays.copyOfRange(keyBytes, start + coordinateLength, start + 2 * coordinateLength)); // y |
+ +118 + | ++ + + + + + | +|
+ +119 + | +
+
+1
+
+1. rawEcKeyToCose : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::rawEcKeyToCose → KILLED + + + + |
+ return new ByteArray(CBORObject.FromObject(coseKey).EncodeToBytes()); |
+ +120 + | ++ + + + + + | + } |
+ +121 + | ++ + + + + + | +|
+ +122 + | ++ + + + + + | + static PublicKey importCosePublicKey(ByteArray key) |
+ +123 + | ++ + + + + + | + throws CoseException, IOException, InvalidKeySpecException, NoSuchAlgorithmException { |
+ +124 + | ++ + + + + + | + CBORObject cose = CBORObject.DecodeFromBytes(key.getBytes()); |
+ +125 + | ++ + + + + + | + final int kty = cose.get(CBORObject.FromObject(1)).AsInt32(); |
+ +126 + | ++ + + + + + | + switch (kty) { |
+ +127 + | ++ + + + + + | + case 1: |
+ +128 + | ++ + + + + + | + // COSE-JAVA is hardcoded to ed25519-java provider ("EdDSA") which would require an |
+ +129 + | ++ + + + + + | + // additional dependency to parse EdDSA keys via the OneKey constructor |
+ +130 + | +
+
+1
+
+1. importCosePublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCosePublicKey → KILLED + + + + |
+ return importCoseEdDsaPublicKey(cose); |
+ +131 + | ++ + + + + + | + case 2: |
+ +132 + | +
+
+1
+
+1. importCosePublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCosePublicKey → KILLED + + + + |
+ return importCoseP256PublicKey(cose); |
+ +133 + | ++ + + + + + | + case 3: |
+ +134 + | ++ + + + + + | + // COSE-JAVA supports RSA in v1.1.0 but not in v1.0.0 |
+ +135 + | +
+
+1
+
+1. importCosePublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCosePublicKey → KILLED + + + + |
+ return importCoseRsaPublicKey(cose); |
+ +136 + | ++ + + + + + | + default: |
+ +137 + | ++ + + + + + | + throw new IllegalArgumentException("Unsupported key type: " + kty); |
+ +138 + | ++ + + + + + | + } |
+ +139 + | ++ + + + + + | + } |
+ +140 + | ++ + + + + + | +|
+ +141 + | ++ + + + + + | + private static PublicKey importCoseRsaPublicKey(CBORObject cose) |
+ +142 + | ++ + + + + + | + throws NoSuchAlgorithmException, InvalidKeySpecException { |
+ +143 + | ++ + + + + + | + RSAPublicKeySpec spec = |
+ +144 + | ++ + + + + + | + new RSAPublicKeySpec( |
+ +145 + | ++ + + + + + | + new BigInteger(1, cose.get(CBORObject.FromObject(-1)).GetByteString()), |
+ +146 + | ++ + + + + + | + new BigInteger(1, cose.get(CBORObject.FromObject(-2)).GetByteString())); |
+ +147 + | +
+
+1
+
+1. importCoseRsaPublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCoseRsaPublicKey → KILLED + + + + |
+ return KeyFactory.getInstance("RSA").generatePublic(spec); |
+ +148 + | ++ + + + + + | + } |
+ +149 + | ++ + + + + + | +|
+ +150 + | ++ + + + + + | + private static ECPublicKey importCoseP256PublicKey(CBORObject cose) throws CoseException { |
+ +151 + | +
+
+1
+
+1. importCoseP256PublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCoseP256PublicKey → KILLED + + + + |
+ return (ECPublicKey) new OneKey(cose).AsPublicKey(); |
+ +152 + | ++ + + + + + | + } |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + private static PublicKey importCoseEdDsaPublicKey(CBORObject cose) |
+ +155 + | ++ + + + + + | + throws InvalidKeySpecException, NoSuchAlgorithmException { |
+ +156 + | ++ + + + + + | + final int curveId = cose.get(CBORObject.FromObject(-1)).AsInt32(); |
+ +157 + | ++ + + + + + | + switch (curveId) { |
+ +158 + | ++ + + + + + | + case 6: |
+ +159 + | +
+
+1
+
+1. importCoseEdDsaPublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCoseEdDsaPublicKey → KILLED + + + + |
+ return importCoseEd25519PublicKey(cose); |
+ +160 + | ++ + + + + + | + default: |
+ +161 + | ++ + + + + + | + throw new IllegalArgumentException("Unsupported EdDSA curve: " + curveId); |
+ +162 + | ++ + + + + + | + } |
+ +163 + | ++ + + + + + | + } |
+ +164 + | ++ + + + + + | +|
+ +165 + | ++ + + + + + | + private static PublicKey importCoseEd25519PublicKey(CBORObject cose) |
+ +166 + | ++ + + + + + | + throws InvalidKeySpecException, NoSuchAlgorithmException { |
+ +167 + | ++ + + + + + | + final ByteArray rawKey = new ByteArray(cose.get(CBORObject.FromObject(-2)).GetByteString()); |
+ +168 + | ++ + + + + + | + final ByteArray x509Key = |
+ +169 + | +
+
+2
+
+1. importCoseEd25519PublicKey : Replaced integer addition with subtraction → KILLED +2. importCoseEd25519PublicKey : Replaced integer addition with subtraction → KILLED + + + + |
+ new ByteArray(new byte[] {0x30, (byte) (ED25519_CURVE_OID.size() + 3 + rawKey.size())}) |
+ +170 + | ++ + + + + + | + .concat(ED25519_CURVE_OID) |
+ +171 + | +
+
+1
+
+1. importCoseEd25519PublicKey : Replaced integer addition with subtraction → KILLED + + + + |
+ .concat(new ByteArray(new byte[] {0x03, (byte) (rawKey.size() + 1), 0})) |
+ +172 + | ++ + + + + + | + .concat(rawKey); |
+ +173 + | ++ + + + + + | +|
+ +174 + | ++ + + + + + | + KeyFactory kFact = KeyFactory.getInstance("EdDSA"); |
+ +175 + | +
+
+1
+
+1. importCoseEd25519PublicKey : replaced return value with null for com/yubico/webauthn/WebAuthnCodecs::importCoseEd25519PublicKey → KILLED + + + + |
+ return kFact.generatePublic(new X509EncodedKeySpec(x509Key.getBytes())); |
+ +176 + | ++ + + + + + | + } |
+ +177 + | ++ + + + + + | +|
+ +178 + | ++ + + + + + | + static String getJavaAlgorithmName(COSEAlgorithmIdentifier alg) { |
+ +179 + | ++ + + + + + | + switch (alg) { |
+ +180 + | ++ + + + + + | + case EdDSA: |
+ +181 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "EDDSA"; |
+ +182 + | ++ + + + + + | + case ES256: |
+ +183 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA256withECDSA"; |
+ +184 + | ++ + + + + + | + case ES384: |
+ +185 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA384withECDSA"; |
+ +186 + | ++ + + + + + | + case ES512: |
+ +187 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA512withECDSA"; |
+ +188 + | ++ + + + + + | + case RS256: |
+ +189 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA256withRSA"; |
+ +190 + | ++ + + + + + | + case RS384: |
+ +191 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA384withRSA"; |
+ +192 + | ++ + + + + + | + case RS512: |
+ +193 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA512withRSA"; |
+ +194 + | ++ + + + + + | + case RS1: |
+ +195 + | +
+
+1
+
+1. getJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::getJavaAlgorithmName → KILLED + + + + |
+ return "SHA1withRSA"; |
+ +196 + | ++ + + + + + | + default: |
+ +197 + | ++ + + + + + | + throw new IllegalArgumentException("Unknown algorithm: " + alg); |
+ +198 + | ++ + + + + + | + } |
+ +199 + | ++ + + + + + | + } |
+ +200 + | ++ + + + + + | +|
+ +201 + | ++ + + + + + | + static String jwsAlgorithmNameToJavaAlgorithmName(String alg) { |
+ +202 + | +
+
+1
+
+1. jwsAlgorithmNameToJavaAlgorithmName : negated conditional → KILLED + + + + |
+ switch (alg) { |
+ +203 + | ++ + + + + + | + case "RS256": |
+ +204 + | +
+
+1
+
+1. jwsAlgorithmNameToJavaAlgorithmName : replaced return value with "" for com/yubico/webauthn/WebAuthnCodecs::jwsAlgorithmNameToJavaAlgorithmName → KILLED + + + + |
+ return "SHA256withRSA"; |
+ +205 + | ++ + + + + + | + } |
+ +206 + | ++ + + + + + | + throw new IllegalArgumentException("Unknown algorithm: " + alg); |
+ +207 + | ++ + + + + + | + } |
+ +208 + | ++ + + + + + | +} |
Mutations | ||
55 | ++ |
+
+
+
+ 1.1 |
+
58 | ++ |
+
+
+
+ 1.1 |
+
59 | ++ |
+
+
+
+ 1.1 |
+
61 | ++ |
+
+
+
+ 1.1 |
+
62 | ++ |
+
+
+
+ 1.1 |
+
64 | ++ |
+
+
+
+ 1.1 2.2 |
+
68 | ++ |
+
+
+
+ 1.1 |
+
76 | ++ |
+
+
+
+ 1.1 |
+
77 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 5.5 6.6 7.7 |
+
86 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
87 | ++ |
+
+
+
+ 1.1 2.2 |
+
94 | ++ |
+
+
+
+ 1.1 |
+
114 | ++ |
+
+
+
+ 1.1 |
+
116 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
119 | ++ |
+
+
+
+ 1.1 |
+
130 | ++ |
+
+
+
+ 1.1 |
+
132 | ++ |
+
+
+
+ 1.1 |
+
135 | ++ |
+
+
+
+ 1.1 |
+
147 | ++ |
+
+
+
+ 1.1 |
+
151 | ++ |
+
+
+
+ 1.1 |
+
159 | ++ |
+
+
+
+ 1.1 |
+
169 | ++ |
+
+
+
+ 1.1 2.2 |
+
171 | ++ |
+
+
+
+ 1.1 |
+
175 | ++ |
+
+
+
+ 1.1 |
+
181 | ++ |
+
+
+
+ 1.1 |
+
183 | ++ |
+
+
+
+ 1.1 |
+
185 | ++ |
+
+
+
+ 1.1 |
+
187 | ++ |
+
+
+
+ 1.1 |
+
189 | ++ |
+
+
+
+ 1.1 |
+
191 | ++ |
+
+
+
+ 1.1 |
+
193 | ++ |
+
+
+
+ 1.1 |
+
195 | ++ |
+
+
+
+ 1.1 |
+
202 | ++ |
+
+
+
+ 1.1 |
+
204 | ++ |
+
+
+
+ 1.1 |
+
+ +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.databind.JsonNode; |
+ +28 + | ++ + + + + + | +import com.yubico.internal.util.CertificateParser; |
+ +29 + | ++ + + + + + | +import com.yubico.webauthn.data.AttestationObject; |
+ +30 + | ++ + + + + + | +import java.io.IOException; |
+ +31 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +32 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +33 + | ++ + + + + + | +import java.util.ArrayList; |
+ +34 + | ++ + + + + + | +import java.util.List; |
+ +35 + | ++ + + + + + | +import java.util.Optional; |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | +interface X5cAttestationStatementVerifier { |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | + default Optional<X509Certificate> getX5cAttestationCertificate( |
+ +40 + | ++ + + + + + | + AttestationObject attestationObject) throws CertificateException { |
+ +41 + | +
+
+2
+
+1. getX5cAttestationCertificate : replaced return value with Optional.empty for com/yubico/webauthn/X5cAttestationStatementVerifier::getX5cAttestationCertificate → KILLED +2. lambda$getX5cAttestationCertificate$0 : replaced return value with Optional.empty for com/yubico/webauthn/X5cAttestationStatementVerifier::lambda$getX5cAttestationCertificate$0 → KILLED + + + + |
+ return getAttestationTrustPath(attestationObject).flatMap(certs -> certs.stream().findFirst()); |
+ +42 + | ++ + + + + + | + } |
+ +43 + | ++ + + + + + | +|
+ +44 + | ++ + + + + + | + default Optional<List<X509Certificate>> getAttestationTrustPath( |
+ +45 + | ++ + + + + + | + AttestationObject attestationObject) throws CertificateException { |
+ +46 + | ++ + + + + + | + JsonNode x5cNode = getX5cArray(attestationObject); |
+ +47 + | ++ + + + + + | +|
+ +48 + | +
+
+2
+
+1. getAttestationTrustPath : negated conditional → KILLED +2. getAttestationTrustPath : negated conditional → KILLED + + + + |
+ if (x5cNode != null && x5cNode.isArray()) { |
+ +49 + | ++ + + + + + | + List<X509Certificate> certs = new ArrayList<>(x5cNode.size()); |
+ +50 + | ++ + + + + + | +|
+ +51 + | ++ + + + + + | + for (JsonNode binary : x5cNode) { |
+ +52 + | +
+
+1
+
+1. getAttestationTrustPath : negated conditional → KILLED + + + + |
+ if (binary.isBinary()) { |
+ +53 + | ++ + + + + + | + try { |
+ +54 + | ++ + + + + + | + certs.add(CertificateParser.parseDer(binary.binaryValue())); |
+ +55 + | ++ + + + + + | + } catch (IOException e) { |
+ +56 + | ++ + + + + + | + throw new RuntimeException( |
+ +57 + | ++ + + + + + | + "binary.isBinary() was true but binary.binaryValue() failed", e); |
+ +58 + | ++ + + + + + | + } |
+ +59 + | ++ + + + + + | + } else { |
+ +60 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +61 + | ++ + + + + + | + String.format( |
+ +62 + | ++ + + + + + | + "Each element of \"x5c\" property of attestation statement must be a binary value, was: %s", |
+ +63 + | ++ + + + + + | + binary.getNodeType())); |
+ +64 + | ++ + + + + + | + } |
+ +65 + | ++ + + + + + | + } |
+ +66 + | ++ + + + + + | +|
+ +67 + | +
+
+1
+
+1. getAttestationTrustPath : replaced return value with Optional.empty for com/yubico/webauthn/X5cAttestationStatementVerifier::getAttestationTrustPath → KILLED + + + + |
+ return Optional.of(certs); |
+ +68 + | ++ + + + + + | + } else { |
+ +69 + | ++ + + + + + | + return Optional.empty(); |
+ +70 + | ++ + + + + + | + } |
+ +71 + | ++ + + + + + | + } |
+ +72 + | ++ + + + + + | +|
+ +73 + | ++ + + + + + | + default JsonNode getX5cArray(AttestationObject attestationObject) { |
+ +74 + | +
+
+1
+
+1. getX5cArray : replaced return value with null for com/yubico/webauthn/X5cAttestationStatementVerifier::getX5cArray → KILLED + + + + |
+ return attestationObject.getAttestationStatement().get("x5c"); |
+ +75 + | ++ + + + + + | + } |
+ +76 + | ++ + + + + + | +} |
Mutations | ||
41 | ++ |
+
+
+
+ 1.1 2.2 |
+
48 | ++ |
+
+
+
+ 1.1 2.2 |
+
52 | ++ |
+
+
+
+ 1.1 |
+
67 | ++ |
+
+
+
+ 1.1 |
+
74 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
27 | +85% | +89% | +92% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
AndroidSafetynetAttestationStatementVerifier.java | +83% |
+ 100% |
+ 100% |
+
AppleAttestationStatementVerifier.java | +84% |
+ 75% |
+ 86% |
+
AssertionRequest.java | +100% |
+ 100% |
+ 100% |
+
AssertionResult.java | +100% |
+ 94% |
+ 94% |
+
AssertionResultV2.java | +95% |
+ 93% |
+ 93% |
+
CredentialRecord.java | +100% |
+ 100% |
+ 100% |
+
CredentialRepositoryV1ToV2Adapter.java | +56% |
+ 83% |
+ 100% |
+
Crypto.java | +94% |
+ 100% |
+ 100% |
+
FidoU2fAttestationStatementVerifier.java | +71% |
+ 83% |
+ 90% |
+
FinishAssertionOptions.java | +85% |
+ 75% |
+ 100% |
+
FinishAssertionSteps.java | +79% |
+ 88% |
+ 90% |
+
FinishRegistrationOptions.java | +84% |
+ 75% |
+ 100% |
+
FinishRegistrationSteps.java | +82% |
+ 89% |
+ 90% |
+
NoneAttestationStatementVerifier.java | +100% |
+ 100% |
+ 100% |
+
OriginMatcher.java | +97% |
+ 100% |
+ 100% |
+
PackedAttestationStatementVerifier.java | +77% |
+ 86% |
+ 95% |
+
RegisteredCredential.java | +97% |
+ 87% |
+ 93% |
+
RegistrationResult.java | +92% |
+ 93% |
+ 97% |
+
RelyingParty.java | +92% |
+ 95% |
+ 100% |
+
RelyingPartyV2.java | +96% |
+ 100% |
+ 100% |
+
StartAssertionOptions.java | +97% |
+ 100% |
+ 100% |
+
StartRegistrationOptions.java | +96% |
+ 100% |
+ 100% |
+
TokenBindingValidator.java | +79% |
+ 23% |
+ 23% |
+
TpmAttestationStatementVerifier.java | +86% |
+ 84% |
+ 86% |
+
U2fRawRegisterResponse.java | +75% |
+ 100% |
+ 100% |
+
WebAuthnCodecs.java | +85% |
+ 87% |
+ 89% |
+
X5cAttestationStatementVerifier.java | +71% |
+ 100% |
+ 100% |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
68 | +87% | +90% | +94% | +
Name | +Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|---|
com.yubico.webauthn | +27 | +85% |
+ 89% |
+ 92% |
+
com.yubico.webauthn.attestation | +1 | +94% |
+ 93% |
+ 93% |
+
com.yubico.webauthn.data | +33 | +94% |
+ 94% |
+ 97% |
+
com.yubico.webauthn.extension.appid | +1 | +100% |
+ 100% |
+ 100% |
+
com.yubico.webauthn.extension.uvm | +3 | +73% |
+ 50% |
+ 100% |
+
com.yubico.webauthn.meta | +3 | +0% |
+ 0% |
+ 100% |
+
+ +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.internal.util; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import java.io.IOException; |
+ +28 + | ++ + + + + + | +import java.io.InputStream; |
+ +29 + | ++ + + + + + | +import java.nio.ByteBuffer; |
+ +30 + | ++ + + + + + | +import java.nio.ByteOrder; |
+ +31 + | ++ + + + + + | +import java.util.Arrays; |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | +public class BinaryUtil { |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + public static byte[] copy(byte[] bytes) { |
+ +36 + | +
+
+1
+
+1. copy : replaced return value with null for com/yubico/internal/util/BinaryUtil::copy → NO_COVERAGE + + + + |
+ return Arrays.copyOf(bytes, bytes.length); |
+ +37 + | ++ + + + + + | + } |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | + /** |
+ +40 + | ++ + + + + + | + * @param bytes Bytes to encode |
+ +41 + | ++ + + + + + | + */ |
+ +42 + | ++ + + + + + | + public static String toHex(final byte[] bytes) { |
+ +43 + | +
+
+1
+
+1. toHex : Replaced integer multiplication with division → KILLED + + + + |
+ final char[] digits = new char[bytes.length * 2]; |
+ +44 + | +
+
+2
+
+1. toHex : negated conditional → KILLED +2. toHex : changed conditional boundary → KILLED + + + + |
+ for (int i = 0; i < bytes.length; ++i) { |
+ +45 + | +
+
+1
+
+1. toHex : Replaced integer multiplication with division → KILLED + + + + |
+ final int i2 = i * 2; |
+ +46 + | +
+
+2
+
+1. toHex : Replaced Shift Right with Shift Left → KILLED +2. toHex : Replaced bitwise AND with OR → KILLED + + + + |
+ digits[i2] = Character.forDigit((bytes[i] >> 4) & 0x0f, 16); |
+ +47 + | +
+
+2
+
+1. toHex : Replaced bitwise AND with OR → KILLED +2. toHex : Replaced integer addition with subtraction → KILLED + + + + |
+ digits[i2 + 1] = Character.forDigit(bytes[i] & 0x0f, 16); |
+ +48 + | ++ + + + + + | + } |
+ +49 + | +
+
+1
+
+1. toHex : replaced return value with "" for com/yubico/internal/util/BinaryUtil::toHex → KILLED + + + + |
+ return new String(digits); |
+ +50 + | ++ + + + + + | + } |
+ +51 + | ++ + + + + + | +|
+ +52 + | ++ + + + + + | + /** |
+ +53 + | ++ + + + + + | + * @param hex String of hexadecimal digits to decode as bytes. |
+ +54 + | ++ + + + + + | + */ |
+ +55 + | ++ + + + + + | + public static byte[] fromHex(final String hex) { |
+ +56 + | +
+
+2
+
+1. fromHex : negated conditional → KILLED +2. fromHex : Replaced integer modulus with multiplication → KILLED + + + + |
+ if (hex.length() % 2 != 0) { |
+ +57 + | ++ + + + + + | + throw new IllegalArgumentException("Length of hex string is not even: " + hex); |
+ +58 + | ++ + + + + + | + } |
+ +59 + | ++ + + + + + | +|
+ +60 + | +
+
+1
+
+1. fromHex : Replaced integer division with multiplication → KILLED + + + + |
+ final byte[] result = new byte[hex.length() / 2]; |
+ +61 + | +
+
+2
+
+1. fromHex : changed conditional boundary → KILLED +2. fromHex : negated conditional → KILLED + + + + |
+ for (int i = 0; i < hex.length(); ++i) { |
+ +62 + | ++ + + + + + | + final int d = Character.digit(hex.charAt(i), 16); |
+ +63 + | +
+
+2
+
+1. fromHex : changed conditional boundary → KILLED +2. fromHex : negated conditional → KILLED + + + + |
+ if (d < 0) { |
+ +64 + | ++ + + + + + | + throw new IllegalArgumentException("Invalid hex digit at index " + i + " in: " + hex); |
+ +65 + | ++ + + + + + | + } |
+ +66 + | +
+
+6
+
+1. fromHex : Replaced integer multiplication with division → KILLED +2. fromHex : Replaced integer addition with subtraction → KILLED +3. fromHex : Replaced Shift Left with Shift Right → KILLED +4. fromHex : Replaced bitwise OR with AND → KILLED +5. fromHex : Replaced integer modulus with multiplication → KILLED +6. fromHex : Replaced integer division with multiplication → KILLED + + + + |
+ result[i / 2] |= d << (((i + 1) % 2) * 4); |
+ +67 + | ++ + + + + + | + } |
+ +68 + | +
+
+1
+
+1. fromHex : replaced return value with null for com/yubico/internal/util/BinaryUtil::fromHex → KILLED + + + + |
+ return result; |
+ +69 + | ++ + + + + + | + } |
+ +70 + | ++ + + + + + | +|
+ +71 + | ++ + + + + + | + /** |
+ +72 + | ++ + + + + + | + * Parse a single byte from two hexadecimal characters. |
+ +73 + | ++ + + + + + | + * |
+ +74 + | ++ + + + + + | + * @param hex String of hexadecimal digits to decode as bytes. |
+ +75 + | ++ + + + + + | + */ |
+ +76 + | ++ + + + + + | + public static byte singleFromHex(String hex) { |
+ +77 + | +
+
+1
+
+1. singleFromHex : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → NO_COVERAGE + + + + |
+ ExceptionUtil.assertTrue( |
+ +78 + | +
+
+1
+
+1. singleFromHex : negated conditional → NO_COVERAGE + + + + |
+ hex.length() == 2, "Argument must be exactly 2 hexadecimal characters, was: %s", hex); |
+ +79 + | +
+
+1
+
+1. singleFromHex : replaced byte return with 0 for com/yubico/internal/util/BinaryUtil::singleFromHex → NO_COVERAGE + + + + |
+ return fromHex(hex)[0]; |
+ +80 + | ++ + + + + + | + } |
+ +81 + | ++ + + + + + | +|
+ +82 + | ++ + + + + + | + /** |
+ +83 + | ++ + + + + + | + * Read one byte as an unsigned 8-bit integer. |
+ +84 + | ++ + + + + + | + * |
+ +85 + | ++ + + + + + | + * <p>Result is of type <code>short</code> because Java doesn't have unsigned types. |
+ +86 + | ++ + + + + + | + * |
+ +87 + | ++ + + + + + | + * @return A value between 0 and 255, inclusive. |
+ +88 + | ++ + + + + + | + */ |
+ +89 + | ++ + + + + + | + public static short getUint8(byte b) { |
+ +90 + | ++ + + + + + | + // Prepend a zero so we can parse it as a signed int16 instead of a signed int8 |
+ +91 + | +
+
+1
+
+1. getUint8 : replaced short return with 0 for com/yubico/internal/util/BinaryUtil::getUint8 → KILLED + + + + |
+ return ByteBuffer.wrap(new byte[] {0, b}).order(ByteOrder.BIG_ENDIAN).getShort(); |
+ +92 + | ++ + + + + + | + } |
+ +93 + | ++ + + + + + | +|
+ +94 + | ++ + + + + + | + /** |
+ +95 + | ++ + + + + + | + * Read 2 bytes as a big endian unsigned 16-bit integer. |
+ +96 + | ++ + + + + + | + * |
+ +97 + | ++ + + + + + | + * <p>Result is of type <code>int</code> because Java doesn't have unsigned types. |
+ +98 + | ++ + + + + + | + * |
+ +99 + | ++ + + + + + | + * @return A value between 0 and 2^16- 1, inclusive. |
+ +100 + | ++ + + + + + | + */ |
+ +101 + | ++ + + + + + | + public static int getUint16(byte[] bytes) { |
+ +102 + | +
+
+1
+
+1. getUint16 : negated conditional → KILLED + + + + |
+ if (bytes.length == 2) { |
+ +103 + | ++ + + + + + | + // Prepend zeroes so we can parse it as a signed int32 instead of a signed int16 |
+ +104 + | +
+
+1
+
+1. getUint16 : replaced int return with 0 for com/yubico/internal/util/BinaryUtil::getUint16 → KILLED + + + + |
+ return ByteBuffer.wrap(new byte[] {0, 0, bytes[0], bytes[1]}) |
+ +105 + | ++ + + + + + | + .order(ByteOrder.BIG_ENDIAN) |
+ +106 + | ++ + + + + + | + .getInt(); |
+ +107 + | ++ + + + + + | + } else { |
+ +108 + | ++ + + + + + | + throw new IllegalArgumentException("Argument must be 2 bytes, was: " + bytes.length); |
+ +109 + | ++ + + + + + | + } |
+ +110 + | ++ + + + + + | + } |
+ +111 + | ++ + + + + + | +|
+ +112 + | ++ + + + + + | + /** |
+ +113 + | ++ + + + + + | + * Read 4 bytes as a big endian unsigned 32-bit integer. |
+ +114 + | ++ + + + + + | + * |
+ +115 + | ++ + + + + + | + * <p>Result is of type <code>long</code> because Java doesn't have unsigned types. |
+ +116 + | ++ + + + + + | + * |
+ +117 + | ++ + + + + + | + * @return A value between 0 and 2^32 - 1, inclusive. |
+ +118 + | ++ + + + + + | + */ |
+ +119 + | ++ + + + + + | + public static long getUint32(byte[] bytes) { |
+ +120 + | +
+
+1
+
+1. getUint32 : negated conditional → KILLED + + + + |
+ if (bytes.length == 4) { |
+ +121 + | ++ + + + + + | + // Prepend zeroes so we can parse it as a signed int32 instead of a signed int16 |
+ +122 + | +
+
+1
+
+1. getUint32 : replaced long return with 0 for com/yubico/internal/util/BinaryUtil::getUint32 → KILLED + + + + |
+ return ByteBuffer.wrap(new byte[] {0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3]}) |
+ +123 + | ++ + + + + + | + .order(ByteOrder.BIG_ENDIAN) |
+ +124 + | ++ + + + + + | + .getLong(); |
+ +125 + | ++ + + + + + | + } else { |
+ +126 + | ++ + + + + + | + throw new IllegalArgumentException("Argument must be 4 bytes, was: " + bytes.length); |
+ +127 + | ++ + + + + + | + } |
+ +128 + | ++ + + + + + | + } |
+ +129 + | ++ + + + + + | +|
+ +130 + | ++ + + + + + | + public static byte[] encodeUint16(int value) { |
+ +131 + | +
+
+3
+
+1. encodeUint16 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +2. encodeUint16 : changed conditional boundary → KILLED +3. encodeUint16 : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue(value >= 0, "Argument must be non-negative, was: %d", value); |
+ +132 + | +
+
+3
+
+1. encodeUint16 : changed conditional boundary → SURVIVED +2. encodeUint16 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → KILLED +3. encodeUint16 : negated conditional → KILLED + + + + |
+ ExceptionUtil.assertTrue( |
+ +133 + | ++ + + + + + | + value < 65536, "Argument must be smaller than 2^16=65536, was: %d", value); |
+ +134 + | ++ + + + + + | +|
+ +135 + | ++ + + + + + | + ByteBuffer b = ByteBuffer.allocate(4); |
+ +136 + | ++ + + + + + | + b.order(ByteOrder.BIG_ENDIAN); |
+ +137 + | ++ + + + + + | + b.putInt(value); |
+ +138 + | ++ + + + + + | + b.rewind(); |
+ +139 + | +
+
+1
+
+1. encodeUint16 : replaced return value with null for com/yubico/internal/util/BinaryUtil::encodeUint16 → KILLED + + + + |
+ return Arrays.copyOfRange(b.array(), 2, 4); |
+ +140 + | ++ + + + + + | + } |
+ +141 + | ++ + + + + + | +|
+ +142 + | ++ + + + + + | + public static byte[] encodeUint32(long value) { |
+ +143 + | +
+
+3
+
+1. encodeUint32 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → NO_COVERAGE +2. encodeUint32 : negated conditional → NO_COVERAGE +3. encodeUint32 : changed conditional boundary → NO_COVERAGE + + + + |
+ ExceptionUtil.assertTrue(value >= 0, "Argument must be non-negative, was: %d", value); |
+ +144 + | +
+
+3
+
+1. encodeUint32 : removed call to com/yubico/internal/util/ExceptionUtil::assertTrue → NO_COVERAGE +2. encodeUint32 : negated conditional → NO_COVERAGE +3. encodeUint32 : changed conditional boundary → NO_COVERAGE + + + + |
+ ExceptionUtil.assertTrue( |
+ +145 + | ++ + + + + + | + value < 4294967296L, "Argument must be smaller than 2^32=4294967296, was: %d", value); |
+ +146 + | ++ + + + + + | +|
+ +147 + | ++ + + + + + | + ByteBuffer b = ByteBuffer.allocate(8); |
+ +148 + | ++ + + + + + | + b.order(ByteOrder.BIG_ENDIAN); |
+ +149 + | ++ + + + + + | + b.putLong(value); |
+ +150 + | ++ + + + + + | + b.rewind(); |
+ +151 + | +
+
+1
+
+1. encodeUint32 : replaced return value with null for com/yubico/internal/util/BinaryUtil::encodeUint32 → NO_COVERAGE + + + + |
+ return Arrays.copyOfRange(b.array(), 4, 8); |
+ +152 + | ++ + + + + + | + } |
+ +153 + | ++ + + + + + | +|
+ +154 + | ++ + + + + + | + public static byte[] readAll(InputStream is) throws IOException { |
+ +155 + | ++ + + + + + | + byte[] buffer = new byte[1024]; |
+ +156 + | ++ + + + + + | + int bufferLen = 0; |
+ +157 + | ++ + + + + + | + while (true) { |
+ +158 + | +
+
+1
+
+1. readAll : Replaced integer subtraction with addition → NO_COVERAGE + + + + |
+ final int moreLen = is.read(buffer, bufferLen, buffer.length - bufferLen); |
+ +159 + | +
+
+2
+
+1. readAll : changed conditional boundary → NO_COVERAGE +2. readAll : negated conditional → NO_COVERAGE + + + + |
+ if (moreLen <= 0) { |
+ +160 + | +
+
+1
+
+1. readAll : replaced return value with null for com/yubico/internal/util/BinaryUtil::readAll → NO_COVERAGE + + + + |
+ return Arrays.copyOf(buffer, bufferLen); |
+ +161 + | ++ + + + + + | + } else { |
+ +162 + | +
+
+1
+
+1. readAll : Replaced integer addition with subtraction → NO_COVERAGE + + + + |
+ bufferLen += moreLen; |
+ +163 + | +
+
+1
+
+1. readAll : negated conditional → NO_COVERAGE + + + + |
+ if (bufferLen == buffer.length) { |
+ +164 + | +
+
+1
+
+1. readAll : Replaced integer multiplication with division → NO_COVERAGE + + + + |
+ buffer = Arrays.copyOf(buffer, buffer.length * 2); |
+ +165 + | ++ + + + + + | + } |
+ +166 + | ++ + + + + + | + } |
+ +167 + | ++ + + + + + | + } |
+ +168 + | ++ + + + + + | + } |
+ +169 + | ++ + + + + + | +} |
Mutations | ||
36 | ++ |
+
+
+
+ 1.1 |
+
43 | ++ |
+
+
+
+ 1.1 |
+
44 | ++ |
+
+
+
+ 1.1 2.2 |
+
45 | ++ |
+
+
+
+ 1.1 |
+
46 | ++ |
+
+
+
+ 1.1 2.2 |
+
47 | ++ |
+
+
+
+ 1.1 2.2 |
+
49 | ++ |
+
+
+
+ 1.1 |
+
56 | ++ |
+
+
+
+ 1.1 2.2 |
+
60 | ++ |
+
+
+
+ 1.1 |
+
61 | ++ |
+
+
+
+ 1.1 2.2 |
+
63 | ++ |
+
+
+
+ 1.1 2.2 |
+
66 | ++ |
+
+
+
+ 1.1 2.2 3.3 4.4 5.5 6.6 |
+
68 | ++ |
+
+
+
+ 1.1 |
+
77 | ++ |
+
+
+
+ 1.1 |
+
78 | ++ |
+
+
+
+ 1.1 |
+
79 | ++ |
+
+
+
+ 1.1 |
+
91 | ++ |
+
+
+
+ 1.1 |
+
102 | ++ |
+
+
+
+ 1.1 |
+
104 | ++ |
+
+
+
+ 1.1 |
+
120 | ++ |
+
+
+
+ 1.1 |
+
122 | ++ |
+
+
+
+ 1.1 |
+
131 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
132 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
139 | ++ |
+
+
+
+ 1.1 |
+
143 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
144 | ++ |
+
+
+
+ 1.1 2.2 3.3 |
+
151 | ++ |
+
+
+
+ 1.1 |
+
158 | ++ |
+
+
+
+ 1.1 |
+
159 | ++ |
+
+
+
+ 1.1 2.2 |
+
160 | ++ |
+
+
+
+ 1.1 |
+
162 | ++ |
+
+
+
+ 1.1 |
+
163 | ++ |
+
+
+
+ 1.1 |
+
164 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +// Copyright (c) 2014-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.internal.util; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import java.io.ByteArrayInputStream; |
+ +28 + | ++ + + + + + | +import java.io.DataInputStream; |
+ +29 + | ++ + + + + + | +import java.io.IOException; |
+ +30 + | ++ + + + + + | +|
+ +31 + | ++ + + + + + | +/** Provides an easy way to read a byte array in chunks. */ |
+ +32 + | ++ + + + + + | +public class ByteInputStream extends DataInputStream { |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | + public ByteInputStream(byte[] data) { |
+ +35 + | ++ + + + + + | + super(new ByteArrayInputStream(data)); |
+ +36 + | ++ + + + + + | + } |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + public byte[] read(int numberOfBytes) throws IOException { |
+ +39 + | ++ + + + + + | + byte[] readBytes = new byte[numberOfBytes]; |
+ +40 + | +
+
+1
+
+1. read : removed call to com/yubico/internal/util/ByteInputStream::readFully → NO_COVERAGE + + + + |
+ readFully(readBytes); |
+ +41 + | +
+
+1
+
+1. read : replaced return value with null for com/yubico/internal/util/ByteInputStream::read → NO_COVERAGE + + + + |
+ return readBytes; |
+ +42 + | ++ + + + + + | + } |
+ +43 + | ++ + + + + + | +|
+ +44 + | ++ + + + + + | + public byte[] readAll() throws IOException { |
+ +45 + | ++ + + + + + | + byte[] readBytes = new byte[available()]; |
+ +46 + | +
+
+1
+
+1. readAll : removed call to com/yubico/internal/util/ByteInputStream::readFully → NO_COVERAGE + + + + |
+ readFully(readBytes); |
+ +47 + | +
+
+1
+
+1. readAll : replaced return value with null for com/yubico/internal/util/ByteInputStream::readAll → NO_COVERAGE + + + + |
+ return readBytes; |
+ +48 + | ++ + + + + + | + } |
+ +49 + | ++ + + + + + | +|
+ +50 + | ++ + + + + + | + public int readInteger() throws IOException { |
+ +51 + | +
+
+1
+
+1. readInteger : replaced int return with 0 for com/yubico/internal/util/ByteInputStream::readInteger → NO_COVERAGE + + + + |
+ return readInt(); |
+ +52 + | ++ + + + + + | + } |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + public byte readSigned() throws IOException { |
+ +55 + | +
+
+1
+
+1. readSigned : replaced byte return with 0 for com/yubico/internal/util/ByteInputStream::readSigned → NO_COVERAGE + + + + |
+ return readByte(); |
+ +56 + | ++ + + + + + | + } |
+ +57 + | ++ + + + + + | +|
+ +58 + | ++ + + + + + | + public int readUnsigned() throws IOException { |
+ +59 + | +
+
+1
+
+1. readUnsigned : replaced int return with 0 for com/yubico/internal/util/ByteInputStream::readUnsigned → NO_COVERAGE + + + + |
+ return readUnsignedByte(); |
+ +60 + | ++ + + + + + | + } |
+ +61 + | ++ + + + + + | +} |
Mutations | ||
40 | ++ |
+
+
+
+ 1.1 |
+
41 | ++ |
+
+
+
+ 1.1 |
+
46 | ++ |
+
+
+
+ 1.1 |
+
47 | ++ |
+
+
+
+ 1.1 |
+
51 | ++ |
+
+
+
+ 1.1 |
+
55 | ++ |
+
+
+
+ 1.1 |
+
59 | ++ |
+
+
+
+ 1.1 |
+
+ +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.internal.util; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import java.io.ByteArrayInputStream; |
+ +28 + | ++ + + + + + | +import java.io.InputStream; |
+ +29 + | ++ + + + + + | +import java.nio.ByteBuffer; |
+ +30 + | ++ + + + + + | +import java.security.MessageDigest; |
+ +31 + | ++ + + + + + | +import java.security.NoSuchAlgorithmException; |
+ +32 + | ++ + + + + + | +import java.security.cert.Certificate; |
+ +33 + | ++ + + + + + | +import java.security.cert.CertificateException; |
+ +34 + | ++ + + + + + | +import java.security.cert.CertificateFactory; |
+ +35 + | ++ + + + + + | +import java.security.cert.X509Certificate; |
+ +36 + | ++ + + + + + | +import java.util.Arrays; |
+ +37 + | ++ + + + + + | +import java.util.Base64; |
+ +38 + | ++ + + + + + | +import java.util.List; |
+ +39 + | ++ + + + + + | +import java.util.Optional; |
+ +40 + | ++ + + + + + | +|
+ +41 + | ++ + + + + + | +public class CertificateParser { |
+ +42 + | ++ + + + + + | + public static final String ID_FIDO_GEN_CE_AAGUID = "1.3.6.1.4.1.45724.1.1.4"; |
+ +43 + | ++ + + + + + | + private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder(); |
+ +44 + | ++ + + + + + | +|
+ +45 + | ++ + + + + + | + private static final List<String> FIXSIG = |
+ +46 + | ++ + + + + + | + Arrays.asList( |
+ +47 + | ++ + + + + + | + "CN=Yubico U2F EE Serial 776137165", |
+ +48 + | ++ + + + + + | + "CN=Yubico U2F EE Serial 1086591525", |
+ +49 + | ++ + + + + + | + "CN=Yubico U2F EE Serial 1973679733", |
+ +50 + | ++ + + + + + | + "CN=Yubico U2F EE Serial 13503277888", |
+ +51 + | ++ + + + + + | + "CN=Yubico U2F EE Serial 13831167861", |
+ +52 + | ++ + + + + + | + "CN=Yubico U2F EE Serial 14803321578"); |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + private static final int UNUSED_BITS_BYTE_INDEX_FROM_END = 257; |
+ +55 + | ++ + + + + + | +|
+ +56 + | ++ + + + + + | + public static X509Certificate parsePem(String pemEncodedCert) throws CertificateException { |
+ +57 + | +
+
+1
+
+1. parsePem : replaced return value with null for com/yubico/internal/util/CertificateParser::parsePem → KILLED + + + + |
+ return parseDer( |
+ +58 + | ++ + + + + + | + pemEncodedCert |
+ +59 + | ++ + + + + + | + .replaceAll("-----BEGIN CERTIFICATE-----", "") |
+ +60 + | ++ + + + + + | + .replaceAll("-----END CERTIFICATE-----", "") |
+ +61 + | ++ + + + + + | + .replaceAll("\n", "")); |
+ +62 + | ++ + + + + + | + } |
+ +63 + | ++ + + + + + | +|
+ +64 + | ++ + + + + + | + public static X509Certificate parseDer(String base64DerEncodedCert) throws CertificateException { |
+ +65 + | +
+
+1
+
+1. parseDer : replaced return value with null for com/yubico/internal/util/CertificateParser::parseDer → KILLED + + + + |
+ return parseDer(BASE64_DECODER.decode(base64DerEncodedCert)); |
+ +66 + | ++ + + + + + | + } |
+ +67 + | ++ + + + + + | +|
+ +68 + | ++ + + + + + | + public static X509Certificate parseDer(byte[] derEncodedCert) throws CertificateException { |
+ +69 + | +
+
+1
+
+1. parseDer : replaced return value with null for com/yubico/internal/util/CertificateParser::parseDer → KILLED + + + + |
+ return parseDer(new ByteArrayInputStream(derEncodedCert)); |
+ +70 + | ++ + + + + + | + } |
+ +71 + | ++ + + + + + | +|
+ +72 + | ++ + + + + + | + public static X509Certificate parseDer(InputStream is) throws CertificateException { |
+ +73 + | ++ + + + + + | + X509Certificate cert = |
+ +74 + | ++ + + + + + | + (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is); |
+ +75 + | ++ + + + + + | + // Some known certs have an incorrect "unused bits" value, which causes problems on newer |
+ +76 + | ++ + + + + + | + // versions of BouncyCastle. |
+ +77 + | +
+
+1
+
+1. parseDer : negated conditional → SURVIVED + + + + |
+ if (FIXSIG.contains(cert.getSubjectX500Principal().getName())) { |
+ +78 + | ++ + + + + + | + byte[] encoded = cert.getEncoded(); |
+ +79 + | ++ + + + + + | +|
+ +80 + | +
+
+2
+
+1. parseDer : changed conditional boundary → SURVIVED +2. parseDer : negated conditional → KILLED + + + + |
+ if (encoded.length >= UNUSED_BITS_BYTE_INDEX_FROM_END) { |
+ +81 + | +
+
+1
+
+1. parseDer : Replaced integer subtraction with addition → KILLED + + + + |
+ encoded[encoded.length - UNUSED_BITS_BYTE_INDEX_FROM_END] = |
+ +82 + | ++ + + + + + | + 0; // Fix the "unused bits" field (should always be 0). |
+ +83 + | ++ + + + + + | + } else { |
+ +84 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +85 + | ++ + + + + + | + String.format( |
+ +86 + | ++ + + + + + | + "Expected DER encoded cert to be at least %d bytes, was %d: %s", |
+ +87 + | ++ + + + + + | + UNUSED_BITS_BYTE_INDEX_FROM_END, encoded.length, cert)); |
+ +88 + | ++ + + + + + | + } |
+ +89 + | ++ + + + + + | +|
+ +90 + | ++ + + + + + | + cert = |
+ +91 + | ++ + + + + + | + (X509Certificate) |
+ +92 + | ++ + + + + + | + CertificateFactory.getInstance("X.509") |
+ +93 + | ++ + + + + + | + .generateCertificate(new ByteArrayInputStream(encoded)); |
+ +94 + | ++ + + + + + | + } |
+ +95 + | +
+
+1
+
+1. parseDer : replaced return value with null for com/yubico/internal/util/CertificateParser::parseDer → KILLED + + + + |
+ return cert; |
+ +96 + | ++ + + + + + | + } |
+ +97 + | ++ + + + + + | +|
+ +98 + | ++ + + + + + | + /** |
+ +99 + | ++ + + + + + | + * Compute a Subject Key Identifier as defined as method (1) in RFC 5280 section 4.2.1.2. |
+ +100 + | ++ + + + + + | + * |
+ +101 + | ++ + + + + + | + * @throws NoSuchAlgorithmException if the SHA-1 hash algorithm is not available. |
+ +102 + | ++ + + + + + | + * @see <a href="https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2">Internet X.509 |
+ +103 + | ++ + + + + + | + * Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile, |
+ +104 + | ++ + + + + + | + * section 4.2.1.2. Subject Key Identifier</a> |
+ +105 + | ++ + + + + + | + */ |
+ +106 + | ++ + + + + + | + public static byte[] computeSubjectKeyIdentifier(final Certificate cert) |
+ +107 + | ++ + + + + + | + throws NoSuchAlgorithmException { |
+ +108 + | ++ + + + + + | + final byte[] spki = cert.getPublicKey().getEncoded(); |
+ +109 + | ++ + + + + + | +|
+ +110 + | ++ + + + + + | + // SubjectPublicKeyInfo ::= SEQUENCE { |
+ +111 + | ++ + + + + + | + // algorithm AlgorithmIdentifier, |
+ +112 + | ++ + + + + + | + // subjectPublicKey BIT STRING } |
+ +113 + | ++ + + + + + | + final byte algLength = spki[2 + 1]; |
+ +114 + | ++ + + + + + | +|
+ +115 + | ++ + + + + + | + // BIT STRING begins with one octet specifying number of unused bits at end; |
+ +116 + | ++ + + + + + | + // this is not included in the content to hash for a Subject Key Identifier. |
+ +117 + | +
+
+2
+
+1. computeSubjectKeyIdentifier : Replaced integer addition with subtraction → KILLED +2. computeSubjectKeyIdentifier : Replaced integer addition with subtraction → KILLED + + + + |
+ final int spkBitsStart = 2 + 2 + 2 + algLength + 1; |
+ +118 + | ++ + + + + + | +|
+ +119 + | +
+
+1
+
+1. computeSubjectKeyIdentifier : replaced return value with null for com/yubico/internal/util/CertificateParser::computeSubjectKeyIdentifier → KILLED + + + + |
+ return MessageDigest.getInstance("SHA-1") |
+ +120 + | ++ + + + + + | + .digest(Arrays.copyOfRange(spki, spkBitsStart, spki.length)); |
+ +121 + | ++ + + + + + | + } |
+ +122 + | ++ + + + + + | +|
+ +123 + | ++ + + + + + | + /** |
+ +124 + | ++ + + + + + | + * Parses an AAGUID into bytes. Refer to <a |
+ +125 + | ++ + + + + + | + * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-packed-attestation-cert-requirements">Packed |
+ +126 + | ++ + + + + + | + * Attestation Statement Certificate Requirements</a> on the W3C web site for details of the ASN.1 |
+ +127 + | ++ + + + + + | + * structure that this method parses. |
+ +128 + | ++ + + + + + | + * |
+ +129 + | ++ + + + + + | + * @param bytes the bytes making up value of the extension |
+ +130 + | ++ + + + + + | + * @return the bytes of the AAGUID |
+ +131 + | ++ + + + + + | + */ |
+ +132 + | ++ + + + + + | + private static byte[] parseAaguid(byte[] bytes) { |
+ +133 + | ++ + + + + + | +|
+ +134 + | +
+
+2
+
+1. parseAaguid : negated conditional → NO_COVERAGE +2. parseAaguid : negated conditional → NO_COVERAGE + + + + |
+ if (bytes != null && bytes.length == 20) { |
+ +135 + | ++ + + + + + | + ByteBuffer buffer = ByteBuffer.wrap(bytes); |
+ +136 + | ++ + + + + + | +|
+ +137 + | +
+
+1
+
+1. parseAaguid : negated conditional → NO_COVERAGE + + + + |
+ if (buffer.get() == (byte) 0x04 |
+ +138 + | +
+
+1
+
+1. parseAaguid : negated conditional → NO_COVERAGE + + + + |
+ && buffer.get() == (byte) 0x12 |
+ +139 + | +
+
+1
+
+1. parseAaguid : negated conditional → NO_COVERAGE + + + + |
+ && buffer.get() == (byte) 0x04 |
+ +140 + | +
+
+1
+
+1. parseAaguid : negated conditional → NO_COVERAGE + + + + |
+ && buffer.get() == (byte) 0x10) { |
+ +141 + | ++ + + + + + | + byte[] aaguidBytes = new byte[16]; |
+ +142 + | ++ + + + + + | + buffer.get(aaguidBytes); |
+ +143 + | ++ + + + + + | +|
+ +144 + | +
+
+1
+
+1. parseAaguid : replaced return value with null for com/yubico/internal/util/CertificateParser::parseAaguid → NO_COVERAGE + + + + |
+ return aaguidBytes; |
+ +145 + | ++ + + + + + | + } |
+ +146 + | ++ + + + + + | + } |
+ +147 + | ++ + + + + + | +|
+ +148 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +149 + | ++ + + + + + | + "X.509 extension 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) is not valid."); |
+ +150 + | ++ + + + + + | + } |
+ +151 + | ++ + + + + + | +|
+ +152 + | ++ + + + + + | + public static Optional<byte[]> parseFidoAaguidExtension(X509Certificate cert) { |
+ +153 + | ++ + + + + + | + Optional<byte[]> result = |
+ +154 + | ++ + + + + + | + Optional.ofNullable(cert.getExtensionValue(ID_FIDO_GEN_CE_AAGUID)) |
+ +155 + | ++ + + + + + | + .map(CertificateParser::parseAaguid); |
+ +156 + | +
+
+1
+
+1. parseFidoAaguidExtension : removed call to java/util/Optional::ifPresent → NO_COVERAGE + + + + |
+ result.ifPresent( |
+ +157 + | ++ + + + + + | + aaguid -> { |
+ +158 + | +
+
+1
+
+1. lambda$parseFidoAaguidExtension$0 : negated conditional → NO_COVERAGE + + + + |
+ if (cert.getCriticalExtensionOIDs().contains(ID_FIDO_GEN_CE_AAGUID)) { |
+ +159 + | ++ + + + + + | + throw new IllegalArgumentException( |
+ +160 + | ++ + + + + + | + String.format( |
+ +161 + | ++ + + + + + | + "X.509 extension %s (id-fido-gen-ce-aaguid) must not be marked critical.", |
+ +162 + | ++ + + + + + | + ID_FIDO_GEN_CE_AAGUID)); |
+ +163 + | ++ + + + + + | + } |
+ +164 + | ++ + + + + + | + }); |
+ +165 + | +
+
+1
+
+1. parseFidoAaguidExtension : replaced return value with Optional.empty for com/yubico/internal/util/CertificateParser::parseFidoAaguidExtension → NO_COVERAGE + + + + |
+ return result; |
+ +166 + | ++ + + + + + | + } |
+ +167 + | ++ + + + + + | +} |
Mutations | ||
57 | ++ |
+
+
+
+ 1.1 |
+
65 | ++ |
+
+
+
+ 1.1 |
+
69 | ++ |
+
+
+
+ 1.1 |
+
77 | ++ |
+
+
+
+ 1.1 |
+
80 | ++ |
+
+
+
+ 1.1 2.2 |
+
81 | ++ |
+
+
+
+ 1.1 |
+
95 | ++ |
+
+
+
+ 1.1 |
+
117 | ++ |
+
+
+
+ 1.1 2.2 |
+
119 | ++ |
+
+
+
+ 1.1 |
+
134 | ++ |
+
+
+
+ 1.1 2.2 |
+
137 | ++ |
+
+
+
+ 1.1 |
+
138 | ++ |
+
+
+
+ 1.1 |
+
139 | ++ |
+
+
+
+ 1.1 |
+
140 | ++ |
+
+
+
+ 1.1 |
+
144 | ++ |
+
+
+
+ 1.1 |
+
156 | ++ |
+
+
+
+ 1.1 |
+
158 | ++ |
+
+
+
+ 1.1 |
+
165 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.internal.util; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.util.ArrayList; |
+ +4 + | ++ + + + + + | +import java.util.Collections; |
+ +5 + | ++ + + + + + | +import java.util.HashMap; |
+ +6 + | ++ + + + + + | +import java.util.HashSet; |
+ +7 + | ++ + + + + + | +import java.util.List; |
+ +8 + | ++ + + + + + | +import java.util.Map; |
+ +9 + | ++ + + + + + | +import java.util.Set; |
+ +10 + | ++ + + + + + | +import java.util.SortedSet; |
+ +11 + | ++ + + + + + | +import java.util.TreeSet; |
+ +12 + | ++ + + + + + | +|
+ +13 + | ++ + + + + + | +public class CollectionUtil { |
+ +14 + | ++ + + + + + | +|
+ +15 + | ++ + + + + + | + /** |
+ +16 + | ++ + + + + + | + * Make an unmodifiable shallow copy of the argument. |
+ +17 + | ++ + + + + + | + * |
+ +18 + | ++ + + + + + | + * @return A shallow copy of <code>m</code> which cannot be modified |
+ +19 + | ++ + + + + + | + */ |
+ +20 + | ++ + + + + + | + public static <K, V> Map<K, V> immutableMap(Map<K, V> m) { |
+ +21 + | +
+
+1
+
+1. immutableMap : replaced return value with Collections.emptyMap for com/yubico/internal/util/CollectionUtil::immutableMap → KILLED + + + + |
+ return Collections.unmodifiableMap(new HashMap<>(m)); |
+ +22 + | ++ + + + + + | + } |
+ +23 + | ++ + + + + + | +|
+ +24 + | ++ + + + + + | + /** |
+ +25 + | ++ + + + + + | + * Make an unmodifiable shallow copy of the argument. |
+ +26 + | ++ + + + + + | + * |
+ +27 + | ++ + + + + + | + * @return A shallow copy of <code>l</code> which cannot be modified |
+ +28 + | ++ + + + + + | + */ |
+ +29 + | ++ + + + + + | + public static <T> List<T> immutableList(List<T> l) { |
+ +30 + | +
+
+1
+
+1. immutableList : replaced return value with Collections.emptyList for com/yubico/internal/util/CollectionUtil::immutableList → KILLED + + + + |
+ return Collections.unmodifiableList(new ArrayList<>(l)); |
+ +31 + | ++ + + + + + | + } |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | + /** |
+ +34 + | ++ + + + + + | + * Alias of <code>s == null ? Collections.emptyList() : CollectionUtil.immutableList(s)</code>. |
+ +35 + | ++ + + + + + | + */ |
+ +36 + | ++ + + + + + | + public static <T> List<T> immutableListOrEmpty(List<T> l) { |
+ +37 + | +
+
+2
+
+1. immutableListOrEmpty : negated conditional → NO_COVERAGE +2. immutableListOrEmpty : replaced return value with Collections.emptyList for com/yubico/internal/util/CollectionUtil::immutableListOrEmpty → NO_COVERAGE + + + + |
+ return l == null ? Collections.emptyList() : immutableList(l); |
+ +38 + | ++ + + + + + | + } |
+ +39 + | ++ + + + + + | +|
+ +40 + | ++ + + + + + | + /** |
+ +41 + | ++ + + + + + | + * Make an unmodifiable shallow copy of the argument. |
+ +42 + | ++ + + + + + | + * |
+ +43 + | ++ + + + + + | + * @return A shallow copy of <code>s</code> which cannot be modified |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | ++ + + + + + | + public static <T> Set<T> immutableSet(Set<T> s) { |
+ +46 + | +
+
+1
+
+1. immutableSet : replaced return value with Collections.emptySet for com/yubico/internal/util/CollectionUtil::immutableSet → KILLED + + + + |
+ return Collections.unmodifiableSet(new HashSet<>(s)); |
+ +47 + | ++ + + + + + | + } |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** Alias of <code>s == null ? Collections.emptySet() : CollectionUtil.immutableSet(s)</code>. */ |
+ +50 + | ++ + + + + + | + public static <T> Set<T> immutableSetOrEmpty(Set<T> s) { |
+ +51 + | +
+
+2
+
+1. immutableSetOrEmpty : replaced return value with Collections.emptySet for com/yubico/internal/util/CollectionUtil::immutableSetOrEmpty → NO_COVERAGE +2. immutableSetOrEmpty : negated conditional → NO_COVERAGE + + + + |
+ return s == null ? Collections.emptySet() : immutableSet(s); |
+ +52 + | ++ + + + + + | + } |
+ +53 + | ++ + + + + + | +|
+ +54 + | ++ + + + + + | + /** |
+ +55 + | ++ + + + + + | + * Make an unmodifiable shallow copy of the argument. |
+ +56 + | ++ + + + + + | + * |
+ +57 + | ++ + + + + + | + * @return A shallow copy of <code>s</code> which cannot be modified |
+ +58 + | ++ + + + + + | + */ |
+ +59 + | ++ + + + + + | + public static <T> SortedSet<T> immutableSortedSet(Set<T> s) { |
+ +60 + | +
+
+1
+
+1. immutableSortedSet : replaced return value with null for com/yubico/internal/util/CollectionUtil::immutableSortedSet → KILLED + + + + |
+ return Collections.unmodifiableSortedSet(new TreeSet<>(s)); |
+ +61 + | ++ + + + + + | + } |
+ +62 + | ++ + + + + + | +} |
Mutations | ||
21 | ++ |
+
+
+
+ 1.1 |
+
30 | ++ |
+
+
+
+ 1.1 |
+
37 | ++ |
+
+
+
+ 1.1 2.2 |
+
46 | ++ |
+
+
+
+ 1.1 |
+
51 | ++ |
+
+
+
+ 1.1 2.2 |
+
60 | ++ |
+
+
+
+ 1.1 |
+
+ +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.internal.util; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import java.util.Iterator; |
+ +28 + | ++ + + + + + | +import java.util.SortedSet; |
+ +29 + | ++ + + + + + | +|
+ +30 + | ++ + + + + + | +public class ComparableUtil { |
+ +31 + | ++ + + + + + | +|
+ +32 + | ++ + + + + + | + public static <T extends Comparable<T>> int compareComparableSets( |
+ +33 + | ++ + + + + + | + SortedSet<T> a, SortedSet<T> b) { |
+ +34 + | +
+
+1
+
+1. compareComparableSets : negated conditional → KILLED + + + + |
+ if (a.size() == b.size()) { |
+ +35 + | ++ + + + + + | + final Iterator<T> as = a.iterator(); |
+ +36 + | ++ + + + + + | + final Iterator<T> bs = b.iterator(); |
+ +37 + | ++ + + + + + | +|
+ +38 + | +
+
+2
+
+1. compareComparableSets : negated conditional → KILLED +2. compareComparableSets : negated conditional → KILLED + + + + |
+ while (as.hasNext() && bs.hasNext()) { |
+ +39 + | ++ + + + + + | + final int comp = as.next().compareTo(bs.next()); |
+ +40 + | +
+
+1
+
+1. compareComparableSets : negated conditional → KILLED + + + + |
+ if (comp != 0) { |
+ +41 + | +
+
+1
+
+1. compareComparableSets : replaced int return with 0 for com/yubico/internal/util/ComparableUtil::compareComparableSets → KILLED + + + + |
+ return comp; |
+ +42 + | ++ + + + + + | + } |
+ +43 + | ++ + + + + + | + } |
+ +44 + | ++ + + + + + | +|
+ +45 + | +
+
+1
+
+1. compareComparableSets : negated conditional → KILLED + + + + |
+ if (as.hasNext()) { |
+ +46 + | +
+
+1
+
+1. compareComparableSets : replaced int return with 0 for com/yubico/internal/util/ComparableUtil::compareComparableSets → NO_COVERAGE + + + + |
+ return 1; |
+ +47 + | +
+
+1
+
+1. compareComparableSets : negated conditional → KILLED + + + + |
+ } else if (bs.hasNext()) { |
+ +48 + | +
+
+1
+
+1. compareComparableSets : replaced int return with 0 for com/yubico/internal/util/ComparableUtil::compareComparableSets → NO_COVERAGE + + + + |
+ return -1; |
+ +49 + | ++ + + + + + | + } else { |
+ +50 + | ++ + + + + + | + return 0; |
+ +51 + | ++ + + + + + | + } |
+ +52 + | ++ + + + + + | + } else { |
+ +53 + | +
+
+2
+
+1. compareComparableSets : replaced int return with 0 for com/yubico/internal/util/ComparableUtil::compareComparableSets → KILLED +2. compareComparableSets : Replaced integer subtraction with addition → KILLED + + + + |
+ return a.size() - b.size(); |
+ +54 + | ++ + + + + + | + } |
+ +55 + | ++ + + + + + | + } |
+ +56 + | ++ + + + + + | +} |
Mutations | ||
34 | ++ |
+
+
+
+ 1.1 |
+
38 | ++ |
+
+
+
+ 1.1 2.2 |
+
40 | ++ |
+
+
+
+ 1.1 |
+
41 | ++ |
+
+
+
+ 1.1 |
+
45 | ++ |
+
+
+
+ 1.1 |
+
46 | ++ |
+
+
+
+ 1.1 |
+
47 | ++ |
+
+
+
+ 1.1 |
+
48 | ++ |
+
+
+
+ 1.1 |
+
53 | ++ |
+
+
+
+ 1.1 2.2 |
+
+ +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.internal.util; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import lombok.experimental.UtilityClass; |
+ +28 + | ++ + + + + + | +import org.slf4j.Logger; |
+ +29 + | ++ + + + + + | +|
+ +30 + | ++ + + + + + | +@UtilityClass |
+ +31 + | ++ + + + + + | +public class ExceptionUtil { |
+ +32 + | ++ + + + + + | +|
+ +33 + | ++ + + + + + | + public static RuntimeException wrapAndLog(Logger log, String message, Throwable t) { |
+ +34 + | ++ + + + + + | + RuntimeException err = new RuntimeException(message, t); |
+ +35 + | ++ + + + + + | + log.error(err.getMessage(), err); |
+ +36 + | +
+
+1
+
+1. wrapAndLog : replaced return value with null for com/yubico/internal/util/ExceptionUtil::wrapAndLog → NO_COVERAGE + + + + |
+ return err; |
+ +37 + | ++ + + + + + | + } |
+ +38 + | ++ + + + + + | +|
+ +39 + | ++ + + + + + | + public static void assertTrue( |
+ +40 + | ++ + + + + + | + boolean condition, String failureMessageTemplate, Object... failureMessageArgs) { |
+ +41 + | +
+
+1
+
+1. assertTrue : negated conditional → KILLED + + + + |
+ if (!condition) { |
+ +42 + | ++ + + + + + | + throw new IllegalArgumentException(String.format(failureMessageTemplate, failureMessageArgs)); |
+ +43 + | ++ + + + + + | + } |
+ +44 + | ++ + + + + + | + } |
+ +45 + | ++ + + + + + | +} |
Mutations | ||
36 | ++ |
+
+
+
+ 1.1 |
+
41 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.internal.util; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import com.fasterxml.jackson.annotation.JsonInclude.Include; |
+ +4 + | ++ + + + + + | +import com.fasterxml.jackson.core.Base64Variants; |
+ +5 + | ++ + + + + + | +import com.fasterxml.jackson.databind.DeserializationFeature; |
+ +6 + | ++ + + + + + | +import com.fasterxml.jackson.databind.ObjectMapper; |
+ +7 + | ++ + + + + + | +import com.fasterxml.jackson.databind.json.JsonMapper; |
+ +8 + | ++ + + + + + | +import com.fasterxml.jackson.databind.node.ObjectNode; |
+ +9 + | ++ + + + + + | +import com.fasterxml.jackson.dataformat.cbor.CBORFactory; |
+ +10 + | ++ + + + + + | +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; |
+ +11 + | ++ + + + + + | +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; |
+ +12 + | ++ + + + + + | +import com.upokecenter.cbor.CBORObject; |
+ +13 + | ++ + + + + + | +import java.io.IOException; |
+ +14 + | ++ + + + + + | +|
+ +15 + | ++ + + + + + | +public class JacksonCodecs { |
+ +16 + | ++ + + + + + | +|
+ +17 + | ++ + + + + + | + public static ObjectMapper cbor() { |
+ +18 + | +
+
+1
+
+1. cbor : replaced return value with null for com/yubico/internal/util/JacksonCodecs::cbor → NO_COVERAGE + + + + |
+ return new ObjectMapper(new CBORFactory()).setBase64Variant(Base64Variants.MODIFIED_FOR_URL); |
+ +19 + | ++ + + + + + | + } |
+ +20 + | ++ + + + + + | +|
+ +21 + | ++ + + + + + | + public static ObjectMapper json() { |
+ +22 + | +
+
+1
+
+1. json : replaced return value with null for com/yubico/internal/util/JacksonCodecs::json → NO_COVERAGE + + + + |
+ return JsonMapper.builder() |
+ +23 + | ++ + + + + + | + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) |
+ +24 + | ++ + + + + + | + .serializationInclusion(Include.NON_ABSENT) |
+ +25 + | ++ + + + + + | + .defaultBase64Variant(Base64Variants.MODIFIED_FOR_URL) |
+ +26 + | ++ + + + + + | + .addModule(new Jdk8Module()) |
+ +27 + | ++ + + + + + | + .addModule(new JavaTimeModule()) |
+ +28 + | ++ + + + + + | + .build(); |
+ +29 + | ++ + + + + + | + } |
+ +30 + | ++ + + + + + | +|
+ +31 + | ++ + + + + + | + public static CBORObject deepCopy(CBORObject a) { |
+ +32 + | +
+
+1
+
+1. deepCopy : replaced return value with null for com/yubico/internal/util/JacksonCodecs::deepCopy → NO_COVERAGE + + + + |
+ return CBORObject.DecodeFromBytes(a.EncodeToBytes()); |
+ +33 + | ++ + + + + + | + } |
+ +34 + | ++ + + + + + | +|
+ +35 + | ++ + + + + + | + public static ObjectNode deepCopy(ObjectNode a) { |
+ +36 + | ++ + + + + + | + try { |
+ +37 + | +
+
+1
+
+1. deepCopy : replaced return value with null for com/yubico/internal/util/JacksonCodecs::deepCopy → NO_COVERAGE + + + + |
+ return (ObjectNode) json().readTree(json().writeValueAsString(a)); |
+ +38 + | ++ + + + + + | + } catch (IOException e) { |
+ +39 + | ++ + + + + + | + throw new RuntimeException(e); |
+ +40 + | ++ + + + + + | + } |
+ +41 + | ++ + + + + + | + } |
+ +42 + | ++ + + + + + | +} |
Mutations | ||
18 | ++ |
+
+
+
+ 1.1 |
+
22 | ++ |
+
+
+
+ 1.1 |
+
32 | ++ |
+
+
+
+ 1.1 |
+
37 | ++ |
+
+
+
+ 1.1 |
+
+ +1 + | ++ + + + + + | +package com.yubico.internal.util; |
+ +2 + | ++ + + + + + | +|
+ +3 + | ++ + + + + + | +import java.util.Optional; |
+ +4 + | ++ + + + + + | +import java.util.function.BinaryOperator; |
+ +5 + | ++ + + + + + | +import java.util.function.Supplier; |
+ +6 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +7 + | ++ + + + + + | +import lombok.NonNull; |
+ +8 + | ++ + + + + + | +import lombok.experimental.UtilityClass; |
+ +9 + | ++ + + + + + | +|
+ +10 + | ++ + + + + + | +/** Utilities for working with {@link Optional} values. */ |
+ +11 + | ++ + + + + + | +@UtilityClass |
+ +12 + | ++ + + + + + | +public class OptionalUtil { |
+ +13 + | ++ + + + + + | +|
+ +14 + | ++ + + + + + | + /** |
+ +15 + | ++ + + + + + | + * If <code>primary</code> is present, return it unchanged. Otherwise return <code> |
+ +16 + | ++ + + + + + | + * secondary</code>. |
+ +17 + | ++ + + + + + | + */ |
+ +18 + | ++ + + + + + | + public static <T> Optional<T> orOptional(Optional<T> primary, Optional<T> secondary) { |
+ +19 + | +
+
+1
+
+1. orOptional : negated conditional → NO_COVERAGE + + + + |
+ if (primary.isPresent()) { |
+ +20 + | +
+
+1
+
+1. orOptional : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::orOptional → NO_COVERAGE + + + + |
+ return primary; |
+ +21 + | ++ + + + + + | + } else { |
+ +22 + | +
+
+1
+
+1. orOptional : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::orOptional → NO_COVERAGE + + + + |
+ return secondary; |
+ +23 + | ++ + + + + + | + } |
+ +24 + | ++ + + + + + | + } |
+ +25 + | ++ + + + + + | +|
+ +26 + | ++ + + + + + | + /** |
+ +27 + | ++ + + + + + | + * If <code>primary</code> is present, return it unchanged. Otherwise return the result of <code> |
+ +28 + | ++ + + + + + | + * recover</code>. |
+ +29 + | ++ + + + + + | + */ |
+ +30 + | ++ + + + + + | + public static <T> Optional<T> orElseOptional(Optional<T> primary, Supplier<Optional<T>> recover) { |
+ +31 + | +
+
+1
+
+1. orElseOptional : negated conditional → NO_COVERAGE + + + + |
+ if (primary.isPresent()) { |
+ +32 + | +
+
+1
+
+1. orElseOptional : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::orElseOptional → NO_COVERAGE + + + + |
+ return primary; |
+ +33 + | ++ + + + + + | + } else { |
+ +34 + | +
+
+1
+
+1. orElseOptional : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::orElseOptional → NO_COVERAGE + + + + |
+ return recover.get(); |
+ +35 + | ++ + + + + + | + } |
+ +36 + | ++ + + + + + | + } |
+ +37 + | ++ + + + + + | +|
+ +38 + | ++ + + + + + | + /** |
+ +39 + | ++ + + + + + | + * Returns a sequential {@link Stream} with this {@link Optional} as its source. |
+ +40 + | ++ + + + + + | + * |
+ +41 + | ++ + + + + + | + * @param o the {@link Optional} to interpret as a {@link Stream} |
+ +42 + | ++ + + + + + | + * @return a sequential {@link Stream} containing the value of this {@link Optional} if present, |
+ +43 + | ++ + + + + + | + * otherwise an empty {@link Stream}. |
+ +44 + | ++ + + + + + | + */ |
+ +45 + | +
+
+1
+
+1. stream : negated conditional → NO_COVERAGE + + + + |
+ public static <T> Stream<T> stream(@NonNull Optional<T> o) { |
+ +46 + | +
+
+1
+
+1. stream : replaced return value with Stream.empty for com/yubico/internal/util/OptionalUtil::stream → NO_COVERAGE + + + + |
+ return o.map(Stream::of).orElseGet(Stream::empty); |
+ +47 + | ++ + + + + + | + } |
+ +48 + | ++ + + + + + | +|
+ +49 + | ++ + + + + + | + /** |
+ +50 + | ++ + + + + + | + * If both <code>a</code> and <code>b</code> are present, return <code>f(a, b)</code>. |
+ +51 + | ++ + + + + + | + * |
+ +52 + | ++ + + + + + | + * <p>If only <code>a</code> is present, return <code>a</code>. |
+ +53 + | ++ + + + + + | + * |
+ +54 + | ++ + + + + + | + * <p>Otherwise, return <code>b</code>. |
+ +55 + | ++ + + + + + | + */ |
+ +56 + | ++ + + + + + | + public static <T> Optional<T> zipWith(Optional<T> a, Optional<T> b, BinaryOperator<T> f) { |
+ +57 + | +
+
+2
+
+1. zipWith : negated conditional → NO_COVERAGE +2. zipWith : negated conditional → NO_COVERAGE + + + + |
+ if (a.isPresent() && b.isPresent()) { |
+ +58 + | +
+
+1
+
+1. zipWith : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::zipWith → NO_COVERAGE + + + + |
+ return Optional.of(f.apply(a.get(), b.get())); |
+ +59 + | +
+
+1
+
+1. zipWith : negated conditional → NO_COVERAGE + + + + |
+ } else if (a.isPresent()) { |
+ +60 + | +
+
+1
+
+1. zipWith : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::zipWith → NO_COVERAGE + + + + |
+ return a; |
+ +61 + | ++ + + + + + | + } else { |
+ +62 + | +
+
+1
+
+1. zipWith : replaced return value with Optional.empty for com/yubico/internal/util/OptionalUtil::zipWith → NO_COVERAGE + + + + |
+ return b; |
+ +63 + | ++ + + + + + | + } |
+ +64 + | ++ + + + + + | + } |
+ +65 + | ++ + + + + + | +} |
Mutations | ||
19 | ++ |
+
+
+
+ 1.1 |
+
20 | ++ |
+
+
+
+ 1.1 |
+
22 | ++ |
+
+
+
+ 1.1 |
+
31 | ++ |
+
+
+
+ 1.1 |
+
32 | ++ |
+
+
+
+ 1.1 |
+
34 | ++ |
+
+
+
+ 1.1 |
+
45 | ++ |
+
+
+
+ 1.1 |
+
46 | ++ |
+
+
+
+ 1.1 |
+
57 | ++ |
+
+
+
+ 1.1 2.2 |
+
58 | ++ |
+
+
+
+ 1.1 |
+
59 | ++ |
+
+
+
+ 1.1 |
+
60 | ++ |
+
+
+
+ 1.1 |
+
62 | ++ |
+
+
+
+ 1.1 |
+
+ +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.internal.util; |
+ +26 + | ++ + + + + + | +|
+ +27 + | ++ + + + + + | +import java.util.Iterator; |
+ +28 + | ++ + + + + + | +import java.util.Set; |
+ +29 + | ++ + + + + + | +import java.util.stream.Collectors; |
+ +30 + | ++ + + + + + | +import java.util.stream.Stream; |
+ +31 + | ++ + + + + + | +import java.util.stream.StreamSupport; |
+ +32 + | ++ + + + + + | +import lombok.experimental.UtilityClass; |
+ +33 + | ++ + + + + + | +|
+ +34 + | ++ + + + + + | +@UtilityClass |
+ +35 + | ++ + + + + + | +public class StreamUtil { |
+ +36 + | ++ + + + + + | +|
+ +37 + | ++ + + + + + | + public static <T> Stream<T> toStream(Iterator<T> it) { |
+ +38 + | +
+
+1
+
+1. lambda$toStream$0 : replaced return value with null for com/yubico/internal/util/StreamUtil::lambda$toStream$0 → NO_COVERAGE + + + + |
+ Iterable<T> iterable = () -> it; |
+ +39 + | +
+
+1
+
+1. toStream : replaced return value with Stream.empty for com/yubico/internal/util/StreamUtil::toStream → NO_COVERAGE + + + + |
+ return StreamSupport.stream(iterable.spliterator(), false); |
+ +40 + | ++ + + + + + | + } |
+ +41 + | ++ + + + + + | +|
+ +42 + | ++ + + + + + | + public static <T> Set<T> toSet(Iterator<T> it) { |
+ +43 + | +
+
+1
+
+1. toSet : replaced return value with Collections.emptySet for com/yubico/internal/util/StreamUtil::toSet → NO_COVERAGE + + + + |
+ return CollectionUtil.immutableSet(toStream(it).collect(Collectors.toSet())); |
+ +44 + | ++ + + + + + | + } |
+ +45 + | ++ + + + + + | +} |
Mutations | ||
38 | ++ |
+
+
+
+ 1.1 |
+
39 | ++ |
+
+
+
+ 1.1 |
+
43 | ++ |
+
+
+
+ 1.1 |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
9 | +41% | +46% | +95% | +
Name | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
BinaryUtil.java | +54% |
+ 64% |
+ 97% |
+
ByteInputStream.java | +0% |
+ 0% |
+ 100% |
+
CertificateParser.java | +51% |
+ 43% |
+ 82% |
+
CollectionUtil.java | +57% |
+ 50% |
+ 100% |
+
ComparableUtil.java | +73% |
+ 82% |
+ 100% |
+
ExceptionUtil.java | +43% |
+ 50% |
+ 100% |
+
JacksonCodecs.java | +0% |
+ 0% |
+ 100% |
+
OptionalUtil.java | +0% |
+ 0% |
+ 100% |
+
StreamUtil.java | +0% |
+ 0% |
+ 100% |
+
Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|
9 | +41% | +46% | +95% | +
Name | +Number of Classes | +Line Coverage | +Mutation Coverage | +Test Strength | +
---|---|---|---|---|
com.yubico.internal.util | +9 | +41% |
+ 46% |
+ 95% |
+