File Coverage

blib/lib/Crypt/OpenPGP/PacketFactory.pm
Criterion Covered Total %
statement 366 366 100.0
branch 46 66 69.7
condition 13 21 61.9
subroutine 122 122 100.0
pod 2 2 100.0
total 549 577 95.1


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::PacketFactory;
2 23     23   198393 use strict;
  23         51  
  23         689  
3 23     23   141 use warnings;
  23         46  
  23         1132  
4              
5             our $VERSION = '1.19'; # VERSION
6              
7 22     22   179 use Crypt::OpenPGP::Constants qw( :packet );
  22         102  
  22         567  
8 21     21   6140 use Crypt::OpenPGP::ErrorHandler;
  21         60  
  21         747  
9 21     21   171 use base qw( Crypt::OpenPGP::ErrorHandler );
  21         66  
  21         28364  
10              
11             our %PACKET_TYPES = (
12             PGP_PKT_PUBKEY_ENC() => { class => 'Crypt::OpenPGP::SessionKey' },
13             PGP_PKT_SIGNATURE() => { class => 'Crypt::OpenPGP::Signature' },
14             PGP_PKT_SYMKEY_ENC() => { class => 'Crypt::OpenPGP::SKSessionKey' },
15             PGP_PKT_ONEPASS_SIG() => { class => 'Crypt::OpenPGP::OnePassSig' },
16             PGP_PKT_SECRET_KEY() => { class => 'Crypt::OpenPGP::Certificate',
17             args => [ 1, 0 ] },
18             PGP_PKT_PUBLIC_KEY() => { class => 'Crypt::OpenPGP::Certificate',
19             args => [ 0, 0 ] },
20             PGP_PKT_SECRET_SUBKEY() => { class => 'Crypt::OpenPGP::Certificate',
21             args => [ 1, 1 ] },
22             PGP_PKT_USER_ID() => { class => 'Crypt::OpenPGP::UserID' },
23             PGP_PKT_USER_ATTRIBUTE() => { class => 'Crypt::OpenPGP::UserAttribute' },
24             PGP_PKT_PUBLIC_SUBKEY() => { class => 'Crypt::OpenPGP::Certificate',
25             args => [ 0, 1 ] },
26             PGP_PKT_COMPRESSED() => { class => 'Crypt::OpenPGP::Compressed' },
27             PGP_PKT_ENCRYPTED() => { class => 'Crypt::OpenPGP::Ciphertext' },
28             PGP_PKT_MARKER() => { class => 'Crypt::OpenPGP::Marker' },
29             PGP_PKT_PLAINTEXT() => { class => 'Crypt::OpenPGP::Plaintext' },
30             PGP_PKT_RING_TRUST() => { class => 'Crypt::OpenPGP::Trust' },
31             PGP_PKT_ENCRYPTED_MDC() => { class => 'Crypt::OpenPGP::Ciphertext',
32             args => [ 1 ] },
33             PGP_PKT_MDC() => { class => 'Crypt::OpenPGP::MDC' },
34             );
35              
36             our %PACKET_TYPES_BY_CLASS = map { $PACKET_TYPES{$_}{class} => $_ } keys %PACKET_TYPES;
37              
38             sub parse {
39 460     460 1 9941 my $class = shift;
40 460         1230 my($buf, $find, $parse) = @_;
41 460 100 66     2533 return unless $buf && $buf->offset < $buf->length;
42 366         3442 my(%find, %parse);
43 366 100       1065 if ($find) {
44 98 50       641 %find = ref($find) eq 'ARRAY' ? (map { $_ => 1 } @$find) :
  483         1645  
45             ($find => 1);
46             }
47             else {
48 276         2190 %find = map { $_ => 1 } keys %PACKET_TYPES;
  4564         21919  
49             }
50 366 100       1856 if ($parse) {
51 94 50       408 %parse = ref($parse) eq 'ARRAY' ? (map { $_ => 1 } @$parse) :
  310         2444  
52             ($parse => 1);
53             }
54             else {
55 280         2006 %parse = %find;
56             }
57              
58 366         1164 my($type, $len, $partial, $hdrlen, $b);
59             do {
60 856         3651 ($type, $len, $partial, $hdrlen) = $class->_parse_header($buf);
61 856 100       5798 $b = $buf->extract($len ? $len : $buf->length - $buf->offset);
62 856 100       47877 return unless $type;
63 366         801 } while !$find{$type}; ## Skip
64              
65 354         1885 while ($partial) {
66 7         22 my $off = $buf->offset;
67 7         147 (my($nlen), $partial) = $class->_parse_new_len_header($buf);
68 6         60 $len += $nlen + ($buf->offset - $off);
69 6         19 $b->append( $buf->get_bytes($nlen) );
70             }
71              
72 353         852 my $obj;
73 352 100 66     2390 if ($parse{$type} && (my $ref = $PACKET_TYPES{$type})) {
74 346         1025 my $pkt_class = $ref->{class};
75 346 100       1295 my @args = $ref->{args} ? @{ $ref->{args} } : ();
  109         482  
76 346     18   40402 eval "use $pkt_class;";
  11     17   4206  
  11     17   58  
  11     16   300  
  11     15   4520  
  11     15   60  
  11     14   289  
  11     14   3051  
  11     10   60  
  11     9   272  
  11     9   1352  
  11     9   45  
  11     1   235  
  11     1   854  
  11     1   47  
  11     1   211  
  11     1   1535  
  11     1   44  
  11     1   217  
  10     1   785  
  10     1   38  
  10     1   302  
  10     1   1288  
  10     1   37  
  10     1   223  
  10     1   3415  
  10     1   78  
  10     1   212  
  9     1   1759  
  9     1   68  
  9     1   176  
  9     1   1157  
  9     1   45  
  9     1   198  
  9     1   80  
  9     1   48  
  9     1   213  
  1     1   4  
  1     1   22  
  1     1   10  
  1     1   4  
  1     1   21  
  1     1   9  
  1     1   4  
  1     1   45  
  1     1   10  
  1     1   3  
  1     1   20  
  1     1   10  
  1     1   3  
  1     1   20  
  1     1   10  
  1     1   3  
  1     1   25  
  1     1   6  
  1     1   1  
  1     1   15  
  1     1   9  
  1     1   2  
  1     1   36  
  1     1   13  
  1     1   3  
  1     1   20  
  1     1   9  
  1     1   3  
  1     1   21  
  1     1   10  
  1     1   4  
  1     1   24  
  1     1   9  
  1     1   3  
  1     1   20  
  1     1   10  
  1     1   2  
  1     1   24  
  1     1   10  
  1     1   2  
  1     1   22  
  1     1   9  
  1     1   4  
  1     1   18  
  1     1   11  
  1     1   3  
  1     1   21  
  1     1   13  
  1     1   3  
  1     1   37  
  1     1   14  
  1     1   3  
  1     1   22  
  1     1   10  
  1     1   2  
  1     1   20  
  1     1   10  
  1     1   2  
  1     1   59  
  1     1   11  
  1     1   3  
  1     1   21  
  1     1   10  
  1     1   3  
  1     1   46  
  1     1   10  
  1     1   3  
  1     1   24  
  1     1   9  
  1     1   3  
  1     1   21  
  1     1   10  
  1     1   3  
  1     1   21  
  1     1   10  
  1     1   4  
  1         20  
  1         9  
  1         3  
  1         23  
  1         9  
  1         2  
  1         52  
  1         10  
  1         3  
  1         20  
  1         9  
  1         2  
  1         21  
  1         13  
  1         2  
  1         19  
  1         14  
  1         3  
  1         23  
  1         6  
  1         2  
  1         14  
  1         6  
  1         2  
  1         16  
  1         11  
  1         2  
  1         23  
  1         10  
  1         3  
  1         25  
  1         11  
  1         2  
  1         25  
  1         10  
  1         3  
  1         22  
  1         9  
  1         2  
  1         22  
  1         9  
  1         3  
  1         20  
  1         10  
  1         3  
  1         23  
  1         28  
  1         2  
  1         23  
  1         11  
  1         3  
  1         20  
  1         9  
  1         3  
  1         23  
  1         14  
  1         3  
  1         22  
  1         16  
  1         2  
  1         24  
  1         8  
  1         1  
  1         17  
  1         6  
  1         2  
  1         16  
  1         8  
  1         2  
  1         36  
  1         6  
  1         1  
  1         14  
  1         8  
  1         2  
  1         15  
  1         7  
  1         2  
  1         31  
  1         15  
  1         2  
  1         22  
  1         16  
  1         3  
  1         25  
  1         8  
  1         2  
  1         19  
  1         9  
  1         4  
  1         27  
  1         6  
  1         2  
  1         13  
  1         7  
  1         1004  
  1         23  
  1         39  
  1         2  
  1         16  
  1         6  
  1         2  
  1         42  
  1         10  
  1         2  
  1         25  
  1         7  
  1         2  
  1         14  
  1         25  
  1         4  
  1         25  
  1         8  
  1         1  
  1         39  
  1         7  
  1         16  
  1         16  
  1         10  
  1         3  
  1         27  
  1         8  
  1         3  
  1         15  
  1         6  
  1         1  
  1         14  
  1         27  
  1         3  
  1         22  
  1         13  
  1         2  
  1         22  
  1         6  
  1         2  
  1         15  
77 346 50       1799 return $class->error("Loading $pkt_class failed: $@") if $@;
78 346         2496 $obj = $pkt_class->parse($b, @args);
79             }
80             else {
81 11         55 $obj = { type => $type, length => $len,
82             __pkt_len => $len + $hdrlen, __unparsed => 1 };
83             }
84 352         5089 $obj;
85             }
86              
87             sub _parse_header {
88 854     857   1660 my $class = shift;
89 854         1721 my($buf) = @_;
90 854 100 66     3363 return unless $buf && $buf->offset < $buf->length;
91              
92 839         6466 my $off_start = $buf->offset;
93 839         3910 my $tag = $buf->get_int8;
94 839 50       16196 return $class->error("Parse error: bit 7 not set!")
95             unless $tag & 0x80;
96 839         1601 my $is_new = $tag & 0x40;
97 839         1631 my($type, $len, $partial);
98 839 100       1982 if ($is_new) {
99 130         289 $type = $tag & 0x3f;
100 130         775 ($len, $partial) = $class->_parse_new_len_header($buf);
101             }
102             else {
103 710         1427 $type = ($tag>>2)&0xf;
104 710 100       2002 my $lenbytes = (($tag&3)==3) ? 0 : (1<<($tag & 3));
105 710         1188 $len = 0;
106 710         2322 for (1..$lenbytes) {
107 992         5408 $len <<= 8;
108 992         2233 $len += $buf->get_int8;
109             }
110             }
111 839         13657 ($type, $len, $partial, $buf->offset - $off_start);
112             }
113              
114             sub _parse_new_len_header {
115 130     137   257 my $class = shift;
116 130         272 my($buf) = @_;
117 130 50 33     580 return unless $buf && $buf->offset < $buf->length;
118 130         1143 my $lb1 = $buf->get_int8;
119 130         2190 my($partial, $len);
120 130 50       337 if ($lb1 <= 191) {
    0          
    0          
121 130         244 $len = $lb1;
122             } elsif ($lb1 <= 223) {
123 1         3 $len = (($lb1-192) << 8) + $buf->get_int8 + 192;
124             } elsif ($lb1 < 255) {
125 1         22 $partial++;
126 1         9 $len = 1 << ($lb1 & 0x1f);
127             } else {
128 1         3 $len = $buf->get_int32;
129             }
130 130         464 ($len, $partial);
131             }
132              
133             sub save {
134 63     70 1 4826 my $class = shift;
135 63         196 my @objs = @_;
136 63         167 my $ser = '';
137 63         193 for my $obj (@objs) {
138 96         936 my $body = $obj->save;
139 96         13919 my $len = length($body);
140             my $type = $obj->can('pkt_type') ? $obj->pkt_type :
141 96 100       966 $PACKET_TYPES_BY_CLASS{ref($obj)};
142 96 100       497 my $hdrlen = $obj->can('pkt_hdrlen') ? $obj->pkt_hdrlen : undef;
143 96         412 my $buf = Crypt::OpenPGP::Buffer->new;
144 96 100 66     1405 if ($obj->{is_new} || $type > 15) {
145 4         14 my $tag = 0xc0 | ($type & 0x3f);
146 4         29 $buf->put_int8($tag);
147 4 50       105 return $class->error("Can't write partial length packets")
148             unless $len;
149 4 50       15 if ($len < 192) {
    0          
150 4         37 $buf->put_int8($len);
151             } elsif ($len < 8384) {
152 1         14 $len -= 192;
153 1         4 $buf->put_int8(int($len / 256) + 192);
154 1         25 $buf->put_int8($len % 256);
155             } else {
156 1         5 $buf->put_int8(0xff);
157 1         2 $buf->put_int32($len);
158             }
159             }
160             else {
161 93 100       360 unless ($hdrlen) {
162 80 50       345 if (!defined $len) {
    100          
    50          
163 1         2 $hdrlen = 0;
164             } elsif ($len < 256) {
165 65         147 $hdrlen = 1;
166             } elsif ($len < 65536) {
167 16         50 $hdrlen = 2;
168             } else {
169 1         3 $hdrlen = 4;
170             }
171             }
172 93 50 66     514 return $class->error("Packet overflow: overflow preset len")
173             if $hdrlen == 1 && $len > 255;
174 93 50 66     374 $hdrlen = 4 if $hdrlen == 2 && $len > 65535;
175 93         266 my $tag = 0x80 | ($type << 2);
176 93 50       332 if ($hdrlen == 0) {
    100          
    50          
177 1         6 $buf->put_int8($tag | 3);
178             } elsif ($hdrlen == 1) {
179 65         232 $buf->put_int8($tag);
180 65         537 $buf->put_int8($len);
181             } elsif ($hdrlen == 2) {
182 29         160 $buf->put_int8($tag | 1);
183 29         356 $buf->put_int16($len);
184             } else {
185 1         14 $buf->put_int8($tag | 2);
186 1         5 $buf->put_int32($len);
187             }
188             }
189 96         783 $buf->put_bytes($body);
190 96         1104 $ser .= $buf->bytes;
191             }
192 63         1125 $ser;
193             }
194              
195             1;
196             __END__