Authen-WebAuthn
view release on metacpan or search on metacpan
lib/Authen/WebAuthn.pm view on Meta::CPAN
#If x5c is not present, self attestation is in use.
}
else {
return attest_packed_self( $attestation_statement, $authenticator_data,
$signed_value );
}
}
sub attest_packed_x5c {
my ( $attestation_statement, $authenticator_data, $signed_value ) = @_;
my $x5c_der = $attestation_statement->{x5c}->[0];
my $sig_alg = $attestation_statement->{alg};
my $sig = $attestation_statement->{sig};
my ( $x5c, $key, $key_alg );
eval {
$x5c = Crypt::OpenSSL::X509->new_from_string( $x5c_der,
Crypt::OpenSSL::X509::FORMAT_ASN1 );
$key = $x5c->pubkey();
};
croak "Cannot extract public key from attestation certificate: $@" if ($@);
# Verify that sig is a valid signature over the concatenation of
# authenticatorData and clientDataHash using the attestation public key in
# attestnCert with the algorithm specified in alg.
my $attestation_verifier = eval { getPEMPubKeyVerifier( $key, $sig_alg ) };
croak "Cannot get signature validator for attestation: $@" if ($@);
# Verify that attestnCert meets the requirements in §â¯8.2.1 Packed
# Attestation Statement Certificate Requirements.
eval { attest_packed_check_cert_requirements($x5c) };
croak "Attestation certificate does not satisfy requirements: $@" if ($@);
# If attestnCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4
# (id-fido-gen-ce-aaguid) verify that the value of this extension matches
# the aaguid in authenticatorData.
my $aaguid_ext = $x5c->extensions_by_oid->{'1.3.6.1.4.1.45724.1.1.4'};
if ($aaguid_ext) {
my $ad_aaguid = $authenticator_data->{attestedCredentialData}->{aaguid};
my $cert_aaguid = $aaguid_ext->value;
croak "Invalid id-fido-gen-ce-aaguid extension format"
unless $cert_aaguid =~ /^#0410.{32}$/;
# Reformat aaguids so they can be compared
($cert_aaguid) = $cert_aaguid =~ /^#0410(.{32})$/;
$ad_aaguid =~ s/-//g;
$ad_aaguid = uc($ad_aaguid);
croak "AAGUID from certificate ($cert_aaguid)"
. " does not match AAGUID from authenticator data ($ad_aaguid)"
if $ad_aaguid ne $cert_aaguid;
}
# Optionally, inspect x5c and consult externally provided knowledge to
# determine whether attStmt conveys a Basic or AttCA attestation.
# TODO
# If successful, return implementation-specific values representing
# attestation type Basic, AttCA or uncertainty, and attestation trust path
# x5c.
if ( $attestation_verifier->( $sig, $signed_value ) ) {
return {
success => 1,
type => "Basic",
trust_path => $attestation_statement->{x5c},
aaguid => $authenticator_data->{attestedCredentialData}->{aaguid},
};
}
else {
croak "Invalid attestation signature";
}
}
# Implements 8.2.1. Packed Attestation Statement Certificate Requirements
sub attest_packed_check_cert_requirements {
my ($x5c) = @_;
my $version = $x5c->version;
# Version MUST be set to 3
# (which is indicated by an ASN.1 INTEGER with value 2).
croak "Invalid certificate version" unless $version eq "02";
# Subject field
croak "Missing subject C" unless $x5c->subject_name->get_entry_by_type("C");
croak "Missing subject O" unless $x5c->subject_name->get_entry_by_type("O");
croak "Missing subject CN"
unless $x5c->subject_name->get_entry_by_type("CN");
croak "Missing subject OU"
unless $x5c->subject_name->get_entry_by_type("OU");
croak "Unexpected OU"
unless $x5c->subject_name->get_entry_by_type("OU")->value eq
"Authenticator Attestation";
# The Basic Constraints extension MUST have the CA component set to false.
my $isCa = $x5c->extensions_by_oid->{"2.5.29.19"}->basicC("ca");
croak "Basic Constraints CA is true" if $isCa;
return;
}
sub attest_packed_self {
my ( $attestation_statement, $authenticator_data, $signed_value ) = @_;
my $sig = $attestation_statement->{sig};
my $sign_alg_num = $attestation_statement->{alg};
my $cose_key =
$authenticator_data->{attestedCredentialData}->{credentialPublicKey};
# Validate that alg matches the algorithm of the credentialPublicKey in
# authenticatorData.
my $cose_alg =
$authenticator_data->{attestedCredentialData}->{credentialPublicKeyAlg};
my $sign_alg = $COSE_ALG->{$sign_alg_num}->{name};
croak "Unknown key type in attestation data: $sign_alg_num"
unless ($sign_alg);
unless ( $sign_alg eq $cose_alg ) {
croak "Attestation algorithm $sign_alg does not match "
( run in 2.382 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )