File Coverage

lib/Crypt/Perl/RSA/PKCS1_v1_5.pm
Criterion Covered Total %
statement 42 43 97.6
branch 3 4 75.0
condition n/a
subroutine 13 13 100.0
pod 0 2 0.0
total 58 62 93.5


line stmt bran cond sub pod time code
1             package Crypt::Perl::RSA::PKCS1_v1_5;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::RSA::PKCS1_v1_5 - PKCS1 v1.5 signature padding
8              
9             =head1 SYNOPSIS
10              
11             my $digest = Digest::SHA::sha256('This is my message.');
12              
13             my $sig = Crypt::Perl::RSA::PKCS1_v1_5::encode(
14             $digest,
15             'sha256', #digest OID; see below
16             2048, #the bit length of the key’s modulus
17             );
18              
19             #This value should match $digest.
20             my $digest_dec = Crypt::Perl::RSA::PKCS1_v1_5::decode(
21             $sig,
22             'sha256',
23             );
24              
25             =head1 LIST OF DIGEST OIDs
26              
27             =over 4
28              
29             =item * sha512
30              
31             =item * sha384
32              
33             =item * sha256
34              
35             =back
36              
37             The following are considered too weak for good security now;
38             they’re included for historical interest.
39              
40             =over 4
41              
42             =item * sha1
43              
44             =item * md5
45              
46             =item * md2
47              
48             =back
49              
50             =cut
51              
52 4     4   57590 use strict;
  4         16  
  4         173  
53 4     4   21 use warnings;
  4         7  
  4         90  
54              
55 4     4   320 use Crypt::Perl::X ();
  4         7  
  4         105  
56              
57             #----------------------------------------------------------------------
58             #RFC 3447, page 42
59              
60             #These are too weak for modern hardware, but we’ll include them anyway.
61 4     4   19 use constant DER_header_md2 => "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10";
  4         9  
  4         258  
62 4     4   23 use constant DER_header_md5 => "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10";
  4         6  
  4         206  
63 4     4   23 use constant DER_header_sha1 => "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14";
  4         8  
  4         192  
64              
65              
66             #As of December 2016, the following are considered safe for general use.
67 4     4   22 use constant DER_header_sha256 => "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
  4         8  
  4         204  
68 4     4   19 use constant DER_header_sha384 => "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30";
  4         8  
  4         194  
69 4     4   20 use constant DER_header_sha512 => "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40";
  4         7  
  4         1292  
70             #----------------------------------------------------------------------
71              
72             #RFC 3447, section 9.2
73             sub encode {
74 268     268 0 978 my ($digest, $digest_oid, $emLen) = @_;
75              
76             #print "encoding: [$digest_oid]\n";
77 268         786 my $encoded = _asn1_DigestInfo( $digest, $digest_oid );
78              
79 268 50       1023 if ( $emLen < length($encoded) + 11 ) {
80 0         0 die Crypt::Perl::X::create('Generic', sprintf "intended encoded message length (%d bytes) is too short--must be at least %d bytes", $emLen, 11 + length $encoded);
81             }
82              
83             #NB: The length of $encoded will be a function solely of $digest_oid.
84              
85 268         946 my $PS = "\x{ff}" x ($emLen - length($encoded) - 3);
86              
87 268         887 return "\0\1$PS\0$encoded";
88             }
89              
90             #Assume that we already validated the length.
91             sub decode {
92 580     580 0 2797 my ($octets, $digest_oid) = @_;
93              
94             #printf "$digest_oid - %v02x\n", $octets;
95              
96 580         1865 my $hdr = _get_der_header($digest_oid);
97              
98 580 100       6319 $octets =~ m<\A \x00 \x01 \xff+ \x00 \Q$hdr\E >x or do {
99 56         604 my $err = sprintf "Invalid EMSA-PKCS1-v1_5/$digest_oid: %v02x", $octets;
100 56         410 die Crypt::Perl::X::create('Generic', $err);
101             };
102              
103 524         9114 return substr( $octets, $+[0] );
104             }
105              
106             sub _get_der_header {
107 848     848   1772 my ($oid) = @_;
108              
109 848         7801 return __PACKAGE__->can("DER_header_$oid")->();
110             }
111              
112             sub _asn1_DigestInfo {
113 268     268   820 my ($digest, $oid) = @_;
114              
115 268         520 return _get_der_header($oid) . $digest;
116             }
117              
118             #sub _asn1_DigestInfo {
119             # my ($digest, $algorithm_oid) = @_;
120             #
121             # #We shouldn’t need Convert::ASN1 for this.
122             # my $asn1 = Crypt::Sign::RSA::Convert_ASN1->new();
123             # $asn1->prepare_or_die(
124             # q<
125             # AlgorithmIdentifier ::= SEQUENCE {
126             # algorithm OBJECT IDENTIFIER,
127             # parameters NULL
128             # }
129             #
130             # DigestInfo ::= SEQUENCE {
131             # alg AlgorithmIdentifier,
132             # digest OCTET STRING
133             # }
134             # >,
135             # );
136             #
137             # my $parser = $asn1->find_or_die('DigestInfo');
138             #
139             # return $parser->encode_or_die(
140             # digest => $digest,
141             #
142             # #RFC 3447 says to use “sha256WithRSAEncryption”, but
143             # #OpenSSL’s RSA_sign() uses just “sha256”. (??)
144             # alg => { algorithm => $algorithm_oid, parameters => 1 },
145             # );
146             #}
147              
148             1;