Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,205 @@
package Crypt::RSA::DataFormat;
use strict;
use warnings;
## Crypt::RSA::DataFormat -- Functions for converting, shaping and
## creating and reporting data formats.
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use vars qw(@ISA);
use Math::BigInt try => 'GMP, Pari';
use Math::Prime::Util qw/random_bytes/;
use Digest::SHA qw(sha1);
use Carp;
use base qw(Exporter);
our @EXPORT_OK = qw(i2osp os2ip h2osp octet_xor octet_len bitsize
generate_random_octet mgf1 steak);
sub i2osp {
my ($num, $l) = @_;
$num = 0 if !defined $num;
$l = 0 if !defined $l;
my $result;
if (ref($num) ne 'Math::BigInt' && $num <= ~0) {
$result = '';
do {
$result = chr($num & 0xFF) . $result;
$num >>= 8;
} while $num;
} else {
$num = Math::BigInt->new("$num") unless ref($num) eq 'Math::BigInt';
my $hex = $num->as_hex;
# Remove the 0x and any leading zeros (shouldn't be any)
$hex =~ s/^0x0*//;
# Add a leading zero if needed to line up bytes correctly.
substr($hex, 0, 0, '0') if length($hex) % 2;
# Pack hex string into a octet string.
$result = pack("H*", $hex);
}
if ($l) {
my $rlen = length($result);
# Return undef if too large to fit
return if $l < $rlen;
# Zero pad the front if they want a longer string
substr( $result, 0, 0, chr(0) x ($l-$rlen) ) if $rlen < $l;
}
return $result;
}
sub os2ip {
my $string = shift;
my($hex) = unpack('H*', $string);
return Math::BigInt->new("0x$hex");
}
sub h2osp {
my $hex = shift;
$hex =~ s/\s//g;
$hex =~ s/^0x0*//;
# Add a leading zero if needed to line up bytes correctly.
substr($hex, 0, 0, '0') if length($hex) % 2;
# Pack into a string.
my $result = pack("H*", $hex);
return $result;
}
sub generate_random_octet {
my $l = shift; # Ignore the strength parameter, if any
return random_bytes($l);
}
sub bitsize {
my $n = shift;
my $bits = 0;
if (ref($n) eq 'Math::BigInt') {
$bits = length($n->as_bin) - 2;
} else {
while ($n) { $bits++; $n >>= 1; }
}
$bits;
}
sub octet_len {
return int( (bitsize(shift) + 7) / 8 );
}
# This could be made even faster doing 4 bytes at a time
sub octet_xor {
my ($a, $b) = @_;
# Ensure length($a) >= length($b)
($a, $b) = ($b, $a) if length($b) > length($a);
my $alen = length($a);
# Prepend null bytes to the beginning of $b to make the lengths equal
substr($b, 0, 0, chr(0) x ($alen-length($b))) if $alen > length($b);
# xor all bytes
my $r = '';
$r .= chr( ord(substr($a,$_,1)) ^ ord(substr($b,$_,1)) ) for 0 .. $alen-1;
return $r;
}
# http://rfc-ref.org/RFC-TEXTS/3447/chapter11.html
sub mgf1 {
my ($seed, $l) = @_;
my $hlen = 20; # SHA-1 is 160 bits
my $imax = int(($l + $hlen - 1) / $hlen) - 1;
my $T = "";
foreach my $i (0 .. $imax) {
$T .= sha1($seed . i2osp ($i, 4));
}
my ($output) = unpack "a$l", $T;
return $output;
}
sub steak {
my ($text, $blocksize) = @_;
my $textsize = length($text);
my $chunkcount = $textsize % $blocksize
? int($textsize/$blocksize) + 1 : $textsize/$blocksize;
my @segments = unpack "a$blocksize"x$chunkcount, $text;
return @segments;
}
1;
=head1 NAME
Crypt::RSA::DataFormat - Data creation, conversion and reporting primitives.
=head1 DESCRIPTION
This module implements several data creation, conversion and reporting
primitives used throughout the Crypt::RSA implementation. Primitives are
available as exportable functions.
=head1 FUNCTIONS
=over 4
=item B<i2osp> Integer, Length
Integer To Octet String Primitive. Converts an integer into its
equivalent octet string representation of length B<Length>. If
necessary, the resulting string is prefixed with nulls. If
B<Length> is not provided, returns an octet string of shortest
possible length.
=item B<h2osp> Hex String
Hex To Octet String Primitive. Converts a I<hex string> into its
equivalent octet string representation and returns an octet
string of shortest possible length. The hex string is not
prefixed with C<0x>, etc.
=item B<os2ip> String
Octet String to Integer Primitive. Converts an octet string into its
equivalent integer representation.
=item B<generate_random_octet> Length
Generates a random octet string of length B<Length>.
=item B<bitsize> Integer
Returns the length of the B<Integer> in bits.
=item B<octet_len> Integer
Returns the octet length of the integer. If the length is not a whole
number, the fractional part is dropped to make it whole.
=item B<octet_xor> String1, String2
Returns the result of B<String1> XOR B<String2>.
=item B<steak> String, Length
Returns an array of segments of length B<Length> from B<String>. The final
segment can be smaller than B<Length>.
=back
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=cut

View File

@@ -0,0 +1,82 @@
package Crypt::RSA::Debug;
use strict;
use warnings;
## Crypt::RSA::Debug
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use vars qw(@ISA @EXPORT_OK);
require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = qw(debug debuglevel);
my $DEBUG = 0;
sub debug{
return unless $DEBUG;
my ($caller, undef) = caller;
my (undef,undef,$line,$sub) = caller(1); $sub =~ s/.*://;
$sub = sprintf "%12s()%4d", $sub, $line;
$sub .= " | " . (shift);
$sub =~ s/\x00/[0]/g;
$sub =~ s/\x01/[1]/g;
$sub =~ s/\x02/[2]/g;
$sub =~ s/\x04/[4]/g;
$sub =~ s/\x05/[5]/g;
$sub =~ s/\xff/[-]/g;
$sub =~ s/[\x00-\x1f]/\./g;
$sub =~ s/[\x7f-\xfe]/_/g;
print "$sub\n";
}
sub debuglevel {
my ($level) = shift;
$DEBUG = $level;
}
=head1 NAME
Crypt::RSA::Debug - Debug routine for Crypt::RSA.
=head1 SYNOPSIS
use Crypt::RSA::Debug qw(debug);
debug ("oops!");
=head1 DESCRIPTION
The module provides support for the I<print> method of debugging!
=head1 FUNCTION
=over 4
=item B<debug> String
Prints B<String> on STDOUT, along with caller's function name and line number.
=item B<debuglevel> Integer
Sets the class data I<debuglevel> to specified value. The value
defaults to 0. Callers can use the debuglevel facility by
comparing $Crypt::RSA::DEBUG to the desired debug level before
generating a debug statement.
=back
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=cut
1;

View File

@@ -0,0 +1,286 @@
package Crypt::RSA::ES::OAEP;
use strict;
use warnings;
## Crypt::RSA::ES::OAEP
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use base 'Crypt::RSA::Errorhandler';
use Math::Prime::Util qw/random_bytes/;
use Crypt::RSA::DataFormat qw(bitsize os2ip i2osp octet_xor mgf1 octet_len);
use Crypt::RSA::Primitives;
use Crypt::RSA::Debug qw(debug);
use Digest::SHA qw(sha1);
use Sort::Versions qw(versioncmp);
use Carp;
$Crypt::RSA::ES::OAEP::VERSION = '1.99';
sub new {
my ($class, %params) = @_;
my $self = bless { primitives => new Crypt::RSA::Primitives,
P => "",
hlen => 20,
VERSION => $Crypt::RSA::ES::OAEP::VERSION,
}, $class;
if ($params{Version}) {
if (versioncmp($params{Version}, '1.15') == -1) {
$$self{P} = "Crypt::RSA";
$$self{VERSION} = $params{Version};
} elsif (versioncmp($params{Version}, $$self{VERSION}) == 1) {
croak "Required version ($params{Version}) greater than installed version ($$self{VERSION}) of $class.\n";
}
}
return $self;
}
sub encrypt {
my ($self, %params) = @_;
my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
return $self->error ("Missing Message or Plaintext parameter", \$key, \%params) unless $M;
return $self->error ($key->errstr, \$M, $key, \%params) unless $key->check;
my $k = octet_len ($key->n); debug ("octet_len of modulus: $k");
my $em = $self->encode ($M, $self->{P}, $k-1) ||
return $self->error ($self->errstr, \$M, $key, \%params);
my $m = os2ip ($em);
my $c = $self->{primitives}->core_encrypt ( Plaintext => $m, Key => $key );
my $ec = i2osp ($c, $k); debug ("ec: $ec");
return $ec;
}
sub decrypt {
my ($self, %params) = @_;
my $key = $params{Key}; my $C = $params{Cyphertext} || $params{Ciphertext};
return $self->error ("Missing Cyphertext or Ciphertext parameter", \$key, \%params) unless $C;
return $self->error ($key->errstr, $key, \%params) unless $key->check;
my $k = octet_len ($key->n);
my $c = os2ip ($C);
if (bitsize($c) > bitsize($key->n)) {
return $self->error ("Decryption error.", $key, \%params)
}
my $m = $self->{primitives}->core_decrypt (Cyphertext => $c, Key => $key) ||
return $self->error ("Decryption error.", $key, \%params);
my $em = i2osp ($m, $k-1) ||
return $self->error ("Decryption error.", $key, \%params);
my $M; $self->errstrrst; # reset the errstr
unless ($M = $self->decode ($em, $$self{P})) {
return $self->error ("Decryption error.", $key, \%params) if $self->errstr();
return $M;
}
return $M;
}
sub encode {
my ($self, $M, $P, $emlen) = @_;
my $hlen = $$self{hlen};
my $mlen = length($M);
return $self->error ("Message too long.", \$P, \$M) if $mlen > $emlen-(2*$hlen)-1;
my ($PS, $pslen) = ("", 0);
if ($pslen = $emlen-(2*$hlen+1+$mlen)) {
$PS = chr(0)x$pslen;
}
my $phash = $self->hash ($P);
my $db = $phash . $PS . chr(1) . $M;
my $seed = random_bytes($hlen);
my $dbmask = $self->mgf ($seed, $emlen-$hlen);
my $maskeddb = octet_xor ($db, $dbmask);
my $seedmask = $self->mgf ($maskeddb, $hlen);
my $maskedseed = octet_xor ($seed, $seedmask);
my $em = $maskedseed . $maskeddb;
debug ("emlen == $emlen");
debug ("M == $M [" . length($M) . "]");
debug ("PS == $PS [$pslen]");
debug ("phash == $phash [" . length($phash) . "]");
debug ("seed == $seed [" . length($seed) . "]");
debug ("seedmask == $seedmask [" . length($seedmask) . "]");
debug ("db == $db [" . length($db) . "]");
debug ("dbmask == $dbmask [" . length($dbmask) . "]");
debug ("maskeddb == $maskeddb [" . length($maskeddb) . "]");
debug ("em == $em [" . length($em) . "]");
return $em;
}
sub decode {
my ($self, $em, $P) = @_;
my $hlen = $$self{hlen};
debug ("P == $P");
return $self->error ("Decoding error.", \$P) if length($em) < 2*$hlen+1;
my $maskedseed = substr $em, 0, $hlen;
my $maskeddb = substr $em, $hlen;
my $seedmask = $self->mgf ($maskeddb, $hlen);
my $seed = octet_xor ($maskedseed, $seedmask);
my $dbmask = $self->mgf ($seed, length($em) - $hlen);
my $db = octet_xor ($maskeddb, $dbmask);
my $phash = $self->hash ($P);
debug ("em == $em [" . length($em) . "]");
debug ("phash == $phash [" . length($phash) . "]");
debug ("seed == $seed [" . length($seed) . "]");
debug ("seedmask == $seedmask [" . length($seedmask) . "]");
debug ("maskedseed == $maskedseed [" . length($maskedseed) . "]");
debug ("db == $db [" . length($db) . "]");
debug ("maskeddb == $maskeddb [" . length($maskeddb) . "]");
debug ("dbmask == $dbmask [" . length($dbmask) . "]");
my ($phashorig) = substr $db, 0, $hlen;
debug ("phashorig == $phashorig [" . length($phashorig) . "]");
return $self->error ("Decoding error.", \$P) unless $phashorig eq $phash;
$db = substr $db, $hlen;
my ($chr0, $chr1) = (chr(0), chr(1));
my ($ps, $m);
debug ("db == $db [" . length($db) . "]");
unless ( ($ps, undef, $m) = $db =~ /^($chr0*)($chr1)(.*)$/s ) {
return $self->error ("Decoding error.", \$P);
}
return $m;
}
sub hash {
my ($self, $data) = @_;
return sha1 ($data);
}
sub mgf {
my ($self, @data) = @_;
return mgf1 (@data);
}
sub encryptblock {
my ($self, %params) = @_;
return octet_len ($params{Key}->n) - 42;
}
sub decryptblock {
my ($self, %params) = @_;
return octet_len ($params{Key}->n);
}
# should be able to call this as a class method.
sub version {
my $self = shift;
return $self->{VERSION};
}
1;
=head1 NAME
Crypt::RSA::ES::OAEP - Plaintext-aware encryption with RSA.
=head1 SYNOPSIS
my $oaep = new Crypt::RSA::ES::OAEP;
my $ct = $oaep->encrypt( Key => $key, Message => $message ) ||
die $oaep->errstr;
my $pt = $oaep->decrypt( Key => $key, Cyphertext => $ct ) ||
die $oaep->errstr;
=head1 DESCRIPTION
This module implements Optimal Asymmetric Encryption, a plaintext-aware
encryption scheme based on RSA. The notion of plaintext-aware implies it's
computationally infeasible to obtain full or partial information about a
message from a cyphertext, and computationally infeasible to generate a
valid cyphertext without knowing the corresponding message.
Plaintext-aware schemes, such as OAEP, are semantically secure,
non-malleable and secure against chosen-ciphertext attack. For more
information on OAEP and plaintext-aware encryption, see [3], [9] & [13].
=head1 METHODS
=head2 B<new()>
Constructor.
=head2 B<version()>
Returns the version number of the module.
=head2 B<encrypt()>
Encrypts a string with a public key and returns the encrypted string
on success. encrypt() takes a hash argument with the following
mandatory keys:
=over 4
=item B<Message>
A string to be encrypted. The length of this string should not exceed k-42
octets, where k is the octet length of the RSA modulus. If Message is
longer than k-42, the method will fail and set $self->errstr to "Message
too long." This means the key must be at least _336_ bits long if you are
to use OAEP.
=item B<Key>
Public key of the recipient, a Crypt::RSA::Key::Public object.
=back
=head2 B<decrypt()>
Decrypts cyphertext with a private key and returns plaintext on
success. $self->errstr is set to "Decryption Error." or appropriate
error on failure. decrypt() takes a hash argument with the following
mandatory keys:
=over 4
=item B<Cyphertext>
A string encrypted with encrypt(). The length of the cyphertext must be k
octets, where k is the length of the RSA modulus.
=item B<Key>
Private key of the receiver, a Crypt::RSA::Key::Private object.
=item B<Version>
Version of the module that was used for creating the Cyphertext. This is
an optional argument. When present, decrypt() will ensure before
proceeding that the installed version of the module can successfully
decrypt the Cyphertext.
=back
=head1 ERROR HANDLING
See ERROR HANDLING in Crypt::RSA(3) manpage.
=head1 BIBLIOGRAPHY
See BIBLIOGRAPHY in Crypt::RSA(3) manpage.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3), Crypt::RSA::Primitives(3), Crypt::RSA::Keys(3),
Crypt::RSA::SSA::PSS(3)
=cut

View File

@@ -0,0 +1,215 @@
package Crypt::RSA::ES::PKCS1v15;
use strict;
use warnings;
## Crypt::RSA::ES::PKCS1v15
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use base 'Crypt::RSA::Errorhandler';
use Math::Prime::Util qw/random_bytes/;
use Crypt::RSA::DataFormat qw(bitsize octet_len os2ip i2osp);
use Crypt::RSA::Primitives;
use Crypt::RSA::Debug qw(debug);
use Carp;
$Crypt::RSA::ES::PKCS1v15::VERSION = '1.99';
sub new {
my ($class, %params) = @_;
my $self = bless { primitives => new Crypt::RSA::Primitives,
VERSION => $Crypt::RSA::ES::PKCS1v15::VERSION,
}, $class;
if ($params{Version}) {
# do versioning here.
}
return $self;
}
sub encrypt {
my ($self, %params) = @_;
my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
return $self->error ("No Message or Plaintext parameter", \$key, \%params) unless $M;
return $self->error ($key->errstr, \$M, $key, \%params) unless $key->check;
my $k = octet_len ($key->n); debug ("octet_len of modulus: $k");
my $em = $self->encode ($M, $k-1) ||
return $self->error ($self->errstr, \$M, $key, \%params);
debug ("encoded: $em");
my $m = os2ip ($em);
my $c = $self->{primitives}->core_encrypt (Plaintext => $m, Key => $key);
my $ec = i2osp ($c, $k); debug ("cyphertext: $ec");
return $ec;
}
sub decrypt {
my ($self, %params) = @_;
my $key = $params{Key}; my $C = $params{Cyphertext} || $params{Ciphertext};
return $self->error ("No Cyphertext or Ciphertext parameter", \$key, \%params) unless $C;
return $self->error ($key->errstr, $key, \%params) unless $key->check;
my $k = octet_len ($key->n);
my $c = os2ip ($C);
debug ("bitsize(c): " . bitsize($c));
debug ("bitsize(n): " . bitsize($key->n));
if (bitsize($c) > bitsize($key->n)) {
return $self->error ("Decryption error.", $key, \%params)
}
my $m = $self->{primitives}->core_decrypt (Cyphertext => $c, Key => $key) ||
return $self->error ("Decryption error.", $key, \%params);
my $em = i2osp ($m, $k-1) ||
return $self->error ("Decryption error.", $key, \%params);
my $M; $self->errstrrst; # reset the errstr
unless ($M = $self->decode ($em)) {
return $self->error ("Decryption error.", $key, \%params) if $self->errstr();
return $M;
}
return $M;
}
sub encode {
my ($self, $M, $emlen) = @_;
$M = $M || ""; my $mlen = length($M);
return $self->error ("Message too long.", \$M) if $mlen > $emlen-10;
my $pslen = $emlen-$mlen-2;
# my $PS = join('', map { chr( 1+urandomm(255) ) } 1 .. $pslen);
my $PS = '';
while (length($PS) < $pslen) {
$PS .= random_bytes( $pslen - length($PS) );
$PS =~ s/\x00//g;
}
my $em = chr(2).$PS.chr(0).$M;
return $em;
}
sub decode {
my ($self, $em) = @_;
return $self->error ("Decoding error.") if length($em) < 10;
debug ("to decode: $em");
my ($chr0, $chr2) = (chr(0), chr(2));
my ($ps, $M);
unless ( ($ps, $M) = $em =~ /^$chr2(.*?)$chr0(.*)$/s ) {
return $self->error ("Decoding error.");
}
return $self->error ("Decoding error.") if length($ps) < 8;
return $M;
}
sub encryptblock {
my ($self, %params) = @_;
return octet_len ($params{Key}->n) - 11;
}
sub decryptblock {
my ($self, %params) = @_;
return octet_len ($params{Key}->n);
}
sub version {
my $self = shift;
return $self->{VERSION};
}
1;
=head1 NAME
Crypt::RSA::ES::PKCS1v15 - PKCS #1 v1.5 padded encryption scheme based on RSA.
=head1 SYNOPSIS
my $pkcs = new Crypt::RSA::ES::PKCS1v15;
my $ct = $pkcs->encrypt( Key => $key, Message => $message ) ||
die $pkcs->errstr;
my $pt = $pkcs->decrypt( Key => $key, Cyphertext => $ct ) ||
die $pkcs->errstr;
=head1 DESCRIPTION
This module implements PKCS #1 v1.5 padded encryption scheme based on RSA.
See [13] for details on the encryption scheme.
=head1 METHODS
=head2 B<new()>
Constructor.
=head2 B<version()>
Returns the version number of the module.
=head2 B<encrypt()>
Encrypts a string with a public key and returns the encrypted string
on success. encrypt() takes a hash argument with the following
mandatory keys:
=over 4
=item B<Message>
A string to be encrypted. The length of this string should not exceed k-10
octets, where k is the octet length of the RSA modulus. If Message is
longer than k-10, the method will fail and set $self->errstr to "Message
too long."
=item B<Key>
Public key of the recipient, a Crypt::RSA::Key::Public object.
=back
=head2 B<decrypt()>
Decrypts cyphertext with a private key and returns plaintext on
success. $self->errstr is set to "Decryption Error." or appropriate
error on failure. decrypt() takes a hash argument with the following
mandatory keys:
=over 4
=item B<Cyphertext>
A string encrypted with encrypt(). The length of the cyphertext must be k
octets, where k is the length of the RSA modulus.
=item B<Key>
Private key of the receiver, a Crypt::RSA::Key::Private object.
=back
=head1 ERROR HANDLING
See ERROR HANDLING in Crypt::RSA(3) manpage.
=head1 BIBLIOGRAPHY
See BIBLIOGRAPHY in Crypt::RSA(3) manpage.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3), Crypt::RSA::Primitives(3), Crypt::RSA::Keys(3),
Crypt::RSA::SSA::PSS(3)
=cut

View File

@@ -0,0 +1,132 @@
package Crypt::RSA::Errorhandler;
use strict;
use warnings;
## Crypt::RSA::Errorhandler -- Base class that provides error
## handling functionality.
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
sub new {
bless {}, shift
}
sub error {
my ($self, $errstr, @towipe) = @_;
$$self{errstr} = "$errstr\n";
for (@towipe) {
my $var = $_;
if (ref($var) =~ /Crypt::RSA/) {
$var->DESTROY();
} elsif (ref($var) eq "SCALAR") {
$$var = "";
} elsif (ref($var) eq "ARRAY") {
@$var = ();
} elsif (ref($var) eq "HASH") {
%$var = ();
}
}
return;
}
sub errstr {
my $self = shift;
return $$self{errstr};
}
sub errstrrst {
my $self = shift;
$$self{errstr} = "";
}
1;
=head1 NAME
Crypt::RSA::Errorhandler - Error handling mechanism for Crypt::RSA.
=head1 SYNOPSIS
package Foo;
use Crypt::RSA::Errorhandler;
@ISA = qw(Crypt::RSA::Errorhandler);
sub alive {
..
..
return
$self->error ("Awake, awake! Ring the alarum bell. \
Murther and treason!", $dagger)
if $self->murdered($king);
}
package main;
use Foo;
my $foo = new Foo;
$foo->alive($king) or print $foo->errstr();
# prints "Awake, awake! ... "
=head1 DESCRIPTION
Crypt::RSA::Errorhandler encapsulates the error handling mechanism used
by the modules in Crypt::RSA bundle. Crypt::RSA::Errorhandler doesn't
have a constructor and is meant to be inherited. The derived modules use
its two methods, error() and errstr(), to communicate error messages to
the caller.
When a method of the derived module fails, it calls $self->error() and
returns undef to the caller. The error message passed to error() is made
available to the caller through the errstr() accessor. error() also
accepts a list of sensitive data that it wipes out (undef'es) before
returning.
The caller should B<never> call errstr() to check for errors. errstr()
should be called only when a method indicates (usually through an undef
return value) that an error has occurred. This is because errstr() is
never overwritten and will always contain a value after the occurence of
first error.
=head1 METHODS
=over 4
=item B<new()>
Barebones constructor.
=item B<error($mesage, ($wipeme, $wipemetoo))>
The first argument to error() is $message which is placed in $self-
>{errstr} and the remaining arguments are interpreted as
variables containing sensitive data that are wiped out from the
memory. error() always returns undef.
=item B<errstr()>
errstr() is an accessor method for $self->{errstr}.
=item B<errstrrst()>
This method sets $self->{errstr} to an empty string.
=back
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3)
=cut

View File

@@ -0,0 +1,258 @@
package Crypt::RSA::Key;
use strict;
use warnings;
## Crypt::RSA::Keys
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use base 'Class::Loader';
use base 'Crypt::RSA::Errorhandler';
use Math::Prime::Util qw(random_nbit_prime miller_rabin_random is_frobenius_khashin_pseudoprime);
use Crypt::RSA::DataFormat qw(bitsize);
use Math::BigInt try => 'GMP, Pari';
use Crypt::RSA::Key::Private;
use Crypt::RSA::Key::Public;
use Carp;
$Crypt::RSA::Key::VERSION = '1.99';
my %MODMAP = (
Native_PKF => { Module => "Crypt::RSA::Key::Public" },
Native_SKF => { Module => "Crypt::RSA::Key::Private" },
SSH_PKF => { Module => "Crypt::RSA::Key::Public::SSH" },
SSH_SKF => { Module => "Crypt::RSA::Key::Private::SSH" },
);
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
$self->_storemap ( %MODMAP );
return $self;
}
sub generate {
my ($self, %params) = @_;
my $key;
unless ($params{q} && $params{p} && $params{e}) {
return $self->error ("Missing argument.") unless $params{Size};
return $self->error ("Keysize too small.") if
$params{Size} < 48;
return $self->error ("Odd keysize.") if
$params{Size} % 2;
my $size = int($params{Size}/2);
my $verbosity = $params{Verbosity} || 0;
# Switch from Maurer prime to nbit prime, then add some more primality
# testing. This is faster and gives us a wider set of possible primes.
# We really ought to consider the distribution. See:
# https://crocs.fi.muni.cz/_media/public/papers/usenixsec16_1mrsakeys_trfimu_201603.pdf
# for comments on p/q selection.
while (1) {
my $p = random_nbit_prime($size);
my $q = random_nbit_prime($size);
$p = Math::BigInt->new("$p") unless ref($p) eq 'Math::BigInt';
$q = Math::BigInt->new("$q") unless ref($q) eq 'Math::BigInt';
# For unbiased rejection sampling, generate both p/q if size too small.
next unless bitsize($p * $q) == $params{Size};
# Verify primes aren't too close together.
if ($params{Size} >= 256) {
my $threshold = Math::BigInt->new(2)->bpow($params{Size}/2 - 100);
my $diff = $p->copy->bsub($q)->babs;
next if $diff <= $threshold;
}
# We could check p-1 and q-1 smoothness.
# p and q have passed the strong BPSW test, so it would be shocking
# if they were not prime. We'll add a few more tests because they're
# cheap and we want to be extra careful, but also don't want to spend
# the time doing a full primality proof.
do { carp "$p passes BPSW but fails Frobenius test!"; next; }
unless is_frobenius_khashin_pseudoprime($p);
do { carp "$q passes BPSW but fails Frobenius test!"; next; }
unless is_frobenius_khashin_pseudoprime($q);
do { carp "$p fails Miller-Rabin testing!"; next; }
unless miller_rabin_random($p,3);
do { carp "$q fails Miller-Rabin testing!"; next; }
unless miller_rabin_random($q,3);
$key = { p => $p, q => $q, e => Math::BigInt->new(65537) };
last;
}
}
if ($params{KF}) {
$params{PKF} = { Name => "$params{KF}_PKF" };
$params{SKF} = { Name => "$params{KF}_SKF" }
}
my $pubload = $params{PKF} ? $params{PKF} : { Name => "Native_PKF" };
my $priload = $params{SKF} ? $params{SKF} : { Name => "Native_SKF" };
my $pubkey = $self->_load (%$pubload) ||
return $self->error ("Couldn't load the public key module: $@");
my $prikey = $self->_load ((%$priload), Args => ['Cipher' => $params{Cipher}, 'Password' => $params{Password} ]) ||
return $self->error ("Couldn't load the private key module: $@");
$pubkey->Identity ($params{Identity});
$prikey->Identity ($params{Identity});
$pubkey->e ($$key{e} || $params{e});
$prikey->e ($$key{e} || $params{e});
$prikey->p ($$key{p} || $params{p});
$prikey->q ($$key{q} || $params{q});
$prikey->phi ( ($prikey->p - 1) * ($prikey->q - 1) );
$prikey->d ( ($pubkey->e)->copy->bmodinv($prikey->phi) );
$prikey->n ( $prikey->p * $prikey->q );
$pubkey->n ( $prikey->n );
$prikey->dp ($prikey->d % ($prikey->p - 1));
$prikey->dq ($prikey->d % ($prikey->q - 1));
$prikey->u ( ($prikey->p)->copy->bmodinv($prikey->q) );
return $self->error ("d is too small. Regenerate.") if
bitsize($prikey->d) < 0.25 * bitsize($prikey->n);
$$key{p} = 0; $$key{q} = 0; $$key{e} = 0;
if ($params{Filename}) {
$pubkey->write (Filename => "$params{Filename}.public");
$prikey->write (Filename => "$params{Filename}.private");
}
return ($pubkey, $prikey);
}
1;
=head1 NAME
Crypt::RSA::Key - RSA Key Pair Generator.
=head1 SYNOPSIS
my $keychain = new Crypt::RSA::Key;
my ($public, $private) = $keychain->generate (
Identity => 'Lord Macbeth <macbeth@glamis.com>',
Size => 2048,
Password => 'A day so foul & fair',
Verbosity => 1,
) or die $keychain->errstr();
=head1 DESCRIPTION
This module provides a method to generate an RSA key pair.
=head1 METHODS
=head2 new()
Constructor.
=head2 generate()
generate() generates an RSA key of specified bitsize. It returns a list of
two elements, a Crypt::RSA::Key::Public object that holds the public part
of the key pair and a Crypt::RSA::Key::Private object that holds that
private part. On failure, it returns undef and sets $self->errstr to
appropriate error string. generate() takes a hash argument with the
following keys:
=over 4
=item B<Size>
Bitsize of the key to be generated. This should be an even integer > 48.
Bitsize is a mandatory argument.
=item B<Password>
String with which the private key will be encrypted. If Password is not
provided the key will be stored unencrypted.
=item B<Identity>
A string that identifies the owner of the key. This string usually takes
the form of a name and an email address. The identity is not bound to the
key with a signature. However, a future release or another module will
provide this facility.
=item B<Cipher>
The block cipher which is used for encrypting the private key. Defaults to
`Blowfish'. Cipher could be set to any value that works with Crypt::CBC(3)
and Tie::EncryptedHash(3).
=item B<Verbosity>
When set to 1, generate() will draw a progress display on STDOUT.
=item B<Filename>
The generated key pair will be written to disk, in $Filename.public and
$Filename.private files, if this argument is provided. Disk writes can be
deferred by skipping this argument and achieved later with the write()
method of Crypt::RSA::Key::Public(3) and Crypt::RSA::Key::Private(3).
=item B<KF>
A string that specifies the key format. As of this writing, two key
formats, `Native' and `SSH', are supported. KF defaults to `Native'.
=item B<SKF>
Secret (Private) Key Format. Instead of specifying KF, the user could
choose to specify secret and public key formats separately. The value for
SKF can be a string ("Native" or "SSH") or a hash reference that specifies
a module name, its constructor and constructor arguments. The specified
module is loaded with Class::Loader(3) and must be interface compatible
with Crypt::RSA::Key::Private(3).
=item B<PKF>
Public Key Format. This option is like SKF but for the public key.
=back
=head1 ERROR HANDLING
See B<ERROR HANDLING> in Crypt::RSA(3) manpage.
=head1 BUGS
There's an inefficiency in the way generate() ensures the key pair is
exactly Size bits long. This will be fixed in a future release.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3), Crypt::RSA::Key::Public(3), Crypt::RSA::Key::Private(3),
Tie::EncryptedHash(3), Class::Loader(3),
Math::Prime::Util(3)
=cut

View File

@@ -0,0 +1,348 @@
package Crypt::RSA::Key::Private;
use strict;
use warnings;
## Crypt::RSA::Key::Private
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use vars qw($AUTOLOAD $VERSION);
use base 'Crypt::RSA::Errorhandler';
use Tie::EncryptedHash;
use Data::Dumper;
use Math::BigInt try => 'GMP, Pari';
use Math::Prime::Util qw/is_prime/;
use Carp;
$Crypt::RSA::Key::Private::VERSION = '1.99';
sub new {
my ($class, %params) = @_;
my $self = { Version => $Crypt::RSA::Key::Private::VERSION };
if ($params{Filename}) {
bless $self, $class;
$self = $self->read (%params);
return bless $self, $class;
} else {
bless $self, $class;
$self->Identity ($params{Identity}) if $params{Identity};
$self->Cipher ($params{Cipher}||"Blowfish");
$self->Password ($params{Password}) if $params{Password};
return $self;
}
}
sub AUTOLOAD {
my ($self, $value) = @_;
my $key = $AUTOLOAD; $key =~ s/.*:://;
if ($key =~ /^(e|n|d|p|q|dp|dq|u|phi)$/) {
my $prikey = \$self->{private}{"_$key"};
if (defined $value) {
$self->{Checked} = 0;
if (ref $value eq 'Math::BigInt') {
$$prikey = $value;
} elsif (ref $value eq 'Math::Pari') {
$$prikey = Math::BigInt->new($value->pari2pv);
} else {
$$prikey = Math::BigInt->new("$value");
}
}
if (defined $$prikey) {
$$prikey = Math::BigInt->new("$$prikey") unless ref($$prikey) eq 'Math::BigInt';
return $$prikey;
}
return $self->{private_encrypted}{"_$key"} if defined $self->{private_encrypted}{"_$key"};
return;
} elsif ($key =~ /^Identity|Cipher|Password$/) {
$self->{$key} = $value if $value;
return $self->{$key};
} elsif ($key =~ /^Checked$/) {
my ($package) = caller();
$self->{Checked} = $value if ($value && $package eq "Crypt::RSA::Key::Private") ;
return $self->{Checked};
}
}
sub hide {
my ($self) = @_;
return unless $$self{Password};
$self->{private_encrypted} = new Tie::EncryptedHash
__password => $self->{Password},
__cipher => $self->{Cipher};
for (keys %{$$self{private}}) {
$$self{private_encrypted}{$_} = $$self{private}{$_}->bstr;
}
my $private = $self->{private_encrypted};
delete $private->{__password};
delete $$self{private};
delete $$self{Password};
# Mark ourselves as hidden
$self->{Hidden} = 1;
}
sub reveal {
my ($self, %params) = @_;
$$self{Password} = $params{Password} if $params{Password};
return unless $$self{Password};
$$self{private_encrypted}{__password} = $params{Password};
for (keys %{$$self{private_encrypted}}) {
$$self{private}{$_} = Math::BigInt->new("$$self{private_encrypted}{$_}");
}
$self->{Hidden} = 0;
}
sub check {
my ($self) = @_;
return 1 if $self->{Checked};
return $self->error ("Cannot check hidden key - call reveal first.")
if $self->{Hidden};
return $self->error ("Incomplete key.") unless
($self->n && $self->d) || ($self->n && $self->p && $self->q);
if ($self->p && $self->q) {
return $self->error ("n is not a number.") if $self->n =~ /\D/;
return $self->error ("p is not a number.") if $self->p =~ /\D/;
return $self->error ("q is not a number.") if $self->q =~ /\D/;
return $self->error ("n is not p*q." ) unless $self->n == $self->p * $self->q;
return $self->error ("p is not prime.") unless is_prime( $self->p );
return $self->error ("q is not prime.") unless is_prime( $self->q );
}
if ($self->e) {
# d * e == 1 mod lcm(p-1, q-1)
return $self->error ("e is not a number.") if $self->e =~ /\D/;
my $k = Math::BigInt::blcm($self->p-1, $self->q-1);
my $KI = ($self->e)->copy->bmul($self->d)->bmodinv($k);
return $self->error ("Bad `d'.") unless $KI == 1;
}
if ($self->dp) {
# dp == d mod (p-1)
return $self->error ("Bad `dp'.") unless $self->dp == $self->d % ($self->p - 1);
}
if ($self->dq) {
# dq == d mod (q-1)
return $self->error ("Bad `dq'.") unless $self->dq == $self->d % ($self->q - 1);
}
if ($self->u && $self->q && $self->p) {
my $m = ($self->p)->copy->bmodinv($self->q);
return $self->error ("Bad `u'.") unless $self->u == $m;
}
$self->Checked(1);
return 1;
}
sub DESTROY {
my $self = shift;
delete $$self{private_encrypted}{__password};
delete $$self{private_encrypted};
delete $$self{private};
delete $$self{Password};
undef $self;
}
sub write {
my ($self, %params) = @_;
$self->hide();
my $string = $self->serialize (%params);
open(my $disk, '>', $params{Filename}) or
croak "Can't open $params{Filename} for writing.";
binmode $disk;
print $disk $string;
close $disk;
}
sub read {
my ($self, %params) = @_;
open(my $disk, '<', $params{Filename}) or
croak "Can't open $params{Filename} to read.";
binmode $disk;
my @key = <$disk>;
close $disk;
$self = $self->deserialize (String => \@key);
$self->reveal(%params);
return $self;
}
sub serialize {
my ($self, %params) = @_;
if ($$self{private}) { # this is an unencrypted key
for (keys %{$$self{private}}) {
$$self{private}{$_} = ($$self{private}{$_})->bstr;
}
}
return Dumper $self;
}
sub deserialize {
my ($self, %params) = @_;
my $string = join'', @{$params{String}};
$string =~ s/\$VAR1 =//;
$self = eval $string;
if ($$self{private}) { # the key is unencrypted
for (keys %{$$self{private}}) {
$$self{private}{$_} = Math::BigInt->new("$$self{private}{$_}");
}
return $self;
}
my $private = new Tie::EncryptedHash;
%$private = %{$$self{private_encrypted}};
$self->{private_encrypted} = $private;
return $self;
}
1;
=head1 NAME
Crypt::RSA::Key::Private -- RSA Private Key Management.
=head1 SYNOPSIS
$key = new Crypt::RSA::Key::Private (
Identity => 'Lord Banquo <banquo@lochaber.com>',
Password => 'The earth hath bubbles',
);
$key->hide();
$key->write( Filename => 'rsakeys/banquo.private' );
$akey = new Crypt::RSA::Key::Private (
Filename => 'rsakeys/banquo.private'
);
$akey->reveal ( Password => 'The earth hath bubbles' );
=head1 DESCRIPTION
Crypt::RSA::Key::Private provides basic private key management
functionality for Crypt::RSA private keys. Following methods are
available:
=over 4
=item B<new()>
The constructor. Takes a hash, usually with two arguments: C<Filename> and
C<Password>. C<Filename> indicates a file from which the private key
should be read. More often than not, private keys are kept encrypted with
a symmetric cipher and MUST be decrypted before use. When a C<Password>
argument is provided, the key is also decrypted before it is returned by
C<new()>. Here's a complete list of arguments accepted by C<new()> (all of
which are optional):
=over 4
=item Identity
A string identifying the owner of the key. Canonically, a name and
email address.
=item Filename
Name of the file that contains the private key.
=item Password
Password with which the private key is encrypted, or should be encrypted
(in case of a new key).
=item Cipher
Name of the symmetric cipher in which the private key is encrypted (or
should be encrypted). The default is "Blowfish" and possible values
include DES, IDEA, Twofish and other ciphers supported by Crypt::CBC.
=back
=item B<reveal()>
If the key is not decrypted at C<new()>, it can be decrypted by
calling C<reveal()> with a C<Password> argument.
=item B<hide()>
C<hide()> causes the key to be encrypted by the chosen symmetric cipher
and password.
=item B<write()>
Causes the key to be written to a disk file specified by the
C<Filename> argument. C<write()> will call C<hide()> before
writing the key to disk. If you wish to store the key in plain,
don't specify a password at C<new()>.
=item B<read()>
Causes the key to be read from a disk file specified by
C<Filename> into the object. If C<Password> is provided, the
method automatically calls reveal() to decrypt the key.
=item B<serialize()>
Creates a Data::Dumper(3) serialization of the private key and
returns the string representation.
=item B<deserialize()>
Accepts a serialized key under the C<String> parameter and
coverts it into the perl representation stored in the object.
=item C<check()>
Check the consistency of the key. If the key checks out, it sets
$self->{Checked} = 1. Returns undef on failure.
=back
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA::Key(3), Crypt::RSA::Public(3)
=cut

View File

@@ -0,0 +1,271 @@
package Crypt::RSA::Key::Private::SSH::Buffer;
use strict;
use warnings;
## Crypt::RSA::Key::Private::SSH
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use Crypt::RSA::DataFormat qw( os2ip bitsize i2osp );
use Data::Buffer;
use base qw( Data::Buffer );
sub get_mp_int {
my $buf = shift;
my $off = $buf->{offset};
my $bits = unpack "n", $buf->bytes($off, 2);
my $bytes = int(($bits+7)/8);
my $p = os2ip( $buf->bytes($off+2, $bytes) );
$buf->{offset} += 2 + $bytes;
$p;
}
sub put_mp_int {
my $buf = shift;
my $int = shift;
my $bits = bitsize($int);
$buf->put_int16($bits);
$buf->put_chars( i2osp($int) );
}
package Crypt::RSA::Key::Private::SSH;
use strict;
use warnings;
use constant PRIVKEY_ID => "SSH PRIVATE KEY FILE FORMAT 1.1\n";
use vars qw( %CIPHERS %CIPHERS_TEXT );
# Having to name all the ciphers here is not extensible, but we're stuck
# with it given the RSA1 format. I don't think any of this is standardized.
# OpenSSH supports only: none, des, 3des, and blowfish here. This set of
# numbers below 10 match. Values above 10 are well supported by Perl modules.
BEGIN {
# CIPHERS : Used by deserialize to map numbers to modules.
%CIPHERS = (
# 0 = none
1 => [ 'IDEA' ],
2 => [ 'DES', 'DES_PP' ],
3 => [ 'DES_EDE3' ],
# From what I can see, none of the 3+ RC4 modules are CBC compatible
# 5 => [ 'RC4' ],
6 => [ 'Blowfish', 'Blowfish_PP' ],
10 => [ 'Twofish2' ],
11 => [ 'CAST5', 'CAST5_PP' ],
12 => [ 'Rijndael', 'OpenSSL::AES' ],
13 => [ 'RC6' ],
14 => [ 'Camellia', 'Camellia_PP' ],
# Crypt::Serpent is broken and abandonded.
);
# CIPHERS_TEXT : Used by serialize to map names to modules to numbers
%CIPHERS_TEXT = (
'NONE' => 0,
'IDEA' => 1,
'DES' => 2,
'DES_EDE3' => 3,
'DES3' => 3,
'3DES' => 3,
'TRIPLEDES' => 3,
# 'RC4' => 5,
# 'ARC4' => 5,
# 'ARCFOUR' => 5,
'BLOWFISH' => 6,
'TWOFISH' => 10,
'TWOFISH2' => 10,
'CAST5' => 11,
'CAST5_PP' => 11,
'CAST5PP' => 11,
'CAST-5' => 11,
'CAST-128' => 11,
'CAST128' => 11,
'RIJNDAEL' => 12,
'AES' => 12,
'OPENSSL::AES'=>12,
'RC6' => 13,
'CAMELLIA' => 14,
);
}
use Carp qw( croak );
use Data::Buffer;
use Crypt::CBC 2.17; # We want a good version
use Crypt::RSA::Key::Private;
use base qw( Crypt::RSA::Key::Private );
sub deserialize {
my($key, %params) = @_;
my $passphrase = defined $params{Password} ? $params{Password}
: defined $key->Password ? $key->Password
: '';
my $string = $params{String};
croak "Must supply String=>'blob' to deserialize" unless defined $string;
$string = join('', @$string) if ref($string) eq 'ARRAY';
croak "Cowardly refusing to deserialize on top of a hidden key"
if $key->{Hidden};
my $buffer = new Crypt::RSA::Key::Private::SSH::Buffer;
$buffer->append($string);
my $id = $buffer->bytes(0, length(PRIVKEY_ID), '');
croak "Bad key file format" unless $id eq PRIVKEY_ID;
$buffer->bytes(0, 1, '');
my $cipher_type = $buffer->get_int8;
$buffer->get_int32; ## Reserved data.
$buffer->get_int32; ## Private key bits.
$key->n( $buffer->get_mp_int );
$key->e( $buffer->get_mp_int );
$key->Identity( $buffer->get_str ); ## Comment.
if ($cipher_type != 0) {
my $cipher_names = $CIPHERS{$cipher_type} or
croak "Unknown cipher '$cipher_type' used in key file";
my $cipher_name;
foreach my $name (@$cipher_names) {
my $class = "Crypt::$name";
(my $file = $class) =~ s=::|'=/=g;
if ( eval { require "$file.pm"; 1 } ) {
$cipher_name = $name; last;
}
}
if (!defined $cipher_name) {
croak "Unsupported cipher '$cipher_names->[0]': $@";
}
my $cipher = Crypt::CBC->new( -key => $passphrase,
-cipher => $cipher_name );
my $decrypted =
$cipher->decrypt($buffer->bytes($buffer->offset));
$buffer->empty;
$buffer->append($decrypted);
}
my $check1 = $buffer->get_int8;
my $check2 = $buffer->get_int8;
unless ($check1 == $buffer->get_int8 &&
$check2 == $buffer->get_int8) {
croak "Bad passphrase supplied for key file";
}
$key->d( $buffer->get_mp_int );
$key->u( $buffer->get_mp_int );
$key->p( $buffer->get_mp_int );
$key->q( $buffer->get_mp_int );
# Restore other variables.
$key->phi( ($key->p - 1) * ($key->q - 1) );
$key->dp( $key->d % ($key->p - 1) );
$key->dq( $key->d % ($key->q - 1) );
# Our passphrase may be just temporary for the serialization, and have
# nothing to do with the key. So don't store it.
#$key->{Password} = $passphrase unless defined $key->{Password};
$key;
}
sub serialize {
my($key, %params) = @_;
# We could reveal it, but (1) what if it was hidden with a different
# password, and (2) they may not want to revealed (even if hidden after).
croak "Cowardly refusing to serialize a hidden key"
if $key->{Hidden};
my $passphrase = defined $params{Password} ? $params{Password}
: defined $key->Password ? $key->Password
: '';
my $cipher_name = defined $params{Cipher} ? $params{Cipher}
: defined $key->Cipher ? $key->Cipher
: 'Blowfish';
# If they've given us no passphrase, we will be unencrypted.
my $cipher_type = 0;
if ($passphrase ne '') {
$cipher_type = $CIPHERS_TEXT{ uc $cipher_name };
croak "Unknown cipher: '$cipher_name'" unless defined $cipher_type;
}
my $buffer = new Crypt::RSA::Key::Private::SSH::Buffer;
my($check1, $check2);
$buffer->put_int8($check1 = int rand 255);
$buffer->put_int8($check2 = int rand 255);
$buffer->put_int8($check1);
$buffer->put_int8($check2);
$buffer->put_mp_int($key->d);
$buffer->put_mp_int($key->u);
$buffer->put_mp_int($key->p);
$buffer->put_mp_int($key->q);
$buffer->put_int8(0)
while $buffer->length % 8;
my $encrypted = new Crypt::RSA::Key::Private::SSH::Buffer;
$encrypted->put_chars(PRIVKEY_ID);
$encrypted->put_int8(0);
$encrypted->put_int8($cipher_type);
$encrypted->put_int32(0);
$encrypted->put_int32(Crypt::RSA::DataFormat::bitsize($key->n));
$encrypted->put_mp_int($key->n);
$encrypted->put_mp_int($key->e);
$encrypted->put_str($key->Identity || '');
if ($cipher_type) {
my $cipher_names = $CIPHERS{$cipher_type};
my $cipher_name;
foreach my $name (@$cipher_names) {
my $class = "Crypt::$name";
(my $file = $class) =~ s=::|'=/=g;
if ( eval { require "$file.pm"; 1 } ) {
$cipher_name = $name; last;
}
}
if (!defined $cipher_name) {
croak "Unsupported cipher '$cipher_names->[0]': $@";
}
my $cipher = Crypt::CBC->new( -key => $passphrase,
-cipher => $cipher_name );
$encrypted->append( $cipher->encrypt($buffer->bytes) );
}
else {
$encrypted->append($buffer->bytes);
}
$encrypted->bytes;
}
sub hide {}
=head1 NAME
Crypt::RSA::Key::Private::SSH - SSH Private Key Import
=head1 SYNOPSIS
Crypt::RSA::Key::Private::SSH is a class derived from
Crypt::RSA::Key::Private that provides serialize() and
deserialize() methods for SSH keys in the SSH1 format.
Alternative formats (SSH2, PEM) are not implemented.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt> wrote the original version.
Dana Jacobsen E<lt>dana@acm.orgE<gt> wrote the new version.
=cut
1;

View File

@@ -0,0 +1,190 @@
package Crypt::RSA::Key::Public;
use strict;
use warnings;
## Crypt::RSA::Key::Public
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use vars qw($AUTOLOAD);
use Carp;
use Data::Dumper;
use base 'Crypt::RSA::Errorhandler';
use Math::BigInt try => 'GMP, Pari';
$Crypt::RSA::Key::Public::VERSION = '1.99';
sub new {
my ($class, %params) = @_;
my $self = { Version => $Crypt::RSA::Key::Public::VERSION };
if ($params{Filename}) {
bless $self, $class;
$self = $self->read (%params);
return bless $self, $class;
} else {
return bless $self, $class;
}
}
sub AUTOLOAD {
my ($self, $value) = @_;
my $key = $AUTOLOAD; $key =~ s/.*:://;
if ($key =~ /^n|e$/) {
if (defined $value) {
if (ref $value eq 'Math::BigInt') {
$self->{$key} = $value;
} elsif (ref $value eq 'Math::Pari') {
$self->{$key} = Math::BigInt->new($value->pari2pv);
} else {
$self->{$key} = Math::BigInt->new("$value");
}
}
if (defined $self->{$key}) {
$self->{$key} = Math::BigInt->new("$self->{$key}") unless ref($self->{$key}) eq 'Math::BigInt';
return $self->{$key};
}
return;
} elsif ($key =~ /^Identity$/) {
$self->{$key} = $value if $value;
return $self->{$key};
}
}
sub DESTROY {
my $self = shift;
undef $self;
}
sub check {
my $self = shift;
return $self->error ("Incomplete key.")
unless defined $self->n && defined $self->e;
return 1;
}
sub write {
my ($self, %params) = @_;
$self->hide();
my $string = $self->serialize (%params);
open(my $disk, '>', $params{Filename}) or
croak "Can't open $params{Filename} for writing.";
binmode $disk;
print $disk $string;
close $disk;
}
sub read {
my ($self, %params) = @_;
open(my $disk, '<', $params{Filename}) or
croak "Can't open $params{Filename} to read.";
binmode $disk;
my @key = <$disk>;
close $disk;
$self = $self->deserialize (String => \@key);
return $self;
}
sub serialize {
my ($self, %params) = @_;
# Convert bigints to strings
foreach my $key (qw/n e/) {
$self->{$key} = $self->{$key}->bstr if ref($self->{$key}) eq 'Math::BigInt';
}
return Dumper $self;
}
sub deserialize {
my ($self, %params) = @_;
my $string = join'', @{$params{String}};
$string =~ s/\$VAR1 =//;
$self = eval $string;
return $self;
}
1;
=head1 NAME
Crypt::RSA::Key::Public -- RSA Public Key Management.
=head1 SYNOPSIS
$key = new Crypt::RSA::Key::Public;
$key->write ( Filename => 'rsakeys/banquo.public' );
$akey = new Crypt::RSA::Key::Public (
Filename => 'rsakeys/banquo.public'
);
=head1 DESCRIPTION
Crypt::RSA::Key::Public provides basic key management functionality for
Crypt::RSA public keys. Following methods are available:
=over 4
=item B<new()>
The constructor. Reads the public key from a disk file when
called with a C<Filename> argument.
=item B<write()>
Causes the key to be written to a disk file specified by the
C<Filename> argument.
=item B<read()>
Causes the key to be read from a disk file specified by
C<Filename> into the object.
=item B<serialize()>
Creates a Data::Dumper(3) serialization of the private key and
returns the string representation.
=item B<deserialize()>
Accepts a serialized key under the C<String> parameter and
coverts it into the perl representation stored in the object.
=item C<check()>
Check the consistency of the key. Returns undef on failure.
=back
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA::Key(3), Crypt::RSA::Key::Private(3)
=cut

View File

@@ -0,0 +1,53 @@
package Crypt::RSA::Key::Public::SSH;
use strict;
use warnings;
## Crypt::RSA::Key::Private::SSH
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use Carp qw/croak/;
use Crypt::RSA::DataFormat qw(bitsize);
use Crypt::RSA::Key::Public;
use base qw( Crypt::RSA::Key::Public );
sub deserialize {
my ($self, %params) = @_;
my $string = $params{String};
croak "Must supply String=>'blob' to deserialize" unless defined $string;
$string = join('', @$string) if ref($string) eq 'ARRAY';
my ($bitsize, $e, $n, $ident) = split /\s/, $string, 4;
$self->n ($n);
$self->e ($e);
$self->Identity ($ident);
return $self;
}
sub serialize {
my ($self, %params) = @_;
my $bitsize = bitsize ($self->n);
my $string = join ' ', $bitsize, $self->e, $self->n, $self->Identity;
return $string;
}
1;
=head1 NAME
Crypt::RSA::Key::Public::SSH - SSH Public Key Import
=head1 SYNOPSIS
Crypt::RSA::Key::Public::SSH is a class derived from
Crypt::RSA::Key::Public that provides serialize() and
deserialze() methods for SSH 2.x keys.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=cut

View File

@@ -0,0 +1,140 @@
package Crypt::RSA::Primitives;
use strict;
use warnings;
## Crypt::RSA::Primitives -- Cryptography and encoding primitives
## used by Crypt::RSA.
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use base 'Crypt::RSA::Errorhandler';
use Crypt::RSA::Debug qw(debug);
use Math::BigInt try => 'GMP, Pari';
use Carp;
sub new {
return bless {}, shift;
}
sub core_encrypt {
# procedure:
# c = (m ** e) mod n
my ($self, %params) = @_;
my $key = $params{Key};
$self->error ("Bad key.", \%params, $key) unless $key->check();
my $plaintext = (defined $params{Message}) ? $params{Message} : $params{Plaintext};
$plaintext = Math::BigInt->new("$plaintext") if ref($plaintext) ne 'Math::BigInt';
debug ("pt == $plaintext");
my $e = $key->e;
my $n = $key->n;
return $self->error ("Numeric representation of plaintext is out of bound.",
\$plaintext, $key, \%params) if $plaintext > $n;
my $c = $plaintext->bmodpow($e, $n);
debug ("ct == $c");
$n = undef;
$e = undef;
return $c;
}
sub core_decrypt {
# procedure:
# p = (c ** d) mod n
my ($self, %params) = @_;
my $key = $params{Key};
$self->error ("Bad key.") unless $key->check();
my $cyphertext = defined $params{Cyphertext} ? $params{Cyphertext} : $params{Ciphertext};
$cyphertext = Math::BigInt->new("$cyphertext") if ref($cyphertext) ne 'Math::BigInt';
my $n = $key->n;
my $d = $key->d;
return $self->error ("Decryption error.") if $cyphertext > $n;
my $pt;
if ($key->p && $key->q) {
# Garner's CRT algorithm
my $p = $key->p;
my $q = $key->q;
$key->u ($p->copy->bmodinv($q)) unless defined $key->u;
$key->dp($d % ($p-1) ) unless defined $key->dp;
$key->dq($d % ($q-1) ) unless defined $key->dq;
my $u = $key->u;
my $dp = $key->dp;
my $dq = $key->dq;
my $p2 = ($cyphertext % $p)->bmodpow($dp, $p);
my $q2 = ($cyphertext % $q)->bmodpow($dq, $q);
$pt = $p2 + ($p * ((($q2 - $p2) * $u) % $q));
}
else {
$pt = $cyphertext->copy->bmodpow($d, $n);
}
debug ("ct == $cyphertext");
debug ("pt == $pt");
return $pt;
}
sub core_sign {
my ($self, %params) = @_;
$params{Cyphertext} = $params{Message} || $params{Plaintext};
return $self->core_decrypt (%params);
}
sub core_verify {
my ($self, %params) = @_;
$params{Plaintext} = $params{Signature};
return $self->core_encrypt (%params);
}
1;
=head1 NAME
Crypt::RSA::Primitives - RSA encryption, decryption, signature and verification primitives.
=head1 SYNOPSIS
my $prim = new Crypt::RSA::Primitives;
my $ctxt = $prim->core_encrypt (Key => $key, Plaintext => $string);
my $ptxt = $prim->core_decrypt (Key => $key, Cyphertext => $ctxt);
my $sign = $prim->core_sign (Key => $key, Message => $string);
my $vrfy = $prim->core_verify (Key => $key, Signature => $sig);
=head1 DESCRIPTION
This module implements RSA encryption, decryption, signature and
verification primitives. These primitives should only be used in the
context of an encryption or signing scheme. See Crypt::RSA::ES::OAEP(3),
and Crypt::RSA::SS::PSS(3) for the implementation of two such schemes.
=head1 ERROR HANDLING
See B<ERROR HANDLING> in Crypt::RSA(3) manpage.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3), Crypt::RSA::Key(3), Crypt::RSA::ES::OAEP(3),
Crypt::RSA::SS::PSS(3)
=cut

View File

@@ -0,0 +1,275 @@
package Crypt::RSA::SS::PKCS1v15;
use strict;
use warnings;
## Crypt::RSA::SS:PKCS1v15
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use base 'Crypt::RSA::Errorhandler';
use Crypt::RSA::DataFormat qw(octet_len os2ip i2osp h2osp);
use Crypt::RSA::Primitives;
use Crypt::RSA::Debug qw(debug);
use Digest::SHA qw(sha1 sha224 sha256 sha384 sha512);
use Digest::MD5 qw(md5);
use Digest::MD2 qw(md2);
$Crypt::RSA::SS::PKCS1v15::VERSION = '1.99';
# See if we have a bug-fixed RIPEMD-160.
my $ripe_hash = undef;
if (eval { require Crypt::RIPEMD160; $Crypt::RIPEMD160::VERSION >= 0.05; }) {
$ripe_hash = sub { my $r=new Crypt::RIPEMD160; $r->add(shift); $r->digest();};
}
sub new {
my ($class, %params) = @_;
my $self = bless {
primitives => new Crypt::RSA::Primitives,
digest => $params{Digest} || 'SHA1',
encoding => {
# See http://rfc-ref.org/RFC-TEXTS/3447/chapter9.html
MD2 =>[\&md2, "30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10"],
MD5 =>[\&md5, "30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10"],
SHA1 =>[\&sha1, "30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14"],
SHA224=>[\&sha224,"30 2d 30 0d 06 09 60 86 48 01 65 03 04 02 04 05 00 04 1c"],
SHA256=>[\&sha256,"30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20"],
SHA384=>[\&sha384,"30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30"],
SHA512=>[\&sha512,"30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40"],
RIPEMD160=>[$ripe_hash,"30 21 30 09 06 05 2B 24 03 02 01 05 00 04 14"],
},
VERSION => $Crypt::RSA::SS::PKCS1v15::VERSION,
}, $class;
# Allow "sha256", "sha-256", "RipeMD-160", etc.
$self->{digest} =~ tr/a-z/A-Z/;
$self->{digest} =~ s/[^A-Z0-9]//g;
if ($params{Version}) {
# do versioning here
}
return $self;
}
sub sign {
my ($self, %params) = @_;
my $key = $params{Key};
my $M = $params{Message} || $params{Plaintext};
return $self->error ("No Message or Plaintext parameter", \$key, \%params) unless $M;
return $self->error ("No Key parameter", \$M, \%params) unless $key;
my $k = octet_len ($key->n);
my $em;
unless ($em = $self->encode ($M, $k)) {
return $self->error ($self->errstr, \$key, \%params, \$M)
if $self->errstr eq "Message too long.";
return $self->error ("Modulus too short.", \$key, \%params, \$M)
if $self->errstr eq "Intended encoded message length too short";
# Other error
return $self->error ($self->errstr, \$key, \%params, \$M);
}
my $m = os2ip ($em);
my $sig = $self->{primitives}->core_sign (Key => $key, Message => $m);
return i2osp ($sig, $k);
}
sub verify {
my ($self, %params) = @_;
my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
my $S = $params{Signature};
return $self->error ("No Message or Plaintext parameter", \$key, \%params) unless $M;
return $self->error ("No Key parameter", \$M, \$S, \%params) unless $key;
return $self->error ("No Signature parameter", \$key, \$M, \%params) unless $S;
my $k = octet_len ($key->n);
return $self->error ("Invalid signature.", \$key, \$M, \%params) if length($S) != $k;
my $s = os2ip ($S);
my $m = $self->{primitives}->core_verify (Key => $key, Signature => $s) ||
$self->error ("Invalid signature.", \$M, $key, \%params);
my $em = i2osp ($m, $k) ||
return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
my $em1;
unless ($em1 = $self->encode ($M, $k)) {
return $self->error ($self->errstr, \$key, \%params, \$M)
if $self->errstr eq "Message too long.";
return $self->error ("Modulus too short.", \$key, \%params, \$M)
if $self->errstr eq "Intended encoded message length too short.";
}
debug ("em: $em"); debug ("em1: $em1");
return 1 if $em eq $em1;
return $self->error ("Invalid signature.", \$M, \$key, \%params);
}
sub encode {
my ($self, $M, $emlen) = @_;
my $encoding = $self->{encoding}->{$self->{digest}};
return $self->error ("Invalid encoding: $self->{digest}") unless defined $encoding;
my ($hashfunc, $digestinfo) = @$encoding;
return $self->error ("encoding method $self->{digest} not supported") unless defined $hashfunc;
# Changed to match RFC 2313 (PKCS#1 v1.5) and 3447 (PKCS#1 v2.1).
# There was apparently some confusion from XML documentation that
# printed a different set of instructions here. See, for example:
# http://osdir.com/ml/mozilla.crypto/2005-05/msg00300.htm
# However, previously emlen was always k-1, so the result ended up
# being identical anyway. One change is that we now return if there
# is not enough padding. Previously the error string would be set
# but processing would continue.
#
# Refs:
# http://rfc-ref.org/RFC-TEXTS/3447/chapter9.html
# https://tools.ietf.org/html/rfc2313
my $H = $hashfunc->($M);
my $alg = h2osp($digestinfo);
return $self->error ("Invalid digest results: $self->{digest}") unless defined $H && length($H) > 0;
my $T = $alg . $H;
return $self->error ("Intended encoded message length too short.", \$M) if $emlen < length($T) + 11;
my $pslen = $emlen - length($T) - 3;
my $PS = chr(0xff) x $pslen;
my $em = chr(0) . chr(1) . $PS . chr(0) . $T;
return $em;
}
sub version {
my $self = shift;
return $self->{VERSION};
}
sub signblock {
return -1;
}
sub verifyblock {
my ($self, %params) = @_;
return octet_len($params{Key}->n);
}
1;
=head1 NAME
Crypt::RSA::SS::PKCS1v15 - PKCS #1 v1.5 signatures.
=head1 SYNOPSIS
my $pkcs = new Crypt::RSA::SS::PKCS1v15 (
Digest => 'MD5'
);
my $signature = $pkcs->sign (
Message => $message,
Key => $private,
) || die $pss->errstr;
my $verify = $pkcs->verify (
Message => $message,
Key => $key,
Signature => $signature,
) || die $pss->errstr;
=head1 DESCRIPTION
This module implements PKCS #1 v1.5 signatures based on RSA. See [13]
for details on the scheme.
=head1 METHODS
=head2 B<new()>
Constructor. Takes a hash as argument with the following key:
=over 4
=item B<Digest>
Name of the Message Digest algorithm. Three Digest algorithms are
supported: MD2, MD5, SHA1, SHA224, SHA256, SHA384, and SHA512.
Digest defaults to SHA1.
=back
=head2 B<version()>
Returns the version number of the module.
=head2 B<sign()>
Computes a PKCS #1 v1.5 signature on a message with the private key of the
signer. sign() takes a hash argument with the following mandatory keys:
=over 4
=item B<Message>
Message to be signed, a string of arbitrary length.
=item B<Key>
Private key of the signer, a Crypt::RSA::Key::Private object.
=back
=head2 B<verify()>
Verifies a signature generated with sign(). Returns a true value on
success and false on failure. $self->errstr is set to "Invalid signature."
or appropriate error on failure. verify() takes a hash argument with the
following mandatory keys:
=over 4
=item B<Key>
Public key of the signer, a Crypt::RSA::Key::Public object.
=item B<Message>
The original signed message, a string of arbitrary length.
=item B<Signature>
Signature computed with sign(), a string.
=back
=head1 ERROR HANDLING
See ERROR HANDLING in Crypt::RSA(3) manpage.
=head1 BIBLIOGRAPHY
See BIBLIOGRAPHY in Crypt::RSA(3) manpage.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3), Crypt::RSA::Primitives(3), Crypt::RSA::Keys(3),
Crypt::RSA::EME::OAEP(3)
=cut

View File

@@ -0,0 +1,271 @@
package Crypt::RSA::SS::PSS;
use strict;
use warnings;
## Crypt::RSA::SS:PSS
##
## Copyright (c) 2001, Vipul Ved Prakash. All rights reserved.
## This code is free software; you can redistribute it and/or modify
## it under the same terms as Perl itself.
use base 'Crypt::RSA::Errorhandler';
use Math::Prime::Util qw/random_bytes/;
use Crypt::RSA::DataFormat qw(octet_len os2ip i2osp octet_xor mgf1);
use Crypt::RSA::Primitives;
use Crypt::RSA::Debug qw(debug);
use Digest::SHA qw(sha1);
$Crypt::RSA::SS::PSS::VERSION = '1.99';
sub new {
my ($class, %params) = @_;
my $self = bless { primitives => new Crypt::RSA::Primitives,
hlen => 20,
VERSION => $Crypt::RSA::SS::PSS::VERSION,
}, $class;
if ($params{Version}) {
# do versioning here
}
return $self;
}
sub sign {
my ($self, %params) = @_;
my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
return $self->error("No Key parameter", \$M, \%params) unless $key;
return $self->error("No Message or Plaintext parameter", \$key, \%params) unless $M;
my $k = octet_len ($key->n);
my $salt = random_bytes($self->{hlen});
my $em = $self->encode ($M, $salt, $k-1);
my $m = os2ip ($em);
my $sig = $self->{primitives}->core_sign (Key => $key, Message => $m);
my $S = i2osp ($sig, $k);
return ($S, $salt) if wantarray;
return $S;
}
sub verify_with_salt {
my ($self, %params) = @_;
my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
my $S = $params{Signature}; my $salt = $params{Salt};
return $self->error("No Key parameter", \$S, \%params) unless $key;
return $self->error("No Signature parameter", \$key, \%params) unless $S;
my $k = octet_len ($key->n);
my $em;
unless ($em = $self->encode ($M, $salt, $k-1)) {
return if $self->errstr eq "Message too long.";
return $self->error ("Modulus too short.", \$M, \$S, \$key, \%params) if
$self->errstr eq "Intended encoded message length too short.";
}
return $self->error ("Invalid signature.", \$M, \$S, $key, \%params) if length($S) < $k;
my $s = os2ip ($S);
my $m = $self->{primitives}->core_verify (Key => $key, Signature => $s) ||
$self->error ("Invalid signature.", \$M, \$S, $key, \%params);
my $em1 = i2osp ($m, $k-1) ||
return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
return 1 if $em eq $em1;
return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
}
sub verify {
my ($self, %params) = @_;
my $key = $params{Key}; my $M = $params{Message} || $params{Plaintext};
my $S = $params{Signature};
return $self->error("No Key parameter", \$S, \$M, \%params) unless $key;
return $self->error("No Signature parameter", \$key, \$M, \%params) unless $S;
return $self->error("No Message or Plaintext parameter", \$key, \$S, \%params) unless $M;
my $k = octet_len ($key->n);
my $s = os2ip ($S);
my $m = $self->{primitives}->core_verify (Key => $key, Signature => $s) ||
$self->error ("Invalid signature.", \$M, \$S, $key, \%params);
my $em1 = i2osp ($m, $k-1) ||
return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
return 1 if $self->verify_with_salt_recovery ($M, $em1);
return $self->error ("Invalid signature.", \$M, \$S, $key, \%params);
}
sub encode {
my ($self, $M, $salt, $emlen) = @_;
return $self->error ("Intended encoded message length too short.", \$M, \$salt )
if $emlen < 2 * $self->{hlen};
my $H = $self->hash ("$salt$M");
my $padlength = $emlen - (2*$$self{hlen});
my $PS = chr(0)x$padlength;
my $db = $salt . $PS;
my $dbmask = $self->mgf ($H, $emlen - $self->{hlen});
my $maskeddb = octet_xor ($db, $dbmask);
my $em = $H . $maskeddb;
return $em;
}
sub verify_with_salt_recovery {
my ($self, $M, $EM) = @_;
my $hlen = $$self{hlen};
my $emlen = length ($EM);
return $self->error ("Encoded message too short.", \$M, \$EM) if $emlen < (2*$hlen);
my $H = substr $EM, 0, $hlen;
my $maskeddb = substr $EM, $hlen;
my $dbmask = $self->mgf ($H, $emlen-$hlen);
my $db = octet_xor ($maskeddb, $dbmask);
my $salt = substr $db, 0, $hlen;
my $PS = substr $db, $hlen;
my $check = chr(0) x ($emlen-(2*$hlen)); debug ("PS: $PS");
return $self->error ("Inconsistent.") unless $check eq $PS;
my $H1 = $self->hash ("$salt$M");
return 1 if $H eq $H1;
return $self->error ("Inconsistent.");
}
sub hash {
my ($self, $data) = @_;
return sha1 ($data);
}
sub mgf {
my ($self, @data) = @_;
return mgf1 (@data);
}
sub version {
my $self = shift;
return $self->{VERSION};
}
sub signblock {
return -1;
}
sub verifyblock {
my ($self, %params) = @_;
return octet_len($params{Key}->n);
}
1;
=head1 NAME
Crypt::RSA::SS::PSS - Probabilistic Signature Scheme based on RSA.
=head1 SYNOPSIS
my $pss = new Crypt::RSA::SS::PSS;
my $signature = $pss->sign (
Message => $message,
Key => $private,
) || die $pss->errstr;
my $verify = $pss->verify (
Message => $message,
Key => $key,
Signature => $signature,
) || die $pss->errstr;
=head1 DESCRIPTION
PSS (Probabilistic Signature Scheme) is a provably secure method of
creating digital signatures with RSA. "Provable" means that the
difficulty of forging signatures can be directly related to inverting
the RSA function. "Probabilistic" alludes to the randomly generated salt
value included in the signature to enhance security. For more details
on PSS, see [4] & [13].
=head1 METHODS
=head2 B<new()>
Constructor.
=head2 B<version()>
Returns the version number of the module.
=head2 B<sign()>
Computes a PSS signature on a message with the private key of the signer.
In scalar context, sign() returns the computed signature. In array
context, it returns the signature and the random salt. The signature can
verified with verify() or verify_with_salt(). sign() takes a hash argument
with the following mandatory keys:
=over 4
=item B<Message>
Message to be signed, a string of arbitrary length.
=item B<Key>
Private key of the signer, a Crypt::RSA::Key::Private object.
=back
=head2 B<verify()>
Verifies a signature generated with sign(). The salt is recovered from the
signature and need not be passed. Returns a true value on success and
false on failure. $self->errstr is set to "Invalid signature." or
appropriate error on failure. verify() takes a hash argument with the
following mandatory keys:
=over 4
=item B<Key>
Public key of the signer, a Crypt::RSA::Key::Public object.
=item B<Message>
The original signed message, a string of arbitrary length.
=item B<Signature>
Signature computed with sign(), a string.
=item B<Version>
Version of the module that was used for creating the Signature. This is an
optional argument. When present, verify() will ensure before proceeding
that the installed version of the module can successfully verify the
Signature.
=back
=head2 B<verify_with_salt()>
Verifies a signature given the salt. Takes the same arguments as verify()
in addition to B<Salt>, which is a 20-byte string returned by sign() in
array context.
=head1 ERROR HANDLING
See ERROR HANDLING in Crypt::RSA(3) manpage.
=head1 BIBLIOGRAPHY
See BIBLIOGRAPHY in Crypt::RSA(3) manpage.
=head1 AUTHOR
Vipul Ved Prakash, E<lt>mail@vipul.netE<gt>
=head1 SEE ALSO
Crypt::RSA(3), Crypt::RSA::Primitives(3), Crypt::RSA::Keys(3),
Crypt::RSA::EME::OAEP(3)
=cut