File Coverage

blib/lib/Crypt/OpenPGP/KeyRing.pm
Criterion Covered Total %
statement 107 118 90.6
branch 32 42 76.1
condition 23 28 82.1
subroutine 19 21 90.4
pod 4 11 36.3
total 185 220 84.0


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::KeyRing;
2 12     12   790831 use strict;
  12         27  
  12         526  
3 12     12   89 use warnings;
  12         41  
  12         1067  
4              
5             our $VERSION = '1.19'; # VERSION
6              
7 12         90 use Crypt::OpenPGP::Constants qw( PGP_PKT_USER_ID
8             PGP_PKT_PUBLIC_KEY
9             PGP_PKT_SECRET_KEY
10             PGP_PKT_PUBLIC_SUBKEY
11 12     12   626 PGP_PKT_SECRET_SUBKEY );
  12         54  
12 12     12   6260 use Crypt::OpenPGP::Buffer;
  12         43  
  12         528  
13 12     12   8001 use Crypt::OpenPGP::KeyBlock;
  12         38  
  12         438  
14 12     12   80 use Crypt::OpenPGP::PacketFactory;
  12         24  
  12         228  
15 12     12   48 use Crypt::OpenPGP::ErrorHandler;
  12         21  
  12         243  
16 12     12   54 use base qw( Crypt::OpenPGP::ErrorHandler );
  12         22  
  12         21805  
17              
18             sub new {
19 18     18 1 11981 my $class = shift;
20 18         173 my $ring = bless { }, $class;
21 18         298 $ring->init(@_);
22             }
23              
24             sub init {
25 18     18 0 52 my $ring = shift;
26 18         113 my %param = @_;
27 18   50     320 $ring->{_data} = $param{Data} || '';
28 18 50 33     235 if (!$ring->{_data} && (my $file = $param{Filename})) {
29 18         102 local *FH;
30 18 100       1833 open FH, $file or
31             return (ref $ring)->error("Can't open keyring $file: $!");
32 17         102 binmode FH;
33 17         39 { local $/; $ring->{_data} = }
  17         103  
  17         1173  
34 17         272 close FH;
35             }
36 17 100       182 if ($ring->{_data} =~ /^-----BEGIN/) {
37 2         1132 require Crypt::OpenPGP::Armour;
38 2 50       25 my $rec = Crypt::OpenPGP::Armour->unarmour($ring->{_data}) or
39             return (ref $ring)->error("Unarmour failed: " .
40             Crypt::OpenPGP::Armour->errstr);
41 2         11 $ring->{_data} = $rec->{Data};
42             }
43 17         136 $ring;
44             }
45              
46             sub save {
47 0     0 0 0 my $ring = shift;
48 0         0 my @blocks = $ring->blocks;
49 0         0 my $res = '';
50 0         0 for my $block (@blocks) {
51 0         0 $res .= $block->save;
52             }
53 0         0 $res;
54             }
55              
56             sub read {
57 1     1 0 1196 my $ring = shift;
58 1 50       8 return $ring->error("No data to read") unless $ring->{_data};
59 1         36 my $buf = Crypt::OpenPGP::Buffer->new;
60 1         32 $buf->append($ring->{_data});
61 1         14 $ring->restore($buf);
62 1         21 1;
63             }
64              
65             sub restore {
66 1     1 0 23 my $ring = shift;
67 1         6 my($buf) = @_;
68 1         7 $ring->{blocks} = [];
69 1         3 my($kb);
70 1         18 while (my $packet = Crypt::OpenPGP::PacketFactory->parse($buf)) {
71 5 100 100     31 if (ref($packet) eq "Crypt::OpenPGP::Certificate" &&
72             !$packet->is_subkey) {
73 1         16 $kb = Crypt::OpenPGP::KeyBlock->new;
74 1         5 $ring->add($kb);
75             }
76 5 50       41 $kb->add($packet) if $kb;
77             }
78             }
79              
80             sub add {
81 1     1 0 2 my $ring = shift;
82 1         3 my($entry) = @_;
83 1         3 push @{ $ring->{blocks} }, $entry;
  1         66  
84             }
85              
86             sub find_keyblock_by_keyid {
87 44     44 1 10948 my $ring = shift;
88 44         165 my($key_id) = @_;
89 44         286 my $ref = $ring->{by_keyid}{$key_id};
90 44 100       152 unless ($ref) {
91 21         61 my $len = length($key_id);
92             my @kbs = $ring->find_keyblock(
93 63     63   432 sub { substr($_[0]->key_id, -$len, $len) eq $key_id },
94 21         282 [ PGP_PKT_PUBLIC_KEY, PGP_PKT_SECRET_KEY,
95             PGP_PKT_PUBLIC_SUBKEY, PGP_PKT_SECRET_SUBKEY ], 1 );
96 21 100       423 return unless @kbs;
97 17         156 $ref = $ring->{by_keyid}{ $key_id } = \@kbs;
98             }
99 40 100       376 return wantarray ? @$ref : $ref->[0];
100             }
101              
102             sub find_keyblock_by_uid {
103 3     3 1 12 my $ring = shift;
104 3         12 my($uid) = @_;
105 6     6   21 $ring->find_keyblock(sub { $_[0]->id =~ /$uid/i },
106 3         39 [ PGP_PKT_USER_ID ], 1 );
107             }
108              
109             sub find_keyblock_by_index {
110 0     0 1 0 my $ring = shift;
111 0         0 my($index) = @_;
112             ## XXX should not have to read entire keyring
113 0         0 $ring->read;
114 0         0 ($ring->blocks)[$index];
115             }
116              
117             sub find_keyblock {
118 24     24 0 61 my $ring = shift;
119 24         84 my($test, $pkttypes, $multiple) = @_;
120 24   50     111 $pkttypes ||= [];
121 24 50       148 return $ring->error("No data to read") unless $ring->{_data};
122 24         319 my $buf = Crypt::OpenPGP::Buffer->new_with_init($ring->{_data});
123 24         1031 my($last_kb_start_offset, $last_kb_start_cert, @kbs);
124 24         107 while (my $pkt = Crypt::OpenPGP::PacketFactory->parse($buf,
125             [ PGP_PKT_SECRET_KEY, PGP_PKT_PUBLIC_KEY,
126             @$pkttypes ], $pkttypes)) {
127 73 100 66     941 if (($pkt->{__unparsed} && ($pkt->{type} == PGP_PKT_SECRET_KEY ||
      100        
      100        
      100        
128             $pkt->{type} == PGP_PKT_PUBLIC_KEY)) ||
129             (ref($pkt) eq 'Crypt::OpenPGP::Certificate' && !$pkt->is_subkey)) {
130 34         123 $last_kb_start_offset = $buf->offset;
131 34         233 $last_kb_start_cert = $pkt;
132             }
133 73 100 100     445 next unless !$pkt->{__unparsed} && $test->($pkt);
134 20         277 my $kb = Crypt::OpenPGP::KeyBlock->new;
135              
136             ## Rewind buffer; if start-cert is parsed, rewind to offset
137             ## after start-cert--otherwise rewind before start-cert
138 20 100       90 if ($last_kb_start_cert->{__unparsed}) {
139             $buf->set_offset($last_kb_start_offset -
140 3         23 $last_kb_start_cert->{__pkt_len});
141 3         20 my $cert = Crypt::OpenPGP::PacketFactory->parse($buf);
142 3         30 $kb->add($cert);
143             } else {
144 17         171 $buf->set_offset($last_kb_start_offset);
145 17         174 $kb->add($last_kb_start_cert);
146             }
147             {
148 20         66 my $off = $buf->offset;
  164         613  
149 164         1165 my $packet = Crypt::OpenPGP::PacketFactory->parse($buf);
150 164 100       816 last unless $packet;
151 150 100 100     1168 $buf->set_offset($off),
152             last if ref($packet) eq "Crypt::OpenPGP::Certificate" &&
153             !$packet->is_subkey;
154 144 50       1047 $kb->add($packet) if $kb;
155 144         372 redo;
156             }
157 20 50       635 unless ($multiple) {
158 0 0       0 return wantarray ? ($kb, $pkt) : $kb;
159             } else {
160 20 100       233 return $kb unless wantarray;
161 18         349 push @kbs, $kb;
162             }
163             }
164 22         385 @kbs;
165             }
166              
167 1 50   1 0 592 sub blocks { $_[0]->{blocks} ? @{ $_[0]->{blocks} } : () }
  1         5  
168              
169             1;
170             __END__