File Coverage

blib/lib/App/Greple/PgpDecryptor.pm
Criterion Covered Total %
statement 22 106 20.7
branch 1 32 3.1
condition n/a
subroutine 7 18 38.8
pod 3 9 33.3
total 33 165 20.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             PgpDecryptor - Module for decrypt PGP data
4              
5             =head1 SYNOPSIS
6              
7             my $pgp = App::Greple::PgpDecryptor->new;
8              
9             =head1 DESCRIPTION
10              
11             =head2 initialize
12              
13             Initialize object.
14              
15             Without parameter, read passphrase from terminal.
16              
17             $pgp->initialize();
18              
19             Provide passphrase string or file descriptor if available.
20              
21             $pgp->initialize({passphrase => passphrase});
22              
23             $pgp->initialize({passphrase_fd => fd});
24              
25             =head2 decrypt
26              
27             Decrypt data. Pass the encrypted data and get the result.
28              
29             $decrypted = $pgp->decript($encrpted);
30              
31             =head2 decrypt_comand
32              
33             Return decrypt command string. You can use this command to decrypt
34             data. Call B after command execution.
35              
36             open(STDIN, '-|') or exec $pgp->decrypt_command;
37              
38             =head2 reset
39              
40             Reset internal status.
41              
42             =cut
43              
44              
45             package App::Greple::PgpDecryptor;
46              
47 1     1   1101 use v5.24;
  1         3  
48 1     1   4 use warnings;
  1         1  
  1         54  
49 1     1   5 use Carp;
  1         1  
  1         492  
50              
51             sub new {
52 0     0 0   my $obj = bless {
53             FH => undef,
54             }, shift;
55 0           $obj;
56             }
57              
58             sub initialize {
59 0     0 1   my $obj = shift;
60 0 0         my $opt = @_ ? shift : {};
61 0           my $passphrase = "";
62              
63 0 0         if (my $fd = $opt->{passphrase_fd}) {
64 0           $obj->fh(_openfh($fd));
65             }
66             else {
67 0 0         if (not defined $obj->fh) {
68 0           $obj->fh(_openfh());
69             }
70 0 0         if (defined $opt->{passphrase}) {
71 0           $passphrase = $opt->{passphrase};
72             } else {
73 0           _readphrase(\$passphrase);
74             }
75 0           $obj->setphrase(\$passphrase);
76              
77             ##
78             ## Destroy data as much as possible
79             ##
80 0           $passphrase =~ s/./\0/g;
81 0           $passphrase = "";
82 0           undef $passphrase;
83             }
84              
85 0           $obj;
86             }
87              
88             sub setphrase {
89 0     0 0   my $obj = shift;
90 0           my $fh = $obj->fh;
91 0           my $passphrase_r = shift;
92              
93 0           $obj->reset;
94 0           $fh->syswrite($$passphrase_r, length($$passphrase_r));
95 0           $obj->reset;
96             }
97              
98             sub getphrase {
99 0     0 0   my $obj = shift;
100 0           my $fh = $obj->fh;
101              
102 0           $obj->reset;
103 0           my $phrase = $fh->getline;
104 0           $obj->reset;
105 0           $phrase;
106             }
107              
108             sub fh {
109 0     0 0   my $obj = shift;
110             @_ ? $obj->{FH} = shift
111 0 0         : $obj->{FH};
112             }
113              
114             sub pgppassfd {
115 0     0 0   my $obj = shift;
116 0           $obj->fh->fileno;
117             }
118              
119             sub decrypt_command {
120 0     0 0   my $obj = shift;
121 0           my $command = "gpg";
122 0           my @option = ( qw(--quiet --batch --decrypt) ,
123             qw(--no-tty --no-mdc-warning) );
124 0           sprintf "$command @option --passphrase-fd %d", $obj->pgppassfd;
125             }
126              
127             sub reset {
128 0     0 1   my $obj = shift;
129 0 0         defined $obj->fh or return;
130 0 0         $obj->fh->sysseek(0, 0) or die $!;
131             }
132              
133             sub _openfh {
134 1     1   6 use Fcntl;
  1         2  
  1         205  
135 1     1   418 use IO::File;
  1         6709  
  1         280  
136              
137 0     0     my $fd = shift;
138 0           my $fh;
139              
140 0 0         if (defined $fd) {
141 0           $fh = IO::Handle->new;
142 0           $fh->fdopen($fd, "w+");
143             } else {
144 0           $fh = new_tmpfile IO::File;
145 0 0         defined $fh or die "new_tmpefile: $!";
146             }
147              
148 0 0         $fh->fcntl(F_SETFD, 0) or die "fcntl F_SETFD failed: $!\n";
149              
150 0           return $fh;
151             }
152              
153             my $noecho;
154             my $restore;
155             BEGIN {
156 1     1   3 ($noecho, $restore) = eval {
157 1         194 require Term::ReadKey;
158 0         0 import Term::ReadKey;
159 0         0 (sub { ReadMode('noecho', @_) }, sub { ReadMode('restore', @_) });
  0         0  
  0         0  
160             };
161 1 50       6 if (not defined $noecho) {
162 1         4 $noecho = sub { system 'stty -echo < /dev/tty' };
  0         0  
163 1         137 $restore = sub { system 'stty echo < /dev/tty' };
  0         0  
164             }
165             }
166              
167             sub _readphrase {
168 0     0     my $passphrase_r = shift;
169              
170 0           print STDERR "Enter PGP Passphrase> ";
171 0 0         open my $tty, '<', '/dev/tty' or die;
172 0           $noecho->($tty);
173 0 0         if (defined (my $pass = ReadLine(0, $tty))) {
174 0           chomp($$passphrase_r = $pass);
175             }
176 0           $restore->($tty);
177 0           close $tty;
178 0           print STDERR "\n";
179              
180 0           $passphrase_r;
181             }
182              
183             sub decrypt {
184 1     1   464 use IPC::Open2;
  1         2983  
  1         196  
185              
186 0     0 1   my $obj = shift;
187 0           my $enc_data = shift;
188              
189 0           $obj->reset;
190              
191 0           my $pid = open2(\*RDRFH, \*WTRFH, $obj->decrypt_command);
192              
193 0 0         if (length($enc_data) <= 1024 * 16) {
194 0           print WTRFH $enc_data;
195             }
196             else {
197 0           my $pid = fork;
198 0 0         croak "process fork failed" if not defined $pid;
199 0 0         if ($pid == 0) {
200 0           print WTRFH $enc_data;
201 0           close WTRFH;
202 0           close RDRFH;
203 0           exit;
204             }
205             }
206 0           close WTRFH;
207 0           my $dec_data = do { local $/ ; };
  0            
  0            
208 0           close RDRFH;
209              
210 0           $dec_data;
211             }
212              
213             1;