File Coverage

blib/lib/Crypt/OpenPGP/SessionKey.pm
Criterion Covered Total %
statement 84 90 93.3
branch 10 18 55.5
condition 4 9 44.4
subroutine 16 17 94.1
pod 5 7 71.4
total 119 141 84.4


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::SessionKey;
2 2     2   17 use strict;
  2         4  
  2         88  
3 2     2   12 use warnings;
  2         5  
  2         199  
4              
5             our $VERSION = '1.19'; # VERSION
6              
7 2     2   14 use Crypt::OpenPGP::Constants qw( DEFAULT_CIPHER );
  2         4  
  2         25  
8 2     2   1383 use Crypt::OpenPGP::Key::Public;
  2         7  
  2         89  
9 2     2   16 use Crypt::OpenPGP::Util qw( mp2bin bin2mp bitsize );
  2         4  
  2         206  
10 2     2   16 use Crypt::OpenPGP::Buffer;
  2         4  
  2         99  
11 2     2   12 use Crypt::OpenPGP::ErrorHandler;
  2         4  
  2         52  
12 2     2   21 use base qw( Crypt::OpenPGP::ErrorHandler );
  2         4  
  2         3949  
13              
14 36     36 1 266 sub key_id { $_[0]->{key_id} }
15              
16             sub new {
17 25     25 1 64 my $class = shift;
18 25         79 my $key = bless { }, $class;
19 25         118 $key->init(@_);
20             }
21              
22             sub init {
23 25     25 0 49 my $key = shift;
24 25         98 my %param = @_;
25 25         95 $key->{version} = 3;
26 25 100 66     152 if ((my $cert = $param{Key}) && (my $sym_key = $param{SymKey})) {
27 12   33     37 my $alg = $param{Cipher} || DEFAULT_CIPHER;
28 12 50       83 my $cipher = Crypt::OpenPGP::Cipher->new($alg) or
29             return (ref $key)->error( Crypt::OpenPGP::Cipher->errstr );
30 12         40 my $keysize = $cipher->keysize;
31 12         34 $sym_key = substr $sym_key, 0, $keysize;
32 12         51 my $pk = $cert->key->public_key;
33 12 50       74 my $enc = $key->_encode($sym_key, $alg, $pk->bytesize) or
34             return (ref $key)->error("Encoding symkey failed: " . $key->errstr);
35 12         92134 $key->{key_id} = $cert->key_id;
36 12 50       76 $key->{C} = $pk->encrypt($enc) or
37             return (ref $key)->error("Encryption failed: " . $pk->errstr);
38 12         42938 $key->{pk_alg} = $pk->alg_id;
39             }
40 25         270 $key;
41             }
42              
43             sub parse {
44 13     13 1 35 my $class = shift;
45 13         39 my($buf) = @_;
46 13         65 my $key = $class->new;
47 13         58 $key->{version} = $buf->get_int8;
48             return $class->error("Unsupported version ($key->{version})")
49 13 50 33     380 unless $key->{version} == 2 || $key->{version} == 3;
50 13         50 $key->{key_id} = $buf->get_bytes(8);
51 13         297 $key->{pk_alg} = $buf->get_int8;
52 13         354 my $pk = Crypt::OpenPGP::Key::Public->new($key->{pk_alg});
53 13         62 my @props = $pk->crypt_props;
54 13         42 for my $e (@props) {
55 26         2471 $key->{C}{$e} = $buf->get_mp_int;
56             }
57 13         3318 $key;
58             }
59              
60             sub save {
61 12     12 1 28 my $key = shift;
62 12         82 my $buf = Crypt::OpenPGP::Buffer->new;
63 12         232 $buf->put_int8($key->{version});
64 12         170 $buf->put_bytes($key->{key_id}, 8);
65 12         163 $buf->put_int8($key->{pk_alg});
66 12         95 my $c = $key->{C};
67 12         92 for my $prop (sort keys %$c) {
68 24         343 $buf->put_mp_int($c->{$prop});
69             }
70 12         295 $buf->bytes;
71             }
72              
73             sub display {
74 0     0 0 0 my $key = shift;
75             my $str = sprintf ":pubkey enc packet: version %d, algo %d, keyid %s\n",
76 0         0 $key->{version}, $key->{pk_alg}, uc unpack('H*', $key->{key_id});
77 0         0 my $c = $key->{C};
78 0         0 for my $prop (sort keys %$c) {
79 0         0 $str .= sprintf " data: [%d bits]\n", bitsize($c->{$prop});
80             }
81 0         0 $str;
82             }
83              
84             sub decrypt {
85 13     13 1 31 my $key = shift;
86 13         279 my($sk) = @_;
87 13 50       48 return $key->error("Invalid secret key ID")
88             unless $key->key_id eq $sk->key_id;
89 13 50       67 my($sym_key, $alg) = __PACKAGE__->_decode($sk->key->decrypt($key->{C}))
90             or return $key->error("Session key decryption failed: " .
91             __PACKAGE__->errstr);
92 13         212 ($sym_key, $alg);
93             }
94              
95             sub _encode {
96 12     12   53512 my $class = shift;
97 12         38 my($sym_key, $sym_alg, $size) = @_;
98 12         36 my $padlen = "$size" - length($sym_key) - 2 - 2 - 2;
99 12         423 my $pad = "\0";
100 12         50 while ($pad =~ tr/\0//) {
101 26         3488 $pad = Crypt::OpenPGP::Util::get_random_bytes($padlen);
102             }
103 12         2623 bin2mp(pack 'na*na*n', 2, $pad, $sym_alg, $sym_key,
104             unpack('%16C*', $sym_key));
105             }
106              
107             sub _decode {
108 13     13   42 my $class = shift;
109 13         53 my($n) = @_;
110 13         111 my $ser = mp2bin($n);
111 13 50       333 return $class->error("Encoded data must start with 2")
112             unless unpack('C', $ser) == 2;
113 13         69 my $csum = unpack 'n', substr $ser, -2, 2, '';
114 13         123 my($pad, $sym_key) = split /\0/, $ser, 2;
115 13         85 my $sym_alg = ord substr $sym_key, 0, 1, '';
116 13 50       86 return $class->error("Encoded data has bad checksum")
117             unless unpack('%16C*', $sym_key) == $csum;
118 13         100 ($sym_key, $sym_alg);
119             }
120              
121             1;
122             __END__