Improve cryptographic functions (#1823)

This commit is contained in:
Xavier Guimard 2019-06-27 18:39:01 +02:00
parent e23611b73b
commit 44a6e25851
21 changed files with 123 additions and 49 deletions

7
debian/NEWS vendored
View File

@ -1,3 +1,10 @@
lemonldap-ng (2.0.5-1) unstable; urgency=medium
This version adds some improvements in cryptographic functions. To take
advantage of them, you must change the master key of LemonLDAP::NG.
-- Xavier Guimard <yadd@debian.org> Thu, 27 Jun 2019 23:19:09 +0200
lemonldap-ng (2.0.0-1) unstable; urgency=medium
2.0 is a major release, many things have been changed. You must read

4
debian/control vendored
View File

@ -211,7 +211,8 @@ Recommends: libapache-session-browseable-perl,
libdbi-perl,
libhttp-parser-xs-perl,
libjson-xs-perl,
liblwp-protocol-https-perl
liblwp-protocol-https-perl,
libstring-random-perl
Suggests: libconvert-base32-perl,
libnet-ldap-perl,
libsoap-lite-perl,
@ -277,7 +278,6 @@ Recommends: libcrypt-openssl-bignum-perl,
libgd-securityimage-perl,
libmime-tools-perl,
libnet-ldap-perl,
libstring-random-perl,
libunicode-string-perl
Suggests: libcrypt-u2f-server-perl,
libdbi-perl,

View File

@ -4,13 +4,13 @@
"Xavier Guimard <x.guimard@free.fr>, Clément Oudot <clement@oodo.net>"
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010",
"generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010",
"license" : [
"open_source"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
"version" : 2
},
"name" : "Lemonldap-NG-Common",
"no_index" : {
@ -41,7 +41,8 @@
"DBI" : "0",
"LWP::Protocol::https" : "0",
"Net::LDAP" : "0",
"SOAP::Lite" : "0"
"SOAP::Lite" : "0",
"String::Random" : "0"
},
"requires" : {
"Apache::Session" : "0",
@ -72,5 +73,5 @@
"x_MailingList" : "mailto:lemonldap-ng-dev@ow2.org"
},
"version" : "v2.0.4",
"x_serialization_backend" : "JSON::PP version 2.27400_02"
"x_serialization_backend" : "JSON::PP version 2.97001"
}

View File

@ -9,7 +9,7 @@ build_requires:
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010'
generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010'
license: open_source
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@ -28,6 +28,7 @@ recommends:
LWP::Protocol::https: '0'
Net::LDAP: '0'
SOAP::Lite: '0'
String::Random: '0'
requires:
Apache::Session: '0'
Cache::Cache: '0'

View File

@ -34,6 +34,7 @@ WriteMakefile(
'Convert::Base32' => 0,
'Cookie::Baker::XS' => 0,
'Crypt::URandom' => 0,
'String::Random' => 0,
'DBI' => 0,
'Net::LDAP' => 0,
'SOAP::Lite' => 0,

View File

@ -11,10 +11,33 @@ package Lemonldap::NG::Common::Crypto;
use strict;
use Crypt::Rijndael;
use MIME::Base64;
use Digest::MD5 qw(md5);
use Digest::SHA;
use bytes;
our $VERSION = '2.0.0';
my ( $newIv, $randG, $hash );
$hash = \&Digest::SHA::sha256;
use constant HMAC_LENGTH => 32;
use constant IV_LENGTH => 16;
BEGIN {
eval { require Crypt::URandom; Crypt::URandom::urandom(IV_LENGTH) };
if ($@) {
$newIv = sub {
return bytes::substr( Digest::SHA::sha1( rand() . time . {} ),
0, IV_LENGTH );
};
$randG = sub { return int( rand( $_[0] ) ) };
}
else {
$newIv = sub { return Crypt::URandom::urandom(IV_LENGTH) };
$randG = sub {
return
int( unpack( "C", Crypt::URandom::urandom(1) ) * $_[0] / 256 );
};
}
}
our $msg;
@ -44,7 +67,7 @@ sub _getCipher {
my ( $self, $key ) = @_;
$key ||= "";
$self->{ciphers}->{$key} ||=
Crypt::Rijndael->new( md5( $self->{key}, $key ), $self->{mode} );
Crypt::Rijndael->new( $hash->( $self->{key}, $key ), $self->{mode} );
return $self->{ciphers}->{$key};
}
@ -53,13 +76,23 @@ sub _getCipher {
# @param data data to encrypt
# @return encrypted data in Base64 format
sub encrypt {
my ( $self, $data ) = @_;
my ( $self, $data, $low ) = @_;
# pad $data so that its length be multiple of 16 bytes
my $l = bytes::length($data) % 16;
$data .= "\0" x ( 16 - $l ) unless ( $l == 0 );
eval { $data = encode_base64( $self->_getCipher->encrypt($data), '' ); };
my $iv =
$low
? bytes::substr( Digest::SHA::sha1( rand() . time . {} ), 0, IV_LENGTH )
: $newIv->();
my $hmac = $hash->($data);
eval {
$data =
encode_base64(
$iv . $self->_getCipher->set_iv($iv)->encrypt( $hmac . $data ),
'' );
};
if ($@) {
$msg = "Crypt::Rijndael error : $@";
return undef;
@ -81,11 +114,22 @@ sub decrypt {
$data =~ s/%2F/\//ig;
$data =~ s/%3D/=/ig;
$data =~ s/%0A/\n/ig;
eval { $data = $self->_getCipher->decrypt( decode_base64($data) ); };
$data = decode_base64($data);
my $iv;
$iv = bytes::substr( $data, 0, IV_LENGTH );
$data = bytes::substr( $data, IV_LENGTH );
eval { $data = $self->_getCipher->set_iv($iv)->decrypt($data); };
if ($@) {
$msg = "Crypt::Rijndael error : $@";
return undef;
}
my $hmac = bytes::substr( $data, 0, HMAC_LENGTH );
$data = bytes::substr( $data, HMAC_LENGTH );
if ( $hash->($data) ne $hmac ) {
$msg = "Bad MAC";
return undef;
}
else {
$msg = '';
@ -141,15 +185,34 @@ sub _cryptHex {
"Lemonldap::NG::Common::Crypto::${sub}Hex error : data length must be multiple of 32";
return undef;
}
my $iv;
if ( $sub eq 'encrypt' ) {
$iv = $newIv->();
}
$data = pack "H*", $data;
eval { $data = $self->_getCipher($key)->$sub($data); };
if ( $sub eq 'decrypt' ) {
$iv = bytes::substr( $data, 0, IV_LENGTH );
$data = bytes::substr( $data, IV_LENGTH );
}
eval { $data = $self->_getCipher($key)->set_iv($iv)->$sub($data); };
if ($@) {
$msg = "Crypt::Rijndael error : $@";
return undef;
}
if ( $sub eq 'encrypt' ) {
$data = $iv . $data;
}
$msg = "";
$data = unpack "H*", $data;
return $data;
}
sub srandom {
eval { require String::Random };
if ($@) {
die 'Missing recommended dependency to String::Random';
}
return String::Random->new( rand_gen => $randG );
}
1;

View File

@ -12,7 +12,7 @@ use strict;
use Convert::PEM;
use Crypt::OpenSSL::RSA;
use Lemonldap::NG::Common::Conf;
use String::Random qw(random_string);
use Lemonldap::NG::Common::Crypto;
my $debug = 0;
@ -28,9 +28,10 @@ print "Configuration loaded\n" if $debug;
#=============================================================================
# Generate new key
#=============================================================================
my $rsa = Crypt::OpenSSL::RSA->generate_key(2048);
my $key_id = random_string("ssssssssss");
my $keys = {
my $rsa = Crypt::OpenSSL::RSA->generate_key(2048);
my $key_id =
Lemonldap::NG::Common::Crypto::srandom()->randpattern("ssssssssss");
my $keys = {
'private' => $rsa->get_private_key_string(),
'public' => $rsa->get_public_key_x509_string(),
'id' => $key_id,

View File

@ -5,7 +5,7 @@
# change 'tests => 1' to 'tests => last_test_to_print';
use Test::More tests => 21;
use Test::More tests => 22;
use Digest::MD5 qw(md5 md5_hex md5_base64);
use strict;
@ -30,7 +30,11 @@ foreach my $i ( 1 .. 17 ) {
my $s = '';
$s = join( '', map { chr( int( rand(94) ) + 33 ) } ( 1 .. $i ) );
ok( $c->decrypt( $c->encrypt($s) ) eq $s,
"Test of base64 encrypting with $i characters string" );
"Test of base64 encrypting with $i characters string" )
or diag "Source: $s\nCypher: "
. $c->encrypt($s)
. "\nUncipher:"
. $c->decrypt( $c->encrypt($s) );
}
my $data = md5_hex(rand);
@ -42,6 +46,9 @@ ok(
# Test a long value, and replace carriage return by %0A
my $long = "f5a1f72e7ab2f7712855a068af0066f36bfcf2c87e6feb9cf4200da1868e1dfe";
my $cryptedlong =
"Da6sYxp9NCXv8+8TirqHmPWwTQHyEGmkCBGCLCX/81dPSMwIQVQNV7X9KG3RrKZfyRmzJR6DZYdU%0Ab75+VH3+CA==";
ok( $c->decrypt($cryptedlong) eq $long, "Test of long value encrypting" );
ok( $c->decrypt( $c->encrypt($long) ) eq $long,
"Test of long value encrypting" );
ok(
$c->decryptHex( $c->encryptHex($long) ) eq $long,
"Test of long value encrypting (hex)"
);

View File

@ -4,13 +4,13 @@
"Xavier Guimard <x.guimard@free.fr>, Clément Oudot <clement@oodo.net>"
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010",
"generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010",
"license" : [
"open_source"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
"version" : 2
},
"name" : "Lemonldap-NG-Handler",
"no_index" : {
@ -59,5 +59,5 @@
"x_MailingList" : "mailto:lemonldap-ng-dev@ow2.org"
},
"version" : "v2.0.4",
"x_serialization_backend" : "JSON::PP version 2.27400_02"
"x_serialization_backend" : "JSON::PP version 2.97001"
}

View File

@ -11,7 +11,7 @@ build_requires:
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010'
generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010'
license: open_source
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html

View File

@ -103,7 +103,7 @@ sub build_jail {
# Import crypto methods for jail
sub encrypt {
return &Lemonldap::NG::Handler::Main::tsv->{cipher}->encrypt(@_);
return &Lemonldap::NG::Handler::Main::tsv->{cipher}->encrypt( $_[0], 1 );
}
sub token {

View File

@ -4,13 +4,13 @@
"Xavier Guimard <x.guimard@free.fr>, Clément Oudot <clement@oodo.net>"
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010",
"generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010",
"license" : [
"open_source"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
"version" : 2
},
"name" : "Lemonldap-NG-Manager",
"no_index" : {
@ -55,5 +55,5 @@
"x_MailingList" : "mailto:lemonldap-ng-dev@ow2.org"
},
"version" : "v2.0.4",
"x_serialization_backend" : "JSON::PP version 2.27400_02"
"x_serialization_backend" : "JSON::PP version 2.97001"
}

View File

@ -9,7 +9,7 @@ build_requires:
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010'
generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010'
license: open_source
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html

View File

@ -4,13 +4,13 @@
"Xavier Guimard <x.guimard@free.fr>, Clément Oudot <clement@oodo.net>"
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010",
"generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010",
"license" : [
"open_source"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
"version" : 2
},
"name" : "Lemonldap-NG-Portal",
"no_index" : {
@ -57,7 +57,6 @@
"Net::OpenID::Consumer" : "0",
"Net::OpenID::Server" : "0",
"SOAP::Lite" : "0",
"String::Random" : "0",
"Unicode::String" : "0",
"Web::ID" : "0"
},
@ -78,5 +77,5 @@
"x_MailingList" : "mailto:lemonldap-ng-dev@ow2.org"
},
"version" : "v2.0.4",
"x_serialization_backend" : "JSON::PP version 2.27400_02"
"x_serialization_backend" : "JSON::PP version 2.97001"
}

View File

@ -14,7 +14,7 @@ build_requires:
configure_requires:
ExtUtils::MakeMaker: '0'
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150010'
generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010'
license: open_source
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@ -43,7 +43,6 @@ recommends:
Net::OpenID::Consumer: '0'
Net::OpenID::Server: '0'
SOAP::Lite: '0'
String::Random: '0'
Unicode::String: '0'
Web::ID: '0'
requires:

View File

@ -27,7 +27,6 @@ WriteMakefile(
'Net::OpenID::Consumer' => 0,
'Net::OpenID::Server' => 0,
'SOAP::Lite' => 0,
'String::Random' => 0,
'Unicode::String' => 0,
'Web::ID' => 0,
},

View File

@ -2,7 +2,6 @@ package Lemonldap::NG::Portal::2F::Ext2F;
use strict;
use Mouse;
use String::Random;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_ERROR
@ -38,7 +37,7 @@ sub init {
$self->error("Missing 'ext2FSendCommand' parameter, aborting");
return 0;
}
$self->random( String::Random->new );
$self->random( Lemonldap::NG::Common::Crypto::srandom() );
$self->logo( $self->conf->{ext2fLogo} )
if ( $self->conf->{ext2fLogo} );
return $self->SUPER::init();

View File

@ -2,7 +2,6 @@ package Lemonldap::NG::Portal::2F::Mail2F;
use strict;
use Mouse;
use String::Random;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_BADCREDENTIALS
PE_ERROR
@ -23,7 +22,7 @@ has prefix => ( is => 'ro', default => 'mail' );
has random => (
is => 'rw',
default => sub {
return String::Random->new;
return Lemonldap::NG::Common::Crypto::srandom();
}
);

View File

@ -8,7 +8,6 @@ use Lemonldap::NG::Common::UserAgent;
use Lemonldap::NG::Common::FormEncode;
use XML::Simple;
use MIME::Base64;
use String::Random;
use HTTP::Request; # SOAP call
use POSIX qw(strftime); # Convert SAML2 date into timestamp
use Time::Local; # Convert SAML2 date into timestamp

View File

@ -8,7 +8,6 @@ package Lemonldap::NG::Portal::Lib::SMTP;
use strict;
use Mouse;
use JSON qw(from_json);
use String::Random;
use MIME::Entity;
use Email::Sender::Simple qw(sendmail);
use Email::Sender::Transport::SMTP qw();
@ -24,7 +23,7 @@ our $transport;
has random => (
is => 'rw',
default => sub {
return String::Random->new;
return Lemonldap::NG::Common::Crypto::srandom();
}
);
has charset => (

View File

@ -44,8 +44,8 @@ my $id1 = expectCookie($res);
my $id2 = expectCookie( $res, 'lemonldaphttp' );
# Check lemonldap Cookie
ok( $id1 =~ /^\w{64}$/, " -> Get cookie : lemonldap=something" )
or explain( $res->[1], "Set-Cookie: lemonldap=$id1" );
ok( $id1 =~ /^\w{64}$/, " -> https cookie is 64 char long" )
or explain( $id1, '64-char string' );
ok( ${ $res->[1] }[3] =~ /HttpOnly=1/, " -> Cookie 'lemonldap' is HttpOnly" )
or explain( $res->[1] );
ok( ${ $res->[1] }[3] =~ /secure/, " -> Cookie 'lemonldap' is secure" )
@ -53,8 +53,8 @@ ok( ${ $res->[1] }[3] =~ /secure/, " -> Cookie 'lemonldap' is secure" )
count(3);
# Check lemonldaphttp Cookie
ok( $id2 =~ /^\w{64}$/, " -> Get cookie lemonldaphttp=something" )
or explain( $res->[1], "Set-Cookie: lemonldaphttp=$id2" );
ok( length($id2) % 32 == 0, " -> http cookie is 96 byte long" )
or explain( $id2, '\w x 32 string' );
ok(
${ $res->[1] }[5] =~ /HttpOnly=1/,
" -> Cookie 'lemonldaphttp' is HttpOnly"