NAME
Web::Authn::Crypto - CryptX signatures and in-process X.509 chain checks
SYNOPSIS
my $pk = Web::Authn::Crypto::cose_to_public_key($decoded_cose);
Web::Authn::Crypto::verify_signature(
public_key => $pk,
alg => $decoded_cose->{alg},
signature => $sig,
data => $auth_data . $client_data_hash,
);
Web::Authn::Crypto::validate_certificate_chain(
x5c => $att_stmt->{x5c},
pem_root_certs => \@roots,
);
DESCRIPTION
Thin layer over Crypt::PK::ECC, Crypt::PK::RSA and Crypt::PK::Ed25519. Also parses X.509 certificates enough to verify that an x5c chain is signed back to RP-supplied roots without spawning openssl(1). That matches py_webauthn's use of OpenSSL.crypto.X509StoreContext, implemented with CryptX for the signatures and a small DER walker for names and validity.
If pem_root_certs is empty, chain pinning is skipped (same as py_webauthn).
FUNCTIONS
cose_to_public_key
my $decoded = Web::Authn::Parse::decode_credential_public_key( $cose );
my $pk = Web::Authn::Crypto::cose_to_public_key( $decoded );
Builds a CryptX public-key object (Crypt::PK::ECC, Crypt::PK::RSA or Crypt::PK::Ed25519) from a decoded COSE key. Pass the hash returned by "decode_credential_public_key" in Web::Authn::Parse (kty, alg, and the type-specific fields).
export_spki_der
my $der = Web::Authn::Crypto::export_spki_der( $pk );
Takes a CryptX public-key object.
Exports a CryptX public key as SubjectPublicKeyInfo DER.
extract_extension_octet
my $raw = Web::Authn::Crypto::extract_extension_octet(
$x5c[0], '1.2.840.113635.100.8.2',
);
Used for Apple's nonce extension 1.2.840.113635.100.8.2.
Returns the OCTET STRING value of an X.509 extension, or undef if the OID is absent. Used for Apple's nonce extension 1.2.840.113635.100.8.2.
$cert_der-
This argument is required. It is the raw DER bytes of the certificate.
$oid-
This argument is required. It is a string containing a dotted OID, for example
1.2.840.113635.100.8.2.
extract_spki_from_cert
my $spki = Web::Authn::Crypto::extract_spki_from_cert( $der );
Walks a certificate DER and returns the SubjectPublicKeyInfo bytes. Pass the raw DER bytes of an X.509 certificate, or an object that overloads stringification.
public_key_from_cert_der
my $pk = Web::Authn::Crypto::public_key_from_cert_der( $x5c[0] );
Loads the subject public key from a certificate. It extracts SubjectPublicKeyInfo and hands it to "public_key_from_der_spki". Pass the raw DER bytes of an X.509 certificate, or an object that overloads stringification.
public_key_from_der_spki
my $pk = Web::Authn::Crypto::public_key_from_der_spki( $spki );
Loads a CryptX public key from SubjectPublicKeyInfo DER. It tries ECC, then RSA, then Ed25519. Pass the raw DER bytes, or an object that overloads stringification.
require_cryptx
Web::Authn::Crypto::require_cryptx();
Throws unless the CryptX PK modules (Crypt::PK::ECC, Crypt::PK::RSA, Crypt::PK::Ed25519) load. This function takes no arguments.
validate_certificate_chain
Web::Authn::Crypto::validate_certificate_chain(
x5c => $att_stmt->{x5c},
pem_root_certs => \@roots,
);
Parses each certificate, checks issuer/subject chaining, notBefore/notAfter, and the signature of cert i under cert i+1's key, then the last cert under a supplied root. Roots may be PEM (one or more BEGIN CERTIFICATE blocks) or raw DER.
If pem_root_certs is empty, chain pinning is skipped (the same behaviour as py_webauthn). This is not a full RFC 5280 path builder: there are no name constraints, policy OIDs, or CRLs.
pem_root_certs-
This argument is optional. It is an array of PEM strings, DER bytes, or scalar references to the same: the trusted roots. It defaults to an empty array.
x5c-
This argument is required. It is an array of certificate DER blobs from the attestation statement, leaf first.
verify_signature
Web::Authn::Crypto::verify_signature(
public_key => $pk,
alg => -7,
signature => $sig,
data => $auth_data . sha256( $client_data_json ),
);
Verifies signature over data with public_key using the COSE algorithm alg. When walking certificate chains, the following signature OIDs are understood: RSA PKCS#1 SHA-1/224/256/384/512, RSA-PSS, ECDSA SHA-1/224/256/384/512, and Ed25519.
This is not a full RFC 5280 path builder (no name constraints, policy OIDs, or CRLs).
alg-
This argument is required. It is an integer: the COSE algorithm identifier (for example
-7for ES256). data-
This argument is required. It is the raw bytes of the signed payload.
public_key-
This argument is required. It is a CryptX public-key object from "cose_to_public_key" or "public_key_from_cert_der".
signature-
This argument is required. It is raw bytes: a DER ECDSA signature, a raw Ed25519 signature, or an RSA signature as produced by the authenticator.
THREAD & PROCESS SAFETY
This module is designed to be fully thread-safe and process-safe, ensuring data integrity across Perl ithreads and mod_perl’s threaded Multi-Processing Modules (MPMs) such as Worker or Event.
AUTHOR
Jacques Deguest <jack@deguest.jp>
SEE ALSO
CryptX, Web::Authn::Attestation, Web::Authn
COPYRIGHT & LICENSE
Copyright(c) 2026 DEGUEST Pte. Ltd.
All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.