File Coverage

blib/lib/SRS/EPP/OpenPGP.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1              
2             package SRS::EPP::OpenPGP;
3              
4 2     2   75277 use 5.010;
  2         5  
  2         63  
5 2     2   363 use Moose;
  0            
  0            
6             use Moose::Util::TypeConstraints;
7             use MooseX::Method::Signatures;
8             use Crypt::OpenPGP;
9             use Crypt::OpenPGP::KeyRing;
10             use Carp;
11              
12             with 'MooseX::Log::Log4perl';
13              
14             BEGIN {
15             class_type "Crypt::OpenPGP::KeyRing";
16             class_type "Crypt::OpenPGP::KeyBlock";
17             class_type "Crypt::OpenPGP::Certificate";
18             }
19              
20             # Crypt::OpenPGP setup.
21             has 'pgp' =>
22             is => "ro",
23             isa => "Crypt::OpenPGP",
24             lazy => 1,
25             default => sub {
26             my $self = shift;
27             Crypt::OpenPGP->new(
28             ($self->_has_secret_keyring ?
29             (SecRing => $self->secret_keyring)
30             : ()),
31             ($self->_has_public_keyring ?
32             (PubRing => $self->public_keyring)
33             : ()),
34             );
35             },
36             ;
37              
38             has 'secret_keyring' =>
39             is => "ro",
40             isa => "Crypt::OpenPGP::KeyRing",
41             lazy => 1,
42             predicate => "_has_secret_keyring",
43             coerce => 1,
44             default => sub {
45             my $self = shift;
46             $self->pgp->{cfg}->get("SecRing");
47             },
48             ;
49             has 'public_keyring' =>
50             is => "ro",
51             isa => "Crypt::OpenPGP::KeyRing",
52             lazy => 1,
53             predicate => "_has_public_keyring",
54             coerce => 1,
55             default => sub {
56             my $self = shift;
57             $self->pgp->{cfg}->get("PubRing");
58             },
59             ;
60             coerce "Crypt::OpenPGP::KeyRing"
61             => from "Str"
62             => via {
63             Crypt::OpenPGP::KeyRing->new(
64             Filename => $_,
65             );
66             };
67              
68             # specifying the default signing/encryption key
69              
70             BEGIN {
71             subtype "SRS::EPP::OpenPGP::key_id"
72             => as "Str",
73             => where {
74             m{^(?:0x)?(?:(?:[0-9a-f]{4}\s?){2}){1,2}$}i;
75             };
76             };
77              
78             has 'uid' =>
79             is => "rw",
80             isa => "SRS::EPP::OpenPGP::key_id",
81             trigger => sub {
82             my $self = shift;
83             my $uid = shift;
84             $self->default_signing_key(
85             $self->find_signing_key($uid)
86             );
87             $self->default_encrypting_key(
88             $self->find_signing_key($uid)
89             );
90             }
91             ;
92              
93             has 'passphrase' =>
94             is => "rw",
95             isa => "Str",
96             ;
97              
98             method unlock_cert( Crypt::OpenPGP::Certificate $cert ) {
99             return unless $cert->is_protected;
100              
101             return if $self->passphrase and
102             $cert->unlock($self->passphrase);
103              
104             my $key_id = $cert->fingerprint_hex;
105             require Scriptalicious;
106              
107             unless (-t STDIN) {
108             $self->logger->fatal("no terminal");
109             die "no terminal";
110             }
111              
112             $self->passphrase(
113             Scriptalicious::prompt_passwd(
114             "Enter passphrase for PGP cert $key_id:"
115             ),
116             );
117             print "\n"; # workaround bug in Scriptalicious..
118              
119             return $self->unlock_cert($cert);
120             }
121              
122             has 'default_signing_key' =>
123             is => "rw",
124             ;
125              
126             has 'default_encrypting_key' =>
127             is => "rw",
128             ;
129              
130             method find_signing_key(SRS::EPP::OpenPGP::key_id $key_id) {
131             my $kb = $self->get_sec_key_block($key_id) or return;
132             my $cert = $kb->signing_key
133             or croak "Invalid signing key $key_id";
134             $self->unlock_cert($cert);
135             $cert->uid($kb->primary_uid);
136             return $cert;
137             }
138              
139             method find_encrypting_key(SRS::EPP::OpenPGP::key_id $key_id) {
140             my $kb = $self->get_sec_key_block($key_id) or return;
141             my $cert = $kb->encrypting_key
142             or croak "Invalid encrypting key $key_id";
143             $self->unlock_cert($cert);
144             $cert->uid($kb->primary_uid);
145             return $cert;
146             }
147              
148              
149             method get_sec_key_block(SRS::EPP::OpenPGP::key_id $key_id?) {
150             my $sec_ring = $self->secret_keyring;
151             $key_id =~ s{^0x}{};
152             my $kb = $key_id
153             ? $sec_ring->find_keyblock_by_keyid( pack("H*", $key_id) )
154             : $sec_ring->find_keyblock_by_index(-1)
155             or croak "Can't find keyblock ("
156             .($key_id ? $key_id : "default")
157             ."): " . $sec_ring->errstr;
158             return $kb;
159             }
160              
161             method get_pub_key_block(SRS::EPP::OpenPGP::key_id $key_id?) {
162             my $pub_ring = $self->public_keyring;
163             $key_id =~ s{^0x}{};
164             my $kb = $key_id
165             ? $pub_ring->find_keyblock_by_keyid( pack("H*", $key_id) )
166             : $pub_ring->find_keyblock_by_index(-1)
167             or croak "Can't find keyblock ("
168             .($key_id ? $key_id : "default")
169             ."): " . $pub_ring->errstr;
170             return $kb;
171             }
172              
173             method get_cert_from_key_text( Str $key_text ) {
174             my $kr = new Crypt::OpenPGP::KeyRing(Data => $key_text)
175             or return;
176             my $kb = $kr->find_keyblock_by_index(-1)
177             or return;
178             my $cert = $kb->signing_key
179             or return;
180             $cert->uid($kb->primary_uid);
181             $cert;
182             }
183              
184             use Encode;
185             use utf8;
186             sub byte_string {
187             if ( utf8::is_utf8($_[0]) ) {
188             encode("utf8", $_[0]);
189             }
190             else {
191             $_[0];
192             }
193             }
194              
195             method verify_detached(Str $data, Str $signature, :$cert, :$key_text) {
196             if ( $key_text ) {
197             $cert ||= $self->get_cert_from_key_text($key_text);
198             }
199             my $pgp = $self->pgp;
200             my $res = $pgp->verify(
201             Data => byte_string($data),
202             Signature => $signature,
203             ( $cert ? (Key => $cert) : () ),
204             );
205             if ( $res ) {
206             my $res_neg = $pgp->verify(
207             Data => "xx.$$.".rand(3),
208             Signature => $signature,
209             ( $cert ? (Key => $cert) : () ),
210             );
211             if ( $res and $res_neg ) {
212             # a full doc was passed in as a signature...
213             $res = 0;
214             }
215             }
216             warn $pgp->errstr if !$res && $pgp->errstr;
217             return $res;
218             }
219              
220             method detached_sign(Str $data, $key?, $passphrase?) {
221             $key ||= $self->default_signing_key;
222             my $pgp = $self->pgp;
223             my $signature = $pgp->sign(
224             Data => byte_string($data),
225             Detach => 1,
226             Armour => 1,
227             Digest => "SHA1",
228             Passphrase => $passphrase//"",
229             Key => $key,
230             );
231              
232             carp "Signing attempt failed: ", $pgp->errstr() unless $signature;
233             return $signature;
234             }
235              
236             1;