]>
jfr.im git - irc/SurrealServices/srsv.git/blob - branches/erry-devel/SrSv/Hash/SaltedHash.pm
1 #########################################################################################
3 ## Copyright(c) 2007 M2000, Inc. ##
5 ## File: SaltedHash.pm ##
6 ## Author: Adam Schrotenboer ##
11 ## Produces salted hashes for various uses. ##
12 ## This module is licensed under the Lesser GNU Public License version 2.1 ##
14 ## Revision History ##
15 ## ================ ##
16 ## 11/13/07: Initial version. ##
19 #########################################################################################
21 ## For more details refer to the implementation specification document ##
22 ## DRCS-xxxxxx Section x.x ##
24 #########################################################################################
25 package SrSv
::Hash
::SaltedHash
;
39 Produces and verifies salted hashes.
43 This module currently only supports SHA256, and requires Digest::SHA.
44 If Digest::SHA is not available, it will however fallback to an included copy of Digest::SHA::PurePerl
50 if(eval { require Digest
::SHA
; } ) {
51 import Digest
::SHA
qw( sha256_base64 sha256 sha1 );
52 print "SrSv::Hash::SaltedHash using Digest::SHA\n";
54 elsif(eval { require Digest
::SHA
::PurePerl
; } ){
55 import Digest
::SHA
::PurePerl
qw( sha256_base64 sha256 sha1 );
56 print "SrSv::Hash::SaltedHash using Digest::SHA::PurePerl\n";
58 die "Unable to find a suitable SHA implementation\n";
64 SHA512 requires 64bit int operations, and thus will be SLOW on 32bit platforms.
65 Current hash string length with SHA256 and 16byte (128bit) salts is 85 characters
66 Be aware that SHA512 with 16byte salt would take approximately ~130 characters
67 So make sure that your password field can hold strings large enough.
68 It is generally considered pointless to make your salt
69 longer than your hash, so 32bytes is longest that is useful
70 for SHA256 and 64 is longest for SHA512.
71 SrSv has a limit of 127 characters for password strings, so don't use SHA512.
74 use Exporter
'import';
77 HASH_ALGORITHM
=> 'SHA256',
81 my $version = 'v1-'.$constants{HASH_SALT_LEN
}.'-r'.$constants{HASH_ROUNDS
};
82 $constants{HASH_VERSION
} = $version;
83 our @EXPORT = qw( makeHash verifyHash );
84 our @EXPORT_OK = ( @EXPORT, keys(%constants), qw( extractMeta extractSalt padBase64 makeHash_v0 makeHash_v1 ));
85 our %EXPORT_TAGS = ( constants
=> [keys(%constants)] );
86 require constant; import
constant (\
%constants);
90 use MIME
::Base64
qw( encode_base64 decode_base64 );
91 use SrSv
::Hash
::Random
qw( randomBytes randomByte );
95 makeHash($secret, $salt, $algorithm, $version)
97 Salt is assumed to be a BINARY STRING.
99 Algorithm currently can only be 'SHA256'
103 sub makeHash
($;$$$) {
104 return makeHash_v1
(@_);
107 =item makeHash_v1($;$$$)
109 makeHash_v1 ($secret, $salt, $algorithm, $version)
111 returns a string that can be processed thusly
112 my ($algorithm, $version, $salt, $hash) = split(':', $string);
114 my ($revision, $saltsize, $rounds) = split('-', $version);
118 sub makeHash_v1
($;$$$) {
119 my ($secret, $salt, $algorithm, $version) = @_;
120 $algorithm = HASH_ALGORITHM
unless $algorithm;
121 $salt = makeBinSalt
(HASH_SALT_LEN
) unless $salt;
122 $version = HASH_VERSION
unless $version;
123 my $string = "$algorithm:$version:";
124 $string .= encode_base64
($salt, '').':';
125 $string .= padBase64
(__makeHash
($secret . $salt, $algorithm));
130 my ($plaintext, $algorithm) = @_;
131 $algorithm = 'sha256';
132 if($algorithm =~ /^sha256$/i) {
133 return sha256_base64
($plaintext);
135 # Other hash algos haven't been implemented yet
136 die "Unknown hash algorithm \"$algorithm\" \"$plaintext\"\n";
140 sub makeHash_v0
($;$$) {
141 my ($secret, $salt, $algorithm) = @_;
142 $algorithm = 'SHA256' unless $algorithm;
143 $salt = makeBinSalt
(4) unless $salt;
144 my $string = "{S$algorithm}";
145 if($algorithm eq 'SHA256') {
146 $string .= encode_base64
(sha256
($secret . $salt) . $salt, '');
147 } elsif ($algorithm eq 'SHA') {
148 $string .= encode_base64
(sha1
($secret . $salt) . $salt, '');
154 my ($b64_digest) = @_;
155 while (length($b64_digest) % 4) {
163 verifyHash($hash, $plain)
165 Verifies that a given $plain matches $hash
170 my ($hash, $plain) = @_;
171 my ($algorithm, $version, $salt) = extractMeta
($hash);
173 if($version eq 'v0') {
174 $hash2 = makeHash_v0
($plain, $salt, $algorithm);
176 $hash2 = makeHash_v1
($plain, $salt, $algorithm, $version);
179 return ($hash eq $hash2 ? 1 : 0);
182 sub makeBinSalt
(;$) {
184 $len = HASH_SALT_LEN
unless $len;
185 return randomBytes
($len);
192 return ($algorithm, $version, $salt) from $hash.
197 if($input =~ /^\{S(\S+)\}(.*)$/) {
199 my $saltedBinHash = decode_base64
($2);
200 my $salt = substr($saltedBinHash, -4);
201 return ($algorithm, 'v0', $salt);
203 my ($algorithm, $version, $salt, $hash) = split(':', $input);
204 return ($algorithm, $version, decode_base64
($salt));
212 return $salt from $hash.
217 my ($algorithm, $version, $salt) = extractMeta
($input);