File Coverage

blib/lib/Crypt/OpenPGP/KeyRing.pm
Criterion Covered Total %
statement 101 115 87.8
branch 30 42 71.4
condition 21 28 75.0
subroutine 18 20 90.0
pod 4 11 36.3
total 174 216 80.5


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::KeyRing;
2 8     8   850 use strict;
  8         11  
  8         337  
3              
4 8         53 use Crypt::OpenPGP::Constants qw( PGP_PKT_USER_ID
5             PGP_PKT_PUBLIC_KEY
6             PGP_PKT_SECRET_KEY
7             PGP_PKT_PUBLIC_SUBKEY
8 8     8   552 PGP_PKT_SECRET_SUBKEY );
  8         11  
9 8     8   3721 use Crypt::OpenPGP::Buffer;
  8         23  
  8         324  
10 8     8   5086 use Crypt::OpenPGP::KeyBlock;
  8         20  
  8         272  
11 8     8   53 use Crypt::OpenPGP::PacketFactory;
  8         15  
  8         175  
12 8     8   37 use Crypt::OpenPGP::ErrorHandler;
  8         13  
  8         231  
13 8     8   40 use base qw( Crypt::OpenPGP::ErrorHandler );
  8         12  
  8         11105  
14              
15             sub new {
16 10     10 1 10719 my $class = shift;
17 10         36 my $ring = bless { }, $class;
18 10         52 $ring->init(@_);
19             }
20              
21             sub init {
22 10     10 0 25 my $ring = shift;
23 10         42 my %param = @_;
24 10   50     119 $ring->{_data} = $param{Data} || '';
25 10 50 33     96 if (!$ring->{_data} && (my $file = $param{Filename})) {
26 10         38 local *FH;
27 10 100       608 open FH, $file or
28             return (ref $ring)->error("Can't open keyring $file: $!");
29 9         31 binmode FH;
30 9         19 { local $/; $ring->{_data} = <FH> }
  9         41  
  9         183  
31 9         151 close FH;
32             }
33 9 50       54 if ($ring->{_data} =~ /^-----BEGIN/) {
34 0         0 require Crypt::OpenPGP::Armour;
35 0 0       0 my $rec = Crypt::OpenPGP::Armour->unarmour($ring->{_data}) or
36             return (ref $ring)->error("Unarmour failed: " .
37             Crypt::OpenPGP::Armour->errstr);
38 0         0 $ring->{_data} = $rec->{Data};
39             }
40 9         48 $ring;
41             }
42              
43             sub save {
44 0     0 0 0 my $ring = shift;
45 0         0 my @blocks = $ring->blocks;
46 0         0 my $res = '';
47 0         0 for my $block (@blocks) {
48 0         0 $res .= $block->save;
49             }
50 0         0 $res;
51             }
52              
53             sub read {
54 1     1 0 1114 my $ring = shift;
55 1 50       6 return $ring->error("No data to read") unless $ring->{_data};
56 1         31 my $buf = Crypt::OpenPGP::Buffer->new;
57 1         30 $buf->append($ring->{_data});
58 1         20 $ring->restore($buf);
59 1         22 1;
60             }
61              
62             sub restore {
63 1     1 0 4 my $ring = shift;
64 1         3 my($buf) = @_;
65 1         5 $ring->{blocks} = [];
66 1         4 my($kb);
67 1         17 while (my $packet = Crypt::OpenPGP::PacketFactory->parse($buf)) {
68 5 100 100     29 if (ref($packet) eq "Crypt::OpenPGP::Certificate" &&
69             !$packet->is_subkey) {
70 1         15 $kb = Crypt::OpenPGP::KeyBlock->new;
71 1         5 $ring->add($kb);
72             }
73 5 50       35 $kb->add($packet) if $kb;
74             }
75             }
76              
77             sub add {
78 1     1 0 3 my $ring = shift;
79 1         3 my($entry) = @_;
80 1         1 push @{ $ring->{blocks} }, $entry;
  1         5  
81             }
82              
83             sub find_keyblock_by_keyid {
84 41     41 1 958 my $ring = shift;
85 41         88 my($key_id) = @_;
86 41         126 my $ref = $ring->{by_keyid}{$key_id};
87 41 100       138 unless ($ref) {
88 16         28 my $len = length($key_id);
89             my @kbs = $ring->find_keyblock(
90 30     30   149 sub { substr($_[0]->key_id, -$len, $len) eq $key_id },
91 16         195 [ PGP_PKT_PUBLIC_KEY, PGP_PKT_SECRET_KEY,
92             PGP_PKT_PUBLIC_SUBKEY, PGP_PKT_SECRET_SUBKEY ], 1 );
93 16 100       211 return unless @kbs;
94 14         76 $ref = $ring->{by_keyid}{ $key_id } = \@kbs;
95             }
96 39 100       270 return wantarray ? @$ref : $ref->[0];
97             }
98              
99             sub find_keyblock_by_uid {
100 3     3 1 7 my $ring = shift;
101 3         8 my($uid) = @_;
102 4     4   21 $ring->find_keyblock(sub { $_[0]->id =~ /$uid/i },
103 3         31 [ PGP_PKT_USER_ID ], 1 );
104             }
105              
106             sub find_keyblock_by_index {
107 0     0 1 0 my $ring = shift;
108 0         0 my($index) = @_;
109             ## XXX should not have to read entire keyring
110 0         0 $ring->read;
111 0         0 ($ring->blocks)[$index];
112             }
113              
114             sub find_keyblock {
115 19     19 0 33 my $ring = shift;
116 19         40 my($test, $pkttypes, $multiple) = @_;
117 19   50     73 $pkttypes ||= [];
118 19 50       73 return $ring->error("No data to read") unless $ring->{_data};
119 19         147 my $buf = Crypt::OpenPGP::Buffer->new_with_init($ring->{_data});
120 19         620 my($last_kb_start_offset, $last_kb_start_cert, @kbs);
121 19         68 while (my $pkt = Crypt::OpenPGP::PacketFactory->parse($buf,
122             [ PGP_PKT_SECRET_KEY, PGP_PKT_PUBLIC_KEY,
123             @$pkttypes ], $pkttypes)) {
124 38 100 66     493 if (($pkt->{__unparsed} && ($pkt->{type} == PGP_PKT_SECRET_KEY ||
      66        
      100        
      66        
125             $pkt->{type} == PGP_PKT_PUBLIC_KEY)) ||
126             (ref($pkt) eq 'Crypt::OpenPGP::Certificate' && !$pkt->is_subkey)) {
127 25         87 $last_kb_start_offset = $buf->offset;
128 25         88 $last_kb_start_cert = $pkt;
129             }
130 38 100 100     233 next unless !$pkt->{__unparsed} && $test->($pkt);
131 17         159 my $kb = Crypt::OpenPGP::KeyBlock->new;
132              
133             ## Rewind buffer; if start-cert is parsed, rewind to offset
134             ## after start-cert--otherwise rewind before start-cert
135 17 100       61 if ($last_kb_start_cert->{__unparsed}) {
136             $buf->set_offset($last_kb_start_offset -
137 3         17 $last_kb_start_cert->{__pkt_len});
138 3         18 my $cert = Crypt::OpenPGP::PacketFactory->parse($buf);
139 3         24 $kb->add($cert);
140             } else {
141 14         86 $buf->set_offset($last_kb_start_offset);
142 14         90 $kb->add($last_kb_start_cert);
143             }
144             {
145 17         27 my $off = $buf->offset;
  83         272  
146 83         488 my $packet = Crypt::OpenPGP::PacketFactory->parse($buf);
147 83 100       391 last unless $packet;
148 71 100 100     393 $buf->set_offset($off),
149             last if ref($packet) eq "Crypt::OpenPGP::Certificate" &&
150             !$packet->is_subkey;
151 66 50       378 $kb->add($packet) if $kb;
152 66         113 redo;
153             }
154 17 50       161 unless ($multiple) {
155 0 0       0 return wantarray ? ($kb, $pkt) : $kb;
156             } else {
157 17 100       95 return $kb unless wantarray;
158 15         247 push @kbs, $kb;
159             }
160             }
161 17         261 @kbs;
162             }
163              
164 1 50   1 0 820 sub blocks { $_[0]->{blocks} ? @{ $_[0]->{blocks} } : () }
  1         7  
165              
166             1;
167             __END__
168              
169             =head1 NAME
170              
171             Crypt::OpenPGP::KeyRing - Key ring object
172              
173             =head1 SYNOPSIS
174              
175             use Crypt::OpenPGP::KeyRing;
176              
177             my $ring = Crypt::OpenPGP::KeyRing->new( Filename => 'foo.ring' );
178              
179             my $key_id = '...';
180             my $kb = $ring->find_keyblock_by_keyid($key_id);
181              
182             =head1 DESCRIPTION
183              
184             I<Crypt::OpenPGP::KeyRing> provides keyring management and key lookup
185             for I<Crypt::OpenPGP>. A I<KeyRing>, in this case, does not necessarily
186             have to be a keyring file; a I<KeyRing> object is just a collection of
187             key blocks, where each key block contains exactly one master key,
188             zero or more subkeys, some user ID packets, some signatures, etc.
189              
190             =head1 USAGE
191              
192             =head2 Crypt::OpenPGP::KeyRing->new( %arg )
193              
194             Constructs a new I<Crypt::OpenPGP::KeyRing> object and returns that
195             object. This has the effect os hooking the object to a particular
196             keyring, so that all subsequent methods called on the I<KeyRing>
197             object will use the data specified in the arguments to I<new>.
198              
199             I<%arg> can contain:
200              
201             =over 4
202              
203             =item * Data
204              
205             A block of data specifying the serialized keyring, presumably as read
206             in from a file on disk. This data can be either in binary form or in
207             ASCII-armoured form; if the latter it will be unarmoured automatically.
208              
209             This argument is optional.
210              
211             =item * Filename
212              
213             The path to a keyring file, or at least, a file containing a key (and
214             perhaps other associated keyblock data). The data in this file can be
215             either in binary form or in ASCII-armoured form; if the latter it will be
216             unarmoured automatically.
217              
218             This argument is optional.
219              
220             =back
221              
222             =head2 $ring->find_keyblock_by_keyid($key_id)
223              
224             Looks up the key ID I<$key_id> in the keyring I<$ring>. I<$key_id>
225             should be either a 4-octet or 8-octet string--it should I<not> be a
226             string of hexadecimal digits. If that is what you have, use I<pack> to
227             convert it to an octet string:
228              
229             pack 'H*', $hex_key_id
230              
231             If a keyblock is found where the key ID of either the master key or
232             subkey matches I<$key_id>, that keyblock will be returned. The
233             definition of "match" depends on the length of I<$key_id>: if it is a
234             16-digit hex number, only exact matches will be returned; if it is an
235             8-digit hex number, any keyblocks containing keys whose last 8 hex
236             digits match I<$key_id> will be returned.
237              
238             In scalar context, only the first keyblock found in the keyring is
239             returned; in list context, all matching keyblocks are returned. In
240             practice, duplicated key IDs are rare, particularly so if you specify
241             the full 16 hex digits in I<$key_id>.
242              
243             Returns false on failure (C<undef> in scalar context, an empty list in
244             list context).
245              
246             =head2 $ring->find_keyblock_by_uid($uid)
247              
248             Given a string I<$uid>, looks up all keyblocks with User ID packets
249             matching the string I<$uid>, including partial matches.
250              
251             In scalar context, returns only the first keyblock with a matching
252             user ID; in list context, returns all matching keyblocks.
253              
254             Returns false on failure.
255              
256             =head2 $ring->find_keyblock_by_index($index)
257              
258             Given an index into a list of keyblocks I<$index>, returns the keyblock
259             (a I<Crypt::OpenPGP::KeyBlock> object) at that index. Accepts negative
260             indexes, so C<-1> will give you the last keyblock in the keyring.
261              
262             =head1 AUTHOR & COPYRIGHTS
263              
264             Please see the Crypt::OpenPGP manpage for author, copyright, and
265             license information.
266              
267             =cut