File Coverage

blib/lib/Crypt/OpenPGP/KeyRing.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::KeyRing;
2 7     7   944 use strict;
  7         16  
  7         321  
3              
4 7         48 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 7     7   771 PGP_PKT_SECRET_SUBKEY );
  7         18  
9 7     7   4725 use Crypt::OpenPGP::Buffer;
  0            
  0            
10             use Crypt::OpenPGP::KeyBlock;
11             use Crypt::OpenPGP::PacketFactory;
12             use Crypt::OpenPGP::ErrorHandler;
13             use base qw( Crypt::OpenPGP::ErrorHandler );
14              
15             sub new {
16             my $class = shift;
17             my $ring = bless { }, $class;
18             $ring->init(@_);
19             }
20              
21             sub init {
22             my $ring = shift;
23             my %param = @_;
24             $ring->{_data} = $param{Data} || '';
25             if (!$ring->{_data} && (my $file = $param{Filename})) {
26             local *FH;
27             open FH, $file or
28             return (ref $ring)->error("Can't open keyring $file: $!");
29             binmode FH;
30             { local $/; $ring->{_data} = }
31             close FH;
32             }
33             if ($ring->{_data} =~ /^-----BEGIN/) {
34             require Crypt::OpenPGP::Armour;
35             my $rec = Crypt::OpenPGP::Armour->unarmour($ring->{_data}) or
36             return (ref $ring)->error("Unarmour failed: " .
37             Crypt::OpenPGP::Armour->errstr);
38             $ring->{_data} = $rec->{Data};
39             }
40             $ring;
41             }
42              
43             sub save {
44             my $ring = shift;
45             my @blocks = $ring->blocks;
46             my $res = '';
47             for my $block (@blocks) {
48             $res .= $block->save;
49             }
50             $res;
51             }
52              
53             sub read {
54             my $ring = shift;
55             return $ring->error("No data to read") unless $ring->{_data};
56             my $buf = Crypt::OpenPGP::Buffer->new;
57             $buf->append($ring->{_data});
58             $ring->restore($buf);
59             1;
60             }
61              
62             sub restore {
63             my $ring = shift;
64             my($buf) = @_;
65             $ring->{blocks} = [];
66             my($kb);
67             while (my $packet = Crypt::OpenPGP::PacketFactory->parse($buf)) {
68             if (ref($packet) eq "Crypt::OpenPGP::Certificate" &&
69             !$packet->is_subkey) {
70             $kb = Crypt::OpenPGP::KeyBlock->new;
71             $ring->add($kb);
72             }
73             $kb->add($packet) if $kb;
74             }
75             }
76              
77             sub add {
78             my $ring = shift;
79             my($entry) = @_;
80             push @{ $ring->{blocks} }, $entry;
81             }
82              
83             sub find_keyblock_by_keyid {
84             my $ring = shift;
85             my($key_id) = @_;
86             my $ref = $ring->{by_keyid}{$key_id};
87             unless ($ref) {
88             my $len = length($key_id);
89             my @kbs = $ring->find_keyblock(
90             sub { substr($_[0]->key_id, -$len, $len) eq $key_id },
91             [ PGP_PKT_PUBLIC_KEY, PGP_PKT_SECRET_KEY,
92             PGP_PKT_PUBLIC_SUBKEY, PGP_PKT_SECRET_SUBKEY ], 1 );
93             return unless @kbs;
94             $ref = $ring->{by_keyid}{ $key_id } = \@kbs;
95             }
96             return wantarray ? @$ref : $ref->[0];
97             }
98              
99             sub find_keyblock_by_uid {
100             my $ring = shift;
101             my($uid) = @_;
102             $ring->find_keyblock(sub { $_[0]->id =~ /$uid/i },
103             [ PGP_PKT_USER_ID ], 1 );
104             }
105              
106             sub find_keyblock_by_index {
107             my $ring = shift;
108             my($index) = @_;
109             ## XXX should not have to read entire keyring
110             $ring->read;
111             ($ring->blocks)[$index];
112             }
113              
114             sub find_keyblock {
115             my $ring = shift;
116             my($test, $pkttypes, $multiple) = @_;
117             $pkttypes ||= [];
118             return $ring->error("No data to read") unless $ring->{_data};
119             my $buf = Crypt::OpenPGP::Buffer->new_with_init($ring->{_data});
120             my($last_kb_start_offset, $last_kb_start_cert, @kbs);
121             while (my $pkt = Crypt::OpenPGP::PacketFactory->parse($buf,
122             [ PGP_PKT_SECRET_KEY, PGP_PKT_PUBLIC_KEY,
123             @$pkttypes ], $pkttypes)) {
124             if (($pkt->{__unparsed} && ($pkt->{type} == PGP_PKT_SECRET_KEY ||
125             $pkt->{type} == PGP_PKT_PUBLIC_KEY)) ||
126             (ref($pkt) eq 'Crypt::OpenPGP::Certificate' && !$pkt->is_subkey)) {
127             $last_kb_start_offset = $buf->offset;
128             $last_kb_start_cert = $pkt;
129             }
130             next unless !$pkt->{__unparsed} && $test->($pkt);
131             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             if ($last_kb_start_cert->{__unparsed}) {
136             $buf->set_offset($last_kb_start_offset -
137             $last_kb_start_cert->{__pkt_len});
138             my $cert = Crypt::OpenPGP::PacketFactory->parse($buf);
139             $kb->add($cert);
140             } else {
141             $buf->set_offset($last_kb_start_offset);
142             $kb->add($last_kb_start_cert);
143             }
144             {
145             my $off = $buf->offset;
146             my $packet = Crypt::OpenPGP::PacketFactory->parse($buf);
147             last unless $packet;
148             $buf->set_offset($off),
149             last if ref($packet) eq "Crypt::OpenPGP::Certificate" &&
150             !$packet->is_subkey;
151             $kb->add($packet) if $kb;
152             redo;
153             }
154             unless ($multiple) {
155             return wantarray ? ($kb, $pkt) : $kb;
156             } else {
157             return $kb unless wantarray;
158             push @kbs, $kb;
159             }
160             }
161             @kbs;
162             }
163              
164             sub blocks { $_[0]->{blocks} ? @{ $_[0]->{blocks} } : () }
165              
166             1;
167             __END__