File Coverage

blib/lib/Crypt/RSA/SS/PSS.pm
Criterion Covered Total %
statement 99 107 92.5
branch 18 38 47.3
condition 7 19 36.8
subroutine 17 19 89.4
pod 5 11 45.4
total 146 194 75.2


line stmt bran cond sub pod time code
1             package Crypt::RSA::SS::PSS;
2 1     1   627 use strict;
  1         1  
  1         25  
3 1     1   5 use warnings;
  1         1  
  1         24  
4              
5             ## Crypt::RSA::SS:PSS
6             ##
7             ## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
8             ## This code is free software; you can redistribute it and/or modify
9             ## it under the same terms as Perl itself.
10              
11 1     1   4 use base 'Crypt::RSA::Errorhandler';
  1         2  
  1         61  
12 1     1   5 use Bytes::Random::Secure qw/random_bytes/;
  1         2  
  1         45  
13 1     1   6 use Crypt::RSA::DataFormat qw(octet_len os2ip i2osp octet_xor mgf1);
  1         1  
  1         58  
14 1     1   484 use Crypt::RSA::Primitives;
  1         3  
  1         36  
15 1     1   7 use Crypt::RSA::Debug qw(debug);
  1         2  
  1         53  
16 1     1   5 use Digest::SHA qw(sha1);
  1         2  
  1         1370  
17              
18             $Crypt::RSA::SS::PSS::VERSION = '1.99';
19              
20             sub new {
21 1     1 1 586 my ($class, %params) = @_;
22 1         9 my $self = bless { primitives => new Crypt::RSA::Primitives,
23             hlen => 20,
24             VERSION => $Crypt::RSA::SS::PSS::VERSION,
25             }, $class;
26 1 50       5 if ($params{Version}) {
27             # do versioning here
28             }
29 1         3 return $self;
30             }
31              
32              
33             sub sign {
34 5     5 1 2565 my ($self, %params) = @_;
35 5   33     17 my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
  5         18  
36 5 50       18 return $self->error("No Key parameter", \$M, \%params) unless $key;
37 5 50       13 return $self->error("No Message or Plaintext parameter", \$key, \%params) unless $M;
38 5         48 my $k = octet_len ($key->n);
39 5         40 my $salt = random_bytes($self->{hlen});
40 5         798 my $em = $self->encode ($M, $salt, $k-1);
41 5         18 my $m = os2ip ($em);
42 5         7743 my $sig = $self->{primitives}->core_sign (Key => $key, Message => $m);
43 5         42 my $S = i2osp ($sig, $k);
44 5 100       29 return ($S, $salt) if wantarray;
45 4         47 return $S;
46             }
47              
48              
49             sub verify_with_salt {
50 1     1 1 14 my ($self, %params) = @_;
51 1   33     3 my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
  1         6  
52 1         3 my $S = $params{Signature}; my $salt = $params{Salt};
  1         2  
53 1 50       6 return $self->error("No Key parameter", \$S, \%params) unless $key;
54 1 50       5 return $self->error("No Signature parameter", \$key, \%params) unless $S;
55 1         15 my $k = octet_len ($key->n);
56 1         3 my $em;
57 1 50       7 unless ($em = $self->encode ($M, $salt, $k-1)) {
58 0 0       0 return if $self->errstr eq "Message too long.";
59 0 0       0 return $self->error ("Modulus too short.", \$M, \$S, \$key, \%params) if
60             $self->errstr eq "Intended encoded message length too short.";
61             }
62 1 50       4 return $self->error ("Invalid signature.", \$M, \$S, $key, \%params) if length($S) < $k;
63 1         8 my $s = os2ip ($S);
64 1   33     1681 my $m = $self->{primitives}->core_verify (Key => $key, Signature => $s) ||
65             $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
66 1   50     35 my $em1 = i2osp ($m, $k-1) ||
67             return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
68 1 50       10 return 1 if $em eq $em1;
69 0         0 return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
70             }
71              
72              
73             sub verify {
74 4     4 1 32 my ($self, %params) = @_;
75 4   33     12 my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
  4         24  
76 4         16 my $S = $params{Signature};
77 4 50       20 return $self->error("No Key parameter", \$S, \$M, \%params) unless $key;
78 4 50       25 return $self->error("No Signature parameter", \$key, \$M, \%params) unless $S;
79 4 50       13 return $self->error("No Message or Plaintext parameter", \$key, \$S, \%params) unless $M;
80 4         55 my $k = octet_len ($key->n);
81 4         22 my $s = os2ip ($S);
82 4   33     7141 my $m = $self->{primitives}->core_verify (Key => $key, Signature => $s) ||
83             $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
84 4   50     145 my $em1 = i2osp ($m, $k-1) ||
85             return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
86 4 50       31 return 1 if $self->verify_with_salt_recovery ($M, $em1);
87 0         0 return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
88             }
89              
90              
91             sub encode {
92 6     6 0 17 my ($self, $M, $salt, $emlen) = @_;
93             return $self->error ("Intended encoded message length too short.", \$M, \$salt )
94 6 50       26 if $emlen < 2 * $self->{hlen};
95 6         41 my $H = $self->hash ("$salt$M");
96 6         19 my $padlength = $emlen - (2*$$self{hlen});
97 6         18 my $PS = chr(0)x$padlength;
98 6         14 my $db = $salt . $PS;
99 6         23 my $dbmask = $self->mgf ($H, $emlen - $self->{hlen});
100 6         20 my $maskeddb = octet_xor ($db, $dbmask);
101 6         14 my $em = $H . $maskeddb;
102 6         16 return $em;
103             }
104              
105              
106             sub verify_with_salt_recovery {
107 4     4 0 9 my ($self, $M, $EM) = @_;
108 4         15 my $hlen = $$self{hlen};
109 4         8 my $emlen = length ($EM);
110 4 50       16 return $self->error ("Encoded message too short.", \$M, \$EM) if $emlen < (2*$hlen);
111 4         11 my $H = substr $EM, 0, $hlen;
112 4         9 my $maskeddb = substr $EM, $hlen;
113 4         18 my $dbmask = $self->mgf ($H, $emlen-$hlen);
114 4         23 my $db = octet_xor ($maskeddb, $dbmask);
115 4         9 my $salt = substr $db, 0, $hlen;
116 4         7 my $PS = substr $db, $hlen;
117 4         10 my $check = chr(0) x ($emlen-(2*$hlen)); debug ("PS: $PS");
  4         20  
118 4 50       17 return $self->error ("Inconsistent.") unless $check eq $PS;
119 4         40 my $H1 = $self->hash ("$salt$M");
120 4 50       44 return 1 if $H eq $H1;
121 0         0 return $self->error ("Inconsistent.");
122             }
123              
124              
125             sub hash {
126 10     10 0 19 my ($self, $data) = @_;
127 10         217 return sha1 ($data);
128             }
129            
130              
131             sub mgf {
132 10     10 0 27 my ($self, @data) = @_;
133 10         41 return mgf1 (@data);
134             }
135              
136              
137             sub version {
138 0     0 1 0 my $self = shift;
139 0         0 return $self->{VERSION};
140             }
141              
142              
143             sub signblock {
144 0     0 0 0 return -1;
145             }
146              
147              
148             sub verifyblock {
149 4     4 0 43 my ($self, %params) = @_;
150 4         56 return octet_len($params{Key}->n);
151             }
152              
153              
154             1;
155              
156             =head1 NAME
157              
158             Crypt::RSA::SS::PSS - Probabilistic Signature Scheme based on RSA.
159              
160             =head1 SYNOPSIS
161              
162             my $pss = new Crypt::RSA::SS::PSS;
163              
164             my $signature = $pss->sign (
165             Message => $message,
166             Key => $private,
167             ) || die $pss->errstr;
168              
169             my $verify = $pss->verify (
170             Message => $message,
171             Key => $key,
172             Signature => $signature,
173             ) || die $pss->errstr;
174              
175              
176             =head1 DESCRIPTION
177              
178             PSS (Probabilistic Signature Scheme) is a provably secure method of
179             creating digital signatures with RSA. "Provable" means that the
180             difficulty of forging signatures can be directly related to inverting
181             the RSA function. "Probabilistic" alludes to the randomly generated salt
182             value included in the signature to enhance security. For more details
183             on PSS, see [4] & [13].
184              
185             =head1 METHODS
186              
187             =head2 B
188              
189             Constructor.
190              
191             =head2 B
192              
193             Returns the version number of the module.
194              
195             =head2 B
196              
197             Computes a PSS signature on a message with the private key of the signer.
198             In scalar context, sign() returns the computed signature. In array
199             context, it returns the signature and the random salt. The signature can
200             verified with verify() or verify_with_salt(). sign() takes a hash argument
201             with the following mandatory keys:
202              
203             =over 4
204              
205             =item B
206              
207             Message to be signed, a string of arbitrary length.
208              
209             =item B
210              
211             Private key of the signer, a Crypt::RSA::Key::Private object.
212              
213             =back
214              
215             =head2 B
216              
217             Verifies a signature generated with sign(). The salt is recovered from the
218             signature and need not be passed. Returns a true value on success and
219             false on failure. $self->errstr is set to "Invalid signature." or
220             appropriate error on failure. verify() takes a hash argument with the
221             following mandatory keys:
222              
223             =over 4
224              
225             =item B
226              
227             Public key of the signer, a Crypt::RSA::Key::Public object.
228              
229             =item B
230              
231             The original signed message, a string of arbitrary length.
232              
233             =item B
234              
235             Signature computed with sign(), a string.
236              
237             =item B
238              
239             Version of the module that was used for creating the Signature. This is an
240             optional argument. When present, verify() will ensure before proceeding
241             that the installed version of the module can successfully verify the
242             Signature.
243              
244             =back
245              
246             =head2 B
247              
248             Verifies a signature given the salt. Takes the same arguments as verify()
249             in addition to B, which is a 20-byte string returned by sign() in
250             array context.
251              
252             =head1 ERROR HANDLING
253              
254             See ERROR HANDLING in Crypt::RSA(3) manpage.
255              
256             =head1 BIBLIOGRAPHY
257              
258             See BIBLIOGRAPHY in Crypt::RSA(3) manpage.
259              
260             =head1 AUTHOR
261              
262             Vipul Ved Prakash, Email@vipul.netE
263              
264             =head1 SEE ALSO
265              
266             Crypt::RSA(3), Crypt::RSA::Primitives(3), Crypt::RSA::Keys(3),
267             Crypt::RSA::EME::OAEP(3)
268              
269             =cut
270              
271