File Coverage

blib/lib/Crypt/OpenPGP/SessionKey.pm
Criterion Covered Total %
statement 81 87 93.1
branch 10 18 55.5
condition 4 9 44.4
subroutine 15 16 93.7
pod 5 7 71.4
total 115 137 83.9


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::SessionKey;
2 1     1   4 use strict;
  1         2  
  1         27  
3              
4 1     1   4 use Crypt::OpenPGP::Constants qw( DEFAULT_CIPHER );
  1         1  
  1         6  
5 1     1   583 use Crypt::OpenPGP::Key::Public;
  1         2  
  1         23  
6 1     1   4 use Crypt::OpenPGP::Util qw( mp2bin bin2mp bitsize );
  1         1  
  1         53  
7 1     1   3 use Crypt::OpenPGP::Buffer;
  1         1  
  1         19  
8 1     1   3 use Crypt::OpenPGP::ErrorHandler;
  1         1  
  1         13  
9 1     1   3 use base qw( Crypt::OpenPGP::ErrorHandler );
  1         0  
  1         804  
10              
11 26     26 1 131 sub key_id { $_[0]->{key_id} }
12              
13             sub new {
14 30     30 1 62 my $class = shift;
15 30         87 my $key = bless { }, $class;
16 30         126 $key->init(@_);
17             }
18              
19             sub init {
20 30     30 0 52 my $key = shift;
21 30         103 my %param = @_;
22 30         149 $key->{version} = 3;
23 30 100 66     240 if ((my $cert = $param{Key}) && (my $sym_key = $param{SymKey})) {
24 15   33     58 my $alg = $param{Cipher} || DEFAULT_CIPHER;
25 15 50       123 my $cipher = Crypt::OpenPGP::Cipher->new($alg) or
26             return (ref $key)->error( Crypt::OpenPGP::Cipher->errstr );
27 15         53 my $keysize = $cipher->keysize;
28 15         42 $sym_key = substr $sym_key, 0, $keysize;
29 15         73 my $pk = $cert->key->public_key;
30 15 50       92 my $enc = $key->_encode($sym_key, $alg, $pk->bytesize) or
31             return (ref $key)->error("Encoding symkey failed: " . $key->errstr);
32 15         78343 $key->{key_id} = $cert->key_id;
33 15 50       101 $key->{C} = $pk->encrypt($enc) or
34             return (ref $key)->error("Encryption failed: " . $pk->errstr);
35 15         23952 $key->{pk_alg} = $pk->alg_id;
36             }
37 30         242 $key;
38             }
39              
40             sub parse {
41 15     15 1 33 my $class = shift;
42 15         28 my($buf) = @_;
43 15         57 my $key = $class->new;
44 15         50 $key->{version} = $buf->get_int8;
45             return $class->error("Unsupported version ($key->{version})")
46 15 50 33     312 unless $key->{version} == 2 || $key->{version} == 3;
47 15         48 $key->{key_id} = $buf->get_bytes(8);
48 15         176 $key->{pk_alg} = $buf->get_int8;
49 15         261 my $pk = Crypt::OpenPGP::Key::Public->new($key->{pk_alg});
50 15         69 my @props = $pk->crypt_props;
51 15         41 for my $e (@props) {
52 27         1122 $key->{C}{$e} = $buf->get_mp_int;
53             }
54 15         2118 $key;
55             }
56              
57             sub save {
58 15     15 1 31 my $key = shift;
59 15         84 my $buf = Crypt::OpenPGP::Buffer->new;
60 15         201 $buf->put_int8($key->{version});
61 15         162 $buf->put_bytes($key->{key_id}, 8);
62 15         169 $buf->put_int8($key->{pk_alg});
63 15         84 my $c = $key->{C};
64 15         88 for my $prop (sort keys %$c) {
65 27         339 $buf->put_mp_int($c->{$prop});
66             }
67 15         410 $buf->bytes;
68             }
69              
70             sub display {
71 0     0 0 0 my $key = shift;
72             my $str = sprintf ":pubkey enc packet: version %d, algo %d, keyid %s\n",
73 0         0 $key->{version}, $key->{pk_alg}, uc unpack('H*', $key->{key_id});
74 0         0 my $c = $key->{C};
75 0         0 for my $prop (sort keys %$c) {
76 0         0 $str .= sprintf " data: [%d bits]\n", bitsize($c->{$prop});
77             }
78 0         0 $str;
79             }
80              
81             sub decrypt {
82 12     12 1 23 my $key = shift;
83 12         23 my($sk) = @_;
84 12 50       35 return $key->error("Invalid secret key ID")
85             unless $key->key_id eq $sk->key_id;
86 12 50       52 my($sym_key, $alg) = __PACKAGE__->_decode($sk->key->decrypt($key->{C}))
87             or return $key->error("Session key decryption failed: " .
88             __PACKAGE__->errstr);
89 12         183 ($sym_key, $alg);
90             }
91              
92             sub _encode {
93 15     15   42574 my $class = shift;
94 15         38 my($sym_key, $sym_alg, $size) = @_;
95 15         50 my $padlen = "$size" - length($sym_key) - 2 - 2 - 2;
96 15         416 my $pad = "\0";
97 15         63 while ($pad =~ tr/\0//) {
98 30         2417 $pad = Crypt::OpenPGP::Util::get_random_bytes($padlen);
99             }
100 15         2782 bin2mp(pack 'na*na*n', 2, $pad, $sym_alg, $sym_key,
101             unpack('%16C*', $sym_key));
102             }
103              
104             sub _decode {
105 12     12   31 my $class = shift;
106 12         30 my($n) = @_;
107 12         79 my $ser = mp2bin($n);
108 12 50       103 return $class->error("Encoded data must start with 2")
109             unless unpack('C', $ser) == 2;
110 12         63 my $csum = unpack 'n', substr $ser, -2, 2, '';
111 12         71 my($pad, $sym_key) = split /\0/, $ser, 2;
112 12         42 my $sym_alg = ord substr $sym_key, 0, 1, '';
113 12 50       78 return $class->error("Encoded data has bad checksum")
114             unless unpack('%16C*', $sym_key) == $csum;
115 12         68 ($sym_key, $sym_alg);
116             }
117              
118             1;
119             __END__
120              
121             =head1 NAME
122              
123             Crypt::OpenPGP::SessionKey - Encrypted Session Key
124              
125             =head1 SYNOPSIS
126              
127             use Crypt::OpenPGP::SessionKey;
128              
129             my $public_key = Crypt::OpenPGP::Key::Public->new( 'RSA' );
130             my $key_data = 'f' x 64; ## Not a very good key :)
131              
132             my $skey = Crypt::OpenPGP::SessionKey->new(
133             Key => $public_key,
134             SymKey => $key_data,
135             );
136             my $serialized = $skey->save;
137              
138             my $secret_key = Crypt::OpenPGP::Key::Secret->new( 'RSA' );
139             ( $key_data, my( $alg ) ) = $skey->decrypt( $secret_key );
140              
141             =head1 DESCRIPTION
142              
143             I<Crypt::OpenPGP::SessionKey> implements encrypted session key packets;
144             these packets store public-key-encrypted key data that, when decrypted
145             using the corresponding secret key, can be used to decrypt a block of
146             ciphertext--that is, a I<Crypt::OpenPGP::Ciphertext> object.
147              
148             =head1 USAGE
149              
150             =head2 Crypt::OpenPGP::SessionKey->new( %arg )
151              
152             Creates a new encrypted session key packet object and returns that
153             object. If there are no arguments in I<%arg>, the object is created
154             empty; this is used, for example in I<parse> (below), to create an
155             empty packet which is then filled from the data in the buffer.
156              
157             If you wish to initialize a non-empty object, I<%arg> can contain:
158              
159             =over 4
160              
161             =item * Key
162              
163             A public key object; in other words, an object of a subclass of
164             I<Crypt::OpenPGP::Key::Private>. The public key is used to encrypt the
165             encoded session key such that it can only be decrypted by the secret
166             portion of the key.
167              
168             This argument is required (for a non-empty object).
169              
170             =item * SymKey
171              
172             The symmetric cipher key: a string of octets that make up the key data
173             of the symmetric cipher key. This should be at least long enough for
174             the key length of your chosen cipher (see I<Cipher>, below), or, if
175             you have not specified a cipher, at least 64 bytes (to allow for long
176             cipher key sizes).
177              
178             This argument is required (for a non-empty object).
179              
180             =item * Cipher
181              
182             The name (or ID) of a supported PGP cipher. See I<Crypt::OpenPGP::Cipher>
183             for a list of valid cipher names.
184              
185             This argument is optional; by default I<Crypt::OpenPGP::Cipher> will
186             use C<DES3>.
187              
188             =back
189              
190             =head2 $skey->save
191              
192             Serializes the session key packet and returns the string of octets.
193              
194             =head2 Crypt::OpenPGP::SessionKey->parse($buffer)
195              
196             Given I<$buffer>, a I<Crypt::OpenPGP::Buffer> object holding (or
197             with offset pointing to) an encrypted session key packet, returns
198             a new I<Crypt::OpenPGP::Ciphertext> object, initialized with the
199             data in the buffer.
200              
201             =head2 $skey->decrypt($secret_key)
202              
203             Given a secret key object I<$secret_key> (an object of a subclass of
204             I<Crypt::OpenPGP::Key::Public>), decrypts and decodes the encrypted
205             session key data. The key data includes the symmetric key itself,
206             along with a one-octet ID of the symmetric cipher used to encrypt
207             the message.
208              
209             Returns a list containing two items: the symmetric key and the cipher
210             algorithm ID. These are suitable for passing off to the I<decrypt>
211             method of a I<Crypt::OpenPGP::Ciphertext> object to decrypt a block
212             of encrypted data.
213              
214             =head2 $skey->key_id
215              
216             Returns the key ID of the public key used to encrypt the session key;
217             this is necessary for finding the appropriate secret key to decrypt
218             the key.
219              
220             =head1 AUTHOR & COPYRIGHTS
221              
222             Please see the Crypt::OpenPGP manpage for author, copyright, and
223             license information.
224              
225             =cut