Package com.mcawful

Class PasskeyVerifier

java.lang.Object
com.mcawful.PasskeyVerifier

public final class PasskeyVerifier extends Object
Server-side checks for passkey (WebAuthn) registration and sign-in on the panel.

Attestation is not requested, so the browser hands over the public key in SubjectPublicKeyInfo form and no CBOR parsing is needed. Only ES256 (P-256) and RS256 keys are accepted.

This class holds no bot state. Every verify method throws IllegalArgumentException with the reason when a check fails.

Author:
mcawful
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final long
    How long a challenge stays valid, in milliseconds
    static final int
    Largest accepted authenticator data, in bytes
    static final int
    Largest accepted clientDataJSON, in bytes
    static final int
    Largest credential ID allowed by WebAuthn, in bytes
    static final int
    Largest accepted public key or signature, in bytes.
  • Method Summary

    Modifier and Type
    Method
    Description
    static String
    Creates a challenge which carries its own expiry and an HMAC, so the server does not have to remember challenges it has handed out to unauthenticated clients
    static byte[]
    decode(String data, int maxBytes)
    Decodes a base64url string
    static String
    encode(byte[] data)
    Encodes bytes as base64url without padding, the encoding WebAuthn uses in JSON
    static byte[]
    randomBytes(int length)
    Generates cryptographically secure random bytes
    static long
    verifyAssertion(byte[] clientDataJSON, byte[] authData, byte[] signature, byte[] publicKey, String origin, long signCount)
    Verifies the response to navigator.credentials.get()
    static long
    verifyRegistration(byte[] clientDataJSON, byte[] authData, byte[] publicKey, byte[] credentialId, String challenge, String origin)
    Verifies the response to navigator.credentials.create()

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • CHALLENGE_TTL_MILLIS

      public static final long CHALLENGE_TTL_MILLIS
      How long a challenge stays valid, in milliseconds
      See Also:
    • MAX_CLIENT_DATA

      public static final int MAX_CLIENT_DATA
      Largest accepted clientDataJSON, in bytes
      See Also:
    • MAX_AUTH_DATA

      public static final int MAX_AUTH_DATA
      Largest accepted authenticator data, in bytes
      See Also:
    • MAX_KEY

      public static final int MAX_KEY
      Largest accepted public key or signature, in bytes. A key of this size still fits the column it is stored in
      See Also:
    • MAX_CREDENTIAL_ID

      public static final int MAX_CREDENTIAL_ID
      Largest credential ID allowed by WebAuthn, in bytes
      See Also:
  • Method Details

    • randomBytes

      public static byte[] randomBytes(int length)
      Generates cryptographically secure random bytes
      Parameters:
      length - the number of bytes
      Returns:
      the random bytes
    • encode

      public static String encode(byte[] data)
      Encodes bytes as base64url without padding, the encoding WebAuthn uses in JSON
      Parameters:
      data - the bytes to encode
      Returns:
      the encoded string
    • decode

      public static byte[] decode(String data, int maxBytes)
      Decodes a base64url string
      Parameters:
      data - the string to decode
      maxBytes - the largest decoded size to accept
      Returns:
      the decoded bytes
      Throws:
      IllegalArgumentException - if the string is missing, not base64url, or too large
    • createChallenge

      public static String createChallenge()
      Creates a challenge which carries its own expiry and an HMAC, so the server does not have to remember challenges it has handed out to unauthenticated clients
      Returns:
      the challenge, base64url encoded
    • verifyRegistration

      public static long verifyRegistration(byte[] clientDataJSON, byte[] authData, byte[] publicKey, byte[] credentialId, String challenge, String origin)
      Verifies the response to navigator.credentials.create()
      Parameters:
      clientDataJSON - the raw clientDataJSON
      authData - the raw authenticator data
      publicKey - the public key in SubjectPublicKeyInfo form
      credentialId - the credential ID reported by the browser
      challenge - the challenge that was issued for this registration, as returned by createChallenge()
      origin - the origin the browser must have performed the registration on
      Returns:
      the initial signature counter
      Throws:
      IllegalArgumentException - if any check fails
    • verifyAssertion

      public static long verifyAssertion(byte[] clientDataJSON, byte[] authData, byte[] signature, byte[] publicKey, String origin, long signCount)
      Verifies the response to navigator.credentials.get()
      Parameters:
      clientDataJSON - the raw clientDataJSON
      authData - the raw authenticator data
      signature - the signature
      publicKey - the stored public key in SubjectPublicKeyInfo form
      origin - the origin the passkey was registered on
      signCount - the stored signature counter
      Returns:
      the new signature counter
      Throws:
      IllegalArgumentException - if any check fails