line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package SRS::EPP::OpenPGP; |
3
|
|
|
|
|
|
|
{ |
4
|
|
|
|
|
|
|
$SRS::EPP::OpenPGP::VERSION = '0.22'; |
5
|
|
|
|
|
|
|
} |
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
128902
|
use 5.010; |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
82
|
|
8
|
2
|
|
|
2
|
|
847
|
use Moose; |
|
2
|
|
|
|
|
560710
|
|
|
2
|
|
|
|
|
19
|
|
9
|
2
|
|
|
2
|
|
16786
|
use MooseX::Params::Validate; |
|
2
|
|
|
|
|
13710
|
|
|
2
|
|
|
|
|
21
|
|
10
|
2
|
|
|
2
|
|
836
|
use Moose::Util::TypeConstraints; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
21
|
|
11
|
2
|
|
|
2
|
|
11840
|
use Crypt::OpenPGP; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
use Crypt::OpenPGP::KeyRing; |
13
|
|
|
|
|
|
|
use Carp; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
with 'MooseX::Log::Log4perl'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
BEGIN { |
18
|
|
|
|
|
|
|
class_type "Crypt::OpenPGP::KeyRing"; |
19
|
|
|
|
|
|
|
class_type "Crypt::OpenPGP::KeyBlock"; |
20
|
|
|
|
|
|
|
class_type "Crypt::OpenPGP::Certificate"; |
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Crypt::OpenPGP setup. |
24
|
|
|
|
|
|
|
has 'pgp' => |
25
|
|
|
|
|
|
|
is => "ro", |
26
|
|
|
|
|
|
|
isa => "Crypt::OpenPGP", |
27
|
|
|
|
|
|
|
lazy => 1, |
28
|
|
|
|
|
|
|
default => sub { |
29
|
|
|
|
|
|
|
my $self = shift; |
30
|
|
|
|
|
|
|
Crypt::OpenPGP->new( |
31
|
|
|
|
|
|
|
( |
32
|
|
|
|
|
|
|
$self->_has_secret_keyring |
33
|
|
|
|
|
|
|
? (SecRing => $self->secret_keyring) |
34
|
|
|
|
|
|
|
: () |
35
|
|
|
|
|
|
|
), |
36
|
|
|
|
|
|
|
( |
37
|
|
|
|
|
|
|
$self->_has_public_keyring |
38
|
|
|
|
|
|
|
? (PubRing => $self->public_keyring) |
39
|
|
|
|
|
|
|
: () |
40
|
|
|
|
|
|
|
), |
41
|
|
|
|
|
|
|
); |
42
|
|
|
|
|
|
|
}, |
43
|
|
|
|
|
|
|
; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
coerce "Crypt::OpenPGP::KeyRing" |
46
|
|
|
|
|
|
|
=> from "Str" |
47
|
|
|
|
|
|
|
=> via { |
48
|
|
|
|
|
|
|
Crypt::OpenPGP::KeyRing->new( |
49
|
|
|
|
|
|
|
Filename => $_, |
50
|
|
|
|
|
|
|
); |
51
|
|
|
|
|
|
|
}; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
has 'secret_keyring' => |
54
|
|
|
|
|
|
|
is => "ro", |
55
|
|
|
|
|
|
|
isa => "Crypt::OpenPGP::KeyRing", |
56
|
|
|
|
|
|
|
lazy => 1, |
57
|
|
|
|
|
|
|
predicate => "_has_secret_keyring", |
58
|
|
|
|
|
|
|
coerce => 1, |
59
|
|
|
|
|
|
|
default => sub { |
60
|
|
|
|
|
|
|
my $self = shift; |
61
|
|
|
|
|
|
|
$self->pgp->{cfg}->get("SecRing"); |
62
|
|
|
|
|
|
|
}, |
63
|
|
|
|
|
|
|
; |
64
|
|
|
|
|
|
|
has 'public_keyring' => |
65
|
|
|
|
|
|
|
is => "ro", |
66
|
|
|
|
|
|
|
isa => "Crypt::OpenPGP::KeyRing", |
67
|
|
|
|
|
|
|
lazy => 1, |
68
|
|
|
|
|
|
|
predicate => "_has_public_keyring", |
69
|
|
|
|
|
|
|
coerce => 1, |
70
|
|
|
|
|
|
|
default => sub { |
71
|
|
|
|
|
|
|
my $self = shift; |
72
|
|
|
|
|
|
|
$self->pgp->{cfg}->get("PubRing"); |
73
|
|
|
|
|
|
|
}, |
74
|
|
|
|
|
|
|
; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# specifying the default signing/encryption key |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
BEGIN { |
80
|
|
|
|
|
|
|
subtype "SRS::EPP::OpenPGP::key_id" |
81
|
|
|
|
|
|
|
=> as "Str", |
82
|
|
|
|
|
|
|
=> where { |
83
|
|
|
|
|
|
|
m{^(?:0x)?(?:(?:[0-9a-f]{4}\s?){2}){1,2}$}i; |
84
|
|
|
|
|
|
|
}; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
has 'uid' => |
88
|
|
|
|
|
|
|
is => "rw", |
89
|
|
|
|
|
|
|
isa => "SRS::EPP::OpenPGP::key_id", |
90
|
|
|
|
|
|
|
trigger => sub { |
91
|
|
|
|
|
|
|
my $self = shift; |
92
|
|
|
|
|
|
|
my $uid = shift; |
93
|
|
|
|
|
|
|
$self->default_signing_key( |
94
|
|
|
|
|
|
|
$self->find_signing_key($uid) |
95
|
|
|
|
|
|
|
); |
96
|
|
|
|
|
|
|
$self->default_encrypting_key( |
97
|
|
|
|
|
|
|
$self->find_signing_key($uid) |
98
|
|
|
|
|
|
|
); |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
has 'passphrase' => |
103
|
|
|
|
|
|
|
is => "rw", |
104
|
|
|
|
|
|
|
isa => "Str", |
105
|
|
|
|
|
|
|
; |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub unlock_cert { |
108
|
|
|
|
|
|
|
my $self = shift; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
my ( $cert ) = pos_validated_list( |
111
|
|
|
|
|
|
|
\@_, |
112
|
|
|
|
|
|
|
{ isa => 'Crypt::OpenPGP::Certificate' }, |
113
|
|
|
|
|
|
|
); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
return unless $cert->is_protected; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
return if $self->passphrase and |
118
|
|
|
|
|
|
|
$cert->unlock($self->passphrase); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
my $key_id = $cert->fingerprint_hex; |
121
|
|
|
|
|
|
|
require Scriptalicious; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
unless (-t STDIN) { |
124
|
|
|
|
|
|
|
$self->logger->fatal("no terminal"); |
125
|
|
|
|
|
|
|
die "no terminal"; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
$self->passphrase( |
129
|
|
|
|
|
|
|
Scriptalicious::prompt_passwd( |
130
|
|
|
|
|
|
|
"Enter passphrase for PGP cert $key_id:" |
131
|
|
|
|
|
|
|
), |
132
|
|
|
|
|
|
|
); |
133
|
|
|
|
|
|
|
print "\n"; # workaround bug in Scriptalicious.. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
return $self->unlock_cert($cert); |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
has 'default_signing_key' => ( |
139
|
|
|
|
|
|
|
is => "rw", |
140
|
|
|
|
|
|
|
lazy => 1, |
141
|
|
|
|
|
|
|
default => sub { |
142
|
|
|
|
|
|
|
my $self = shift; |
143
|
|
|
|
|
|
|
my $sec_ring = $self->secret_keyring; |
144
|
|
|
|
|
|
|
my $kb = $self->get_sec_key_block |
145
|
|
|
|
|
|
|
or die "no secret key block"; |
146
|
|
|
|
|
|
|
my $cert = $kb->signing_key |
147
|
|
|
|
|
|
|
or croak "Invalid default secret key; specify pgp_keyid in config"; |
148
|
|
|
|
|
|
|
$self->unlock_cert($cert); |
149
|
|
|
|
|
|
|
$cert->uid($kb->primary_uid); |
150
|
|
|
|
|
|
|
$cert; |
151
|
|
|
|
|
|
|
}, |
152
|
|
|
|
|
|
|
); |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
has 'default_encrypting_key' => |
155
|
|
|
|
|
|
|
is => "rw", |
156
|
|
|
|
|
|
|
; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub find_signing_key { |
159
|
|
|
|
|
|
|
my $self = shift; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
my ( $key_id ) = pos_validated_list( |
162
|
|
|
|
|
|
|
\@_, |
163
|
|
|
|
|
|
|
{ isa => 'SRS::EPP::OpenPGP::key_id' }, |
164
|
|
|
|
|
|
|
); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
my $kb = $self->get_sec_key_block($key_id) or return; |
167
|
|
|
|
|
|
|
my $cert = $kb->signing_key |
168
|
|
|
|
|
|
|
or croak "Invalid signing key $key_id"; |
169
|
|
|
|
|
|
|
$self->unlock_cert($cert); |
170
|
|
|
|
|
|
|
$cert->uid($kb->primary_uid); |
171
|
|
|
|
|
|
|
return $cert; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub find_encrypting_key { |
175
|
|
|
|
|
|
|
my $self = shift; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
my ( $key_id ) = pos_validated_list( |
178
|
|
|
|
|
|
|
\@_, |
179
|
|
|
|
|
|
|
{ isa => 'SRS::EPP::OpenPGP::key_id' }, |
180
|
|
|
|
|
|
|
); |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
my $kb = $self->get_sec_key_block($key_id) or return; |
183
|
|
|
|
|
|
|
my $cert = $kb->encrypting_key |
184
|
|
|
|
|
|
|
or croak "Invalid encrypting key $key_id"; |
185
|
|
|
|
|
|
|
$self->unlock_cert($cert); |
186
|
|
|
|
|
|
|
$cert->uid($kb->primary_uid); |
187
|
|
|
|
|
|
|
return $cert; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub get_sec_key_block { |
191
|
|
|
|
|
|
|
my $self = shift; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my ( $key_id ) = pos_validated_list( |
194
|
|
|
|
|
|
|
\@_, |
195
|
|
|
|
|
|
|
{ isa => 'SRS::EPP::OpenPGP::key_id', optional => 1 }, |
196
|
|
|
|
|
|
|
); |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my $sec_ring = $self->secret_keyring; |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
my $func = sub{$sec_ring->find_keyblock_by_index(@_)}; |
201
|
|
|
|
|
|
|
my $param = -1; |
202
|
|
|
|
|
|
|
my $label = "default"; |
203
|
|
|
|
|
|
|
if ($key_id) { |
204
|
|
|
|
|
|
|
$key_id =~ s{^0x}{}; |
205
|
|
|
|
|
|
|
$func = sub{$sec_ring->find_keyblock_by_keyid(@_)}; |
206
|
|
|
|
|
|
|
$param = pack("H*", $key_id); |
207
|
|
|
|
|
|
|
$label = $key_id; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my $kb = $func->($param) |
211
|
|
|
|
|
|
|
or croak "Can't find keyblock ($label): " . $sec_ring->errstr; |
212
|
|
|
|
|
|
|
return $kb; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub get_pub_key_block { |
216
|
|
|
|
|
|
|
my $self = shift; |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
my ( $key_id ) = pos_validated_list( |
219
|
|
|
|
|
|
|
\@_, |
220
|
|
|
|
|
|
|
{ isa => 'SRS::EPP::OpenPGP::key_id', optional => 1 }, |
221
|
|
|
|
|
|
|
); |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
my $pub_ring = $self->public_keyring; |
224
|
|
|
|
|
|
|
$key_id =~ s{^0x}{}; |
225
|
|
|
|
|
|
|
my $kb = $key_id |
226
|
|
|
|
|
|
|
? $pub_ring->find_keyblock_by_keyid( pack("H*", $key_id) ) |
227
|
|
|
|
|
|
|
: $pub_ring->find_keyblock_by_index(-1) |
228
|
|
|
|
|
|
|
or croak "Can't find keyblock (" |
229
|
|
|
|
|
|
|
.($key_id ? $key_id : "default") |
230
|
|
|
|
|
|
|
."): " . $pub_ring->errstr; |
231
|
|
|
|
|
|
|
return $kb; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub get_cert_from_key_text{ |
235
|
|
|
|
|
|
|
my $self = shift; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
my ( $key_text ) = pos_validated_list( |
238
|
|
|
|
|
|
|
\@_, |
239
|
|
|
|
|
|
|
{ isa => 'Str' }, |
240
|
|
|
|
|
|
|
); |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
my $kr = new Crypt::OpenPGP::KeyRing(Data => $key_text) |
243
|
|
|
|
|
|
|
or return; |
244
|
|
|
|
|
|
|
my $kb = $kr->find_keyblock_by_index(-1) |
245
|
|
|
|
|
|
|
or return; |
246
|
|
|
|
|
|
|
my $cert = $kb->signing_key |
247
|
|
|
|
|
|
|
or return; |
248
|
|
|
|
|
|
|
$cert->uid($kb->primary_uid); |
249
|
|
|
|
|
|
|
$cert; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
use Encode; |
253
|
|
|
|
|
|
|
use utf8; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub byte_string { |
256
|
|
|
|
|
|
|
if ( utf8::is_utf8($_[0]) ) { |
257
|
|
|
|
|
|
|
encode("utf8", $_[0]); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
else { |
260
|
|
|
|
|
|
|
$_[0]; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub verify_detached { |
265
|
|
|
|
|
|
|
my $self = shift; |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
my ( $data, $signature, $cert, $key_text ) = validated_list( |
268
|
|
|
|
|
|
|
\@_, |
269
|
|
|
|
|
|
|
data => { isa => 'Str' }, |
270
|
|
|
|
|
|
|
signature => { isa => 'Str' }, |
271
|
|
|
|
|
|
|
cert => { optional => 1 }, |
272
|
|
|
|
|
|
|
key_text => { optional => 1 }, |
273
|
|
|
|
|
|
|
); |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
if ($key_text) { |
276
|
|
|
|
|
|
|
$cert ||= $self->get_cert_from_key_text($key_text); |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
my $pgp = $self->pgp; |
279
|
|
|
|
|
|
|
my $res = $pgp->verify( |
280
|
|
|
|
|
|
|
Data => byte_string($data), |
281
|
|
|
|
|
|
|
Signature => $signature, |
282
|
|
|
|
|
|
|
( $cert ? (Key => $cert) : () ), |
283
|
|
|
|
|
|
|
); |
284
|
|
|
|
|
|
|
if ($res) { |
285
|
|
|
|
|
|
|
my $res_neg = $pgp->verify( |
286
|
|
|
|
|
|
|
Data => "xx.$$.".rand(3), |
287
|
|
|
|
|
|
|
Signature => $signature, |
288
|
|
|
|
|
|
|
( $cert ? (Key => $cert) : () ), |
289
|
|
|
|
|
|
|
); |
290
|
|
|
|
|
|
|
if ( $res and $res_neg ) { |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# a full doc was passed in as a signature... |
293
|
|
|
|
|
|
|
$res = 0; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
warn $pgp->errstr if !$res && $pgp->errstr; |
297
|
|
|
|
|
|
|
return $res; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub detached_sign { |
301
|
|
|
|
|
|
|
my $self = shift; |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
my ( $data, $key, $passphrase ) = pos_validated_list( |
304
|
|
|
|
|
|
|
\@_, |
305
|
|
|
|
|
|
|
{ isa => 'Str' }, |
306
|
|
|
|
|
|
|
{ optional => 1 }, |
307
|
|
|
|
|
|
|
{ optional => 1 }, |
308
|
|
|
|
|
|
|
); |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
$key ||= $self->default_signing_key; |
312
|
|
|
|
|
|
|
my $pgp = $self->pgp; |
313
|
|
|
|
|
|
|
my $signature = $pgp->sign( |
314
|
|
|
|
|
|
|
Data => byte_string($data), |
315
|
|
|
|
|
|
|
Detach => 1, |
316
|
|
|
|
|
|
|
Armour => 1, |
317
|
|
|
|
|
|
|
Digest => "SHA1", |
318
|
|
|
|
|
|
|
Passphrase => $passphrase//"", |
319
|
|
|
|
|
|
|
Key => $key, |
320
|
|
|
|
|
|
|
); |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
carp "Signing attempt failed: ", $pgp->errstr() unless $signature; |
323
|
|
|
|
|
|
|
return $signature; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
1; |