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 -7 for 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.