1 package App
::GroupSecret
::Crypt
;
2 # ABSTRACT: Collection of crypto-related subroutines
7 our $VERSION = '9999.999'; # VERSION
9 use Exporter
qw(import);
13 use Symbol
qw(gensym);
14 use namespace
::clean
-except
=> [qw(import)];
17 generate_secure_random_bytes
18 read_openssh_public_key
19 read_openssh_key_fingerprint
26 our $OPENSSL = 'openssl';
27 our $SSH_KEYGEN = 'ssh-keygen';
29 sub _croak
{ require Carp
; Carp
::croak
(@_) }
30 sub _usage
{ _croak
("Usage: @_\n") }
32 =func generate_secure_random_bytes
34 $bytes = generate_secure_random_bytes
($num_bytes);
36 Get a certain number of secure random bytes
.
40 sub generate_secure_random_bytes
{
41 my $size = shift or _usage
(q{generate_secure_random_bytes($num_bytes)});
43 my @cmd = ($OPENSSL, 'rand', $size);
46 my $pid = open2
($out, undef, @cmd);
51 my $exit_code = $status >> 8;
52 _croak
'Failed to generate secure random bytes' if $exit_code != 0;
54 return do { local $/; <$out> };
57 =func read_openssh_public_key
59 $pem_public_key = read_openssh_public_key
($public_key_filepath);
61 Read a RFC4716
(SSH2
) public key from a file
, converting it to PKCS8
(PEM
).
65 sub read_openssh_public_key
{
66 my $filepath = shift or _usage
(q{read_openssh_public_key($filepath)});
68 my @cmd = ($SSH_KEYGEN, qw{-e -m PKCS8 -f}, $filepath);
71 my $pid = open2
($out, undef, @cmd);
76 my $exit_code = $status >> 8;
77 _croak
'Failed to read OpenSSH public key' if $exit_code != 0;
79 return do { local $/; <$out> };
82 =func read_openssh_key_fingerprint
84 $fingerprint = read_openssh_key_fingerprint
($filepath);
86 Get the fingerprint of an OpenSSH private
or public key
.
90 sub read_openssh_key_fingerprint
{
91 my $filepath = shift or _usage
(q{read_openssh_key_fingerprint($filepath)});
93 # try with the -E flag first
94 my @cmd = ($SSH_KEYGEN, qw{-l -E md5 -f}, $filepath);
98 my $pid = open3
(undef, $out, $err, @cmd);
103 my $exit_code = $status >> 8;
104 if ($exit_code != 0) {
105 my $error_str = do { local $/; <$err> };
106 _croak
'Failed to read SSH2 key fingerprint' if $error_str !~ /unknown option -- E/s;
108 @cmd = ($SSH_KEYGEN, qw{-l -f}, $filepath);
111 $pid = open2
($out, undef, @cmd);
116 $exit_code = $status >> 8;
117 _croak
'Failed to read SSH2 key fingerprint' if $exit_code != 0;
120 my $line = do { local $/; <$out> };
123 my ($bits, $fingerprint, $comment, $type) = $line =~ m!^(\d+) (?:MD5:)?([^ ]+) (.*) \(([^\)]+)\)$!;
125 $fingerprint =~ s/://g;
129 fingerprint
=> $fingerprint,
137 $plaintext = decrypt_rsa
($ciphertext_filepath, $private_key_filepath);
138 $plaintext = decrypt_rsa
(\
$ciphertext, $private_key_filepath);
139 decrypt_rsa
($ciphertext_filepath, $private_key_filepath, $plaintext_filepath);
140 decrypt_rsa
(\
$ciphertext, $private_key_filepath, $plaintext_filepath);
142 Do RSA decryption
. Turn ciphertext into plaintext
.
147 my $filepath = shift or _usage
(q{decrypt_rsa($filepath, $keypath)});
148 my $privkey = shift or _usage
(q{decrypt_rsa($filepath, $keypath)});
152 if (ref $filepath eq 'SCALAR') {
153 $temp = File
::Temp-
>new(UNLINK
=> 1);
154 print $temp $$filepath;
156 $filepath = $temp->filename;
159 my @cmd = ($OPENSSL, qw{rsautl -decrypt -oaep -in}, $filepath, '-inkey', $privkey);
160 push @cmd, ('-out', $outfile) if $outfile;
163 my $pid = open2
($out, undef, @cmd);
168 my $exit_code = $status >> 8;
169 _croak
'Failed to decrypt ciphertext' if $exit_code != 0;
171 return do { local $/; <$out> };
176 $ciphertext = decrypt_rsa
($plaintext_filepath, $public_key_filepath);
177 $ciphertext = decrypt_rsa
(\
$plaintext, $public_key_filepath);
178 decrypt_rsa
($plaintext_filepath, $public_key_filepath, $ciphertext_filepath);
179 decrypt_rsa
(\
$plaintext, $public_key_filepath, $ciphertext_filepath);
181 Do RSA encryption
. Turn plaintext into ciphertext
.
186 my $filepath = shift or _usage
(q{encrypt_rsa($filepath, $keypath)});
187 my $pubkey = shift or _usage
(q{encrypt_rsa($filepath, $keypath)});
191 if (ref $filepath eq 'SCALAR') {
192 $temp1 = File
::Temp-
>new(UNLINK
=> 1);
193 print $temp1 $$filepath;
195 $filepath = $temp1->filename;
198 my $key = read_openssh_public_key
($pubkey);
200 my $temp2 = File
::Temp-
>new(UNLINK
=> 1);
203 my $keypath = $temp2->filename;
205 my @cmd = ($OPENSSL, qw{rsautl -encrypt -oaep -pubin -inkey}, $keypath, '-in', $filepath);
206 push @cmd, ('-out', $outfile) if $outfile;
209 my $pid = open2
($out, undef, @cmd);
214 my $exit_code = $status >> 8;
215 _croak
'Failed to encrypt plaintext' if $exit_code != 0;
217 return do { local $/; <$out> };
220 =func decrypt_aes_256_cbc
222 $plaintext = decrypt_aes_256_cbc
($ciphertext_filepath, $secret);
223 $plaintext = decrypt_aes_256_cbc
(\
$ciphertext, $secret);
224 decrypt_aes_256_cbc
($ciphertext_filepath, $secret, $plaintext_filepath);
225 decrypt_aes_256_cbc
(\
$ciphertext, $secret, $plaintext_filepath);
227 Do symmetric decryption
. Turn ciphertext into plaintext
.
231 sub decrypt_aes_256_cbc
{
232 my $filepath = shift or _usage
(q{decrypt_aes_256_cbc($ciphertext, $secret)});
233 my $secret = shift or _usage
(q{decrypt_aes_256_cbc($ciphertext, $secret)});
237 if (ref $filepath eq 'SCALAR') {
238 $temp = File
::Temp-
>new(UNLINK
=> 1);
239 print $temp $$filepath;
241 $filepath = $temp->filename;
244 my @cmd = ($OPENSSL, qw{aes-256-cbc -d -pass stdin -md sha256 -in}, $filepath);
245 push @cmd, ('-out', $outfile) if $outfile;
248 my $pid = open2
($out, $in, @cmd);
256 my $exit_code = $status >> 8;
257 _croak
'Failed to decrypt ciphertext' if $exit_code != 0;
259 return do { local $/; <$out> };
262 =func encrypt_aes_256_cbc
264 $ciphertext = encrypt_aes_256_cbc
($plaintext_filepath, $secret);
265 $ciphertext = encrypt_aes_256_cbc
(\
$plaintext, $secret);
266 encrypt_aes_256_cbc
($plaintext_filepath, $secret, $ciphertext_filepath);
267 encrypt_aes_256_cbc
(\
$plaintext, $secret, $ciphertext_filepath);
269 Do symmetric encryption
. Turn plaintext into ciphertext
.
273 sub encrypt_aes_256_cbc
{
274 my $filepath = shift or _usage
(q{encrypt_aes_256_cbc($plaintext, $secret)});
275 my $secret = shift or _usage
(q{encrypt_aes_256_cbc($plaintext, $secret)});
279 if (ref $filepath eq 'SCALAR') {
280 $temp = File
::Temp-
>new(UNLINK
=> 1);
281 print $temp $$filepath;
283 $filepath = $temp->filename;
286 my @cmd = ($OPENSSL, qw{aes-256-cbc -pass stdin -md sha256 -in}, $filepath);
287 push @cmd, ('-out', $outfile) if $outfile;
290 my $pid = open2
($out, $in, @cmd);
298 my $exit_code = $status >> 8;
299 _croak
'Failed to encrypt plaintext' if $exit_code != 0;
301 return do { local $/; <$out> };