File Coverage

blib/lib/Crypt/OpenPGP.pm
Criterion Covered Total %
statement 327 424 77.1
branch 170 314 54.1
condition 38 82 46.3
subroutine 30 33 90.9
pod 7 12 58.3
total 572 865 66.1


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP;
2 11     11   1378643 use strict;
  11         25  
  11         470  
3 11     11   137 use warnings;
  11         23  
  11         730  
4 11     11   265 use 5.008_001;
  11         40  
5              
6             our $VERSION = '1.19'; # VERSION
7              
8 11     11   5862 use Crypt::OpenPGP::Constants qw( DEFAULT_CIPHER );
  11         34  
  11         98  
9 11     11   6434 use Crypt::OpenPGP::KeyRing;
  11         37  
  11         462  
10 11     11   5564 use Crypt::OpenPGP::Plaintext;
  11         54  
  11         409  
11 11     11   5902 use Crypt::OpenPGP::Message;
  11         44  
  11         500  
12 11     11   77 use Crypt::OpenPGP::PacketFactory;
  11         20  
  11         342  
13 11     11   5672 use Crypt::OpenPGP::Config;
  11         42  
  11         513  
14 11     11   76 use Crypt::OpenPGP::Util;
  11         35  
  11         616  
15              
16 11     11   57 use Crypt::OpenPGP::ErrorHandler;
  11         22  
  11         311  
17 11     11   48 use base qw( Crypt::OpenPGP::ErrorHandler );
  11         18  
  11         1405  
18              
19 11     11   16727 use File::HomeDir;
  11         77145  
  11         945  
20 11     11   88 use File::Spec;
  11         21  
  11         6278  
21              
22             our %COMPAT;
23              
24             ## pgp2 and pgp5 do not trim trailing whitespace from "canonical text"
25             ## signatures, only from cleartext signatures.
26             ## See:
27             ## http://cert.uni-stuttgart.de/archive/ietf-openpgp/2000/01/msg00033.html
28             $Crypt::OpenPGP::Globals::Trim_trailing_ws = 1;
29              
30             {
31             my $env = sub {
32             my $dir = shift; my @paths;
33             if (exists $ENV{$dir}) { for (@_) { push @paths, "$ENV{$dir}/$_" } }
34             return @paths ? @paths : ();
35             };
36              
37             my $home = sub {
38             my( @path ) = @_;
39             my $home_dir = File::HomeDir->my_home or return;
40             return File::Spec->catfile( $home_dir, @path );
41             };
42              
43             %COMPAT = (
44             PGP2 => {
45             'sign' => { Digest => 'MD5', Version => 3 },
46             'encrypt' => { Cipher => 'IDEA', Compress => 'ZIP' },
47             'keygen' => { Type => 'RSA', Cipher => 'IDEA',
48             Version => 3, Digest => 'MD5' },
49             'PubRing' => [
50             $env->('PGPPATH','pubring.pgp'),
51             $home->( '.pgp', 'pubring.pgp' ),
52             ],
53             'SecRing' => [
54             $env->('PGPPATH','secring.pgp'),
55             $home->( '.pgp', 'secring.pgp' ),
56             ],
57             'Config' => [
58             $env->('PGPPATH', 'config.txt'),
59             $home->( '.pgp', 'config.txt' ),
60             ],
61             },
62              
63             PGP5 => {
64             'sign' => { Digest => 'SHA1', Version => 3 },
65             'encrypt' => { Cipher => 'DES3', Compress => 'ZIP' },
66             'keygen' => { Type => 'DSA', Cipher => 'DES3',
67             Version => 4, Digest => 'SHA1' },
68             'PubRing' => [
69             $env->('PGPPATH','pubring.pkr'),
70             $home->( '.pgp', 'pubring.pkr' ),
71             ],
72             'SecRing' => [
73             $env->('PGPPATH','secring.skr'),
74             $home->( '.pgp', 'secring.skr' ),
75             ],
76             'Config' => [
77             $env->('PGPPATH', 'pgp.cfg'),
78             $home->( '.pgp', 'pgp.cfg' ),
79             ],
80             },
81              
82             GnuPG => {
83             'sign' => { Digest => 'SHA256', Version => 4 },
84             'encrypt' => { Cipher => 'Rijndael', Compress => 'Zlib',
85             MDC => 1 },
86             'keygen' => { Type => 'RSA', Cipher => 'Rijndael',
87             Version => 4, Digest => 'SHA256' },
88             'Config' => [
89             $env->('GNUPGHOME', 'options'),
90             $home->( '.gnupg', 'options' ),
91             ],
92             'PubRing' => [
93             $env->('GNUPGHOME', 'pubring.gpg'),
94             $home->( '.gnupg', 'pubring.gpg' ),
95             ],
96             'SecRing' => [
97             $env->('GNUPGHOME', 'secring.gpg'),
98             $home->( '.gnupg', 'secring.gpg' ),
99             ],
100             },
101             );
102             }
103              
104 9     9 0 3806 sub version_string { __PACKAGE__ . ' ' . $VERSION }
105              
106 19     19 0 122 sub pubrings { $_[0]->{pubrings} }
107 15     15 0 131 sub secrings { $_[0]->{secrings} }
108              
109 11     11   141 use constant PUBLIC => 1;
  11         23  
  11         901  
110 11     11   68 use constant SECRET => 2;
  11         32  
  11         68966  
111              
112             sub add_ring {
113 13     13 0 54 my $pgp = shift;
114 13         58 my($type, $ring) = @_;
115 13 50       62 unless (ref($ring) eq 'Crypt::OpenPGP::KeyRing') {
116 13 100       250 $ring = Crypt::OpenPGP::KeyRing->new( Filename => $ring )
117             or return Crypt::OpenPGP::KeyRing->errstr;
118             }
119 12 100       76 if ($type == SECRET) {
120 6         23 push @{ $pgp->{secrings} }, $ring;
  6         35  
121             } else {
122 6         18 push @{ $pgp->{pubrings} }, $ring;
  6         29  
123             }
124 12         47 $ring;
125             }
126              
127             sub new {
128 10     10 1 1622304 my $class = shift;
129 10         96 my $pgp = bless { }, $class;
130 10         132 $pgp->init(@_);
131             }
132              
133             sub _first_exists {
134 15     15   31 my($list) = @_;
135 15         34 for my $f (@$list) {
136 15 50       41 next unless $f;
137 15 50       390 return $f if -e $f;
138             }
139             }
140              
141             sub init {
142 10     10 0 57 my $pgp = shift;
143 10         202 $pgp->{pubrings} = [];
144 10         68 $pgp->{secrings} = [];
145 10         115 my %param = @_;
146 10         40 my $cfg_file = delete $param{ConfigFile};
147 10 50       264 my $cfg = $pgp->{cfg} = Crypt::OpenPGP::Config->new(%param) or
148             return Crypt::OpenPGP::Config->errstr;
149 10 50 66     175 if (!$cfg_file && (my $compat = $cfg->get('Compat'))) {
150 0         0 $cfg_file = _first_exists($COMPAT{$compat}{Config});
151             }
152 10 100       81 if ($cfg_file) {
153 2         10 $cfg->read_config($param{Compat}, $cfg_file);
154             }
155             ## Load public and secret keyrings.
156 10         59 for my $s (qw( PubRing SecRing )) {
157 20 100       111 unless (defined $cfg->get($s)) {
158 7 100       37 my @compats = $param{Compat} ? ($param{Compat}) : keys %COMPAT;
159 7         15 for my $compat (@compats) {
160 15         65 my $ring = _first_exists($COMPAT{$compat}{$s});
161 15 50       54 $cfg->set($s, $ring), last if $ring;
162             }
163             }
164 20 100       126 if (my $ring = $cfg->get($s)) {
165 13 100       209 $pgp->add_ring($s eq 'PubRing' ? PUBLIC : SECRET, $ring);
166             }
167             }
168 10         81 $pgp;
169             }
170              
171             sub handle {
172 0     0 1 0 my $pgp = shift;
173 0         0 my %param = @_;
174 0         0 my($data);
175 0 0       0 unless ($data = $param{Data}) {
176             my $file = $param{Filename} or
177 0 0       0 return $pgp->error("Need either 'Data' or 'Filename' to decrypt");
178 0 0       0 $data = $pgp->_read_files($file) or return $pgp->error($pgp->errstr);
179             }
180 0 0       0 my $msg = Crypt::OpenPGP::Message->new( Data => $data ) or
181             return $pgp->error("Reading data packets failed: " .
182             Crypt::OpenPGP::Message->errstr);
183 0         0 my @pieces = $msg->pieces;
184 0 0       0 return $pgp->error("No packets found in message") unless @pieces;
185 0         0 while (ref($pieces[0]) eq 'Crypt::OpenPGP::Marker') {
186 0         0 shift @pieces;
187             }
188 0 0       0 if (ref($pieces[0]) eq 'Crypt::OpenPGP::Compressed') {
189 0 0       0 $data = $pieces[0]->decompress or
190             return $pgp->error("Decompression error: " . $pieces[0]->errstr);
191 0 0       0 $msg = Crypt::OpenPGP::Message->new( Data => $data ) or
192             return $pgp->error("Reading decompressed data failed: " .
193             Crypt::OpenPGP::Message->errstr);
194 0         0 @pieces = $msg->pieces;
195             }
196 0         0 my $class = ref($pieces[0]);
197 0         0 my(%res);
198 0 0 0     0 if ($class eq 'Crypt::OpenPGP::OnePassSig' ||
199             $class eq 'Crypt::OpenPGP::Signature') {
200 0         0 my($valid, $sig) = $pgp->verify( Signature => $data );
201 0 0       0 return $pgp->error("Error verifying signature: " . $pgp->errstr)
202             if !defined $valid;
203 0         0 $res{Validity} = $valid;
204 0         0 $res{Signature} = $sig;
205             } else {
206 0   0     0 my $cb = $param{PassphraseCallback} || \&_default_passphrase_cb;
207 0         0 my($pt, $valid, $sig) = $pgp->decrypt(
208             Data => $data,
209             PassphraseCallback => $cb,
210             );
211 0 0       0 return $pgp->error("Decryption failed: " . $pgp->errstr)
212             unless defined $pt;
213 0 0 0     0 return $pgp->error("Error verifying signature: " . $pgp->errstr)
214             if !defined($valid) && $pgp->errstr !~ /^No Signature/;
215 0         0 $res{Plaintext} = $pt;
216 0 0       0 $res{Validity} = $valid if defined $valid;
217 0 0       0 $res{Signature} = $sig if defined $sig;
218             }
219 0         0 \%res;
220             }
221              
222             sub _default_passphrase_cb {
223 0     0   0 my($cert) = @_;
224 0         0 my $prompt;
225 0 0       0 if ($cert) {
226 0         0 $prompt = sprintf qq(
227             You need a passphrase to unlock the secret key for
228             user "%s".
229             %d-bit %s key, ID %s
230              
231             Enter passphrase: ), $cert->uid,
232             $cert->key->size,
233             $cert->key->alg,
234             substr($cert->key_id_hex, -8, 8);
235             } else {
236 0         0 $prompt = "Enter passphrase: ";
237             }
238 0         0 _prompt($prompt, '', 1);
239             }
240              
241             sub _prompt {
242 0     0   0 my($prompt, $def, $noecho) = @_;
243 0         0 require Term::ReadKey;
244 0         0 Term::ReadKey->import;
245 0 0       0 print STDERR $prompt . ($def ? "[$def] " : "");
246 0 0       0 if ($noecho) {
247 0         0 ReadMode('noecho');
248             }
249 0         0 chomp(my $ans = ReadLine(0));
250 0         0 ReadMode('restore');
251 0         0 print STDERR "\n";
252 0 0       0 $ans ? $ans : $def;
253             }
254              
255             sub sign {
256 6     6 1 9251 my $pgp = shift;
257 6         61 my %param = @_;
258 6 50       64 $pgp->_merge_compat(\%param, 'sign') or
259             return $pgp->error( $pgp->errstr );
260 6         13 my($cert, $data);
261 6         1416 require Crypt::OpenPGP::Signature;
262 6 50       49 unless ($data = $param{Data}) {
263             my $file = $param{Filename} or
264 0 0       0 return $pgp->error("Need either 'Data' or 'Filename' to sign");
265 0 0       0 $data = $pgp->_read_files($file) or return $pgp->error($pgp->errstr);
266             }
267 6 100       25 unless ($cert = $param{Key}) {
268 5 50       24 my $kid = $param{KeyID} or return $pgp->error("No KeyID specified");
269 5 50       42 my $ring = $pgp->secrings->[0]
270             or return $pgp->error("No secret keyrings");
271 5 50       72 my $kb = $ring->find_keyblock_by_keyid(pack 'H*', $kid) or
272             return $pgp->error("Could not find secret key with KeyID $kid");
273 5         30 $cert = $kb->signing_key;
274 5         22 $cert->uid($kb->primary_uid);
275             }
276 6 100       28 if ($cert->is_protected) {
277 2         7 my $pass = $param{Passphrase};
278 2 50 33     23 if (!defined $pass && (my $cb = $param{PassphraseCallback})) {
279 0         0 $pass = $cb->($cert);
280             }
281 2 50       8 return $pgp->error("Need passphrase to unlock secret key")
282             unless $pass;
283 2 50       12 $cert->unlock($pass) or
284             return $pgp->error("Secret key unlock failed: " . $cert->errstr);
285             }
286 6         27 my @ptarg;
287 6 50       31 push @ptarg, ( Filename => $param{Filename} ) if $param{Filename};
288 6 100       22 if ($param{Clearsign}) {
289 1         4 push @ptarg, ( Mode => 't' );
290             ## In clear-signed messages, the line ending before the signature
291             ## is not considered part of the signed text.
292 1         24 (my $tmp = $data) =~ s!\r?\n$!!;
293 1         4 push @ptarg, ( Data => $tmp );
294             } else {
295 5         17 push @ptarg, ( Data => $data );
296             }
297 6         66 my $pt = Crypt::OpenPGP::Plaintext->new(@ptarg);
298 6         12 my @sigarg;
299 6 50       26 if (my $hash_alg = $param{Digest}) {
300 0 0       0 my $dgst = Crypt::OpenPGP::Digest->new($hash_alg) or
301             return $pgp->error( Crypt::OpenPGP::Digest->errstr );
302 0         0 @sigarg = ( Digest => $dgst->alg_id );
303             }
304 6 100       29 push @sigarg, (Type => 0x01) if $param{Clearsign};
305             my $sig = Crypt::OpenPGP::Signature->new(
306             Data => $pt,
307             Key => $cert,
308             Version => $param{Version},
309 6         81 @sigarg,
310             );
311 6 100       43 if ($param{Clearsign}) {
312 1         5 $param{Armour} = $param{Detach} = 1;
313             }
314             my $sig_data = Crypt::OpenPGP::PacketFactory->save($sig,
315 6 100       67 $param{Detach} ? () : ($pt));
316 6 100       33 if ($param{Armour}) {
317 3         840 require Crypt::OpenPGP::Armour;
318             $sig_data = Crypt::OpenPGP::Armour->armour(
319             Data => $sig_data,
320 3 100       35 Object => ($param{Detach} ? 'SIGNATURE' : 'MESSAGE'),
    50          
321             ) or return $pgp->error( Crypt::OpenPGP::Armour->errstr );
322             }
323 6 100       49 if ($param{Clearsign}) {
324 1         9 require Crypt::OpenPGP::Util;
325 1         11 my $hash = Crypt::OpenPGP::Digest->alg($sig->{hash_alg});
326 1         17 my $data = Crypt::OpenPGP::Util::dash_escape($data);
327 1 50       8 $data .= "\n" unless $data =~ /\n$/;
328 1 50       9 $sig_data = "-----BEGIN PGP SIGNED MESSAGE-----\n" .
329             ($hash eq 'MD5' ? '' : "Hash: $hash\n") .
330             "\n" .
331             $data .
332             $sig_data;
333             }
334 6         185 $sig_data;
335             }
336              
337             sub verify {
338 10     10 1 10129 my $pgp = shift;
339 10         77 my %param = @_;
340 10         36 my $wants_object = wantarray;
341 10         24 my($data, $sig);
342 10         2781 require Crypt::OpenPGP::Signature;
343             $param{Signature} or $param{SigFile} or
344 10 50 66     97 return $pgp->error("Need Signature or SigFile to verify");
345             my %arg = $param{Signature} ? (Data => $param{Signature}) :
346 10 100       78 (Filename => $param{SigFile});
347 10 100       37 $arg{IsPacketStream} = 1 if $param{IsPacketStream};
348 10 50       116 my $msg = Crypt::OpenPGP::Message->new( %arg ) or
349             return $pgp->error("Reading signature failed: " .
350             Crypt::OpenPGP::Message->errstr);
351 10         44 my @pieces = $msg->pieces;
352 10 100       65 if (ref($pieces[0]) eq 'Crypt::OpenPGP::Compressed') {
353 1 50       7 $data = $pieces[0]->decompress or
354             return $pgp->error("Decompression error: " . $pieces[0]->errstr);
355 1 50       15 $msg = Crypt::OpenPGP::Message->new( Data => $data ) or
356             return $pgp->error("Reading decompressed data failed: " .
357             Crypt::OpenPGP::Message->errstr);
358 1         7 @pieces = $msg->pieces;
359             }
360 10 100       59 if (ref($pieces[0]) eq 'Crypt::OpenPGP::OnePassSig') {
    50          
361 1         5 ($data, $sig) = @pieces[1,2];
362             } elsif (ref($pieces[0]) eq 'Crypt::OpenPGP::Signature') {
363 9         29 ($sig, $data) = @pieces[0,1];
364             } else {
365 0         0 return $pgp->error("SigFile contents are strange");
366             }
367 10 100       47 my @pte = ($sig->{type} == 1 ? (Mode => "t") : () );
368 10 100       38 unless ($data) {
369 3 100       10 if ($param{Data}) {
370 2         25 $data = Crypt::OpenPGP::Plaintext->new( Data => $param{Data}, @pte );
371             }
372             else {
373             ## if no Signature or detached sig in SigFile
374 0         0 my @files = ref($param{Files}) eq 'ARRAY' ? @{ $param{Files} } :
375 1 50       4 $param{Files};
376 1         15 my $fdata = $pgp->_read_files(@files);
377 1 50       7 return $pgp->error("Reading data files failed: " . $pgp->errstr)
378             unless defined $fdata;
379 0         0 $data = Crypt::OpenPGP::Plaintext->new( Data => $fdata, @pte );
380             }
381             }
382 9         27 my($cert, $kb);
383 9 100       55 unless ($cert = $param{Key}) {
384 8         47 my $key_id = $sig->key_id;
385 8         70 my $ring = $pgp->pubrings->[0];
386 8 50 33     133 unless ($ring && ($kb = $ring->find_keyblock_by_keyid($key_id))) {
387 0         0 my $cfg = $pgp->{cfg};
388 0 0 0     0 if ($cfg->get('AutoKeyRetrieve') && $cfg->get('KeyServer')) {
389 0         0 require Crypt::OpenPGP::KeyServer;
390 0         0 my $server = Crypt::OpenPGP::KeyServer->new(
391             Server => $cfg->get('KeyServer'),
392             );
393 0         0 $kb = $server->find_keyblock_by_keyid($key_id);
394             }
395 0 0       0 return $pgp->error("Could not find public key with KeyID " .
396             unpack('H*', $key_id))
397             unless $kb;
398             }
399 8         51 $cert = $kb->key_by_id($sig->key_id);
400             }
401              
402             ## pgp2 and pgp5 do not trim trailing whitespace from "canonical text"
403             ## signatures, only from cleartext signatures. So we first try to verify
404             ## the signature using proper RFC4880 canonical text, then if that fails,
405             ## retry without trimming trailing whitespace.
406             ## See:
407             ## http://cert.uni-stuttgart.de/archive/ietf-openpgp/2000/01/msg00033.html
408 9         22 my($dgst, $found);
409 9         31 for (1, 0) {
410 9         46 local $Crypt::OpenPGP::Globals::Trim_trailing_ws = $_;
411 9 50       46 $dgst = $sig->hash_data($data) or
412             return $pgp->error( $sig->errstr );
413 9 50       64 $found++, last if substr($dgst, 0, 2) eq $sig->{chk};
414             }
415 9 50       28 return $pgp->error("Message hash does not match signature checkbytes")
416             unless $found;
417 9 100 66     56 my $valid = $cert->key->public_key->verify($sig, $dgst) ?
    50          
418             ($kb && $kb->primary_uid ? $kb->primary_uid : 1) : 0;
419              
420 9 100       5265165 $wants_object ? ($valid, $sig) : $valid;
421             }
422              
423             sub encrypt {
424 23     23 1 24968 my $pgp = shift;
425 23         132 my %param = @_;
426 23 50       183 $pgp->_merge_compat(\%param, 'encrypt') or
427             return $pgp->error( $pgp->errstr );
428 23         50 my($data);
429 23         2002 require Crypt::OpenPGP::Cipher;
430 23         1406 require Crypt::OpenPGP::Ciphertext;
431 23 50       237 unless ($data = $param{Data}) {
432             my $file = $param{Filename} or
433 0 0       0 return $pgp->error("Need either 'Data' or 'Filename' to encrypt");
434 0 0       0 $data = $pgp->_read_files($file) or return $pgp->error($pgp->errstr);
435             }
436 23         50 my $ptdata;
437 23 100       559 if ($param{SignKeyID}) {
438             $ptdata = $pgp->sign(
439             Data => $data,
440             KeyID => $param{SignKeyID},
441             Compat => $param{Compat},
442             Armour => 0,
443             Passphrase => $param{SignPassphrase},
444             PassphraseCallback => $param{SignPassphraseCallback},
445             )
446 1 50       9 or return;
447             } else {
448             my $pt = Crypt::OpenPGP::Plaintext->new( Data => $data,
449 22 50       235 $param{Filename} ? (Filename => $param{Filename}) : () );
450 22         185 $ptdata = Crypt::OpenPGP::PacketFactory->save($pt);
451             }
452 23 100       101 if (my $alg = $param{Compress}) {
453 1         878 require Crypt::OpenPGP::Compressed;
454 1         14 $alg = Crypt::OpenPGP::Compressed->alg_id($alg);
455 1 50       7 my $cdata = Crypt::OpenPGP::Compressed->new( Data => $ptdata,
456             Alg => $alg ) or return $pgp->error("Compression error: " .
457             Crypt::OpenPGP::Compressed->errstr);
458 1         14 $ptdata = Crypt::OpenPGP::PacketFactory->save($cdata);
459             }
460 23         123 my $key_data = Crypt::OpenPGP::Util::get_random_bytes(32);
461             my $sym_alg = $param{Cipher} ?
462 23 50       3701 Crypt::OpenPGP::Cipher->alg_id($param{Cipher}) : DEFAULT_CIPHER;
463 23         60 my(@sym_keys);
464 23 100 100     165 if ($param{Recipients} && !ref($param{Recipients})) {
465 3         12 $param{Recipients} = [ $param{Recipients} ];
466             }
467 23 100       101 if (my $kid = delete $param{KeyID}) {
468 4 50       18 my @kid = ref $kid eq 'ARRAY' ? @$kid : $kid;
469 4         7 push @{ $param{Recipients} }, @kid;
  4         38  
470             }
471 23 100 100     171 if ($param{Key} || $param{Recipients}) {
    50          
472 12         905 require Crypt::OpenPGP::SessionKey;
473 12         34 my @keys;
474 12 100       50 if (my $recips = $param{Recipients}) {
475 11 50       58 my @recips = ref $recips eq 'ARRAY' ? @$recips : $recips;
476 11         71 my $ring = $pgp->pubrings->[0];
477 11         24 my %seen;
478             my $server;
479 11         23 my $cfg = $pgp->{cfg};
480 11 50 33     61 if ($cfg->get('AutoKeyRetrieve') && $cfg->get('KeyServer')) {
481 0         0 require Crypt::OpenPGP::KeyServer;
482 0         0 $server = Crypt::OpenPGP::KeyServer->new(
483             Server => $cfg->get('KeyServer'),
484             );
485             }
486 11         37 for my $r (@recips) {
487 15         43 my($lr, @kb) = (length($r));
488 15 100 100     143 if (($lr == 8 || $lr == 16) && $r !~ /[^\da-fA-F]/) {
      66        
489 14         64 my $id = pack 'H*', $r;
490 14 50       106 @kb = $ring->find_keyblock_by_keyid($id) if $ring;
491 14 50 66     79 @kb = $server->find_keyblock_by_keyid($id)
492             if !@kb && $server;
493             } else {
494 1 50       7 @kb = $ring->find_keyblock_by_uid($r) if $ring;
495 1 50 33     8 @kb = $server->find_keyblock_by_uid($r)
496             if !@kb && $server;
497             }
498 15         47 for my $kb (@kb) {
499 12 50       63 next unless my $cert = $kb->encrypting_key;
500 12 100       49 next if $seen{ $cert->key_id }++;
501 11         49 $cert->uid($kb->primary_uid);
502 11         77 push @keys, $cert;
503             }
504             }
505 11 50       76 if (my $cb = $param{RecipientsCallback}) {
506 0         0 @keys = @{ $cb->(\@keys) };
  0         0  
507             }
508             }
509 12 100       64 if ($param{Key}) {
510 0         0 push @keys, ref $param{Key} eq 'ARRAY' ? @{$param{Key}} :
511 1 50       8 $param{Key};
512             }
513 12 50       38 return $pgp->error("No known recipients for encryption")
514             unless @keys;
515 12         22 for my $key (@keys) {
516 12 50       109 push @sym_keys, Crypt::OpenPGP::SessionKey->new(
517             Key => $key,
518             SymKey => $key_data,
519             Cipher => $sym_alg,
520             ) or
521             return $pgp->error( Crypt::OpenPGP::SessionKey->errstr );
522             }
523             }
524             elsif (my $pass = $param{Passphrase}) {
525 11         1597 require Crypt::OpenPGP::SKSessionKey;
526 11         54 require Crypt::OpenPGP::S2k;
527 11         23 my $s2k;
528 11 50 33     53 if ($param{Compat} && $param{Compat} eq 'PGP2') {
529 0         0 $s2k = Crypt::OpenPGP::S2k->new('Simple');
530 0         0 $s2k->{hash} = Crypt::OpenPGP::Digest->new('MD5');
531             } else {
532 11         65 $s2k = Crypt::OpenPGP::S2k->new('Salt_Iter');
533             }
534 11 50       59 my $cipher = Crypt::OpenPGP::Cipher->new($sym_alg) or
535             return $pgp->error( Crypt::OpenPGP::Cipher->errstr );
536 11         35 my $keysize = $cipher->keysize;
537 11         47 $key_data = $s2k->generate($pass, $keysize);
538 11 50       129 push @sym_keys, Crypt::OpenPGP::SKSessionKey->new(
539             Passphrase => $pass,
540             SymKey => $key_data,
541             Cipher => $sym_alg,
542             S2k => $s2k,
543             ) or
544             return $pgp->error( Crypt::OpenPGP::SKSessionKey->errstr );
545             } else {
546 0         0 return $pgp->error("Need something to encrypt with");
547             }
548             my $enc = Crypt::OpenPGP::Ciphertext->new(
549             MDC => $param{MDC},
550 23         362 SymKey => $key_data,
551             Data => $ptdata,
552             Cipher => $sym_alg,
553             );
554             my $enc_data = Crypt::OpenPGP::PacketFactory->save(
555 23 50 33     345 $param{Passphrase} && $param{Compat} && $param{Compat} eq 'PGP2' ?
556             $enc : (@sym_keys, $enc)
557             );
558 23 100       140 if ($param{Armour}) {
559 1         616 require Crypt::OpenPGP::Armour;
560 1 50       7 $enc_data = Crypt::OpenPGP::Armour->armour(
561             Data => $enc_data,
562             Object => 'MESSAGE',
563             ) or return $pgp->error( Crypt::OpenPGP::Armour->errstr );
564             }
565 23         564 $enc_data;
566             }
567              
568             sub decrypt {
569 24     24 1 13284 my $pgp = shift;
570 24         154 my %param = @_;
571 24         78 my $wants_verify = wantarray;
572 24         50 my($data);
573 24 50       156 unless ($data = $param{Data}) {
574             my $file = $param{Filename} or
575 0 0       0 return $pgp->error("Need either 'Data' or 'Filename' to decrypt");
576 0 0       0 $data = $pgp->_read_files($file) or return $pgp->error($pgp->errstr);
577             }
578 24 50       242 my $msg = Crypt::OpenPGP::Message->new( Data => $data ) or
579             return $pgp->error("Reading data packets failed: " .
580             Crypt::OpenPGP::Message->errstr);
581 24         121 my @pieces = $msg->pieces;
582 24 50       96 return $pgp->error("No packets found in message") unless @pieces;
583 24         138 while (ref($pieces[0]) eq 'Crypt::OpenPGP::Marker') {
584 0         0 shift @pieces;
585             }
586 24         65 my($key, $alg);
587 24 100       109 if (ref($pieces[0]) eq 'Crypt::OpenPGP::SessionKey') {
    50          
588 13         38 my($sym_key, $cert, $ring) = (shift @pieces);
589 13 100       65 unless ($cert = $param{Key}) {
590 10 50       70 $ring = $pgp->secrings->[0]
591             or return $pgp->error("No secret keyrings");
592             }
593 13         29 my($kb);
594 13         56 while (ref($sym_key) eq 'Crypt::OpenPGP::SessionKey') {
595 13 100       43 if ($cert) {
596 3 50       55 if ($cert->key_id eq $sym_key->key_id) {
597             shift @pieces
598 3         14 while ref($pieces[0]) eq 'Crypt::OpenPGP::SessionKey';
599 3         9 last;
600             }
601             } else {
602 10 50       57 if ($kb = $ring->find_keyblock_by_keyid($sym_key->key_id)) {
603             shift @pieces
604 10         73 while ref($pieces[0]) eq 'Crypt::OpenPGP::SessionKey';
605 10         38 last;
606             }
607             }
608 0         0 $sym_key = shift @pieces;
609             }
610 13 50 66     114 return $pgp->error("Can't find a secret key to decrypt message")
611             unless $kb || $cert;
612 13 100       46 if ($kb) {
613 10         52 $cert = $kb->key_by_id($sym_key->key_id);
614 10         52 $cert->uid($kb->primary_uid);
615             }
616 13 100       97 if ($cert->is_protected) {
617 2         8 my $pass = $param{Passphrase};
618 2 50 33     9 if (!defined $pass && (my $cb = $param{PassphraseCallback})) {
619 0         0 $pass = $cb->($cert);
620             }
621 2 50       7 return $pgp->error("Need passphrase to unlock secret key")
622             unless $pass;
623 2 50       11 $cert->unlock($pass) or
624             return $pgp->error("Seckey unlock failed: " . $cert->errstr);
625             }
626 13 50       377 ($key, $alg) = $sym_key->decrypt($cert) or
627             return $pgp->error("Symkey decrypt failed: " . $sym_key->errstr);
628             }
629             elsif (ref($pieces[0]) eq 'Crypt::OpenPGP::SKSessionKey') {
630 11         23 my $sym_key = shift @pieces;
631 11         30 my $pass = $param{Passphrase};
632 11 50 33     45 if (!defined $pass && (my $cb = $param{PassphraseCallback})) {
633 0         0 $pass = $cb->();
634             }
635 11 50       31 return $pgp->error("Need passphrase to decrypt session key")
636             unless $pass;
637 11 50       53 ($key, $alg) = $sym_key->decrypt($pass) or
638             return $pgp->error("Symkey decrypt failed: " . $sym_key->errstr);
639             }
640 24         113 my $enc = $pieces[0];
641              
642             ## If there is still no symkey and symmetric algorithm, *and* the
643             ## first packet is a Crypt::OpenPGP::Ciphertext packet, assume that
644             ## the packet is encrypted using a symmetric key, using a 'Simple' s2k.
645 24 50 33     158 if (!$key && !$alg && ref($enc) eq 'Crypt::OpenPGP::Ciphertext') {
      33        
646             my $pass = $param{Passphrase} or
647 0 0       0 return $pgp->error("Need passphrase to decrypt session key");
648 0         0 require Crypt::OpenPGP::Cipher;
649 0         0 require Crypt::OpenPGP::S2k;
650 0         0 my $ciph = Crypt::OpenPGP::Cipher->new('IDEA');
651 0         0 my $s2k = Crypt::OpenPGP::S2k->new('Simple');
652 0         0 $s2k->{hash} = Crypt::OpenPGP::Digest->new('MD5');
653 0         0 $key = $s2k->generate($pass, $ciph->keysize);
654 0         0 $alg = $ciph->alg_id;
655             }
656              
657 24 50       246 $data = $enc->decrypt($key, $alg) or
658             return $pgp->error("Ciphertext decrypt failed: " . $enc->errstr);
659              
660             ## This is a special hack: if decrypt gets a signed, encrypted message,
661             ## it needs to be able to pass back the decrypted text *and* a flag
662             ## saying whether the signature is valid or not. But in some cases,
663             ## you don't know ahead of time if there is a signature at all--and if
664             ## there isn't, there is no way of knowing whether the signature is valid,
665             ## or if there isn't a signature at all. So this prepopulates the internal
666             ## errstr with the string "No Signature\n"--if there is a signature, and
667             ## there is an error during verification, the second return value will be
668             ## undef, and the errstr will contain the error that occurred. If there is
669             ## *not* a signature, the second return value will still be undef, but
670             ## the errstr is guaranteed to be "No Signature\n".
671 24         254 $pgp->error("No Signature");
672              
673 24         87 my($valid, $sig);
674 24         202 $msg = Crypt::OpenPGP::Message->new( Data => $data,
675             IsPacketStream => 1 );
676 24         109 @pieces = $msg->pieces;
677              
678             ## If the first packet in the decrypted data is compressed,
679             ## decompress it and set the list of packets to the result.
680 24 100       116 if (ref($pieces[0]) eq 'Crypt::OpenPGP::Compressed') {
681 2 50       104 $data = $pieces[0]->decompress or
682             return $pgp->error("Decompression error: " . $pieces[0]->errstr);
683 2         16 $msg = Crypt::OpenPGP::Message->new( Data => $data,
684             IsPacketStream => 1 );
685 2         10 @pieces = $msg->pieces;
686             }
687              
688 24         52 my($pt);
689 24 100 66     169 if (ref($pieces[0]) eq 'Crypt::OpenPGP::OnePassSig' ||
690             ref($pieces[0]) eq 'Crypt::OpenPGP::Signature') {
691 1         2 $pt = $pieces[1];
692 1 50       10 if ($wants_verify) {
693 1         18 ($valid, $sig) =
694             $pgp->verify( Signature => $data, IsPacketStream => 1 );
695             }
696             } else {
697 23         48 $pt = $pieces[0];
698             }
699              
700 24 100       174 $wants_verify ? ($pt->data, $valid, $sig) : $pt->data;
701             }
702              
703             sub keygen {
704 2     2 1 8799 my $pgp = shift;
705 2         19 my %param = @_;
706 2         1107 require Crypt::OpenPGP::Certificate;
707 2         53 require Crypt::OpenPGP::Key;
708 2         35 require Crypt::OpenPGP::KeyBlock;
709 2         2677 require Crypt::OpenPGP::Signature;
710 2         962 require Crypt::OpenPGP::UserID;
711              
712             $param{Type} or
713 2 50       14 return $pgp->error("Need a Type of key to generate");
714 2   50     32 $param{Size} ||= 1024;
715 2   50     16 $param{Version} ||= 4;
716 2 100       19 $param{Version} = 3 if $param{Type} eq 'RSA';
717              
718 2         43 my $kb_pub = Crypt::OpenPGP::KeyBlock->new;
719 2         6 my $kb_sec = Crypt::OpenPGP::KeyBlock->new;
720              
721 2         20 my($pub, $sec) = Crypt::OpenPGP::Key->keygen($param{Type}, %param);
722 2 50 33     39 die Crypt::OpenPGP::Key->errstr unless $pub && $sec;
723             my $pubcert = Crypt::OpenPGP::Certificate->new(
724             Key => $pub,
725             Version => $param{Version}
726 2 50       87 ) or
727             die Crypt::OpenPGP::Certificate->errstr;
728             my $seccert = Crypt::OpenPGP::Certificate->new(
729             Key => $sec,
730             Passphrase => $param{Passphrase},
731             Version => $param{Version}
732 2 50       17 ) or
733             die Crypt::OpenPGP::Certificate->errstr;
734 2         43 $kb_pub->add($pubcert);
735 2         13 $kb_sec->add($seccert);
736              
737 2         103 my $id = Crypt::OpenPGP::UserID->new( Identity => $param{Identity} );
738 2         24 $kb_pub->add($id);
739 2         7 $kb_sec->add($id);
740              
741             my $sig = Crypt::OpenPGP::Signature->new(
742             Data => [ $pubcert, $id ],
743             Key => $seccert,
744             Version => $param{Version},
745 2         60 Type => 0x13,
746             );
747 2         36 $kb_pub->add($sig);
748 2         8 $kb_sec->add($sig);
749              
750 2         82 ($kb_pub, $kb_sec);
751             }
752              
753             sub _read_files {
754 1     1   2 my $pgp = shift;
755 1 50       3 return $pgp->error("No files specified") unless @_;
756 1         3 my @files = @_;
757 1         9 my $data = '';
758 1         4 for my $file (@files) {
759 1   50     8 $file ||= '';
760 1         3 local *FH;
761 1 50       75 open FH, $file or return $pgp->error("Error opening $file: $!");
762 0         0 binmode FH;
763 0         0 { local $/; $data .= }
  0         0  
  0         0  
764 0 0       0 close FH or warn "Warning: Got error closing $file: $!";
765             }
766 0         0 $data;
767             }
768              
769             {
770             my @MERGE_CONFIG = qw( Cipher Armour Digest );
771             sub _merge_compat {
772 29     29   67 my $pgp = shift;
773 29         114 my($param, $meth) = @_;
774 29   50     335 my $compat = $param->{Compat} || $pgp->{cfg}->get('Compat') || return 1;
775 0 0         my $ref = $COMPAT{$compat}{$meth} or
776             return $pgp->error("No settings for Compat class '$compat'");
777 0           for my $arg (keys %$ref) {
778 0 0         $param->{$arg} = $ref->{$arg} unless exists $param->{$arg};
779             }
780 0           for my $key (@MERGE_CONFIG) {
781             $param->{$key} = $pgp->{cfg}->get($key)
782 0 0         unless exists $param->{$key};
783             }
784 0           1;
785             }
786             }
787              
788             1;
789              
790             __END__