File Coverage

blib/lib/Crypt/PBE/CLI.pm
Criterion Covered Total %
statement 74 118 62.7
branch 26 54 48.1
condition 2 5 40.0
subroutine 13 16 81.2
pod 0 6 0.0
total 115 199 57.7


line stmt bran cond sub pod time code
1             package Crypt::PBE::CLI;
2              
3 1     1   74374 use strict;
  1         11  
  1         33  
4 1     1   6 use warnings;
  1         1  
  1         33  
5 1     1   644 use utf8;
  1         14  
  1         6  
6              
7 1     1   615 use Term::ReadKey;
  1         2252  
  1         131  
8 1     1   558 use MIME::Base64;
  1         711  
  1         68  
9 1     1   849 use Getopt::Long qw( GetOptionsFromArray :config gnu_compat );
  1         13586  
  1         11  
10 1     1   927 use Pod::Usage;
  1         53421  
  1         192  
11 1     1   11 use Carp;
  1         3  
  1         72  
12              
13 1     1   588 use Crypt::PBE::PBES1;
  1         4  
  1         58  
14 1     1   602 use Crypt::PBE::PBES2;
  1         5  
  1         1592  
15              
16             our $VERSION = '0.102';
17              
18             my @cli_options = qw(
19             help|h
20             man
21             version
22             verbose|v
23             null|0
24              
25             input=s
26             password=s
27             count=i
28             hash=s
29             hmac=s
30             encryption=s
31             digest=s
32              
33             scheme=s
34             algorithm=s
35              
36             encrypt
37             decrypt
38             list-algorithms
39             );
40              
41             my $pbe_mapping = {
42             'PBEWithMD2AndDES' => { scheme => 'pbes1', hash => 'md2', encryption => 'des' },
43             'PBEWithMD5AndDES' => { scheme => 'pbes1', hash => 'md5', encryption => 'des' },
44             'PBEWithSHA1AndDES' => { scheme => 'pbes1', hash => 'sha1', encryption => 'des' },
45              
46             'PBEWithHmacSHA1AndAES_128' => { scheme => 'pbes2', hmac => 'hmac-sha1', encryption => 'aes-128' },
47             'PBEWithHmacSHA1AndAES_192' => { scheme => 'pbes2', hmac => 'hmac-sha1', encryption => 'aes-192' },
48             'PBEWithHmacSHA1AndAES_256' => { scheme => 'pbes2', hmac => 'hmac-sha1', encryption => 'aes-256' },
49             'PBEWithHmacSHA224AndAES_128' => { scheme => 'pbes2', hmac => 'hmac-sha224', encryption => 'aes-128' },
50             'PBEWithHmacSHA224AndAES_192' => { scheme => 'pbes2', hmac => 'hmac-sha224', encryption => 'aes-192' },
51             'PBEWithHmacSHA224AndAES_256' => { scheme => 'pbes2', hmac => 'hmac-sha224', encryption => 'aes-256' },
52             'PBEWithHmacSHA256AndAES_128' => { scheme => 'pbes2', hmac => 'hmac-sha256', encryption => 'aes-128' },
53             'PBEWithHmacSHA256AndAES_192' => { scheme => 'pbes2', hmac => 'hmac-sha256', encryption => 'aes-192' },
54             'PBEWithHmacSHA256AndAES_256' => { scheme => 'pbes2', hmac => 'hmac-sha256', encryption => 'aes-256' },
55             'PBEWithHmacSHA384AndAES_128' => { scheme => 'pbes2', hmac => 'hmac-sha384', encryption => 'aes-128' },
56             'PBEWithHmacSHA384AndAES_192' => { scheme => 'pbes2', hmac => 'hmac-sha384', encryption => 'aes-192' },
57             'PBEWithHmacSHA384AndAES_256' => { scheme => 'pbes2', hmac => 'hmac-sha384', encryption => 'aes-256' },
58             'PBEWithHmacSHA512AndAES_128' => { scheme => 'pbes2', hmac => 'hmac-sha512', encryption => 'aes-128' },
59             'PBEWithHmacSHA512AndAES_192' => { scheme => 'pbes2', hmac => 'hmac-sha512', encryption => 'aes-192' },
60             'PBEWithHmacSHA512AndAES_256' => { scheme => 'pbes2', hmac => 'hmac-sha512', encryption => 'aes-256' },
61             };
62              
63             sub cli_error {
64 0     0 0 0 my ($error) = @_;
65 0         0 $error =~ s/ at .* line \d+.*//;
66 0         0 print "ERROR: $error\n";
67 0         0 exit 255;
68             }
69              
70             sub cli_readkey {
71              
72 0     0 0 0 my ($message) = @_;
73              
74 0         0 my $value = undef;
75              
76 0         0 print $message;
77 0         0 ReadMode 'noecho';
78              
79 0         0 $value = ReadLine 0;
80 0         0 chomp $value;
81              
82 0         0 ReadMode 'normal';
83 0         0 print "\n";
84              
85 0         0 return $value;
86              
87             }
88              
89             sub show_version {
90              
91 1     1 0 654 require Crypt::PBE;
92 1         9 require Crypt::CBC;
93 1         616 require Crypt::DES;
94 1         1033 require Crypt::OpenSSL::AES;
95              
96 1         27 print <<"EOF";
97             pkcs5-tool v$VERSION
98              
99             CORE
100             Perl ($^V, $^O)
101             Crypt::PBE ($Crypt::PBE::VERSION)
102              
103             CRYPT MODULES
104             Crypt::CBC ($Crypt::CBC::VERSION)
105             Crypt::DES ($Crypt::DES::VERSION)
106             Crypt::OpenSSL::AES ($Crypt::OpenSSL::AES::VERSION)
107              
108             DIGEST MODULES
109             Digest::MD2 ($Digest::MD2::VERSION)
110             Digest::MD5 ($Digest::MD5::VERSION)
111             Digest::SHA ($Digest::SHA::VERSION)
112              
113             EOF
114              
115 1         7 return 0;
116              
117             }
118              
119             sub file_read {
120 0     0 0 0 my ($filename) = @_;
121              
122 0 0       0 open( my $fh, '<', $filename ) or die "Can't open file: $!";
123              
124 0         0 my $content = do { local $/; <$fh> };
  0         0  
  0         0  
125 0         0 chomp($content);
126              
127 0         0 close $fh;
128              
129 0         0 return $content;
130             }
131              
132             sub parse_value {
133              
134 16     16 0 36 my ($value) = @_;
135              
136 16 50       42 return if ( !$value );
137              
138 16 100       80 if ( $value =~ /^(file|env)\:(.*)/ ) {
139              
140 6 50       32 if ( $1 eq 'file' ) {
141 0 0       0 return cli_error('File not found') if ( !-f $2 );
142 0         0 return file_read($2);
143             }
144              
145 6 50       23 if ( $1 eq 'env' ) {
146 6 50       28 return cli_error('Environment not found') if ( !defined $ENV{$2} );
147 6         26 return $ENV{$2};
148             }
149              
150             }
151              
152 10         33 return $value;
153              
154             }
155              
156             sub run {
157              
158 10     10 0 6151 my ( $class, $arguments ) = @_;
159              
160 10         31 my $options = {};
161              
162 10 50       88 GetOptionsFromArray( $arguments, $options, @cli_options ) or pod2usage( -verbose => 0 );
163              
164 10   50     15142 $options->{count} ||= 1_000;
165              
166             # Detect input from STDIN
167 10 50 33     334 if ( -p STDIN || -f STDIN ) {
168 0         0 $options->{input} = do { local $/; };
  0         0  
  0         0  
169             }
170              
171 10 50       70 pod2usage( -exitstatus => 0, -verbose => 2 ) if ( $options->{man} );
172 10 50       36 pod2usage( -exitstatus => 0, -verbose => 0 ) if ( $options->{help} );
173              
174 10 100       50 return show_version if ( $options->{version} );
175              
176 9 100       32 if ( $options->{'list-algorithms'} ) {
177 1         3 print join( "\n", sort keys %{$pbe_mapping} ) . "\n";
  1         22  
178 1         7 return 0;
179             }
180              
181 8 50       27 pod2usage( -exitstatus => 1, -verbose => 0 ) if ( !$options->{algorithm} );
182 8 50       27 pod2usage( -exitstatus => 1, -verbose => 0 ) if ( !$options->{input} );
183              
184 8         43 my $pbe_params = $pbe_mapping->{ $options->{algorithm} };
185              
186 8 50       28 if ( !$pbe_params ) {
187 0         0 return cli_error 'Invalid algorithm';
188             }
189              
190             # Read password and input data from file or env variable
191 8         39 $options->{password} = parse_value( $options->{password} );
192 8         27 $options->{input} = parse_value( $options->{input} );
193              
194 8 50       31 if ( !$options->{password} ) {
195              
196 0         0 my $input_password = cli_readkey('Password: ');
197              
198 0 0       0 if ( $options->{encrypt} ) {
199 0         0 my $test_password = cli_readkey('Re-type password: ');
200              
201 0 0       0 if ( $input_password ne $test_password ) {
202 0         0 return cli_error 'Password mismatch';
203             }
204             }
205              
206 0         0 $options->{password} = $input_password;
207              
208             }
209              
210 8         23 my $pbes = undef;
211              
212 8 50       33 if ( $pbe_params->{scheme} eq 'pbes1' ) {
213              
214 0 0       0 if ( $options->{verbose} ) {
215 0         0 printf STDERR "Scheme: %s\n", $pbe_params->{scheme};
216 0         0 printf STDERR "Hash: %s\n", $pbe_params->{hash};
217 0         0 printf STDERR "Encryption: %s\n", $pbe_params->{encryption};
218 0         0 printf STDERR "Count: %s\n", $options->{count};
219             }
220              
221             $pbes = Crypt::PBE::PBES1->new(
222             password => $options->{password},
223             count => $options->{count},
224             hash => $pbe_params->{hash},
225             encryption => $pbe_params->{encryption},
226 0         0 );
227              
228             }
229              
230 8 50       30 if ( $pbe_params->{scheme} eq 'pbes2' ) {
231              
232 8 50       31 if ( $options->{verbose} ) {
233 0         0 printf STDERR "Scheme: %s\n", $pbe_params->{scheme};
234 0         0 printf STDERR "HMAC: %s\n", $pbe_params->{hmac};
235 0         0 printf STDERR "Encryption: %s\n", $pbe_params->{encryption};
236 0         0 printf STDERR "Count: %s\n", $options->{count};
237 0 0       0 printf STDERR "Password: %s\n", $options->{password} if ( $options->{password} =~ /^(file|env)\:/ );
238             }
239              
240             $pbes = Crypt::PBE::PBES2->new(
241             password => $options->{password},
242             count => $options->{count},
243             hmac => $pbe_params->{hmac},
244             encryption => $pbe_params->{encryption},
245 8         135 );
246              
247             }
248              
249 8 100       31 if ( $options->{encrypt} ) {
250 4         23 print encode_base64( $pbes->encrypt( $options->{input} ), '' );
251             }
252              
253 8 100       35 if ( $options->{decrypt} ) {
254 4         35 print $pbes->decrypt( decode_base64 $options->{input} );
255             }
256              
257 8 50       4481 print $options->{null} ? "\0" : "\n";
258              
259 8         84 return 0;
260              
261             }
262              
263             1;
264              
265             =encoding utf-8
266              
267             =head1 NAME
268              
269             Crypt::PBE::CLI - PKCS#5 Password-Based Encryption Command Line Interface
270              
271             =head1 SYNOPSIS
272              
273             use Crypt::PBE::CLI qw(run);
274              
275             run(\@ARGV);
276              
277             =head1 DESCRIPTION
278              
279             PKCS#5 Password-Based Encryption Command Line Interface module for C.
280              
281             =head1 AUTHOR
282              
283             L
284              
285             =head1 COPYRIGHT AND LICENSE
286              
287             Copyright © 2018-2021 L
288              
289             You may use and distribute this module according to the same terms
290             that Perl is distributed under.