File Coverage

blib/lib/Mail/DKIM/PrivateKey.pm
Criterion Covered Total %
statement 81 98 82.6
branch 20 34 58.8
condition 2 2 100.0
subroutine 13 15 86.6
pod 2 5 40.0
total 118 154 76.6


line stmt bran cond sub pod time code
1             package Mail::DKIM::PrivateKey;
2 8     8   57 use strict;
  8         15  
  8         227  
3 8     8   39 use warnings;
  8         14  
  8         349  
4             our $VERSION = '1.20230630'; # VERSION
5             # ABSTRACT: a private key loaded in memory for DKIM signing
6              
7             # Copyright 2005-2007 Messiah College. All rights reserved.
8             # Jason Long
9             #
10             # Copyright (c) 2004 Anthony D. Urso. All rights reserved.
11             # This program is free software; you can redistribute it and/or
12             # modify it under the same terms as Perl itself.
13              
14              
15 8     8   51 use base 'Mail::DKIM::Key';
  8         14  
  8         3620  
16 8     8   54 use Carp;
  8         18  
  8         606  
17             *calculate_EM = \&Mail::DKIM::Key::calculate_EM;
18 8     8   4000 use Crypt::OpenSSL::RSA;
  8         55276  
  8         359  
19 8     8   4261 use Crypt::PK::Ed25519;
  8         212794  
  8         7611  
20              
21              
22             sub load {
23 45     45 1 45313 my $class = shift;
24 45         150 my %prms = @_;
25              
26 45         105 my $self = bless {}, $class;
27              
28 45   100     240 $self->{'TYPE'} = ( $prms{'Type'} or 'rsa' );
29              
30 45 100       148 if ( $prms{'Data'} ) {
    50          
    0          
31 17         35 $self->{'DATA'} = $prms{'Data'};
32             }
33             elsif ( defined $prms{'File'} ) {
34 28         44 my @data;
35 28 100       1232 open my $file, '<', $prms{'File'}
36             or die "Error: cannot read $prms{File}: $!\n";
37 26         709 while ( my $line = <$file> ) {
38 366         548 chomp $line;
39 366 100       1050 next if $line =~ /^---/;
40 314         735 push @data, $line;
41             }
42 26         209 $self->{'DATA'} = join '', @data;
43 26         381 close $file;
44             }
45             elsif ( $prms{'Cork'} ) {
46 0         0 $self->{'CORK'} = $prms{'Cork'};
47             }
48             else {
49 0         0 croak 'missing required argument';
50             }
51              
52 43         270 return $self;
53             }
54              
55              
56             sub _convert_rsa {
57 37     37   67 my $self = shift;
58              
59             # have to PKCS1ify the privkey because openssl is too finicky...
60 37         69 my $pkcs = "-----BEGIN RSA PRIVATE KEY-----\n";
61              
62 37         117 for ( my $i = 0 ; $i < length $self->data ; $i += 64 ) {
63 481         860 $pkcs .= substr $self->data, $i, 64;
64 481         941 $pkcs .= "\n";
65             }
66              
67 37         74 $pkcs .= "-----END RSA PRIVATE KEY-----\n";
68              
69 37         60 my $cork;
70              
71             eval {
72 37         148 local $SIG{__DIE__};
73 37         1713 $cork = new_private_key Crypt::OpenSSL::RSA($pkcs);
74 37         229 1
75 37 50       65 } || do {
76 0         0 $self->errorstr($@);
77 0         0 return;
78             };
79              
80 37 50       130 $cork
81             or return;
82              
83             # segfaults on my machine
84             # $cork->check_key or
85             # return;
86              
87 37         143 $self->cork($cork);
88 37         73 return 1;
89             }
90              
91             sub _convert_ed25519 {
92 2     2   4 my $self = shift;
93 2         4 my $cork;
94              
95             eval {
96 2         8 local $SIG{__DIE__};
97 2         25 $cork = new Crypt::PK::Ed25519;
98              
99             # Prepend/append with PEM boilerplate
100 2         134 my $pem = "-----BEGIN ED25519 PRIVATE KEY-----\n";
101 2         9 $pem .= $self->data;
102 2         9 $pem .= "\n";
103 2         6 $pem .= "-----END ED25519 PRIVATE KEY-----\n";
104              
105             # Pass PEM text buffer
106 2 50       8 $cork->import_key(\$pem)
107             or die 'failed to load Ed25519 private key';
108              
109             # Alternatively, import_raw_key() could be used,
110             # but requires the 32-byte key, which must be extracted
111             # from the ASN.1 structure first.
112              
113 2         6704 1
114 2 50       3 } || do {
115 0         0 $self->errorstr($@);
116 0         0 return;
117             };
118              
119 2 50       7 $cork
120             or return;
121              
122 2         10 $self->cork($cork);
123 2         4 return 1;
124             }
125              
126             sub convert {
127 39     39 0 61 my $self = shift;
128              
129 39 50       100 $self->data
130             or return;
131              
132 39 100       135 return $self->_convert_rsa if $self->{TYPE} eq 'rsa';
133 2 50       11 return $self->_convert_ed25519 if $self->{TYPE} eq 'ed25519';
134 0         0 self->errorstr('unsupported key type');
135 0         0 return;
136             }
137              
138             #deprecated
139             sub sign {
140 0     0 0 0 my $self = shift;
141 0         0 my $mail = shift;
142              
143 0         0 return $self->cork->sign($mail);
144             }
145              
146             #deprecated- use sign_digest() instead
147             sub sign_sha1_digest {
148 0     0 0 0 my $self = shift;
149 0         0 my ($digest) = @_;
150 0         0 return $self->sign_digest( 'SHA-1', $digest );
151             }
152              
153              
154             sub _sign_digest_rsa {
155 57     57   87 my $self = shift;
156 57         103 my ( $digest_algorithm, $digest ) = @_;
157              
158 57         221 my $rsa_priv = $self->cork;
159 57         210 $rsa_priv->use_no_padding;
160 57         164 my $k = $rsa_priv->size;
161 57         174 my $EM = calculate_EM( $digest_algorithm, $digest, $k );
162 57         29029 return $rsa_priv->decrypt($EM);
163             }
164              
165             sub _sign_digest_ed25519 {
166 2     2   4 my $self = shift;
167 2         6 my ( $digest_algorithm, $digest ) = @_;
168              
169 2         5 my $ed = $self->cork;
170 2 50       6 if ( !$ed ) {
171 0 0       0 $@ = $@ ne '' ? "Ed25519 failed: $@" : 'Ed25519 unknown problem';
172 0         0 die;
173             }
174 2         6484 return $ed->sign_message($digest);
175             }
176              
177             sub sign_digest {
178 59     59 1 161 my $self = shift;
179 59         132 my ( $digest_algorithm, $digest ) = @_;
180              
181 59 100       218 return $self->_sign_digest_rsa($digest_algorithm, $digest) if $self->{TYPE} eq 'rsa';
182 2 50       9 return $self->_sign_digest_ed25519($digest_algorithm, $digest) if $self->{TYPE} eq 'ed25519';
183 0           die 'unsupported key type';
184             }
185              
186             __END__