File Coverage

blib/lib/Git/Crypt.pm
Criterion Covered Total %
statement 47 97 48.4
branch 5 24 20.8
condition n/a
subroutine 10 14 71.4
pod 0 8 0.0
total 62 143 43.3


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