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 )