1 package App
::GroupSecret
::Crypt
;
2 # ABSTRACT: Collection of crypto-related subroutines
7 our $VERSION = '0.303'; # 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") }
33 sub generate_secure_random_bytes
{
34 my $size = shift or _usage
(q{generate_secure_random_bytes($num_bytes)});
36 my @cmd = ($OPENSSL, 'rand', $size);
39 my $pid = open2
($out, undef, @cmd);
44 my $exit_code = $status >> 8;
45 _croak
'Failed to generate secure random bytes' if $exit_code != 0;
47 return do { local $/; <$out> };
51 sub read_openssh_public_key
{
52 my $filepath = shift or _usage
(q{read_openssh_public_key($filepath)});
54 my @cmd = ($SSH_KEYGEN, qw{-e -m PKCS8 -f}, $filepath);
57 my $pid = open2
($out, undef, @cmd);
62 my $exit_code = $status >> 8;
63 _croak
'Failed to read OpenSSH public key' if $exit_code != 0;
65 return do { local $/; <$out> };
69 sub read_openssh_key_fingerprint
{
70 my $filepath = shift or _usage
(q{read_openssh_key_fingerprint($filepath)});
72 # try with the -E flag first
73 my @cmd = ($SSH_KEYGEN, qw{-l -E md5 -f}, $filepath);
77 my $pid = open3
(undef, $out, $err, @cmd);
82 my $exit_code = $status >> 8;
83 if ($exit_code != 0) {
84 my $error_str = do { local $/; <$err> };
85 _croak
'Failed to read SSH2 key fingerprint' if $error_str !~ /unknown option -- E/s;
87 @cmd = ($SSH_KEYGEN, qw{-l -f}, $filepath);
90 $pid = open2
($out, undef, @cmd);
95 $exit_code = $status >> 8;
96 _croak
'Failed to read SSH2 key fingerprint' if $exit_code != 0;
99 my $line = do { local $/; <$out> };
102 my ($bits, $fingerprint, $comment, $type) = $line =~ m!^(\d+) (?:MD5:)?([^ ]+) (.*) \(([^\)]+)\)$!;
104 $fingerprint =~ s/://g;
108 fingerprint
=> $fingerprint,
116 my $filepath = shift or _usage
(q{decrypt_rsa($filepath, $keypath)});
117 my $privkey = shift or _usage
(q{decrypt_rsa($filepath, $keypath)});
121 if (ref $filepath eq 'SCALAR') {
122 $temp = File
::Temp-
>new(UNLINK
=> 1);
123 print $temp $$filepath;
125 $filepath = $temp->filename;
128 my @cmd = ($OPENSSL, qw{rsautl -decrypt -oaep -in}, $filepath, '-inkey', $privkey);
129 push @cmd, ('-out', $outfile) if $outfile;
132 my $pid = open2
($out, undef, @cmd);
137 my $exit_code = $status >> 8;
138 _croak
'Failed to decrypt ciphertext' if $exit_code != 0;
140 return do { local $/; <$out> };
145 my $filepath = shift or _usage
(q{encrypt_rsa($filepath, $keypath)});
146 my $pubkey = shift or _usage
(q{encrypt_rsa($filepath, $keypath)});
150 if (ref $filepath eq 'SCALAR') {
151 $temp1 = File
::Temp-
>new(UNLINK
=> 1);
152 print $temp1 $$filepath;
154 $filepath = $temp1->filename;
157 my $key = read_openssh_public_key
($pubkey);
159 my $temp2 = File
::Temp-
>new(UNLINK
=> 1);
162 my $keypath = $temp2->filename;
164 my @cmd = ($OPENSSL, qw{rsautl -encrypt -oaep -pubin -inkey}, $keypath, '-in', $filepath);
165 push @cmd, ('-out', $outfile) if $outfile;
168 my $pid = open2
($out, undef, @cmd);
173 my $exit_code = $status >> 8;
174 _croak
'Failed to encrypt plaintext' if $exit_code != 0;
176 return do { local $/; <$out> };
180 sub decrypt_aes_256_cbc
{
181 my $filepath = shift or _usage
(q{decrypt_aes_256_cbc($ciphertext, $secret)});
182 my $secret = shift or _usage
(q{decrypt_aes_256_cbc($ciphertext, $secret)});
186 if (ref $filepath eq 'SCALAR') {
187 $temp = File
::Temp-
>new(UNLINK
=> 1);
188 print $temp $$filepath;
190 $filepath = $temp->filename;
193 my @cmd = ($OPENSSL, qw{aes-256-cbc -d -pass stdin -md sha256 -in}, $filepath);
194 push @cmd, ('-out', $outfile) if $outfile;
197 my $pid = open2
($out, $in, @cmd);
205 my $exit_code = $status >> 8;
206 _croak
'Failed to decrypt ciphertext' if $exit_code != 0;
208 return do { local $/; <$out> };
212 sub encrypt_aes_256_cbc
{
213 my $filepath = shift or _usage
(q{encrypt_aes_256_cbc($plaintext, $secret)});
214 my $secret = shift or _usage
(q{encrypt_aes_256_cbc($plaintext, $secret)});
218 if (ref $filepath eq 'SCALAR') {
219 $temp = File
::Temp-
>new(UNLINK
=> 1);
220 print $temp $$filepath;
222 $filepath = $temp->filename;
225 my @cmd = ($OPENSSL, qw{aes-256-cbc -pass stdin -md sha256 -in}, $filepath);
226 push @cmd, ('-out', $outfile) if $outfile;
229 my $pid = open2
($out, $in, @cmd);
237 my $exit_code = $status >> 8;
238 _croak
'Failed to encrypt plaintext' if $exit_code != 0;
240 return do { local $/; <$out> };
253 App::GroupSecret::Crypt - Collection of crypto-related subroutines
261 =head2 generate_secure_random_bytes
263 $bytes = generate_secure_random_bytes($num_bytes);
265 Get a certain number of secure random bytes.
267 =head2 read_openssh_public_key
269 $pem_public_key = read_openssh_public_key($public_key_filepath);
271 Read a RFC4716 (SSH2) public key from a file, converting it to PKCS8 (PEM).
273 =head2 read_openssh_key_fingerprint
275 $fingerprint = read_openssh_key_fingerprint($filepath);
277 Get the fingerprint of an OpenSSH private or public key.
281 $plaintext = decrypt_rsa($ciphertext_filepath, $private_key_filepath);
282 $plaintext = decrypt_rsa(\$ciphertext, $private_key_filepath);
283 decrypt_rsa($ciphertext_filepath, $private_key_filepath, $plaintext_filepath);
284 decrypt_rsa(\$ciphertext, $private_key_filepath, $plaintext_filepath);
286 Do RSA decryption. Turn ciphertext into plaintext.
290 $ciphertext = decrypt_rsa($plaintext_filepath, $public_key_filepath);
291 $ciphertext = decrypt_rsa(\$plaintext, $public_key_filepath);
292 decrypt_rsa($plaintext_filepath, $public_key_filepath, $ciphertext_filepath);
293 decrypt_rsa(\$plaintext, $public_key_filepath, $ciphertext_filepath);
295 Do RSA encryption. Turn plaintext into ciphertext.
297 =head2 decrypt_aes_256_cbc
299 $plaintext = decrypt_aes_256_cbc($ciphertext_filepath, $secret);
300 $plaintext = decrypt_aes_256_cbc(\$ciphertext, $secret);
301 decrypt_aes_256_cbc($ciphertext_filepath, $secret, $plaintext_filepath);
302 decrypt_aes_256_cbc(\$ciphertext, $secret, $plaintext_filepath);
304 Do symmetric decryption. Turn ciphertext into plaintext.
306 =head2 encrypt_aes_256_cbc
308 $ciphertext = encrypt_aes_256_cbc($plaintext_filepath, $secret);
309 $ciphertext = encrypt_aes_256_cbc(\$plaintext, $secret);
310 encrypt_aes_256_cbc($plaintext_filepath, $secret, $ciphertext_filepath);
311 encrypt_aes_256_cbc(\$plaintext, $secret, $ciphertext_filepath);
313 Do symmetric encryption. Turn plaintext into ciphertext.
317 Please report any bugs or feature requests on the bugtracker website
318 L<https://github.com/chazmcgarvey/groupsecret/issues>
320 When submitting a bug or request, please include a test-file or a
321 patch to an existing test-file that illustrates the bug or desired
326 Charles McGarvey <chazmcgarvey@brokenzipper.com>
328 =head1 COPYRIGHT AND LICENSE
330 This software is Copyright (c) 2017 by Charles McGarvey.
332 This is free software, licensed under:
334 The MIT (X11) License