]> Dogcows Code - chaz/groupsecret/blob - lib/App/GroupSecret/Crypt.pm
avoid declaring unused vars for stdin
[chaz/groupsecret] / lib / App / GroupSecret / Crypt.pm
1 package App::GroupSecret::Crypt;
2 # ABSTRACT: Collection of crypto-related subroutines
3
4 use warnings;
5 use strict;
6
7 our $VERSION = '9999.999'; # VERSION
8
9 use Exporter qw(import);
10 use File::Temp;
11 use IPC::Open2;
12 use namespace::clean -except => [qw(import)];
13
14 our @EXPORT_OK = qw(
15 generate_secure_random_bytes
16 read_openssh_public_key
17 read_openssh_key_fingerprint
18 decrypt_rsa
19 encrypt_rsa
20 decrypt_aes_256_cbc
21 encrypt_aes_256_cbc
22 );
23
24 sub _croak { require Carp; Carp::croak(@_) }
25 sub _usage { _croak("Usage: @_\n") }
26
27 =func generate_secure_random_bytes
28
29 $bytes = generate_secure_random_bytes($num_bytes);
30
31 Get a certain number of secure random bytes.
32
33 =cut
34
35 sub generate_secure_random_bytes {
36 my $size = shift or _usage(q{generate_secure_random_bytes($num_bytes)});
37
38 my @cmd = (qw{openssl rand}, $size);
39
40 my $out;
41 my $pid = open2($out, undef, @cmd);
42
43 waitpid($pid, 0);
44 my $status = $?;
45
46 my $exit_code = $status >> 8;
47 _croak 'Failed to generate secure random bytes' if $exit_code != 0;
48
49 return do { local $/; <$out> };
50 }
51
52 =func read_openssh_public_key
53
54 $pem_public_key = read_openssh_public_key($public_key_filepath);
55
56 Read a RFC4716 (SSH2) public key from a file, converting it to PKCS8 (PEM).
57
58 =cut
59
60 sub read_openssh_public_key {
61 my $filepath = shift or _usage(q{read_openssh_public_key($filepath)});
62
63 my @cmd = (qw{ssh-keygen -e -m PKCS8 -f}, $filepath);
64
65 my $out;
66 my $pid = open2($out, undef, @cmd);
67
68 waitpid($pid, 0);
69 my $status = $?;
70
71 my $exit_code = $status >> 8;
72 _croak 'Failed to read OpenSSH public key' if $exit_code != 0;
73
74 return do { local $/; <$out> };
75 }
76
77 =func read_openssh_key_fingerprint
78
79 $fingerprint = read_openssh_key_fingerprint($filepath);
80
81 Get the fingerprint of an OpenSSH private or public key.
82
83 =cut
84
85 sub read_openssh_key_fingerprint {
86 my $filepath = shift or _usage(q{read_openssh_key_fingerprint($filepath)});
87
88 my @cmd = (qw{ssh-keygen -l -E md5 -f}, $filepath);
89
90 my $out;
91 my $pid = open2($out, undef, @cmd);
92
93 waitpid($pid, 0);
94 my $status = $?;
95
96 my $exit_code = $status >> 8;
97 _croak 'Failed to read SSH2 key fingerprint' if $exit_code != 0;
98
99 my $line = do { local $/; <$out> };
100 chomp $line;
101
102 my ($bits, $fingerprint, $comment, $type) = $line =~ m!^(\d+) MD5:([^ ]+) (.*) \(([^\)]+)\)$!;
103
104 $fingerprint =~ s/://g;
105
106 return {
107 bits => $bits,
108 fingerprint => $fingerprint,
109 comment => $comment,
110 type => lc($type),
111 };
112 }
113
114 =func decrypt_rsa
115
116 $plaintext = decrypt_rsa($ciphertext_filepath, $private_key_filepath);
117 $plaintext = decrypt_rsa(\$ciphertext, $private_key_filepath);
118 decrypt_rsa($ciphertext_filepath, $private_key_filepath, $plaintext_filepath);
119 decrypt_rsa(\$ciphertext, $private_key_filepath, $plaintext_filepath);
120
121 Do RSA decryption. Turn ciphertext into plaintext.
122
123 =cut
124
125 sub decrypt_rsa {
126 my $filepath = shift or _usage(q{decrypt_rsa($filepath, $keypath)});
127 my $privkey = shift or _usage(q{decrypt_rsa($filepath, $keypath)});
128 my $outfile = shift;
129
130 my $temp;
131 if (ref $filepath eq 'SCALAR') {
132 $temp = File::Temp->new(UNLINK => 1);
133 print $temp $$filepath;
134 close $temp;
135 $filepath = $temp->filename;
136 }
137
138 my @cmd = (qw{openssl rsautl -decrypt -oaep -in}, $filepath, '-inkey', $privkey);
139 push @cmd, ('-out', $outfile) if $outfile;
140
141 my $out;
142 my $pid = open2($out, undef, @cmd);
143
144 waitpid($pid, 0);
145 my $status = $?;
146
147 my $exit_code = $status >> 8;
148 _croak 'Failed to decrypt ciphertext' if $exit_code != 0;
149
150 return do { local $/; <$out> };
151 }
152
153 =func encrypt_rsa
154
155 $ciphertext = decrypt_rsa($plaintext_filepath, $public_key_filepath);
156 $ciphertext = decrypt_rsa(\$plaintext, $public_key_filepath);
157 decrypt_rsa($plaintext_filepath, $public_key_filepath, $ciphertext_filepath);
158 decrypt_rsa(\$plaintext, $public_key_filepath, $ciphertext_filepath);
159
160 Do RSA encryption. Turn plaintext into ciphertext.
161
162 =cut
163
164 sub encrypt_rsa {
165 my $filepath = shift or _usage(q{encrypt_rsa($filepath, $keypath)});
166 my $pubkey = shift or _usage(q{encrypt_rsa($filepath, $keypath)});
167 my $outfile = shift;
168
169 my $temp1;
170 if (ref $filepath eq 'SCALAR') {
171 $temp1 = File::Temp->new(UNLINK => 1);
172 print $temp1 $$filepath;
173 close $temp1;
174 $filepath = $temp1->filename;
175 }
176
177 my $key = read_openssh_public_key($pubkey);
178
179 my $temp2 = File::Temp->new(UNLINK => 1);
180 print $temp2 $key;
181 close $temp2;
182 my $keypath = $temp2->filename;
183
184 my @cmd = (qw{openssl rsautl -encrypt -oaep -pubin -inkey}, $keypath, '-in', $filepath);
185 push @cmd, ('-out', $outfile) if $outfile;
186
187 my $out;
188 my $pid = open2($out, undef, @cmd);
189
190 waitpid($pid, 0);
191 my $status = $?;
192
193 my $exit_code = $status >> 8;
194 _croak 'Failed to encrypt plaintext' if $exit_code != 0;
195
196 return do { local $/; <$out> };
197 }
198
199 =func decrypt_aes_256_cbc
200
201 $plaintext = decrypt_aes_256_cbc($ciphertext_filepath, $secret);
202 $plaintext = decrypt_aes_256_cbc(\$ciphertext, $secret);
203 decrypt_aes_256_cbc($ciphertext_filepath, $secret, $plaintext_filepath);
204 decrypt_aes_256_cbc(\$ciphertext, $secret, $plaintext_filepath);
205
206 Do symmetric decryption. Turn ciphertext into plaintext.
207
208 =cut
209
210 sub decrypt_aes_256_cbc {
211 my $filepath = shift or _usage(q{decrypt_aes_256_cbc($ciphertext, $secret)});
212 my $secret = shift or _usage(q{decrypt_aes_256_cbc($ciphertext, $secret)});
213 my $outfile = shift;
214
215 my $temp;
216 if (ref $filepath eq 'SCALAR') {
217 $temp = File::Temp->new(UNLINK => 1);
218 print $temp $$filepath;
219 close $temp;
220 $filepath = $temp->filename;
221 }
222
223 my @cmd = (qw{openssl aes-256-cbc -d -pass stdin -md sha256 -in}, $filepath);
224 push @cmd, ('-out', $outfile) if $outfile;
225
226 my ($in, $out);
227 my $pid = open2($out, $in, @cmd);
228
229 print $in $secret;
230 close($in);
231
232 waitpid($pid, 0);
233 my $status = $?;
234
235 my $exit_code = $status >> 8;
236 _croak 'Failed to decrypt ciphertext' if $exit_code != 0;
237
238 return do { local $/; <$out> };
239 }
240
241 =func encrypt_aes_256_cbc
242
243 $ciphertext = encrypt_aes_256_cbc($plaintext_filepath, $secret);
244 $ciphertext = encrypt_aes_256_cbc(\$plaintext, $secret);
245 encrypt_aes_256_cbc($plaintext_filepath, $secret, $ciphertext_filepath);
246 encrypt_aes_256_cbc(\$plaintext, $secret, $ciphertext_filepath);
247
248 Do symmetric encryption. Turn plaintext into ciphertext.
249
250 =cut
251
252 sub encrypt_aes_256_cbc {
253 my $filepath = shift or _usage(q{encrypt_aes_256_cbc($plaintext, $secret)});
254 my $secret = shift or _usage(q{encrypt_aes_256_cbc($plaintext, $secret)});
255 my $outfile = shift;
256
257 my $temp;
258 if (ref $filepath eq 'SCALAR') {
259 $temp = File::Temp->new(UNLINK => 1);
260 print $temp $$filepath;
261 close $temp;
262 $filepath = $temp->filename;
263 }
264
265 my @cmd = (qw{openssl aes-256-cbc -pass stdin -md sha256 -in}, $filepath);
266 push @cmd, ('-out', $outfile) if $outfile;
267
268 my ($in, $out);
269 my $pid = open2($out, $in, @cmd);
270
271 print $in $secret;
272 close($in);
273
274 waitpid($pid, 0);
275 my $status = $?;
276
277 my $exit_code = $status >> 8;
278 _croak 'Failed to encrypt plaintext' if $exit_code != 0;
279
280 return do { local $/; <$out> };
281 }
282
283 1;
This page took 0.047027 seconds and 4 git commands to generate.