File Coverage

blib/lib/Crypt/OpenPGP/PacketFactory.pm
Criterion Covered Total %
statement 279 279 100.0
branch 45 66 68.1
condition 13 21 61.9
subroutine 93 93 100.0
pod 2 2 100.0
total 432 461 93.7


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::PacketFactory;
2 14     14   546 use strict;
  14         28  
  14         415  
3              
4 14     14   78 use Crypt::OpenPGP::Constants qw( :packet );
  14         21  
  14         164  
5 14     14   3610 use Crypt::OpenPGP::ErrorHandler;
  14         30  
  14         401  
6 14     14   83 use base qw( Crypt::OpenPGP::ErrorHandler );
  14         30  
  14         1015  
7              
8 14     14   83 use vars qw( %PACKET_TYPES %PACKET_TYPES_BY_CLASS );
  14         21  
  14         11547  
9             %PACKET_TYPES = (
10             PGP_PKT_PUBKEY_ENC() => { class => 'Crypt::OpenPGP::SessionKey' },
11             PGP_PKT_SIGNATURE() => { class => 'Crypt::OpenPGP::Signature' },
12             PGP_PKT_SYMKEY_ENC() => { class => 'Crypt::OpenPGP::SKSessionKey' },
13             PGP_PKT_ONEPASS_SIG() => { class => 'Crypt::OpenPGP::OnePassSig' },
14             PGP_PKT_SECRET_KEY() => { class => 'Crypt::OpenPGP::Certificate',
15             args => [ 1, 0 ] },
16             PGP_PKT_PUBLIC_KEY() => { class => 'Crypt::OpenPGP::Certificate',
17             args => [ 0, 0 ] },
18             PGP_PKT_SECRET_SUBKEY() => { class => 'Crypt::OpenPGP::Certificate',
19             args => [ 1, 1 ] },
20             PGP_PKT_USER_ID() => { class => 'Crypt::OpenPGP::UserID' },
21             PGP_PKT_PUBLIC_SUBKEY() => { class => 'Crypt::OpenPGP::Certificate',
22             args => [ 0, 1 ] },
23             PGP_PKT_COMPRESSED() => { class => 'Crypt::OpenPGP::Compressed' },
24             PGP_PKT_ENCRYPTED() => { class => 'Crypt::OpenPGP::Ciphertext' },
25             PGP_PKT_MARKER() => { class => 'Crypt::OpenPGP::Marker' },
26             PGP_PKT_PLAINTEXT() => { class => 'Crypt::OpenPGP::Plaintext' },
27             PGP_PKT_RING_TRUST() => { class => 'Crypt::OpenPGP::Trust' },
28             PGP_PKT_ENCRYPTED_MDC() => { class => 'Crypt::OpenPGP::Ciphertext',
29             args => [ 1 ] },
30             PGP_PKT_MDC() => { class => 'Crypt::OpenPGP::MDC' },
31             );
32              
33             %PACKET_TYPES_BY_CLASS = map { $PACKET_TYPES{$_}{class} => $_ } keys %PACKET_TYPES;
34              
35             sub parse {
36 317     317 1 1495 my $class = shift;
37 317         467 my($buf, $find, $parse) = @_;
38 317 100 66     1506 return unless $buf && $buf->offset < $buf->length;
39 234         1910 my(%find, %parse);
40 234 100       519 if ($find) {
41 57 50       303 %find = ref($find) eq 'ARRAY' ? (map { $_ => 1 } @$find) :
  258         547  
42             ($find => 1);
43             }
44             else {
45 182         1062 %find = map { $_ => 1 } keys %PACKET_TYPES;
  2837         4110  
46             }
47 234 100       845 if ($parse) {
48 53 50       193 %parse = ref($parse) eq 'ARRAY' ? (map { $_ => 1 } @$parse) :
  161         338  
49             ($parse => 1);
50             }
51             else {
52 186         1083 %parse = %find;
53             }
54              
55 234         419 my($type, $len, $partial, $hdrlen, $b);
56             do {
57 397         1010 ($type, $len, $partial, $hdrlen) = $class->_parse_header($buf);
58 397 100       2357 $b = $buf->extract($len ? $len : $buf->length - $buf->offset);
59 397 100       10640 return unless $type;
60 234         343 } while !$find{$type}; ## Skip
61              
62 226         654 while ($partial) {
63 5         6 my $off = $buf->offset;
64 5         88 (my($nlen), $partial) = $class->_parse_new_len_header($buf);
65 5         33 $len += $nlen + ($buf->offset - $off);
66 5         6 $b->append( $buf->get_bytes($nlen) );
67             }
68              
69 226         353 my $obj;
70 226 100 66     1277 if ($parse{$type} && (my $ref = $PACKET_TYPES{$type})) {
71 220         478 my $pkt_class = $ref->{class};
72 220 100       731 my @args = $ref->{args} ? @{ $ref->{args} } : ();
  65         188  
73 220     11   20794 eval "use $pkt_class;";
  7     11   2228  
  7     11   31  
  7     11   132  
  7     11   1298  
  7     11   40  
  7     10   123  
  7     9   1296  
  7     4   33  
  7     3   121  
  7     3   546  
  7     3   38  
  7     1   120  
  7     1   47  
  7     1   37  
  7     1   150  
  7     1   39  
  7     1   34  
  7     1   108  
  6     1   33  
  6     1   25  
  6     1   93  
  6     1   28  
  6     1   26  
  6     1   75  
  1     1   1  
  1     1   16  
  1     1   7  
  1     1   2  
  1     1   24  
  1     1   8  
  1     1   3  
  1     1   24  
  1     1   8  
  1     1   3  
  1     1   21  
  1     1   9  
  1     1   2  
  1     1   23  
  1     1   9  
  1     1   2  
  1     1   19  
  1     1   11  
  1     1   2  
  1     1   23  
  1     1   5  
  1     1   15  
  1     1   17  
  1     1   5  
  1     1   2  
  1     1   14  
  1     1   4  
  1     1   2  
  1     1   13  
  1     1   8  
  1     1   1  
  1     1   25  
  1     1   8  
  1     1   4  
  1     1   22  
  1     1   6  
  1     1   2  
  1     1   17  
  1     1   5  
  1     1   1  
  1     1   12  
  1     1   9  
  1     1   2  
  1     1   21  
  1     1   10  
  1     1   2  
  1     1   19  
  1     1   12  
  1     1   1  
  1     1   26  
  1     1   9  
  1     1   3  
  1     1   21  
  1     1   10  
  1     1   2  
  1     1   26  
  1     1   7  
  1     1   3  
  1     1   22  
  1         9  
  1         2  
  1         28  
  1         8  
  1         4  
  1         24  
  1         8  
  1         2  
  1         27  
  1         8  
  1         1  
  1         23  
  1         12  
  1         2  
  1         25  
  1         7  
  1         2  
  1         16  
  1         7  
  1         1  
  1         15  
  1         5  
  1         2  
  1         11  
  1         4  
  1         1  
  1         13  
  1         5  
  1         2  
  1         14  
  1         5  
  1         1  
  1         12  
  1         4  
  1         1  
  1         18  
  1         8  
  1         2  
  1         31  
  1         10  
  1         2  
  1         20  
  1         11  
  1         2  
  1         23  
  1         8  
  1         4  
  1         26  
  1         8  
  1         2  
  1         24  
  1         7  
  1         3  
  1         22  
  1         8  
  1         4  
  1         20  
  1         10  
  1         2  
  1         25  
  1         8  
  1         2  
  1         23  
  1         7  
  1         1  
  1         18  
  1         6  
  1         2  
  1         17  
  1         7  
  1         3  
  1         23  
  1         7  
  1         2  
  1         17  
  1         8  
  1         2  
  1         18  
74 220 50       878 return $class->error("Loading $pkt_class failed: $@") if $@;
75 220         1600 $obj = $pkt_class->parse($b, @args);
76             }
77             else {
78 11         45 $obj = { type => $type, length => $len,
79             __pkt_len => $len + $hdrlen, __unparsed => 1 };
80             }
81 226         2279 $obj;
82             }
83              
84             sub _parse_header {
85 396     397   491 my $class = shift;
86 396         445 my($buf) = @_;
87 396 100 66     1474 return unless $buf && $buf->offset < $buf->length;
88              
89 388         2453 my $off_start = $buf->offset;
90 388         1394 my $tag = $buf->get_int8;
91 388 50       5398 return $class->error("Parse error: bit 7 not set!")
92             unless $tag & 0x80;
93 387         510 my $is_new = $tag & 0x40;
94 387         518 my($type, $len, $partial);
95 387 100       702 if ($is_new) {
96 136         188 $type = $tag & 0x3f;
97 136         337 ($len, $partial) = $class->_parse_new_len_header($buf);
98             }
99             else {
100 254         519 $type = ($tag>>2)&0xf;
101 254 50       780 my $lenbytes = (($tag&3)==3) ? 0 : (1<<($tag & 3));
102 254         269 $len = 0;
103 254         635 for (1..$lenbytes) {
104 352         1255 $len <<= 8;
105 352         807 $len += $buf->get_int8;
106             }
107             }
108 385         2922 ($type, $len, $partial, $buf->offset - $off_start);
109             }
110              
111             sub _parse_new_len_header {
112 134     138   178 my $class = shift;
113 134         162 my($buf) = @_;
114 134 50 33     537 return unless $buf && $buf->offset < $buf->length;
115 134         863 my $lb1 = $buf->get_int8;
116 134         1382 my($partial, $len);
117 134 50       257 if ($lb1 <= 191) {
    0          
    0          
118 134         192 $len = $lb1;
119             } elsif ($lb1 <= 223) {
120 1         2 $len = (($lb1-192) << 8) + $buf->get_int8 + 192;
121             } elsif ($lb1 < 255) {
122 1         12 $partial++;
123 1         5 $len = 1 << ($lb1 & 0x1f);
124             } else {
125 1         2 $len = $buf->get_int32;
126             }
127 134         303 ($len, $partial);
128             }
129              
130             sub save {
131 59     63 1 1106 my $class = shift;
132 59         138 my @objs = @_;
133 59         129 my $ser = '';
134 59         140 for my $obj (@objs) {
135 95         722 my $body = $obj->save;
136 95         1040 my $len = length($body);
137             my $type = $obj->can('pkt_type') ? $obj->pkt_type :
138 95 100       745 $PACKET_TYPES_BY_CLASS{ref($obj)};
139 95 100       460 my $hdrlen = $obj->can('pkt_hdrlen') ? $obj->pkt_hdrlen : undef;
140 95         311 my $buf = Crypt::OpenPGP::Buffer->new;
141 95 100 66     1112 if ($obj->{is_new} || $type > 15) {
142 3         7 my $tag = 0xc0 | ($type & 0x3f);
143 3         28 $buf->put_int8($tag);
144 3 50       22 return $class->error("Can't write partial length packets")
145             unless $len;
146 3 50       6 if ($len < 192) {
    0          
147 3         26 $buf->put_int8($len);
148             } elsif ($len < 8384) {
149 1         9 $len -= 192;
150 1         2 $buf->put_int8(int($len / 256) + 192);
151 1         20 $buf->put_int8($len % 256);
152             } else {
153 1         10 $buf->put_int8(0xff);
154 1         2 $buf->put_int32($len);
155             }
156             }
157             else {
158 93 100       255 unless ($hdrlen) {
159 82 50       321 if (!defined $len) {
    100          
    50          
160 1         2 $hdrlen = 0;
161             } elsif ($len < 256) {
162 67         105 $hdrlen = 1;
163             } elsif ($len < 65536) {
164 16         34 $hdrlen = 2;
165             } else {
166 1         2 $hdrlen = 4;
167             }
168             }
169 93 50 66     435 return $class->error("Packet overflow: overflow preset len")
170             if $hdrlen == 1 && $len > 255;
171 93 50 66     365 $hdrlen = 4 if $hdrlen == 2 && $len > 65535;
172 93         173 my $tag = 0x80 | ($type << 2);
173 93 50       333 if ($hdrlen == 0) {
    100          
    50          
174 1         8 $buf->put_int8($tag | 3);
175             } elsif ($hdrlen == 1) {
176 67         193 $buf->put_int8($tag);
177 67         481 $buf->put_int8($len);
178             } elsif ($hdrlen == 2) {
179 27         137 $buf->put_int8($tag | 1);
180 27         289 $buf->put_int16($len);
181             } else {
182 1         20 $buf->put_int8($tag | 2);
183 1         11 $buf->put_int32($len);
184             }
185             }
186 95         517 $buf->put_bytes($body);
187 95         675 $ser .= $buf->bytes;
188             }
189 59         745 $ser;
190             }
191              
192             1;
193             __END__
194              
195             =head1 NAME
196              
197             Crypt::OpenPGP::PacketFactory - Parse and save PGP packet streams
198              
199             =head1 SYNOPSIS
200              
201             use Crypt::OpenPGP::PacketFactory;
202              
203             my $buf = Crypt::OpenPGP::Buffer->new;
204             while (my $packet = Crypt::OpenPGP::PacketFactory->parse($buf)) {
205             ## Do something with $packet
206             }
207              
208             my @packets;
209             my $serialized = Crypt::OpenPGP::PacketFactory->save(@packets);
210              
211             =head1 DESCRIPTION
212              
213             I<Crypt::OpenPGP::PacketFactory> parses PGP buffers (objects of type
214             I<Crypt::OpenPGP::Buffer>) and generates packet objects of various
215             packet classes (for example, I<Crypt::OpenPGP::Certificate> objects,
216             I<Crypt::OpenPGP::Signature> objects, etc.). It also takes lists of
217             packets, serializes each of them, and adds type/length headers to
218             them, forming a stream of packets suitable for armouring, writing
219             to disk, sending through email, etc.
220              
221             =head1 USAGE
222              
223             =head2 Crypt::OpenPGP::PacketFactory->parse($buffer [, $find ])
224              
225             Given a buffer object I<$buffer> of type I<Crypt::OpenPGP::Buffer>,
226             iterates through the packets serialized in the buffer, parsing
227             each one, and returning each packet one by one. In other words, given
228             a buffer, it acts as a standard iterator.
229              
230             By default I<parse> parses and returns all packets in the buffer, of
231             any packet type. If you are only looking for packets of a specific
232             type, though, it makes no sense to return every packet; you can
233             control which packets I<parse> parses and returns with I<$find>, which
234             should be a reference to a list of packet types to find in the buffer.
235             Only packets of those types will be parsed and returned to you. You
236             can get the packet type constants from I<Crypt::OpenPGP::Constants>
237             by importing the C<:packet> tag.
238              
239             Returns the next packet in the buffer until the end of the buffer
240             is reached (or until there are no more of the packets which you wish
241             to find), at which point returns a false value.
242              
243             =head2 Crypt::OpenPGP::PacketFactory->save(@packets)
244              
245             Given a list of packets I<@packets>, serializes each packet, then
246             adds a type/length header on to each one, resulting in a string of
247             octets representing the serialized packets, suitable for passing in
248             to I<parse>, or for writing to disk, or anywhere else.
249              
250             =head1 AUTHOR & COPYRIGHTS
251              
252             Please see the Crypt::OpenPGP manpage for author, copyright, and
253             license information.
254              
255             =cut