glasslock/testing
Helpers for generating WebAuthn/FIDO2 test data in unit and integration tests. Exposes high-level builders for common scenarios and low-level building blocks for edge cases.
This module is for testing only. It should not be used in production code.
Build a valid response, then use record update syntax to introduce the flaw under test:
testing.to_registration_json(
testing.RegistrationResponse(..response, credential_type: "invalid-type"),
)
Values the authenticator signs over — the credential ID and public key
inside attestation_object, and authenticator_data with its signature
— are not reachable this way; rebuild them with the lower-level builders.
Example
For a simple registration test:
import glasslock/registration
import glasslock/testing
pub fn registration_test() {
let #(_, challenge) =
registration.new(
relying_party: registration.RelyingParty(id: "example.com", name: "Test"),
user: registration.User(id: <<1, 2, 3>>, name: "test", display_name: "Test"),
origin: "https://example.com",
)
|> registration.build()
let response = testing.build_registration_response(challenge: challenge)
let response_json = testing.to_registration_json(response)
let assert Ok(credential) = registration.verify_json(response_json:, challenge:)
}
Types
Complete response data for an authentication ceremony.
pub type AuthenticationResponse {
AuthenticationResponse(
id: String,
credential_id: BitArray,
credential_type: String,
authenticator_data: BitArray,
client_data_json: BitArray,
signature: BitArray,
user_handle: option.Option(BitArray),
)
}
Constructors
-
AuthenticationResponse( id: String, credential_id: BitArray, credential_type: String, authenticator_data: BitArray, client_data_json: BitArray, signature: BitArray, user_handle: option.Option(BitArray), )Arguments
- id
-
Emitted as
id. Base64url ofcredential_idin a real response. - credential_id
-
Emitted as
rawId. - credential_type
-
Emitted as
type."public-key"for a real credential. - client_data_json
-
UTF-8 encoded client data JSON.
- signature
-
Signature over
authenticator_data || SHA-256(client_data_json).
Authenticator flags for building authenticator data.
pub type AuthenticatorFlags {
AuthenticatorFlags(user_present: Bool, user_verified: Bool)
}
Constructors
-
AuthenticatorFlags(user_present: Bool, user_verified: Bool)Arguments
- user_present
-
WebAuthn UP (user presence) flag: the user touched or interacted with the authenticator.
- user_verified
-
WebAuthn UV (user verification) flag: the authenticator performed biometric or PIN verification.
A COSE key pair for testing WebAuthn flows.
Construct with generate_es256_keypair,
generate_ed25519_keypair, or
generate_rs256_keypair.
pub opaque type KeyPair
Complete response data for a registration ceremony.
pub type RegistrationResponse {
RegistrationResponse(
id: String,
credential_id: BitArray,
credential_type: String,
client_data_json: BitArray,
attestation_object: BitArray,
transports: List(glasslock.Transport),
)
}
Constructors
-
RegistrationResponse( id: String, credential_id: BitArray, credential_type: String, client_data_json: BitArray, attestation_object: BitArray, transports: List(glasslock.Transport), )Arguments
- id
-
Emitted as
id. Base64url ofcredential_idin a real response. - credential_id
-
Emitted as
rawId. Matches the attested credential ID insideattestation_objectin a real response. - credential_type
-
Emitted as
type."public-key"for a real credential. - client_data_json
-
UTF-8 encoded client data JSON.
- attestation_object
-
CBOR-encoded attestation object.
- transports
-
Omitted from the envelope when empty.
Values
pub fn build_attestation_object(
format format: String,
authenticator_data authenticator_data: BitArray,
attestation_statement attestation_statement: List(
#(String, Int),
),
) -> BitArray
Build an attestation object. glasslock accepts only "none" with an
empty statement.
pub fn build_authentication_authenticator_data(
relying_party_id relying_party_id: String,
flags flags: AuthenticatorFlags,
sign_count sign_count: Int,
) -> BitArray
Build authenticator data for authentication (no attested credential).
pub fn build_authentication_response(
challenge challenge: authentication.Challenge,
credential_id credential_id: BitArray,
keypair keypair: KeyPair,
sign_count sign_count: Int,
) -> AuthenticationResponse
Build a complete authentication response for the given challenge.
Uses the keypair to generate a valid signature.
pub fn build_client_data(
type_ type_: String,
challenge challenge: BitArray,
origin origin: String,
cross_origin cross_origin: Bool,
top_origin top_origin: option.Option(String),
) -> BitArray
Build client data JSON with a caller-supplied type field.
Parameters allow constructing invalid data for error testing:
- Use a different
originto test origin mismatch - Use a different
challengeto test challenge mismatch - Set
cross_origin: Truewith a challenge that disallows it
pub fn build_client_data_create(
challenge challenge: BitArray,
origin origin: String,
cross_origin cross_origin: Bool,
) -> BitArray
Build webauthn.create client data JSON for registration.
pub fn build_client_data_get(
challenge challenge: BitArray,
origin origin: String,
cross_origin cross_origin: Bool,
) -> BitArray
Build webauthn.get client data JSON for authentication.
pub fn build_registration_authenticator_data(
relying_party_id relying_party_id: String,
credential_id credential_id: BitArray,
cose_key_cbor cose_key_cbor: BitArray,
flags flags: AuthenticatorFlags,
sign_count sign_count: Int,
) -> BitArray
Build authenticator data for registration (includes attested credential).
Pass cose_key(keypair) for the cose_key_cbor parameter.
pub fn build_registration_response(
challenge challenge: registration.Challenge,
) -> RegistrationResponse
Build a complete registration response for the given challenge.
Generates a fresh ES256 keypair and credential ID. The generated keypair is
not part of the returned response; use
build_registration_response_with_keypair
to supply a keypair of a different algorithm, or to keep the keypair for a
follow-on authentication response.
pub fn build_registration_response_with_keypair(
challenge challenge: registration.Challenge,
keypair keypair: KeyPair,
) -> RegistrationResponse
Build a complete registration response using a caller-supplied keypair.
Useful when the test needs to exercise an algorithm other than ES256. The keypair determines the COSE algorithm embedded in the attested credential.
pub fn cose_key(keypair: KeyPair) -> BitArray
Get the public key in COSE CBOR format. This is the format embedded in authenticator data during registration.
pub fn generate_ed25519_keypair() -> KeyPair
Generate a new random Ed25519 key pair.
pub fn generate_es256_keypair() -> KeyPair
Generate a new random ES256 (P-256) key pair.
pub fn generate_rs256_keypair() -> KeyPair
Generate a new random RS256 (RSA 2048-bit) key pair.
pub fn public_key(keypair: KeyPair) -> glasslock.PublicKey
Get the public key as a parsed glasslock.PublicKey.
Use to construct a stored Credential in tests.
pub fn sign(
keypair keypair: KeyPair,
message message: BitArray,
) -> BitArray
Sign a message using the algorithm stamped on the keypair.
Dispatches to ECDSA, EdDSA, RSA PKCS#1 v1.5, or RSA PSS based on the COSE alg label assigned when the keypair was generated. Returns the wire-format signature bytes a real WebAuthn authenticator would produce: ASN.1 DER for ECDSA, raw for EdDSA, and raw PKCS#1 v1.5 or PSS bytes for RSA.
pub fn sign_authentication_message(
keypair keypair: KeyPair,
authenticator_data authenticator_data: BitArray,
client_data_json client_data_json: BitArray,
) -> BitArray
Sign an authentication message the same way a real authenticator would:
ECDSA/EdDSA/RSA over authenticator_data || SHA-256(client_data_json).
pub fn to_authentication_json(
response: AuthenticationResponse,
) -> String
Convert an authentication response to an AuthenticationResponseJSON string.
pub fn to_registration_json(
response: RegistrationResponse,
) -> String
Convert a registration response to a RegistrationResponseJSON string.