Subject: | Add support for the SHA-2 family of cryptographic hash functions |
Hi,
I noticed that Authen-Passphrase currently lacks support for the SHA-2 family of cryptographic hash functions when I wanted to use salted SHA-512 with DBIx::Class::PassphraseColumn. The attached patch would change this by adding support for SHA-{224,256,384,512} in unsalted and salted variants. Unfortunately, RFC 2307 does not define the scheme for these functions, so I used eg. SSHA512 for salted SHA-512, which is also done by Crypt::SaltedHash and the OpenLDAP module which provides support for SHA-2 [1].
Best regards
Manfred
[1] http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=contrib/slapd-modules/passwd/sha2/README;hb=HEAD
Subject: | sha-2-support.patch |
diff --git a/Build.PL b/Build.PL
index b39ab56..686757f 100644
--- a/Build.PL
+++ b/Build.PL
@@ -31,10 +31,10 @@ Module::Build->new(
"Crypt::PasswdMD5" => "1.0",
"Crypt::UnixCrypt_XS" => "0.08",
"Data::Entropy::Algorithms" => 0,
- "Digest" => "1.00",
+ "Digest" => "1.11",
"Digest::MD4" => "1.2",
"Digest::MD5" => "1.9953",
- "Digest::SHA" => 0,
+ "Digest::SHA" => "5.60",
"MIME::Base64" => "2.21",
"Module::Runtime" => "0.011",
"Params::Classify" => 0,
diff --git a/lib/Authen/Passphrase.pm b/lib/Authen/Passphrase.pm
index 2fb3550..6445a31 100644
--- a/lib/Authen/Passphrase.pm
+++ b/lib/Authen/Passphrase.pm
@@ -401,6 +401,11 @@ identifier is followed by the user's username.
The SHA-1 digest of the passphrase is stored. See
L<Authen::Passphrase::SaltedDigest>.
+=item B<{SHA{224,256,384,512}}>
+
+The SHA-2, {224,256,384,512} bit digest of the passphrase is stored. See
+L<Authen::Passphrase::SaltedDigest>.
+
=item B<{SMD5}>
The MD5 digest of the passphrase plus a salt is stored. See
@@ -411,6 +416,11 @@ L<Authen::Passphrase::SaltedDigest>.
The SHA-1 digest of the passphrase plus a salt is stored.
See L<Authen::Passphrase::SaltedDigest>.
+=item B<{SSHA{224,256,384,512}}>
+
+The SHA-2, {224,256,384,512} bit digest of the passphrase plus a salt is stored.
+See L<Authen::Passphrase::SaltedDigest>.
+
=item B<{UNIX}>
Not a real passphrase scheme, but a placeholder to indicate that Unix
@@ -440,8 +450,16 @@ my %rfc2307_scheme_handler = (
"RMD160" => [ "Authen::Passphrase::SaltedDigest", 0.008 ],
"SASL" => sub($) { croak "{SASL} is a placeholder" },
"SHA" => [ "Authen::Passphrase::SaltedDigest", 0.008 ],
+ "SHA224" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
+ "SHA256" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
+ "SHA384" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
+ "SHA512" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
"SMD5" => [ "Authen::Passphrase::SaltedDigest", 0.008 ],
"SSHA" => [ "Authen::Passphrase::SaltedDigest", 0.008 ],
+ "SSHA224" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
+ "SSHA256" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
+ "SSHA384" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
+ "SSHA512" => [ "Authen::Passphrase::SaltedDigest", 0.009 ],
"UNIX" => sub($) { croak "{UNIX} is a placeholder" },
# "WM-CRY" is handled specially
);
diff --git a/lib/Authen/Passphrase/SaltedDigest.pm b/lib/Authen/Passphrase/SaltedDigest.pm
index cc86ae4..e20dc5a 100644
--- a/lib/Authen/Passphrase/SaltedDigest.pm
+++ b/lib/Authen/Passphrase/SaltedDigest.pm
@@ -81,7 +81,7 @@ use MIME::Base64 2.21 qw(encode_base64 decode_base64);
use Module::Runtime 0.011 qw(is_valid_module_name use_module);
use Params::Classify 0.000 qw(is_string is_blessed);
-our $VERSION = "0.008";
+our $VERSION = "0.009";
use parent "Authen::Passphrase";
@@ -234,8 +234,12 @@ and salt.
The scheme identifiers accepted are "B<{MD4}>" (unsalted MD4), "B<{MD5}>"
(unsalted MD5), "B<{RMD160}>" (unsalted RIPEMD-160), "B<{SHA}>" (unsalted
-SHA-1), "B<{SMD5}>" (salted MD5), and "B<{SSHA}>" (salted SHA-1).
-All scheme identifiers are recognised case-insensitively.
+SHA-1), "B<{SHA224}>" (unsalted SHA-224), "B<{SHA256}>" (unsalted SHA-256),
+"B<{SHA384}>" (unsalted SHA-384), "B<{SHA512}>" (unsalted SHA-512), "B<{SMD5}>"
+(salted MD5), "B<{SSHA}>" (salted SHA-1), "B<{SSHA224}>" (salted SHA-224),
+"B<{SSHA256}>" (salted SHA-256), "B<{SSHA384}>" (salted SHA-384) and
+"B<{SSHA512}>" (salted SHA-512). All scheme identifiers are recognised
+case-insensitively.
=cut
@@ -244,8 +248,16 @@ my %rfc2307_scheme_meaning = (
"MD5" => ["MD5", 16, 0],
"RMD160" => ["Crypt::RIPEMD160-", 20, 0],
"SHA" => ["SHA-1", 20, 0],
+ "SHA224" => ["SHA-224", 28, 0],
+ "SHA256" => ["SHA-256", 32, 0],
+ "SHA384" => ["SHA-384", 48, 0],
+ "SHA512" => ["SHA-512", 64, 0],
"SMD5" => ["MD5", 16, 1],
"SSHA" => ["SHA-1", 20, 1],
+ "SSHA224" => ["SHA-224", 28, 1],
+ "SSHA256" => ["SHA-256", 32, 1],
+ "SSHA384" => ["SHA-384", 48, 1],
+ "SSHA512" => ["SHA-512", 64, 1],
);
sub from_rfc2307 {
@@ -390,6 +402,14 @@ my %rfc2307_scheme_for_digest_name = (
"MD5" => "MD5",
"SHA-1" => "SHA",
"SHA1" => "SHA",
+ "SHA-224" => "SHA224",
+ "SHA224" => "SHA224",
+ "SHA-256" => "SHA256",
+ "SHA256" => "SHA256",
+ "SHA-384" => "SHA384",
+ "SHA384" => "SHA384",
+ "SHA-512" => "SHA512",
+ "SHA512" => "SHA512",
);
my %rfc2307_scheme_for_package_name = (
diff --git a/t/sha.t b/t/sha.t
new file mode 100644
index 0000000..54cc149
--- /dev/null
+++ b/t/sha.t
@@ -0,0 +1,72 @@
+use warnings;
+use strict;
+
+use Test::More tests => 248;
+
+use MIME::Base64 2.21 qw(encode_base64);
+
+BEGIN { use_ok "Authen::Passphrase::SaltedDigest"; }
+
+my $ppr = Authen::Passphrase::SaltedDigest
+ ->from_rfc2307("{SHA}qUqP5cyxm6YcTAhz05Hph5gvu9M=");
+ok $ppr;
+is $ppr->algorithm, "SHA-1";
+is $ppr->salt_hex, '';
+is $ppr->hash_hex, "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3";
+
+my %pprs;
+my $i = 0;
+while(<DATA>) {
+ chomp;
+ s/([^ \n]+) ([^ \n]+) ([^ \n]+) *//;
+ my($algorithm, $scheme, $hash_hex) = ($1, $2, $3, $4);
+ my $hash = pack("H*", $hash_hex);
+ my $ppr = Authen::Passphrase::SaltedDigest
+ ->new(algorithm => $algorithm,
+ ($i & 1) ? (hash => $hash) :
+ (hash_hex => $hash_hex));
+ $i++;
+ ok $ppr;
+ is $ppr->salt_hex, '';
+ is $ppr->salt, '';
+ is $ppr->hash_hex, $hash_hex;
+ is $ppr->hash, $hash;
+ eval { $ppr->passphrase }; isnt $@, "";
+ eval { $ppr->as_crypt }; isnt $@, "";
+ is $ppr->as_rfc2307, "{".$scheme."}".encode_base64($hash, "");
+ $pprs{$_} = $ppr;
+
+ $ppr = Authen::Passphrase::SaltedDigest->from_rfc2307($ppr->as_rfc2307);
+ ok $ppr;
+ is $ppr->salt_hex, '';
+ is $ppr->salt, '';
+ is $ppr->hash_hex, $hash_hex;
+ is $ppr->hash, $hash;
+
+ $ppr = Authen::Passphrase->from_rfc2307($ppr->as_rfc2307);
+ ok $ppr;
+ is $ppr->salt_hex, '';
+ is $ppr->salt, '';
+ is $ppr->hash_hex, $hash_hex;
+ is $ppr->hash, $hash;
+}
+
+foreach my $rightphrase (sort keys %pprs) {
+ my $ppr = $pprs{$rightphrase};
+ foreach my $passphrase (sort keys %pprs) {
+ ok ($ppr->match($passphrase) xor $passphrase ne $rightphrase);
+ }
+}
+
+1;
+
+__DATA__
+SHA-1 SHA da39a3ee5e6b4b0d3255bfef95601890afd80709
+SHA-1 SHA b6589fc6ab0dc82cf12099d1c2d40ab994e8410c 0
+SHA-1 SHA 356a192b7913b04c54574d18c28d46e6395428ab 1
+SHA-1 SHA 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 foo
+SHA-1 SHA 98389f5ae1dfdbad80426cb2cf498dbdd56b70c6 supercalifragilisticexpialidocious
+SHA-224 SHA224 07daf010de7f7f0d8d76a76eb8d1eb40182c8d1e7a3877a6686c9bf0 bar
+SHA-256 SHA256 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 test
+SHA-384 SHA384 967004d25de4abc1bd6a7c9a216254a5ac0733e8ad96dc9f1ea0fad9619da7c32d654ec8ad8ba2f9b5728fed6633bd91 baz
+SHA-512 SHA512 0a50261ebd1a390fed2bf326f2673c145582a6342d523204973d0219337f81616a8069b012587cf5635f6925f1b56c360230c19b273500ee013e030601bf2425 foobar
diff --git a/t/ssha.t b/t/ssha.t
index 6d86237..bf963a0 100644
--- a/t/ssha.t
+++ b/t/ssha.t
@@ -1,7 +1,7 @@
use warnings;
use strict;
-use Test::More tests => 70;
+use Test::More tests => 248;
use MIME::Base64 2.21 qw(encode_base64);
@@ -18,12 +18,12 @@ my %pprs;
my $i = 0;
while(<DATA>) {
chomp;
- s/([^ \n]+) ([^ \n]+) *//;
- my($salt_hex, $hash_hex) = ($1, $2);
+ s/([^ \n]+) ([^ \n]+) ([^ \n]+) ([^ \n]+) *//;
+ my($algorithm, $scheme, $salt_hex, $hash_hex) = ($1, $2, $3, $4);
my $salt = pack("H*", $salt_hex);
my $hash = pack("H*", $hash_hex);
my $ppr = Authen::Passphrase::SaltedDigest
- ->new(algorithm => "SHA-1",
+ ->new(algorithm => $algorithm,
($i & 1) ? (salt => $salt) :
(salt_hex => $salt_hex),
($i & 2) ? (hash => $hash) :
@@ -36,8 +36,22 @@ while(<DATA>) {
is $ppr->hash, $hash;
eval { $ppr->passphrase }; isnt $@, "";
eval { $ppr->as_crypt }; isnt $@, "";
- is $ppr->as_rfc2307, "{SSHA}".encode_base64($hash.$salt, "");
+ is $ppr->as_rfc2307, "{".$scheme."}".encode_base64($hash.$salt, "");
$pprs{$_} = $ppr;
+
+ $ppr = Authen::Passphrase::SaltedDigest->from_rfc2307($ppr->as_rfc2307);
+ ok $ppr;
+ is $ppr->salt_hex, $salt_hex;
+ is $ppr->salt, $salt;
+ is $ppr->hash_hex, $hash_hex;
+ is $ppr->hash, $hash;
+
+ $ppr = Authen::Passphrase->from_rfc2307($ppr->as_rfc2307);
+ ok $ppr;
+ is $ppr->salt_hex, $salt_hex;
+ is $ppr->salt, $salt;
+ is $ppr->hash_hex, $hash_hex;
+ is $ppr->hash, $hash;
}
foreach my $rightphrase (sort keys %pprs) {
@@ -50,8 +64,12 @@ foreach my $rightphrase (sort keys %pprs) {
1;
__DATA__
-616263 a9993e364706816aba3e25717850c26c9cd0d89d
-717765 7cd928d1e6457c57c01f3c9442177fc62cafa56f 0
-212121 2fee6a4e9b98f3bd6de8b1960cfb37f8b44d8bb1 1
-787878 76cdd1408a02a44687fe87c98f8dc43678c4ef5f foo
-707966 b264504de2719cebf898608cf950e1da5f3ae28f supercalifragilisticexpialidocious
+SHA-1 SSHA 616263 a9993e364706816aba3e25717850c26c9cd0d89d
+SHA-1 SSHA 717765 7cd928d1e6457c57c01f3c9442177fc62cafa56f 0
+SHA-1 SSHA 212121 2fee6a4e9b98f3bd6de8b1960cfb37f8b44d8bb1 1
+SHA-1 SSHA 787878 76cdd1408a02a44687fe87c98f8dc43678c4ef5f foo
+SHA-1 SSHA 707966 b264504de2719cebf898608cf950e1da5f3ae28f supercalifragilisticexpialidocious
+SHA-224 SSHA224 482939 1186253382c5b15e694b4351a6d525316b945a2fef6db7f7d6ca1d1c bar
+SHA-256 SSHA256 742935 f0851d4e2f92e8320be35e958545ea686ba9cbc6db53f43d8e5f9e005941fbcf test
+SHA-384 SSHA384 395425 501922945b68430fedb3cff4001db4a6a8e19933596cb503ea0db151b92bbe90be0e69fd781f5683780e1579ce752252 baz
+SHA-512 SSHA512 243722 8c692b969c27e9c50a0a9a779929c99bcaf25cfbfaf62c07a2c8eba9d38f5e6affd9ef2bfe7cf49222e7372a21c2fdcc2643823cd4aaf629aa70999525ab5002 foobar