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 9     9   62 use strict;
  9         20  
  9         308  
3 9     9   42 use warnings;
  9         21  
  9         673  
4             our $VERSION = '1.20240923'; # 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 9     9   56 use base 'Mail::DKIM::Key';
  9         19  
  9         4660  
16 9     9   61 use Carp;
  9         15  
  9         872  
17             *calculate_EM = \&Mail::DKIM::Key::calculate_EM;
18 9     9   5057 use Crypt::OpenSSL::RSA;
  9         36085  
  9         535  
19 9     9   5365 use Crypt::PK::Ed25519;
  9         306498  
  9         10592  
20              
21              
22             sub load {
23 46     46 1 348657 my $class = shift;
24 46         262 my %prms = @_;
25              
26 46         137 my $self = bless {}, $class;
27              
28 46   100     398 $self->{'TYPE'} = ( $prms{'Type'} or 'rsa' );
29              
30 46 100       212 if ( $prms{'Data'} ) {
    50          
    0          
31 17         108 $self->{'DATA'} = $prms{'Data'};
32             }
33             elsif ( defined $prms{'File'} ) {
34 29         92 my @data;
35 29 100       2134 open my $file, '<', $prms{'File'}
36             or die "Error: cannot read $prms{File}: $!\n";
37 27         961 while ( my $line = <$file> ) {
38 381         517 chomp $line;
39 381 100       999 next if $line =~ /^---/;
40 327         762 push @data, $line;
41             }
42 27         271 $self->{'DATA'} = join '', @data;
43 27         526 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 44         370 return $self;
53             }
54              
55              
56             sub _convert_rsa {
57 38     38   71 my $self = shift;
58              
59             # have to PKCS1ify the privkey because openssl is too finicky...
60 38         89 my $pkcs = "-----BEGIN RSA PRIVATE KEY-----\n";
61              
62 38         134 for ( my $i = 0 ; $i < length $self->data ; $i += 64 ) {
63 494         852 $pkcs .= substr $self->data, $i, 64;
64 494         971 $pkcs .= "\n";
65             }
66              
67 38         69 $pkcs .= "-----END RSA PRIVATE KEY-----\n";
68              
69 38         65 my $cork;
70              
71             eval {
72 38         217 local $SIG{__DIE__};
73 38         79846 $cork = new_private_key Crypt::OpenSSL::RSA($pkcs);
74 38         409 1
75 38 50       83 } || do {
76 0         0 $self->errorstr($@);
77 0         0 return;
78             };
79              
80 38 50       154 $cork
81             or return;
82              
83             # segfaults on my machine
84             # $cork->check_key or
85             # return;
86              
87 38         248 $self->cork($cork);
88 38         108 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         12 local $SIG{__DIE__};
97 2         25 $cork = new Crypt::PK::Ed25519;
98              
99             # Prepend/append with PEM boilerplate
100 2         241 my $pem = "-----BEGIN PRIVATE KEY-----\n";
101 2         10 $pem .= $self->data;
102 2         5 $pem .= "\n";
103 2         5 $pem .= "-----END PRIVATE KEY-----\n";
104              
105             # Pass PEM text buffer
106 2 50       18 $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         9014 1
114 2 50       5 } || do {
115 0         0 $self->errorstr($@);
116 0         0 return;
117             };
118              
119 2 50       8 $cork
120             or return;
121              
122 2         11 $self->cork($cork);
123 2         5 return 1;
124             }
125              
126             sub convert {
127 40     40 0 88 my $self = shift;
128              
129 40 50       141 $self->data
130             or return;
131              
132 40 100       273 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 58     58   98 my $self = shift;
156 58         138 my ( $digest_algorithm, $digest ) = @_;
157              
158 58         269 my $rsa_priv = $self->cork;
159 58         428 $rsa_priv->use_no_padding;
160 58         207 my $k = $rsa_priv->size;
161 58         274 my $EM = calculate_EM( $digest_algorithm, $digest, $k );
162 58         59879 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         9 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         8563 return $ed->sign_message($digest);
175             }
176              
177             sub sign_digest {
178 60     60 1 112 my $self = shift;
179 60         166 my ( $digest_algorithm, $digest ) = @_;
180              
181 60 100       297 return $self->_sign_digest_rsa($digest_algorithm, $digest) if $self->{TYPE} eq 'rsa';
182 2 50       12 return $self->_sign_digest_ed25519($digest_algorithm, $digest) if $self->{TYPE} eq 'ed25519';
183 0           die 'unsupported key type';
184             }
185              
186             __END__