File Coverage

blib/lib/Crypt/OpenPGP/S2k.pm
Criterion Covered Total %
statement 92 131 70.2
branch 6 20 30.0
condition 1 3 33.3
subroutine 19 27 70.3
pod 3 5 60.0
total 121 186 65.0


line stmt bran cond sub pod time code
1             package Crypt::OpenPGP::S2k;
2 5     5   25 use strict;
  5         8  
  5         166  
3              
4 5     5   31 use Crypt::OpenPGP::Buffer;
  5         8  
  5         145  
5 5     5   2054 use Crypt::OpenPGP::Digest;
  5         11  
  5         140  
6 5     5   30 use Crypt::OpenPGP::ErrorHandler;
  5         8  
  5         127  
7 5     5   24 use Crypt::OpenPGP::Util;
  5         8  
  5         317  
8 5     5   27 use base qw( Crypt::OpenPGP::ErrorHandler );
  5         8  
  5         398  
9              
10 5     5   30 use vars qw( %TYPES );
  5         8  
  5         2401  
11             %TYPES = (
12             0 => 'Simple',
13             1 => 'Salted',
14             3 => 'Salt_Iter',
15             );
16              
17             sub new {
18 59     59 1 99 my $class = shift;
19 59         98 my $type = shift;
20 59   33     308 $type = $TYPES{ $type } || $type;
21 59 50       174 return $class->error("Invalid type of S2k") unless $type;
22 59         185 my $pkg = join '::', __PACKAGE__, $type;
23 59         195 my $s2k = bless { }, $pkg;
24 59         238 $s2k->init(@_);
25             }
26              
27             sub parse {
28 44     44 1 96 my $class = shift;
29 44         75 my($buf) = @_;
30 44         142 my $id = $buf->get_int8;
31 44         504 my $type = $TYPES{$id};
32 44         170 $class->new($type, $buf);
33             }
34              
35 0     0 0 0 sub init { $_[0] }
36             sub generate {
37 43     43 1 64 my $s2k = shift;
38 43         76 my($passphrase, $keysize) = @_;
39 43         77 my($material, $pass) = ('', 0);
40 43         73 my $hash = $s2k->{hash};
41 43         124 while (length($material) < $keysize) {
42 78         181 my $pad = '' . chr(0) x $pass;
43 78         235 $material .= $s2k->s2k($passphrase, $pad);
44 78         2005 $pass++;
45             }
46 43         239 substr($material, 0, $keysize);
47             }
48             sub set_hash {
49 0     0 0 0 my $s2k = shift;
50 0         0 my($hash_alg) = @_;
51 0 0       0 $s2k->{hash} = ref($hash_alg) ? $hash_alg :
52             Crypt::OpenPGP::Digest->new($hash_alg);
53             }
54              
55             package Crypt::OpenPGP::S2k::Simple;
56 5     5   33 use base qw( Crypt::OpenPGP::S2k );
  5         9  
  5         394  
57              
58 5     5   42 use Crypt::OpenPGP::Constants qw( DEFAULT_DIGEST );
  5         8  
  5         48  
59              
60             sub init {
61 0     0   0 my $s2k = shift;
62 0         0 my($buf) = @_;
63 0 0       0 if ($buf) {
64 0         0 $s2k->{hash_alg} = $buf->get_int8;
65             }
66             else {
67 0         0 $s2k->{hash_alg} = DEFAULT_DIGEST;
68             }
69 0 0       0 if ($s2k->{hash_alg}) {
70 0         0 $s2k->{hash} = Crypt::OpenPGP::Digest->new($s2k->{hash_alg});
71             }
72 0         0 $s2k;
73             }
74              
75 0     0   0 sub s2k { $_[0]->{hash}->hash($_[2] . $_[1]) }
76              
77             sub save {
78 0     0   0 my $s2k = shift;
79 0         0 my $buf = Crypt::OpenPGP::Buffer->new;
80 0         0 $buf->put_int8(1);
81 0         0 $buf->put_int8($s2k->{hash_alg});
82 0         0 $buf->bytes;
83             }
84              
85             package Crypt::OpenPGP::S2k::Salted;
86 5     5   29 use base qw( Crypt::OpenPGP::S2k );
  5         8  
  5         530  
87              
88 5     5   31 use Crypt::OpenPGP::Constants qw( DEFAULT_DIGEST );
  5         8  
  5         29  
89              
90             sub init {
91 0     0   0 my $s2k = shift;
92 0         0 my($buf) = @_;
93 0 0       0 if ($buf) {
94 0         0 $s2k->{hash_alg} = $buf->get_int8;
95 0         0 $s2k->{salt} = $buf->get_bytes(8);
96             }
97             else {
98 0         0 $s2k->{hash_alg} = DEFAULT_DIGEST;
99 0         0 $s2k->{salt} = Crypt::OpenPGP::Util::get_random_bytes(8);
100             }
101 0 0       0 if ($s2k->{hash_alg}) {
102 0         0 $s2k->{hash} = Crypt::OpenPGP::Digest->new($s2k->{hash_alg});
103             }
104 0         0 $s2k;
105             }
106              
107 0     0   0 sub s2k { $_[0]->{hash}->hash($_[0]->{salt} . $_[2] . $_[1]) }
108              
109             sub save {
110 0     0   0 my $s2k = shift;
111 0         0 my $buf = Crypt::OpenPGP::Buffer->new;
112 0         0 $buf->put_int8(2);
113 0         0 $buf->put_int8($s2k->{hash_alg});
114 0         0 $buf->put_bytes($s2k->{salt});
115 0         0 $buf->bytes;
116             }
117              
118             package Crypt::OpenPGP::S2k::Salt_Iter;
119 5     5   34 use base qw( Crypt::OpenPGP::S2k );
  5         8  
  5         401  
120              
121 5     5   32 use Crypt::OpenPGP::Constants qw( DEFAULT_DIGEST );
  5         10  
  5         25  
122              
123             sub init {
124 59     59   97 my $s2k = shift;
125 59         93 my($buf) = @_;
126 59 100       148 if ($buf) {
127 44         142 $s2k->{hash_alg} = $buf->get_int8;
128 44         653 $s2k->{salt} = $buf->get_bytes(8);
129 44         625 $s2k->{count} = $buf->get_int8;
130             }
131             else {
132 15         50 $s2k->{hash_alg} = DEFAULT_DIGEST;
133 15         52 $s2k->{salt} = Crypt::OpenPGP::Util::get_random_bytes(8);
134 15         276400659 $s2k->{count} = 96;
135             }
136 59 50       597 if ($s2k->{hash_alg}) {
137 59         275 $s2k->{hash} = Crypt::OpenPGP::Digest->new($s2k->{hash_alg});
138             }
139 59         241 $s2k;
140             }
141              
142             sub s2k {
143 78     78   117 my $s2k = shift;
144 78         119 my($pass, $pad) = @_;
145 78         116 my $salt = $s2k->{salt};
146 78         206 my $count = (16 + ($s2k->{count} & 15)) << (($s2k->{count} >> 4) + 6);
147 78         99 my $len = length($pass) + 8;
148 78 50       162 if ($count < $len) {
149 0         0 $count = $len;
150             }
151 78         86 my $res = $pad;
152 78         144 while ($count > $len) {
153 402558         290162 $res .= $salt . $pass;
154 402558         459047 $count -= $len;
155             }
156 78 50       227 if ($count < 8) {
157 78         213 $res .= substr($salt, 0, $count);
158             } else {
159 0         0 $res .= $salt;
160 0         0 $count -= 8;
161 0         0 $res .= substr($pass, 0, $count);
162             }
163 78         604 $s2k->{hash}->hash($res);
164             }
165              
166             sub save {
167 11     11   15 my $s2k = shift;
168 11         32 my $buf = Crypt::OpenPGP::Buffer->new;
169 11         71 $buf->put_int8(3);
170 11         61 $buf->put_int8($s2k->{hash_alg});
171 11         59 $buf->put_bytes($s2k->{salt});
172 11         98 $buf->put_int8($s2k->{count});
173 11         51 $buf->bytes;
174             }
175              
176             1;
177             __END__
178              
179             =head1 NAME
180              
181             Crypt::OpenPGP::S2k - String-to-key generation
182              
183             =head1 SYNOPSIS
184              
185             use Crypt::OpenPGP::S2k;
186              
187             # S2k generates an encryption key from a passphrase; in order to
188             # understand how large of a key to generate, we need to know which
189             # cipher we're using, and what the passphrase is.
190             my $cipher = Crypt::OpenPGP::Cipher->new( '...' );
191             my $passphrase = 'foo';
192              
193             my $s2k = Crypt::OpenPGP::S2k->new( 'Salt_Iter' );
194             my $key = $s2k->generate( $passphrase, $cipher->keysize );
195              
196             my $serialized = $s2k->save;
197              
198             =head1 DESCRIPTION
199              
200             I<Crypt::OpenPGP::S2k> implements string-to-key generation for use in
201             generating symmetric cipher keys from standard, arbitrary-length
202             passphrases (like those used to lock secret key files). Since a
203             passphrase can be of any length, and key material must be a very
204             specific length, a method is needed to translate the passphrase into
205             the key. The OpenPGP RFC defines three such methods, each of which
206             this class implements.
207              
208             =head1 USAGE
209              
210             =head2 Crypt::OpenPGP::S2k->new($type)
211              
212             Creates a new type of S2k-generator of type I<$type>; valid values for
213             I<$type> are C<Simple>, C<Salted>, and C<Salt_Iter>. These generator
214             types are described in the OpenPGP RFC section 3.7.
215              
216             Returns the new S2k-generator object.
217              
218             =head2 Crypt::OpenPGP::S2k->parse($buffer)
219              
220             Given a buffer I<$buffer> of type I<Crypt::OpenPGP::Buffer>, determines
221             the type of S2k from the first octet in the buffer (one of the types
222             listed above in I<new>), then creates a new object of that type and
223             initializes the S2k state from the buffer I<$buffer>. Different
224             initializations occur based on the type of S2k.
225              
226             Returns the new S2k-generator object.
227              
228             =head2 $s2k->save
229              
230             Serializes the S2k object and returns the serialized form; this form
231             will differ based on the type of S2k.
232              
233             =head2 $s2k->generate($passphrase, $keysize)
234              
235             Given a passphrase I<$passphrase>, which should be a string of octets
236             of arbitrary length, and a keysize I<$keysize>, generates enough key
237             material to meet the size I<$keysize>, and returns that key material.
238              
239             =head1 AUTHOR & COPYRIGHTS
240              
241             Please see the Crypt::OpenPGP manpage for author, copyright, and
242             license information.
243              
244             =cut