File Coverage

blib/lib/Crypt/OpenPGP/Certificate.pm
Criterion Covered Total %
statement 185 240 77.0
branch 43 70 61.4
condition 7 18 38.8
subroutine 29 37 78.3
pod 20 26 76.9
total 284 391 72.6


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::Certificate;
2 4     4   25 use strict;
  4         9  
  4         152  
3              
4 4     4   2290 use Crypt::OpenPGP::S2k;
  4         12  
  4         133  
5 4     4   1505 use Crypt::OpenPGP::Key::Public;
  4         10  
  4         123  
6 4     4   2441 use Crypt::OpenPGP::Key::Secret;
  4         12  
  4         120  
7 4     4   26 use Crypt::OpenPGP::Buffer;
  4         6  
  4         126  
8 4     4   22 use Crypt::OpenPGP::Util qw( mp2bin bin2mp bitsize );
  4         6  
  4         327  
9 4         30 use Crypt::OpenPGP::Constants qw( DEFAULT_CIPHER
10             PGP_PKT_PUBLIC_KEY
11             PGP_PKT_PUBLIC_SUBKEY
12             PGP_PKT_SECRET_KEY
13 4     4   22 PGP_PKT_SECRET_SUBKEY );
  4         8  
14 4     4   1858 use Crypt::OpenPGP::Cipher;
  4         11  
  4         126  
15 4     4   22 use Crypt::OpenPGP::ErrorHandler;
  4         8  
  4         103  
16 4     4   20 use base qw( Crypt::OpenPGP::ErrorHandler );
  4         7  
  4         12751  
17              
18             {
19             my @PKT_TYPES = (
20             PGP_PKT_PUBLIC_KEY,
21             PGP_PKT_PUBLIC_SUBKEY,
22             PGP_PKT_SECRET_KEY,
23             PGP_PKT_SECRET_SUBKEY
24             );
25             sub pkt_type {
26 2     2 0 3 my $cert = shift;
27 2         12 $PKT_TYPES[ ($cert->{is_secret} << 1) | $cert->{is_subkey} ];
28             }
29             }
30              
31             sub new {
32 65     65 1 113 my $class = shift;
33 65         159 my $cert = bless { }, $class;
34 65         247 $cert->init(@_);
35             }
36              
37             sub init {
38 65     65 0 103 my $cert = shift;
39 65         172 my %param = @_;
40 65 100       231 if (my $key = $param{Key}) {
41 4   50     20 $cert->{version} = $param{Version} || 4;
42 4         7 $cert->{key} = $key;
43 4         35 $cert->{is_secret} = $key->is_secret;
44 4   50     26 $cert->{is_subkey} = $param{Subkey} || 0;
45 4         15 $cert->{timestamp} = time;
46 4         25 $cert->{pk_alg} = $key->alg_id;
47 4 100       11 if ($cert->{version} < 4) {
48 2   50     12 $cert->{validity} = $param{Validity} || 0;
49 2 50       13 $key->alg eq 'RSA' or
50             return (ref $cert)->error("Version 3 keys must be RSA");
51             }
52 4         34 $cert->{s2k} = Crypt::OpenPGP::S2k->new('Salt_Iter');
53              
54 4 100       13 if ($cert->{is_secret}) {
55             $param{Passphrase} or
56 2 50       7 return (ref $cert)->error("Need a Passphrase to lock key");
57 2   33     15 $cert->{cipher} = $param{Cipher} || DEFAULT_CIPHER;
58 2         13 $cert->lock($param{Passphrase});
59             }
60             }
61 65         182 $cert;
62             }
63              
64 0     0 0 0 sub type { $_[0]->{type} }
65 0     0 1 0 sub version { $_[0]->{version} }
66 0     0 1 0 sub timestamp { $_[0]->{timestamp} }
67 0     0 1 0 sub validity { $_[0]->{validity} }
68 0     0 0 0 sub pk_alg { $_[0]->{pk_alg} }
69 64     64 1 493 sub key { $_[0]->{key} }
70 9     9 1 139 sub is_secret { $_[0]->{key}->is_secret }
71 53     53 1 337 sub is_subkey { $_[0]->{is_subkey} }
72 19     19 1 577 sub is_protected { $_[0]->{is_protected} }
73 51     51 1 368 sub can_encrypt { $_[0]->{key}->can_encrypt }
74 11     11 1 74 sub can_sign { $_[0]->{key}->can_sign }
75             sub uid {
76 28     28 0 56 my $cert = shift;
77 28 50       114 $cert->{_uid} = shift if @_;
78 28         58 $cert->{_uid};
79             }
80              
81             sub public_cert {
82 9     9 1 38 my $cert = shift;
83 9 100       39 return $cert unless $cert->is_secret;
84 3         30 my $pub = (ref $cert)->new;
85 3         16 for my $f (qw( version timestamp pk_alg is_subkey )) {
86 12         32 $pub->{$f} = $cert->{$f};
87             }
88 3 50       19 $pub->{validity} = $cert->{validity} if $cert->{version} < 4;
89 3         37 $pub->{key} = $cert->{key}->public_key;
90 3         21 $pub;
91             }
92              
93             sub key_id {
94 136     136 1 3162 my $cert = shift;
95 136 100       472 unless ($cert->{key_id}) {
96 58 100       195 if ($cert->{version} < 4) {
97 8         90 $cert->{key_id} = substr(mp2bin($cert->{key}->n), -8);
98             }
99             else {
100 50         181 $cert->{key_id} = substr($cert->fingerprint, -8);
101             }
102             }
103 136         876 $cert->{key_id};
104             }
105              
106 0     0 1 0 sub key_id_hex { uc unpack 'H*', $_[0]->key_id }
107              
108             sub fingerprint {
109 50     50 1 93 my $cert = shift;
110 50 100       163 unless ($cert->{fingerprint}) {
111 3 50       16 if ($cert->{version} < 4) {
112 0         0 my $dgst = Crypt::OpenPGP::Digest->new('MD5');
113             $cert->{fingerprint} =
114 0         0 $dgst->hash(mp2bin($cert->{key}->n) . mp2bin($cert->{key}->e));
115             }
116             else {
117 3         15 my $data = $cert->public_cert->save;
118 3         102 $cert->{fingerprint} = _gen_v4_fingerprint($data);
119             }
120             }
121 50         280 $cert->{fingerprint};
122             }
123              
124 0     0 1 0 sub fingerprint_hex { uc unpack 'H*', $_[0]->fingerprint }
125              
126             sub fingerprint_words {
127 0     0 1 0 require Crypt::OpenPGP::Words;
128 0         0 Crypt::OpenPGP::Words->encode($_[0]->fingerprint);
129             }
130              
131             sub _gen_v4_fingerprint {
132 50     50   113 my($data) = @_;
133 50         290 my $buf = Crypt::OpenPGP::Buffer->new;
134 50         740 $buf->put_int8(0x99);
135 50         678 $buf->put_int16(length $data);
136 50         520 $buf->put_bytes($data);
137 50         896 my $dgst = Crypt::OpenPGP::Digest->new('SHA1');
138 50         171 $dgst->hash($buf->bytes);
139             }
140              
141             sub parse {
142 58     58 1 107 my $class = shift;
143 58         126 my($buf, $secret, $subkey) = @_;
144 58         202 my $cert = $class->new;
145 58         181 $cert->{is_secret} = $secret;
146 58         143 $cert->{is_subkey} = $subkey;
147              
148 58         191 $cert->{version} = $buf->get_int8;
149 58         997 $cert->{timestamp} = $buf->get_int32;
150 58 100       934 if ($cert->{version} < 4) {
151 11         42 $cert->{validity} = $buf->get_int16;
152             }
153 58         259 $cert->{pk_alg} = $buf->get_int8;
154              
155 58 100       699 my $key_class = 'Crypt::OpenPGP::Key::' . ($secret ? 'Secret' : 'Public');
156 58 50       495 my $key = $cert->{key} = $key_class->new($cert->{pk_alg}) or
157             return $class->error("Key creation failed: " . $key_class->errstr);
158              
159 58         200 my @pub = $key->public_props;
160 58         141 for my $e (@pub) {
161 184         8219 $key->$e($buf->get_mp_int);
162             }
163              
164 58 100       3065 if ($cert->{version} >= 4) {
165 47         308 my $data = $buf->bytes(0, $buf->offset);
166 47         776 $cert->{fingerprint} = _gen_v4_fingerprint($data);
167             }
168              
169 58 100       227 if ($secret) {
170 32         161 $cert->{cipher} = $buf->get_int8;
171 32 50       676 if ($cert->{cipher}) {
172 32         111 $cert->{is_protected} = 1;
173 32 50 33     172 if ($cert->{cipher} == 255 || $cert->{cipher} == 254) {
174 32         113 $cert->{sha1check} = $cert->{cipher} == 254;
175 32         125 $cert->{cipher} = $buf->get_int8;
176 32         569 $cert->{s2k} = Crypt::OpenPGP::S2k->parse($buf);
177             }
178             else {
179 0         0 $cert->{s2k} = Crypt::OpenPGP::S2k->new('Simple');
180 0         0 $cert->{s2k}->set_hash('MD5');
181             }
182              
183 32         112 $cert->{iv} = $buf->get_bytes(8);
184             }
185              
186 32 50       463 if ($cert->{is_protected}) {
187 32 50       114 if ($cert->{version} < 4) {
188 0         0 $cert->{encrypted} = {};
189 0         0 my @sec = $key->secret_props;
190 0         0 for my $e (@sec) {
191 0         0 my $h = $cert->{encrypted}{"${e}h"} = $buf->get_bytes(2);
192 0         0 $cert->{encrypted}{"${e}b"} =
193             $buf->get_bytes(int((unpack('n', $h)+7)/8));
194             }
195 0         0 $cert->{csum} = $buf->get_int16;
196             }
197             else {
198             $cert->{encrypted} =
199 32         197 $buf->get_bytes($buf->length - $buf->offset);
200             }
201             }
202             else {
203 0         0 my @sec = $key->secret_props;
204 0         0 for my $e (@sec) {
205 0         0 $key->$e($buf->get_mp_int);
206             }
207             }
208             }
209              
210 58         779 $cert;
211             }
212              
213             sub save {
214 9     9 1 15 my $cert = shift;
215 9         48 my $buf = Crypt::OpenPGP::Buffer->new;
216              
217 9         104 $buf->put_int8($cert->{version});
218 9         95 $buf->put_int32($cert->{timestamp});
219 9 100       83 if ($cert->{version} < 4) {
220 3         22 $buf->put_int16($cert->{validity});
221             }
222 9         56 $buf->put_int8($cert->{pk_alg});
223              
224 9         38 my $key = $cert->{key};
225 9         51 my @pub = $key->public_props;
226 9         31 for my $e (@pub) {
227 29         656 $buf->put_mp_int($key->$e());
228             }
229              
230 9 50       203 if ($cert->{key}->is_secret) {
231 0 0       0 if ($cert->{cipher}) {
232 0         0 $buf->put_int8(255);
233 0         0 $buf->put_int8($cert->{cipher});
234 0         0 $buf->append($cert->{s2k}->save);
235 0         0 $buf->put_bytes($cert->{iv});
236              
237 0 0       0 if ($cert->{version} < 4) {
238 0         0 my @sec = $key->secret_props;
239 0         0 for my $e (@sec) {
240 0         0 $buf->put_bytes($cert->{encrypted}{"${e}h"});
241 0         0 $buf->put_bytes($cert->{encrypted}{"${e}b"});
242             }
243 0         0 $buf->put_int16($cert->{csum});
244             }
245             else {
246 0         0 $buf->put_bytes($cert->{encrypted});
247             }
248             }
249             else {
250 0         0 my @sec = $key->secret_props;
251 0         0 for my $e (@sec) {
252 0         0 $key->$e($buf->get_mp_int);
253             }
254             }
255             }
256 9         50 $buf->bytes;
257             }
258              
259             sub v3_checksum {
260 1     1 0 2 my $cert = shift;
261 1         2 my $k = $cert->{encrypted};
262 1         1 my $sum = 0;
263 1         7 my @sec = $cert->{key}->secret_props;
264 1         3 for my $e (@sec) {
265 4         10 $sum += unpack '%16C*', $k->{"${e}h"};
266 4         9 $sum += unpack '%16C*', $k->{"${e}b"};
267             }
268 1         3 $sum & 0xFFFF;
269             }
270              
271             sub unlock {
272 8     8 1 80 my $cert = shift;
273 8 50 33     69 return 1 unless $cert->{is_secret} && $cert->{is_protected};
274 8         21 my($passphrase) = @_;
275 8 50       100 my $cipher = Crypt::OpenPGP::Cipher->new($cert->{cipher}) or
276             return $cert->error( Crypt::OpenPGP::Cipher->errstr );
277 8         47 my $key = $cert->{s2k}->generate($passphrase, $cipher->keysize);
278 8         95 $cipher->init($key, $cert->{iv});
279 8         69 my @sec = $cert->{key}->secret_props;
280 8 50       39 if ($cert->{version} < 4) {
281 0         0 my $k = $cert->{encrypted};
282 0         0 my $r = {};
283 0         0 for my $e (@sec) {
284 0         0 $r->{$e} = $k->{"${e}b"};
285 0         0 $k->{"${e}b"} = $cipher->decrypt($r->{$e});
286             }
287 0 0       0 unless ($cert->{csum} == $cert->v3_checksum) {
288 0         0 $k->{"${_}b"} = $r->{$_} for @sec;
289 0         0 return $cert->error("Bad checksum");
290             }
291 0         0 for my $e (@sec) {
292 0         0 $cert->{key}->$e(bin2mp($k->{"${e}b"}));
293             }
294 0 0       0 unless ($cert->{key}->check) {
295 0         0 $k->{"${_}b"} = $r->{$_} for @sec;
296 0         0 return $cert->error("p*q != n");
297             }
298             }
299             else {
300 8         95 my $decrypted = $cipher->decrypt($cert->{encrypted});
301 8 50       38 if ($cert->{sha1check}) {
302 0         0 my $dgst = Crypt::OpenPGP::Digest->new('SHA1');
303 0         0 my $csum = substr $decrypted, -20, 20, '';
304 0 0       0 unless ($dgst->hash($decrypted) eq $csum) {
305 0         0 return $cert->error("Bad SHA-1 hash");
306             }
307             } else {
308 8         70 my $csum = unpack "n", substr $decrypted, -2, 2, '';
309 8         40 my $gen_csum = unpack '%16C*', $decrypted;
310 8 50       45 unless ($csum == $gen_csum) {
311 0         0 return $cert->error("Bad simple checksum");
312             }
313             }
314 8         96 my $buf = Crypt::OpenPGP::Buffer->new;
315 8         139 $buf->append($decrypted);
316 8         58 for my $e (@sec) {
317 8         64 $cert->{key}->$e( $buf->get_mp_int );
318             }
319             }
320              
321 8         483 $cert->{is_protected} = 0;
322              
323 8         183 1;
324             }
325              
326             sub lock {
327 2     2 1 4 my $cert = shift;
328 2 50 33     12 return if !$cert->{is_secret} || $cert->{is_protected};
329 2         4 my($passphrase) = @_;
330 2         20 my $cipher = Crypt::OpenPGP::Cipher->new($cert->{cipher});
331 2         13 my $sym_key = $cert->{s2k}->generate($passphrase, $cipher->keysize);
332 2         15 $cert->{iv} = Crypt::OpenPGP::Util::get_random_bytes(8);
333 2         168 $cipher->init($sym_key, $cert->{iv});
334 2         13 my @sec = $cert->{key}->secret_props;
335 2 100       9 if ($cert->{version} < 4) {
336 1         4 my $k = $cert->{encrypted} = {};
337 1         7 my $key = $cert->key;
338 1         4 for my $e (@sec) {
339 4         3512 $k->{"${e}b"} = mp2bin($key->$e());
340 4         48 $k->{"${e}h"} = pack 'n', bitsize($key->$e());
341             }
342 1         1266 $cert->{csum} = $cert->v3_checksum;
343 1         3 for my $e (@sec) {
344 4         19 $k->{"${e}b"} = $cipher->encrypt( $k->{"${e}b"} );
345             }
346             }
347             else {
348 1         13 my $buf = Crypt::OpenPGP::Buffer->new;
349 1         16 for my $e (@sec) {
350 1         10 $buf->put_mp_int($cert->{key}->$e());
351             }
352 1         16 my $cnt = $buf->bytes;
353 1         18 $cnt .= pack 'n', unpack '%16C*', $cnt;
354 1         11 $cert->{encrypted} = $cipher->encrypt($cnt);
355             }
356              
357 2         7 $cert->{is_protected} = 1;
358 2         37 1;
359             }
360              
361             1;
362             __END__
363              
364             =head1 NAME
365              
366             Crypt::OpenPGP::Certificate - PGP Key certificate
367              
368             =head1 SYNOPSIS
369              
370             use Crypt::OpenPGP::Certificate;
371              
372             my $dsa_secret_key = Crypt::OpenPGP::Key::Secret->new( 'DSA' );
373             my $cert = Crypt::OpenPGP::Certificate->new(
374             Key => $dsa_secret_key,
375             Version => 4,
376             Passphrase => 'foobar',
377             );
378             my $serialized = $cert->save;
379              
380             # Unlock the locked certificate (using the passphrase from above)
381             $cert->unlock( 'foobar' );
382              
383             =head1 DESCRIPTION
384              
385             I<Crypt::OpenPGP::Certificate> encapsulates a PGP key certificate
386             for any underlying public-key algorithm, for public and secret keys,
387             and for master keys and subkeys. All of these scenarios are handled
388             by the same I<Certificate> class.
389              
390             A I<Crypt::OpenPGP::Certificate> object wraps around a
391             I<Crypt::OpenPGP::Key> object; the latter implements all public-key
392             algorithm-specific functionality, while the certificate layer
393             manages some meta-data about the key, as well as the mechanisms
394             for locking and unlocking a secret key (using a passphrase).
395              
396             =head1 USAGE
397              
398             =head2 Crypt::OpenPGP::Certificate->new( %arg )
399              
400             Constructs a new PGP key certificate object and returns that object.
401             If no arguments are provided in I<%arg>, the certificate is empty;
402             this is used in I<parse>, for example, to construct an empty object,
403             then fill it with the data in the buffer.
404              
405             I<%arg> can contain:
406              
407             =over 4
408              
409             =item * Key
410              
411             The public/secret key object, an object of type I<Crypt::OpenPGP::Key>.
412              
413             This argument is required (for a non-empty certificate).
414              
415             =item * Version
416              
417             The certificate packet version, as defined in the OpenPGP RFC. The
418             two valid values are C<3> and C<4>.
419              
420             This argument is optional; if not provided the default is to produce
421             version C<4> certificates. You may wish to override this for
422             compatibility with older versions of PGP.
423              
424             =item * Subkey
425              
426             A boolean flag: if true, indicates that this certificate is a subkey,
427             not a master key.
428              
429             This argument is optional; the default value is C<0>.
430              
431             =item * Validity
432              
433             The number of days that this certificate is valid. This argument only
434             applies when creating a version 3 certificate; version 4 certificates
435             hold this information in a signature.
436              
437             This argument is optional; the default value is C<0>, which means that
438             the certificate never expires.
439              
440             =item * Passphrase
441              
442             If you are creating a certificate for a secret key--indicated by whether
443             or not the I<Key> (above) is a secret key--you will need to lock it
444             (that is, encrypt the secret part of the key). The string provided in
445             I<Passphrase> is used as the passphrase to lock the key.
446              
447             This argument is required if the certificate holds a secret key.
448              
449             =item * Cipher
450              
451             Specifies the symmetric cipher to use when locking (encrypting) the
452             secret part of a secret key. Valid values are any supported symmetric
453             cipher names, which can be found in I<Crypt::OpenPGP::Cipher>.
454              
455             This argument is optional; if not specified, C<DES3> is used.
456              
457             =back
458              
459             =head2 $cert->save
460              
461             Serializes the I<Crypt::OpenPGP::Certificate> object I<$cert> into a
462             string of octets, suitable for saving in a keyring file.
463              
464             =head2 Crypt::OpenPGP::Certificate->parse($buffer)
465              
466             Given I<$buffer>, a I<Crypt::OpenPGP::Buffer> object holding (or with
467             offset point to) a certificate packet, returns a new object of type
468             I<Crypt::OpenPGP::Certificate>, initialized with the data from the
469             buffer.
470              
471             =head2 $cert->lock($passphrase)
472              
473             Locks the secret key data by encrypting that data with I<$passphrase>.
474              
475             Returns true on success, C<undef> on failure; in the case of failure
476             call I<errstr> to get the error message.
477              
478             =head2 $cert->unlock($passphrase)
479              
480             Uses the passphrase I<$passphrase> to unlock (decrypt) the secret
481             part of the key.
482              
483             Returns true on success, C<undef> on failure; in the case of failure
484             call I<errstr> to get the error message.
485              
486             =head2 $cert->fingerprint
487              
488             Returns the key fingerprint as an octet string.
489              
490             =head2 $cert->fingerprint_hex
491              
492             Returns the key fingerprint as a hex string.
493              
494             =head2 $cert->fingerprint_words
495              
496             Returns the key fingerprint as a list of English words, where each word
497             represents one octet from the fingerprint. See I<Crypt::OpenPGP::Words>
498             for more details about the encoding.
499              
500             =head2 $cert->key_id
501              
502             Returns the key ID.
503              
504             =head2 $cert->key_id_hex
505              
506             Returns the key ID as a hex string.
507              
508             =head2 $cert->key
509              
510             Returns the algorithm-specific portion of the certificate, the public
511             or secret key object (an object of type I<Crypt::OpenPGP::Key>).
512              
513             =head2 $cert->public_cert
514              
515             Returns a public version of the certificate, with a public key. If
516             the certificate was already public, the same certificate is returned;
517             if it was a secret certificate, a new I<Crypt::OpenPGP::Certificate>
518             object is created, and the secret key is made into a public version
519             of the key.
520              
521             =head2 $cert->version
522              
523             Returns the version of the certificate (C<3> or C<4>).
524              
525             =head2 $cert->timestamp
526              
527             Returns the creation date and time (in epoch time).
528              
529             =head2 $cert->validity
530              
531             Returns the number of days that the certificate is valid for version
532             3 keys.
533              
534             =head2 $cert->is_secret
535              
536             Returns true if the certificate holds a secret key, false otherwise.
537              
538             =head2 $cert->is_protected
539              
540             Returns true if the certificate is locked, false otherwise.
541              
542             =head2 $cert->is_subkey
543              
544             Returns true if the certificate is a subkey, false otherwise.
545              
546             =head2 $cert->can_encrypt
547              
548             Returns true if the public key algorithm for the certificate I<$cert>
549             can perform encryption/decryption, false otherwise.
550              
551             =head2 $cert->can_sign
552              
553             Returns true if the public key algorithm for the certificate I<$cert>
554             can perform signing/verification, false otherwise.
555              
556             =head1 AUTHOR & COPYRIGHTS
557              
558             Please see the Crypt::OpenPGP manpage for author, copyright, and
559             license information.
560              
561             =cut