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,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