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   54 use strict;
  8         16  
  8         231  
3 8     8   37 use warnings;
  8         14  
  8         345  
4             our $VERSION = '1.20230911'; # 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   48 use base 'Mail::DKIM::Key';
  8         14  
  8         3657  
16 8     8   55 use Carp;
  8         17  
  8         591  
17             *calculate_EM = \&Mail::DKIM::Key::calculate_EM;
18 8     8   4029 use Crypt::OpenSSL::RSA;
  8         55532  
  8         359  
19 8     8   3974 use Crypt::PK::Ed25519;
  8         213048  
  8         8031  
20              
21              
22             sub load {
23 45     45 1 43892 my $class = shift;
24 45         148 my %prms = @_;
25              
26 45         116 my $self = bless {}, $class;
27              
28 45   100     300 $self->{'TYPE'} = ( $prms{'Type'} or 'rsa' );
29              
30 45 100       167 if ( $prms{'Data'} ) {
    50          
    0          
31 17         39 $self->{'DATA'} = $prms{'Data'};
32             }
33             elsif ( defined $prms{'File'} ) {
34 28         46 my @data;
35 28 100       1357 open my $file, '<', $prms{'File'}
36             or die "Error: cannot read $prms{File}: $!\n";
37 26         768 while ( my $line = <$file> ) {
38 366         578 chomp $line;
39 366 100       1067 next if $line =~ /^---/;
40 314         751 push @data, $line;
41             }
42 26         197 $self->{'DATA'} = join '', @data;
43 26         404 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         286 return $self;
53             }
54              
55              
56             sub _convert_rsa {
57 37     37   57 my $self = shift;
58              
59             # have to PKCS1ify the privkey because openssl is too finicky...
60 37         77 my $pkcs = "-----BEGIN RSA PRIVATE KEY-----\n";
61              
62 37         126 for ( my $i = 0 ; $i < length $self->data ; $i += 64 ) {
63 481         829 $pkcs .= substr $self->data, $i, 64;
64 481         967 $pkcs .= "\n";
65             }
66              
67 37         70 $pkcs .= "-----END RSA PRIVATE KEY-----\n";
68              
69 37         65 my $cork;
70              
71             eval {
72 37         145 local $SIG{__DIE__};
73 37         1857 $cork = new_private_key Crypt::OpenSSL::RSA($pkcs);
74 37         236 1
75 37 50       65 } || do {
76 0         0 $self->errorstr($@);
77 0         0 return;
78             };
79              
80 37 50       144 $cork
81             or return;
82              
83             # segfaults on my machine
84             # $cork->check_key or
85             # return;
86              
87 37         147 $self->cork($cork);
88 37         78 return 1;
89             }
90              
91             sub _convert_ed25519 {
92 2     2   5 my $self = shift;
93 2         4 my $cork;
94              
95             eval {
96 2         10 local $SIG{__DIE__};
97 2         34 $cork = new Crypt::PK::Ed25519;
98              
99             # Prepend/append with PEM boilerplate
100 2         159 my $pem = "-----BEGIN ED25519 PRIVATE KEY-----\n";
101 2         9 $pem .= $self->data;
102 2         5 $pem .= "\n";
103 2         6 $pem .= "-----END ED25519 PRIVATE KEY-----\n";
104              
105             # Pass PEM text buffer
106 2 50       13 $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         6830 1
114 2 50       5 } || do {
115 0         0 $self->errorstr($@);
116 0         0 return;
117             };
118              
119 2 50       7 $cork
120             or return;
121              
122 2         12 $self->cork($cork);
123 2         5 return 1;
124             }
125              
126             sub convert {
127 39     39 0 63 my $self = shift;
128              
129 39 50       121 $self->data
130             or return;
131              
132 39 100       168 return $self->_convert_rsa if $self->{TYPE} eq 'rsa';
133 2 50       13 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   91 my $self = shift;
156 57         109 my ( $digest_algorithm, $digest ) = @_;
157              
158 57         242 my $rsa_priv = $self->cork;
159 57         218 $rsa_priv->use_no_padding;
160 57         166 my $k = $rsa_priv->size;
161 57         179 my $EM = calculate_EM( $digest_algorithm, $digest, $k );
162 57         28960 return $rsa_priv->decrypt($EM);
163             }
164              
165             sub _sign_digest_ed25519 {
166 2     2   5 my $self = shift;
167 2         5 my ( $digest_algorithm, $digest ) = @_;
168              
169 2         8 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         6472 return $ed->sign_message($digest);
175             }
176              
177             sub sign_digest {
178 59     59 1 236 my $self = shift;
179 59         128 my ( $digest_algorithm, $digest ) = @_;
180              
181 59 100       224 return $self->_sign_digest_rsa($digest_algorithm, $digest) if $self->{TYPE} eq 'rsa';
182 2 50       11 return $self->_sign_digest_ed25519($digest_algorithm, $digest) if $self->{TYPE} eq 'ed25519';
183 0           die 'unsupported key type';
184             }
185              
186             __END__