File Coverage

blib/lib/Git/Crypt.pm
Criterion Covered Total %
statement 50 100 50.0
branch 5 24 20.8
condition n/a
subroutine 11 15 73.3
pod 0 8 0.0
total 66 147 44.9


line stmt bran cond sub pod time code
1             package Git::Crypt;
2 3     3   48563 use strict;
  3         8  
  3         130  
3 3     3   16 use warnings;
  3         3  
  3         84  
4 3     3   1702 use Moo;
  3         38896  
  3         18  
5 3     3   5696 use IO::All;
  3         38869  
  3         22  
6 3     3   2015 use Crypt::CBC;
  3         10300  
  3         79  
7 3     3   1291 use MIME::Base64;
  3         1697  
  3         170  
8 3     3   1266 use Crypt::Blowfish;
  3         2039  
  3         2216  
9              
10             has files => ( is => 'rw', default => sub { [] } );
11             has salt => ( is => 'rw', default => sub { 0 } );
12             has key => ( is => 'rw', default => sub { 0 } );
13             has cipher_name => ( is => 'rw', default => sub { 'Blowfish' } );
14             has cipher => (
15             is => "lazy",
16             default => sub {
17             my $self = shift;
18             Crypt::CBC->new(
19             -key => $self->key,
20             -cipher => $self->cipher_name,
21             -salt => pack( "H16", $self->salt )
22             );
23             }
24             );
25              
26             sub encrypt {
27 2     2 0 17099 my $self = shift;
28 2         5 my $filename = shift;
29 2 50       6 if ( $filename ) {
30 0         0 my $file = $self->locate( $filename );
31 0 0       0 if ( ! $file ) {
32 0         0 print "File $filename not found. Use 'gitcrypt status' to check files added.\n";
33 0         0 return;
34             }
35 0 0       0 if ( $file->{ is_encrypted } ) {
36 0         0 print "File $filename is already encrypted.\n";
37 0         0 return;
38             }
39 0         0 $self->encrypt_file( $file );
40             } else {
41 2         4 for ( @{ $self->files } ) {
  2         19  
42 4 50       196 next if $_->{is_encrypted};
43 4         13 $self->encrypt_file( $_ );
44             }
45             }
46             }
47              
48             sub encrypt_file {
49 4     4 0 4 my $self = shift;
50 4         5 my $file = shift;
51 12         3739 my @lines_crypted =
52 4         16 map { encode_base64( $self->cipher->encrypt($_), "" )."\n"; } io($file->{file})->getlines;
53 4         639 io($file->{file})->print(@lines_crypted);
54 4         1488 $file->{is_encrypted} = 1;
55             }
56              
57             sub decrypt_file {
58 4     4 0 6 my $self = shift;
59 4         4 my $file = shift;
60 4 50       14 return if ! $file->{is_encrypted};
61 12         3125 my @lines_decrypted = map {
62 4         12 my $line = decode_base64 $_;
63 12         283 $self->cipher->decrypt($line);
64             } io($file->{file})->getlines;
65 4         739 io($file->{file})->print(@lines_decrypted);
66 4         1635 $file->{is_encrypted} = 0;
67             }
68              
69             sub locate {
70 0     0 0 0 my $self = shift;
71 0         0 my $filename = shift;
72 0         0 my $file_found;
73 0 0       0 map { ( $_->{file} eq $filename ) ? ( $file_found = $_ ) : () } @{ $self->files };
  0         0  
  0         0  
74 0         0 return $file_found;
75             }
76              
77             sub decrypt {
78 2     2 0 153 my $self = shift;
79 2         4 my $filename = shift;
80 2 50       6 if ( $filename ) {
81 0         0 my $file = $self->locate( $filename );
82 0 0       0 if ( ! $file ) {
83 0         0 print "File $filename not found. Use 'gitcrypt status' to check files added.\n";
84 0         0 return;
85             }
86 0 0       0 if ( ! $file->{ is_encrypted } ) {
87 0         0 print "File $filename is already decrypted.\n";
88 0         0 return;
89             }
90 0         0 $self->decrypt_file( $file );
91             } else {
92 2         4 for ( @{ $self->files } ) {
  2         10  
93 4         178 my $file = $_;
94 4 50       11 next if ! $file->{is_encrypted};
95 4         13 $self->decrypt_file( $file );
96             }
97             }
98             }
99              
100             sub add {
101 0     0 0   my $self = shift;
102 0           my $files = shift;
103 0           my @current_files = map { $_->{ file } } @{ $self->files };
  0            
  0            
104 0           map {
105 0           print $_, "\n";
106 0           my $file = $_;
107 0 0         push @{ $self->files }, {file => $file, is_encrypted => 0 }
  0            
108             if ! grep /^$file$/, @current_files;
109 0           } @{$files};
110             }
111              
112             sub del {
113 0     0 0   my $self = shift;
114 0           my $files = shift;
115 0           map {
116 0           my $file_to_del = $_;
117 0           print $file_to_del, "\n";
118 0           my $i = 0;
119 0           for ( @{ $self->files } ) {
  0            
120 0 0         if ( $_->{ file } eq $file_to_del ) {
121 0           splice @{ $self->files }, $i, 1;
  0            
122 0           last;
123             }
124 0           $i++;
125             }
126 0           } @{$files};
127             }
128              
129             sub config {
130 0     0 0   my $self = shift;
131             return {
132 0           files => $self->files,
133             cipher_name => $self->cipher_name,
134             key => $self->key,
135             salt => $self->salt,
136             };
137             }
138              
139             our $VERSION = 0.05;
140              
141             =encoding utf8
142              
143             =head1 Git::Crypt
144              
145             Git::Crypt - Encrypt/decrypt sensitive files saved on public repos
146              
147             =head1 SYNOPSIS
148              
149             =head2 gitcrypt Command line tool
150            
151             gitcrypt help #show gitcrypt help
152             gitcrypt init #initialize gitcrypt and its config
153             gitcrypt set cipher Blowfish #set a gitcrypt cipher
154             gitcrypt set key "some key" #set a key
155             gitcrypt set salt "some key" #set a salt
156             gitcrypt change key "new key" #change key
157             gitcrypt change salt "a salt" #change salt
158             gitcrypt list #list files
159             gitcrypt status #show status, files
160             gitcrypt add file1-xy lib/file2 #add files in gitcrypt
161             gitcrypt del file1-ab lib/tests #del files in gitcrypt
162             gitcrypt encrypt #set files on encrypted state
163             gitcrypt encrypt file1 file2 #encrypt specific files
164             gitcrypt decrypt #set files on decrypted state
165             gitcrypt decrypt file1 file2 #decrypt specific files
166             gitcrypt precommit #encrypt for precommit hook. Then decrypt
167              
168             =head2 git hooks integration
169              
170             To integrate in git, use git hooks. Use pre-commit to encrypt and add the encrypted file for commit. And post-commit to keep files decrypted.
171              
172             =head3 Standard git hooks
173              
174             =head4 .git/hooks/pre-commit
175              
176             Auto encrypt every file and set them for commit
177              
178             $ file=.git/hooks/pre-commit ; cat < $file && chmod +x $file
179             #!/usr/bin/env perl
180             `gitcrypt precommit`;
181             exit 0;
182             HOOK
183              
184             If used gitcrypt precommit the files will be encrypted during commit and decrypted right after.
185              
186             This mode works good if only some files are encrypted and others are always decrypted. But all must be encrypted for commit.
187              
188             Another option, is to encrypt everything with "gitcrypt encrypt". And use a post-commit hook to decrypt everything [optional].
189              
190             $ file=.git/hooks/pre-commit ; cat < $file && chmod +x $file
191             #!/usr/bin/env perl
192             `gitcrypt encrypt`;
193             exit 0;
194             HOOK
195              
196             =head4 .git/hooks/post-commit
197              
198             Auto decrypt every file after commit executed. * If precommit hook uses gitcrypt precommit, then this is not necessary because "gitcrypt precommit" will decrypt files automatically.
199              
200             $ file=.git/hooks/post-commit; cat < $file ; chmod +x $file
201             #!/usr/bin/env perl
202             `gitcrypt decrypt`;
203             exit 0;
204             HOOK
205              
206             =head2 Provide the cipher instance
207              
208             my $gitcrypt = Git::Crypt->new(
209             files => [
210             {
211             file => 'file1',
212             is_encrypted => 0,
213             },
214             {
215             file => 'file2',
216             is_encrypted => 0,
217             },
218             ],
219             cipher => Crypt::CBC->new(
220             -key => 'a very very very veeeeery very long key',
221             -cipher => 'Blowfish',
222             -salt => pack("H16", "very very very very loooong salt")
223             )
224             );
225              
226             #gitcrypt->add([qw| file1 file2 |]);
227             #gitcrypt->del([qw| file1 file2 |]);
228             #gitcrypt->list;
229             #gitcrypt->key('some key');
230             #gitcrypt->salt('some key');
231             $gitcrypt->crypt; #save files encrypted
232             $gitcrypt->decrypt; #save files decrypted
233              
234             =head2 Provide key, salt and cipher name
235              
236             my $gitcrypt = Git::Crypt->new(
237             files => [
238             {
239             file => 'file1',
240             is_encrypted => 0,
241             },
242             {
243             file => 'file2',
244             is_encrypted => 0,
245             },
246             ],
247             key => 'a very very very veeeeery very long key',
248             cipher_name => 'Blowfish',
249             salt => pack("H16", "very very very very loooong salt")
250             );
251              
252             #gitcrypt->add([qw| file1 file2 |]);
253             #gitcrypt->del([qw| file1 file2 |]);
254             #gitcrypt->list;
255             #gitcrypt->key('some key');
256             #gitcrypt->salt('some key');
257             $gitcrypt->crypt; #save files encrypted
258             $gitcrypt->decrypt; #save files decrypted
259              
260             =head1 DESCRIPTION
261              
262             Git::Crypt can be used to encrypt files before a git commit. That way its possible to upload encrypted files to public repositories.
263             Git::Crypt encrypts line by line to prevent too many unnecessary diffs between encrypted commits.
264              
265             =head1 Diff on encrypted lines
266              
267             Since gitcrypt encrypts line by line, the diff can show wether its a big/small changes in commit.
268              
269             index b1202fc..6c2f3e6 100644
270             --- a/lib/App/Crypted/CreditCard.pm
271             +++ b/lib/App/Crypted/CreditCard.pm
272             @@ -14,25 +14,29 @@ U2FsdGVkX19iyl3d3d3d3UjtB1nkCoDP
273             U2FsdGVkX19iyl3d3d3d3T3HmAPM6gbK
274             U2FsdGVkX19iyl3d3d3d3XVxkdxldN7U
275             U2FsdGVkX19iyl3d3d3d3X49YqpLm/iZ+Y1xf1iU/BDvVc5ipe6ZgQ==
276             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
277             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
278             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
279             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
280             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
281             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
282             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
283             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
284             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
285             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
286             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
287             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
288             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
289             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
290             -U2FsdGVkX19iyl3d3d3d3Ut9C6nKtOzc
291             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
292             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
293             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
294             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
295             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
296             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
297             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
298             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
299             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
300             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
301             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
302             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
303             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
304             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
305             +U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxgEmpWYlLzsk=
306             U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rx2ego3EqSeUk=
307             U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxsn4D6bxrGww=
308             U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rxsn4D6bxrGww=
309             U2FsdGVkX19iyl3d3d3d3Tz+ZxmSz/rx2ego3EqSeUk=
310             U2FsdGVkX19iyl3d3d3d3T3HmAPM6gbK
311             U2FsdGVkX19iyl3d3d3d3XVxkdxldN7U
312             +U2FsdGVkX19iyl3d3d3d3T0XLptL7y1QEx/u7OaQVy2BZbX3SM7f2w==
313             +U2FsdGVkX19iyl3d3d3d3erugupayFMCneYosuHZNbl3jV0GvJ1HDg==
314             +U2FsdGVkX19iyl3d3d3d3T3HmAPM6gbK
315             +U2FsdGVkX19iyl3d3d3d3XVxkdxldN7U
316             U2FsdGVkX19iyl3d3d3d3X/cRzzeMPXS
317              
318             =head1 AUTHOR
319              
320             Hernan Lopes
321             CPAN ID: HERNAN
322             perldelux
323             hernanlopes@gmail.com
324              
325             =head1 GITHUB
326              
327             https://github.com/hernan604/Git-Crypt
328              
329             =head1 COPYRIGHT
330              
331             This program is free software; you can redistribute
332             it and/or modify it under the same terms as Perl itself.
333              
334             The full text of the license can be found in the
335             LICENSE file included with this module.
336              
337              
338             =head1 SEE ALSO
339              
340             perl(1).
341              
342             =cut
343              
344             1;
345              
346             # The preceding line will help the module return a true value
347