Viewing File: /usr/local/cpanel/3rdparty/perl/536/cpanel-lib/Crypt/Perl/ECDSA/Parse.pm

package Crypt::Perl::ECDSA::Parse;

=encoding utf-8

=head1 NAME

Crypt::Perl::ECDSA::Parse - ECDSA key parsing

=head1 SYNOPSIS

    use Crypt::Perl::ECDSA::Parse ();

    #These accept either DER or PEM, native format or PKCS8.
    #
    my $prkey = Crypt::Perl::ECDSA::Parse::private($buffer);
    my $pbkey = Crypt::Perl::ECDSA::Parse::public($buffer);

=head1 DISCUSSION

See L<Crypt::Perl::ECDSA::PrivateKey> and L<Crypt::Perl::ECDSA::PublicKey>
for descriptions of the interfaces of these two classes.

=cut

use strict;
use warnings;

use Try::Tiny;

use Crypt::Perl::ASN1 ();
use Crypt::Perl::PKCS8 ();
use Crypt::Perl::ToDER ();
use Crypt::Perl::ECDSA::ECParameters ();
use Crypt::Perl::X ();

sub private {
    my ($pem_or_der) = @_;

    require Crypt::Perl::ECDSA::PrivateKey;

    Crypt::Perl::ToDER::ensure_der($pem_or_der);

    my $asn1 = _private_asn1();
    my $asn1_ec = $asn1->find('ECPrivateKey');

    my $struct;
    try {
        $struct = $asn1_ec->decode($pem_or_der);
    }
    catch {
        my $ec_err = $_;

        my $asn1_pkcs8 = $asn1->find('PrivateKeyInfo');

        try {
            my $pk8_struct = $asn1_pkcs8->decode($pem_or_der);

            #It still might succeed, even if this is wrong, so don’t die().
            if ( $pk8_struct->{'privateKeyAlgorithm'}{'algorithm'} ne Crypt::Perl::ECDSA::ECParameters::OID_ecPublicKey() ) {
                warn "Unknown private key algorithm OID: “$pk8_struct->{'privateKeyAlgorithm'}{'algorithm'}”";
            }

            my $asn1_params = $asn1->find('EcpkParameters');
            my $params = $asn1_params->decode($pk8_struct->{'privateKeyAlgorithm'}{'parameters'});

            $struct = $asn1_ec->decode($pk8_struct->{'privateKey'});
            $struct->{'parameters'} = $params;
        }
        catch {
            die Crypt::Perl::X::create('Generic', "Failed to decode private key as either ECDSA native ($ec_err) or PKCS8 ($_)");
        };
    };

    my $key_parts = {
        version => $struct->{'version'},
        private => Crypt::Perl::BigInt->from_bytes($struct->{'privateKey'}),
        public => Crypt::Perl::BigInt->from_bytes($struct->{'publicKey'}[0]),
    };

    return Crypt::Perl::ECDSA::PrivateKey->new($key_parts, $struct->{'parameters'});
}

sub public {
    my ($pem_or_der) = @_;

    require Crypt::Perl::ECDSA::PublicKey;

    Crypt::Perl::ToDER::ensure_der($pem_or_der);

    my $asn1 = _public_asn1();
    my $asn1_ec = $asn1->find('ECPublicKey');

    my $struct;
    try {
        $struct = $asn1_ec->decode($pem_or_der);
    }
    catch {
        die Crypt::Perl::X::create('Generic', "Failed to decode input as ECDSA public key ($_)");
    };

    return Crypt::Perl::ECDSA::PublicKey->new(
        $struct->{'publicKey'}[0],
        $struct->{'keydata'}{'parameters'},
    );
}

sub jwk {
    my ($hr) = @_;

    require Crypt::Perl::ECDSA::NIST;
    require Crypt::Perl::ECDSA::EC::DB;
    require Crypt::Perl::Math;
    require MIME::Base64;

    my $curve_name = Crypt::Perl::ECDSA::NIST::get_curve_name_for_nist($hr->{'crv'});
    my $curve_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_name($curve_name);

    my $keylen = $curve_hr->{'p'}->bit_length();
    my $pub_half_byte_length = Crypt::Perl::Math::ceil( $keylen / 8 );

    my $x = MIME::Base64::decode_base64url($hr->{'x'});
    my $y = MIME::Base64::decode_base64url($hr->{'y'});

    #Make sure both halves are the proper length.
    substr($_, 0, 0) = ("\0" x ($pub_half_byte_length - length)) for ($x, $y);

    my $public = Crypt::Perl::BigInt->from_bytes("\x{04}$x$y");

    if ($hr->{'d'}) {
        require Crypt::Perl::ECDSA::PrivateKey;
        require Crypt::Perl::JWK;

        my %args = (
            version => 1,
            public => $public,
            private => Crypt::Perl::JWK::jwk_num_to_bigint($hr->{'d'}),
        );

        return Crypt::Perl::ECDSA::PrivateKey->new_by_curve_name(\%args, $curve_name);
    }

    require Crypt::Perl::ECDSA::PublicKey;
    return Crypt::Perl::ECDSA::PublicKey->new_by_curve_name( $public, $curve_name);
}

#----------------------------------------------------------------------

sub _private_asn1 {
    my $template = join("\n", Crypt::Perl::ECDSA::PrivateKey->ASN1_PRIVATE(), Crypt::Perl::PKCS8::ASN1());

    return Crypt::Perl::ASN1->new()->prepare($template);
}

sub _public_asn1 {
    my $template = join("\n", Crypt::Perl::ECDSA::PublicKey->ASN1_PUBLIC(), Crypt::Perl::PKCS8::ASN1());

    return Crypt::Perl::ASN1->new()->prepare($template);
}

1;
Back to Directory File Manager