All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yubico.webauthn.data.Extensions Maven / Gradle / Ivy

The newest version!
package com.yubico.webauthn.data;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import com.yubico.webauthn.AssertionResult;
import com.yubico.webauthn.AssertionResultV2;
import com.yubico.webauthn.RegistrationResult;
import com.yubico.webauthn.StartRegistrationOptions;
import com.yubico.webauthn.extension.uvm.KeyProtectionType;
import com.yubico.webauthn.extension.uvm.MatcherProtectionType;
import com.yubico.webauthn.extension.uvm.UserVerificationMethod;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

/** Definitions for WebAuthn extensions. */
@Slf4j
@UtilityClass
public class Extensions {

  /**
   * Definitions for the FIDO AppID Extension (appid).
   *
   * @see §10.1.
   *     FIDO AppID Extension (appid)
   */
  public static class Appid {
    static final String EXTENSION_ID = "appid";
  }

  /**
   * Definitions for the 10.2. FIDO AppID Exclusion Extension (appidExclude).
   *
   * @see 10.2.
   *     FIDO AppID Exclusion Extension (appidExclude)
   */
  public static class AppidExclude {
    static final String EXTENSION_ID = "appidExclude";
  }

  /**
   * Definitions for the Credential Properties Extension (credProps).
   *
   * @see §10.4.
   *     Credential Properties Extension (credProps)
   */
  public static class CredentialProperties {
    static final String EXTENSION_ID = "credProps";

    /**
     * Extension outputs for the Credential Properties Extension (credProps).
     *
     * @see §10.4.
     *     Credential Properties Extension (credProps)
     */
    @Value
    @Builder
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class CredentialPropertiesOutput {
      @JsonProperty("rk")
      private final Boolean rk;

      @JsonProperty("authenticatorDisplayName")
      private final String authenticatorDisplayName;

      @JsonCreator
      private CredentialPropertiesOutput(
          @JsonProperty("rk") Boolean rk,
          @JsonProperty("authenticatorDisplayName") String authenticatorDisplayName) {
        this.rk = rk;
        this.authenticatorDisplayName = authenticatorDisplayName;
      }

      /**
       * This OPTIONAL property, known abstractly as the resident key credential property
       * (i.e., client-side discoverable credential property), is a Boolean value indicating
       * whether the {@link PublicKeyCredential} returned as a result of a registration ceremony is
       * a client-side discoverable credential (passkey).
       *
       * 

If this is true, the credential is a discoverable credential * (passkey). * *

If this is false, the credential is a server-side credential. * *

If this is not present, it is not known whether the credential is a discoverable * credential or a server-side credential. * * @see §10.4. * Credential Properties Extension (credProps) * @see Client-side * discoverable Credential * @see Server-side * Credential * @see Passkey in passkeys.dev reference */ public Optional getRk() { return Optional.ofNullable(rk); } /** * This OPTIONAL property is a human-palatable description of the credential's managing * authenticator, chosen by the user. * *

If the application supports setting "nicknames" for registered credentials, then this * value may be a suitable default value for such a nickname. * *

In an authentication ceremony, if this value is different from the stored nickname, then * the application may want to offer the user to update the stored nickname to match this * value. * * @return A user-chosen or vendor-default display name for the credential, if available. * Otherwise empty. * @see * authenticatorDisplayName in §10.1.3. Credential Properties Extension * (credProps) * @see RegistrationResult#getAuthenticatorDisplayName() * @see AssertionResult#getAuthenticatorDisplayName() * @see AssertionResultV2#getAuthenticatorDisplayName() * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change * as the standard matures. */ @Deprecated public Optional getAuthenticatorDisplayName() { return Optional.ofNullable(authenticatorDisplayName); } } } /** * Definitions for the Large blob storage extension (largeBlob). * * @see §10.5. * Large blob storage extension (largeBlob) */ public static class LargeBlob { static final String EXTENSION_ID = "largeBlob"; /** * Extension inputs for the Large blob storage extension (largeBlob) in * registration ceremonies. * * @see §10.5. * Large blob storage extension (largeBlob) */ @Value public static class LargeBlobRegistrationInput { /** * The Relying Party's preference of whether the created credential should support the * largeBlob extension. */ @JsonProperty private final LargeBlobSupport support; @JsonCreator public LargeBlobRegistrationInput( /** * The Relying Party's preference of whether the created credential should support the * * largeBlob extension. * *

Currently the only valid values are {@link LargeBlobSupport#REQUIRED} and {@link * LargeBlobSupport#PREFERRED}, but custom values MAY be constructed in case more values * are added in future revisions of the extension. */ @JsonProperty("support") LargeBlobSupport support) { this.support = support; } /** * The known valid arguments for the Large blob storage extension (largeBlob) * input in registration ceremonies. * *

Currently the only valid values are {@link LargeBlobSupport#REQUIRED} and {@link * LargeBlobSupport#PREFERRED}, but custom values MAY be constructed in case more values are * added in future revisions of the extension. * * @see #REQUIRED * @see #PREFERRED * @see §10.5. * Large blob storage extension (largeBlob) */ @Value public static class LargeBlobSupport { /** * The authenticator used for registration MUST support the largeBlob * extension. * *

Note: If the client does not support the largeBlob extension, this * requirement MAY be ignored. * *

Note: CTAP authenticators only support largeBlob in combination with * {@link AuthenticatorSelectionCriteria#getResidentKey()} set to REQUIRED in * {@link StartRegistrationOptions#getAuthenticatorSelection()}. * * @see §10.5. * Large blob storage extension (largeBlob) */ public static final LargeBlobSupport REQUIRED = new LargeBlobSupport("required"); /** * If the authenticator used for registration supports the largeBlob extension, * it will be enabled for the created credential. If not supported, the credential will be * created without large blob support. * *

Note: CTAP authenticators only support largeBlob in combination with * {@link AuthenticatorSelectionCriteria#getResidentKey()} set to REQUIRED in * {@link StartRegistrationOptions#getAuthenticatorSelection()}. * * @see §10.5. * Large blob storage extension (largeBlob) */ public static final LargeBlobSupport PREFERRED = new LargeBlobSupport("preferred"); /** * The underlying string value of this {@link LargeBlobSupport} value. * * @see #REQUIRED * @see #PREFERRED */ @JsonValue private final String value; /** * Returns a new {@link Set} containing the {@link #REQUIRED} and {@link #PREFERRED} values. */ public static Set values() { return Stream.of(REQUIRED, PREFERRED).collect(Collectors.toSet()); } } } /** * Extension inputs for the Large blob storage extension (largeBlob) in * authentication ceremonies. * *

Use the {@link #read()} and {@link #write(ByteArray)} factory functions to construct this * type. * * @see §10.5. * Large blob storage extension (largeBlob) */ @Value public static class LargeBlobAuthenticationInput { /** * If true, indicates that the Relying Party would like to fetch the * previously-written blob associated with the asserted credential. * * @see #read() * @see §10.5. * Large blob storage extension (largeBlob) */ @JsonProperty private final boolean read; /** * An opaque byte string that the Relying Party wishes to store with the existing credential. * * @see #write(ByteArray) * @see §10.5. * Large blob storage extension (largeBlob) */ @JsonProperty private final ByteArray write; @JsonCreator private LargeBlobAuthenticationInput( @JsonProperty("read") final Boolean read, @JsonProperty("write") final ByteArray write) { if (read != null && read && write != null) { throw new IllegalArgumentException( "Parameters \"read\" and \"write\" of largeBlob extension must not both be present."); } this.read = read != null && read; this.write = write; } /** * Configure the Large blob storage extension (largeBlob) to fetch the * previously-written blob associated with the asserted credential. * *

Mutually exclusive with {@link #write(ByteArray)}. * * @see §10.5. * Large blob storage extension (largeBlob) */ public static LargeBlobAuthenticationInput read() { return new LargeBlobAuthenticationInput(true, null); } /** * Configure the Large blob storage extension (largeBlob) to store the given byte * array with the existing credential. * *

Mutually exclusive with {@link #read()}. * * @see §10.5. * Large blob storage extension (largeBlob) */ public static LargeBlobAuthenticationInput write(@NonNull final ByteArray write) { return new LargeBlobAuthenticationInput(false, write); } /** * @return true if the read property is set to true, * false otherwise. * @see #read() * @see §10.5. * Large blob storage extension (largeBlob) */ public boolean getRead() { return read; } /** * @return The value of the write property if configured, empty otherwise. * @see #write(ByteArray) * @see §10.5. * Large blob storage extension (largeBlob) */ public Optional getWrite() { return Optional.ofNullable(write); } } /** * Extension outputs for the Large blob storage extension (largeBlob) in * registration ceremonies. * *

Use the {@link #supported(boolean)} factory function to construct this type. * * @see §10.5. * Large blob storage extension (largeBlob) */ @Value public static class LargeBlobRegistrationOutput { /** * true if, and only if, the created credential supports storing large blobs. * * @see §10.5. * Large blob storage extension (largeBlob) * @see LargeBlobRegistrationInput#getSupport() */ @JsonProperty private final boolean supported; @JsonCreator private LargeBlobRegistrationOutput(@JsonProperty("supported") boolean supported) { this.supported = supported; } /** * Create a Large blob storage extension output with the supported output set to * the given value. * * @see * dictionary AuthenticationExtensionsLargeBlobOutputs */ public static LargeBlobRegistrationOutput supported(boolean supported) { return new LargeBlobRegistrationOutput(supported); } } /** * Extension outputs for the Large blob storage extension (largeBlob) in * authentication ceremonies. * * @see §10.5. * Large blob storage extension (largeBlob) */ @Value public static class LargeBlobAuthenticationOutput { @JsonProperty private final ByteArray blob; @JsonProperty private final Boolean written; @JsonCreator private LargeBlobAuthenticationOutput( @JsonProperty("blob") ByteArray blob, @JsonProperty("written") Boolean written) { this.blob = blob; this.written = written; } /** * Create a Large blob storage extension output with the blob output set to the * given value. * *

This corresponds to the extension input {@link LargeBlobAuthenticationInput#read() * LargeBlobAuthenticationInput.read()}. * * @see * dictionary AuthenticationExtensionsLargeBlobOutputs */ public static LargeBlobAuthenticationOutput read(final ByteArray blob) { return new LargeBlobAuthenticationOutput(blob, null); } /** * Create a Large blob storage extension output with the written output set to * the given value. * *

This corresponds to the extension input {@link * LargeBlobAuthenticationInput#write(ByteArray) * LargeBlobAuthenticationInput.write(ByteArray)}. * * @see * dictionary AuthenticationExtensionsLargeBlobOutputs */ public static LargeBlobAuthenticationOutput write(final boolean write) { return new LargeBlobAuthenticationOutput(null, write); } /** * The opaque byte string that was associated with the credential identified by {@link * PublicKeyCredential#getId()}. Only valid if {@link LargeBlobAuthenticationInput#getRead()} * was true. * * @return A present {@link Optional} if {@link LargeBlobAuthenticationInput#getRead()} was * true and the blob content was successfully read. Otherwise (if {@link * LargeBlobAuthenticationInput#getRead()} was false or the content failed to * be read) an empty {@link Optional}. * @see §10.5. * Large blob storage extension (largeBlob) */ public Optional getBlob() { return Optional.ofNullable(blob); } /** * A boolean that indicates that the contents of {@link * LargeBlob.LargeBlobAuthenticationInput#write(ByteArray) * LargeBlobAuthenticationInput#write(ByteArray)} were successfully stored on the * authenticator, associated with the specified credential. * * @return Empty if {@link LargeBlobAuthenticationInput#getWrite()} was not present. Otherwise * true if and only if the value of {@link * LargeBlobAuthenticationInput#getWrite()} was successfully stored by the authenticator. * @see §10.5. * Large blob storage extension (largeBlob) */ public Optional getWritten() { return Optional.ofNullable(written); } } } /** * Definitions for the User Verification Method (uvm) Extension. * * @see §10.3. * User Verification Method Extension (uvm) */ public static class Uvm { static final String EXTENSION_ID = "uvm"; /** * A uvmEntry as defined in §10.3. User * Verification Method Extension (uvm). * * @see §10.3. * User Verification Method Extension (uvm) * @see UserVerificationMethod * @see KeyProtectionType * @see MatcherProtectionType */ @Value public static class UvmEntry { private final UserVerificationMethod userVerificationMethod; private final KeyProtectionType keyProtectionType; private final MatcherProtectionType matcherProtectionType; public UvmEntry( @JsonProperty("userVerificationMethod") UserVerificationMethod userVerificationMethod, @JsonProperty("keyProtectionType") KeyProtectionType keyProtectionType, @JsonProperty("matcherProtectionType") MatcherProtectionType matcherProtectionType) { this.userVerificationMethod = userVerificationMethod; this.keyProtectionType = keyProtectionType; this.matcherProtectionType = matcherProtectionType; } } static Optional> parseAuthenticatorExtensionOutput(CBORObject cbor) { if (validateAuthenticatorExtensionOutput(cbor)) { return Optional.of( cbor.get(EXTENSION_ID).getValues().stream() .map( uvmEntry -> new UvmEntry( UserVerificationMethod.fromValue(uvmEntry.get(0).AsInt32Value()), KeyProtectionType.fromValue( uvmEntry.get(1).AsNumber().ToInt16IfExact()), MatcherProtectionType.fromValue( uvmEntry.get(2).AsNumber().ToInt16IfExact()))) .collect(Collectors.toList())); } else { return Optional.empty(); } } private static boolean validateAuthenticatorExtensionOutput(CBORObject extensions) { if (!extensions.ContainsKey(EXTENSION_ID)) { return false; } CBORObject uvm = extensions.get(EXTENSION_ID); if (uvm.getType() != CBORType.Array) { log.debug( "Invalid CBOR type for \"{}\" extension output: expected array, was: {}", EXTENSION_ID, uvm.getType()); return false; } if (uvm.size() < 1 || uvm.size() > 3) { log.debug( "Invalid length \"{}\" extension output array: expected 1 to 3 (inclusive), was: {}", EXTENSION_ID, uvm.size()); return false; } for (CBORObject entry : uvm.getValues()) { if (entry.getType() != CBORType.Array) { log.debug("Invalid CBOR type for uvmEntry: expected array, was: {}", entry.getType()); return false; } if (entry.size() != 3) { log.debug("Invalid length for uvmEntry: expected 3, was: {}", entry.size()); return false; } for (CBORObject i : entry.getValues()) { if (!(i.isNumber() && i.AsNumber().IsInteger())) { log.debug("Invalid type for uvmEntry element: expected integer, was: {}", i.getType()); return false; } } } return true; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy