File Coverage

lib/Class/Usul/Crypt/Util.pm
Criterion Covered Total %
statement 36 36 100.0
branch 6 6 100.0
condition n/a
subroutine 12 12 100.0
pod 4 4 100.0
total 58 58 100.0


line stmt bran cond sub pod time code
1             package Class::Usul::Crypt::Util;
2              
3 3     3   572 use strict;
  3         8  
  3         90  
4 3     3   18 use warnings;
  3         10  
  3         105  
5              
6 3     3   19 use Class::Usul::Constants qw( FALSE NUL TRUE );
  3         38  
  3         23  
7 3     3   2735 use Class::Usul::Crypt qw( decrypt default_cipher encrypt );
  3         11  
  3         251  
8 3     3   21 use Class::Usul::Functions qw( merge_attributes throw );
  3         8  
  3         24  
9 3     3   3227 use Exporter 5.57 qw( import );
  3         55  
  3         99  
10 3     3   18 use File::DataClass::IO;
  3         7  
  3         35  
11 3     3   345 use Try::Tiny;
  3         8  
  3         2011  
12              
13             our @EXPORT_OK = qw( decrypt_from_config encrypt_for_config
14             get_cipher is_encrypted );
15              
16             my $_args_cache = {};
17              
18             # Private functions
19             my $_extract_crypt_params = sub { # Returns cipher and encrypted text
20             # A single scalar arg not matching the pattern is just a cipher
21             # It really is better this way round. Leave it alone
22             return $_[ 0 ] && $_[ 0 ] =~ m{ \A [{] (.+) [}] (.*) \z }mx
23             ? ($1, $2) : $_[ 0 ] ? ($_[ 0 ]) : (default_cipher, $_[ 0 ]);
24             };
25              
26             my $_get_crypt_args = sub { # Sets cipher, salt, and seed keys in args hash
27             my ($config, $cipher) = @_; my $params = {};
28              
29             # Works if config is an object or a hash
30             merge_attributes $params, $config,
31             [ qw( ctrldir prefix read_secure salt seed seed_file ) ];
32              
33             my $args = { cipher => $cipher,
34             salt => $params->{salt} // $params->{prefix} // NUL };
35             my $file = $params->{seed_file} // $params->{prefix} // 'seed';
36              
37             if ($params->{seed}) { $args->{seed} = $params->{seed} }
38             elsif (defined $_args_cache->{ $file }) {
39             $args->{seed} = $_args_cache->{ $file };
40             }
41             elsif ($params->{read_secure}) {
42             my $cmd = $params->{read_secure}." ${file}";
43              
44             try { $args->{seed} = $_args_cache->{ $file } = qx( $cmd ) }
45             catch { throw "Reading secure file ${file}: ${_}" }
46             }
47             else {
48             my $path = io $file;
49              
50             $path->exists and ($path->stat->{mode} & 0777) == 0600
51             and $args->{seed} = $_args_cache->{ $file } = $path->all;
52              
53             not $args->{seed}
54             and $path = io( [ $params->{ctrldir} // NUL, "${file}.key" ] )
55             and $path->exists and ($path->stat->{mode} & 0777) == 0600
56             and $args->{seed} = $_args_cache->{ $file } = $path->all;
57             }
58              
59             return $args;
60             };
61              
62             # Public functions
63             sub decrypt_from_config ($$) {
64 3     3 1 10 my ($config, $encrypted) = @_;
65              
66 3         12 my ($cipher, $cipher_text) = $_extract_crypt_params->( $encrypted );
67 3         12 my $args = $_get_crypt_args->( $config, $cipher );
68              
69 3 100       613 return $cipher_text ? decrypt $args, $cipher_text : $encrypted;
70             }
71              
72             sub encrypt_for_config ($$;$) {
73 2     2 1 4 my ($config, $plain_text, $encrypted) = @_;
74              
75 2 100       10 $plain_text or return $plain_text;
76              
77 1         3 my ($cipher) = $_extract_crypt_params->( $encrypted );
78 1         3 my $args = $_get_crypt_args->( $config, $cipher );
79              
80 1         1071 return "{${cipher}}".(encrypt $args, $plain_text);
81             }
82              
83             sub get_cipher ($) {
84 1     1 1 5 my ($cipher) = $_extract_crypt_params->( $_[ 0 ] ); return $cipher;
  1         5  
85             }
86              
87             sub is_encrypted ($) {
88 2 100   2 1 1164 return $_[ 0 ] =~ m{ \A [{] .+ [}] .* \z }mx ? TRUE : FALSE;
89             }
90              
91             1;
92              
93             __END__
94              
95             =pod
96              
97             =encoding utf-8
98              
99             =head1 Name
100              
101             Class::Usul::Crypt::Util - Decrypts / encrypts passwords from / to configuration files
102              
103             =head1 Synopsis
104              
105             use Class::Usul::Crypt::Util qw(decrypt_from_config);
106              
107             $password = decrypt_from_config( $encrypted_value_from_file );
108              
109             =head1 Description
110              
111             Decrypts/Encrypts password from/to configuration files
112              
113             =head1 Configuration and Environment
114              
115             Implements a functional interface
116              
117             =head1 Subroutines/Functions
118              
119             =head2 decrypt_from_config
120              
121             $plain_text = decrypt_from_config( $params, $password );
122              
123             Strips the C<{Twofish2}> prefix and then decrypts the password
124              
125             =head2 encrypt_for_config
126              
127             $encrypted_value = encrypt_for_config( $params, $plain_text );
128              
129             Returns the encrypted value of the plain value prefixed with C<{Twofish2}>
130             for storage in a configuration file
131              
132             =head2 get_cipher
133              
134             $cipher = get_cipher( $encrypted_value );
135              
136             Returns the name of the cipher used to encrypt the value
137              
138             =head2 is_encrypted
139              
140             $bool = is_encrypted( $password_or_encrypted_value );
141              
142             Return true if the passed argument matches the pattern for an
143             encrypted value
144              
145             =head2 __extract_crypt_params
146              
147             ($cipher, $password) = __extract_crypt_params( $encrypted_value );
148              
149             Extracts the cipher name and the encrypted password from the value stored
150             in the configuration file. Returns the default cipher and null if the
151             encrypted value does not match the proper pattern. The default cipher is
152             specified by the L<default cipher|Class::Usul::Crypt/default_cipher> function
153              
154             =head2 __get_crypt_args
155              
156             \%crypt_args = __get_crpyt_args( $params, $cipher );
157              
158             Returns the argument hash ref passed to L<Class::Usul::Crypt/encrypt>
159             and L<Class::Usul::Crypt/decrypt>
160              
161             =head1 Diagnostics
162              
163             None
164              
165             =head1 Dependencies
166              
167             =over 3
168              
169             =item L<Class::Usul>
170              
171             =item L<Try::Tiny>
172              
173             =item L<Exporter>
174              
175             =back
176              
177             =head1 Incompatibilities
178              
179             There are no known incompatibilities in this module
180              
181             =head1 Bugs and Limitations
182              
183             There are no known bugs in this module.
184             Please report problems to the address below.
185             Patches are welcome
186              
187             =head1 Acknowledgements
188              
189             Larry Wall - For the Perl programming language
190              
191             =head1 Author
192              
193             Peter Flanigan, C<< <pjfl@cpan.org> >>
194              
195             =head1 License and Copyright
196              
197             Copyright (c) 2017 Peter Flanigan. All rights reserved
198              
199             This program is free software; you can redistribute it and/or modify it
200             under the same terms as Perl itself. See L<perlartistic>
201              
202             This program is distributed in the hope that it will be useful,
203             but WITHOUT WARRANTY; without even the implied warranty of
204             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
205              
206             =cut
207              
208             # Local Variables:
209             # mode: perl
210             # tab-width: 3
211             # End: