File Coverage

blib/lib/Crypt/OpenPGP/Ciphertext.pm
Criterion Covered Total %
statement 76 81 93.8
branch 20 26 76.9
condition 6 11 54.5
subroutine 13 14 92.8
pod 4 7 57.1
total 119 139 85.6


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::Ciphertext;
2 2     2   9 use strict;
  2         2  
  2         65  
3              
4 2     2   11 use Crypt::OpenPGP::Util;
  2         3  
  2         85  
5 2     2   11 use Crypt::OpenPGP::Cipher;
  2         2  
  2         56  
6 2         16 use Crypt::OpenPGP::Constants qw( DEFAULT_CIPHER
7             PGP_PKT_ENCRYPTED
8 2     2   9 PGP_PKT_ENCRYPTED_MDC );
  2         4  
9 2     2   7 use Crypt::OpenPGP::ErrorHandler;
  2         8  
  2         45  
10 2     2   9 use base qw( Crypt::OpenPGP::ErrorHandler );
  2         3  
  2         166  
11              
12 2     2   8 use constant MDC_TRAILER => chr(0xd3) . chr(0x14);
  2         2  
  2         1486  
13              
14 23 100   23 0 135 sub pkt_type { $_[0]->{is_mdc} ? PGP_PKT_ENCRYPTED_MDC : PGP_PKT_ENCRYPTED }
15              
16             sub new {
17 47     47 1 83 my $class = shift;
18 47         101 my $enc = bless { }, $class;
19 47         195 $enc->init(@_);
20             }
21              
22             sub init {
23 47     47 0 117 my $enc = shift;
24 47         172 my %param = @_;
25 47 100 66     265 if ((my $key = $param{SymKey}) && (my $data = $param{Data})) {
26 23   100     135 $enc->{is_mdc} = $param{MDC} || 0;
27 23         52 $enc->{version} = 1;
28 23   33     76 my $alg = $param{Cipher} || DEFAULT_CIPHER;
29 23         115 my $cipher = Crypt::OpenPGP::Cipher->new($alg, $key);
30 23         81 my $bs = $cipher->blocksize;
31 23         104 my $pad = Crypt::OpenPGP::Util::get_random_bytes($bs);
32 23         1389 $pad .= substr $pad, -2, 2;
33 23         154 $enc->{ciphertext} = $cipher->encrypt($pad);
34 23 100       152 $cipher->sync unless $enc->{is_mdc};
35 23         83 $enc->{ciphertext} .= $cipher->encrypt($data);
36              
37 23 100       246 if ($enc->{is_mdc}) {
38 1         492 require Crypt::OpenPGP::MDC;
39 1         6 my $mdc = Crypt::OpenPGP::MDC->new(
40             Data => $pad . $data . MDC_TRAILER );
41 1         7 my $mdc_buf = Crypt::OpenPGP::PacketFactory->save($mdc);
42 1         3 $enc->{ciphertext} .= $cipher->encrypt($mdc_buf);
43             }
44             }
45 47         143 $enc;
46             }
47              
48             sub parse {
49 24     24 1 55 my $class = shift;
50 24         41 my($buf, $is_mdc) = @_;
51 24         87 my $enc = $class->new;
52 24         74 $enc->{is_mdc} = $is_mdc;
53 24 100       71 if ($is_mdc) {
54 2         10 $enc->{version} = $buf->get_int8;
55             }
56 24         103 $enc->{ciphertext} = $buf->get_bytes($buf->length - $buf->offset);
57 24         361 $enc;
58             }
59              
60             sub save {
61 23     23 1 42 my $enc = shift;
62 23         95 my $buf = Crypt::OpenPGP::Buffer->new;
63 23 100       196 if ($enc->{is_mdc}) {
64 1         3 $buf->put_int8($enc->{version});
65             }
66 23         77 $buf->put_bytes($enc->{ciphertext});
67 23         153 $buf->bytes;
68             }
69              
70             sub display {
71 0     0 0 0 my $enc = shift;
72             my $str = ":encrypted data packet:\n" .
73 0         0 " length: " . length($enc->{ciphertext}) . "\n";
74 0 0       0 if ($enc->{is_mdc}) {
75 0         0 $str .= " is_mdc: $enc->{version}\n";
76             }
77 0         0 $str;
78             }
79              
80             sub decrypt {
81 23     23 1 43 my $enc = shift;
82 23         40 my($key, $sym_alg) = @_;
83 23 50       128 my $cipher = Crypt::OpenPGP::Cipher->new($sym_alg, $key) or
84             return $enc->error( Crypt::OpenPGP::Cipher->errstr );
85 23         76 my $padlen = $cipher->blocksize + 2;
86             my $pt = $enc->{prefix} =
87 23         154 $cipher->decrypt(substr $enc->{ciphertext}, 0, $padlen);
88 23 50       114 return $enc->error("Bad checksum")
89             unless substr($pt, -4, 2) eq substr($pt, -2, 2);
90 23 100       139 $cipher->sync unless $enc->{is_mdc};
91 23         89 $pt = $cipher->decrypt(substr $enc->{ciphertext}, $padlen);
92 23 100       92 if ($enc->{is_mdc}) {
93 1         12 my $mdc_buf = Crypt::OpenPGP::Buffer->new_with_init(substr $pt,-22,22);
94 1         33 $pt = substr $pt, 0, -22;
95 1         7 my $mdc = Crypt::OpenPGP::PacketFactory->parse($mdc_buf);
96 1 50 33     10 return $enc->error("Encrypted MDC packet without MDC")
97             unless $mdc && ref($mdc) eq 'Crypt::OpenPGP::MDC';
98 1         7 require Crypt::OpenPGP::Digest;
99 1         7 my $dgst = Crypt::OpenPGP::Digest->new('SHA1');
100 1         8 my $hash = $dgst->hash($enc->{prefix} . $pt . chr(0xd3) . chr(0x14));
101 1 50       6 return $enc->error("SHA-1 hash of plaintext does not match MDC body")
102             unless $mdc->digest eq $hash;
103             }
104 23         298 $pt;
105             }
106              
107             1;
108             __END__
109              
110             =head1 NAME
111              
112             Crypt::OpenPGP::Ciphertext - Encrypted data packet
113              
114             =head1 SYNOPSIS
115              
116             use Crypt::OpenPGP::Ciphertext;
117              
118             my $key_data = 'f' x 64; ## Not a very good key :)
119              
120             my $ct = Crypt::OpenPGP::Ciphertext->new(
121             Data => "foo bar baz",
122             SymKey => $key_data,
123             );
124             my $serialized = $ct->save;
125              
126             my $buffer = Crypt::OpenPGP::Buffer->new;
127             my $ct2 = Crypt::OpenPGP::Ciphertext->parse( $buffer );
128             my $data = $ct->decrypt( $key_data );
129              
130             =head1 DESCRIPTION
131              
132             I<Crypt::OpenPGP::Ciphertext> implements symmetrically encrypted data
133             packets, providing both encryption and decryption functionality. Both
134             standard encrypted data packets and encrypted-MDC (modification
135             detection code) packets are supported by this class. In the first case,
136             the encryption used in the packets is a variant on standard CFB mode,
137             and is described in the OpenPGP RFC, in section 13.9 (OpenPGP CFB mode).
138             In the second case (encrypted-MDC packets), the encryption is performed
139             in standard CFB mode, without the special resync used in PGP's CFB.
140              
141             =head1 USAGE
142              
143             =head2 Crypt::OpenPGP::Ciphertext->new( %arg )
144              
145             Creates a new symmetrically encrypted data packet object and returns
146             that object. If there are no arguments in I<%arg>, the object is
147             created with an empty data container; this is used, for example, in
148             I<parse> (below), to create an empty packet which is then filled from
149             the data in the buffer.
150              
151             If you wish to initialize a non-empty object, I<%arg> can contain:
152              
153             =over 4
154              
155             =item * Data
156              
157             A block of octets that make up the plaintext data to be encrypted.
158              
159             This argument is required (for a non-empty object).
160              
161             =item * SymKey
162              
163             The symmetric cipher key: a string of octets that make up the key data
164             of the symmetric cipher key. This should be at least long enough for
165             the key length of your chosen cipher (see I<Cipher>, below), or, if
166             you have not specified a cipher, at least 64 bytes (to allow for
167             long cipher key sizes).
168              
169             This argument is required (for a non-empty object).
170              
171             =item * Cipher
172              
173             The name (or ID) of a supported PGP cipher. See I<Crypt::OpenPGP::Cipher>
174             for a list of valid cipher names.
175              
176             This argument is optional; by default I<Crypt::OpenPGP::Cipher> will
177             use C<DES3>.
178              
179             =item * MDC
180              
181             When set to a true value, encrypted texts will use encrypted MDC
182             (modification detection code) packets instead of standard encrypted data
183             packets. These are a newer form of encrypted data packets that
184             are followed by a C<SHA-1> hash of the plaintext data. This prevents
185             attacks that modify the encrypted text by using a message digest to
186             detect changes.
187              
188             By default I<MDC> is set to C<0>, and encrypted texts use standard
189             encrypted data packets. Set it to a true value to turn on MDC packets.
190              
191             =back
192              
193             =head2 $ct->save
194              
195             Returns the block of ciphertext created in I<new> (assuming that you
196             created a non-empty packet by specifying some data; otherwise returns
197             an empty string).
198              
199             =head2 Crypt::OpenPGP::Ciphertext->parse($buffer)
200              
201             Given I<$buffer>, a I<Crypt::OpenPGP::Buffer> object holding (or
202             with offset pointing to) a symmetrically encrypted data packet, returns
203             a new I<Crypt::OpenPGP::Ciphertext> object, initialized with the
204             ciphertext in the buffer.
205              
206             =head2 $ct->decrypt($key, $alg)
207              
208             Decrypts the ciphertext in the I<Crypt::OpenPGP::Ciphertext> object
209             and returns the plaintext. I<$key> is the encryption key, and I<$alg>
210             is the name (or ID) of the I<Crypt::OpenPGP::Cipher> type used to
211             encrypt the message. Obviously you can't just guess at these
212             parameters; this method (along with I<parse>, above) is best used along
213             with the I<Crypt::OpenPGP::SessionKey> object, which holds an encrypted
214             version of the key and cipher algorithm.
215              
216             =head1 AUTHOR & COPYRIGHTS
217              
218             Please see the Crypt::OpenPGP manpage for author, copyright, and
219             license information.
220              
221             =cut