scrypt 2.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -2
- data/README.md +3 -3
- data/Rakefile +5 -3
- data/ext/scrypt/Rakefile +5 -1
- data/ext/scrypt/cpusupport.h +105 -0
- data/ext/scrypt/crypto_scrypt.c +257 -0
- data/ext/scrypt/crypto_scrypt.h +1 -0
- data/ext/scrypt/crypto_scrypt_internal.h +0 -0
- data/ext/scrypt/crypto_scrypt_smix.c +214 -0
- data/ext/scrypt/crypto_scrypt_smix.h +14 -0
- data/ext/scrypt/{crypto_scrypt-sse.c → crypto_scrypt_smix_sse2.c} +21 -142
- data/ext/scrypt/crypto_scrypt_smix_sse2.h +16 -0
- data/ext/scrypt/insecure_memzero.c +19 -0
- data/ext/scrypt/insecure_memzero.h +37 -0
- data/ext/scrypt/sha256.c +344 -229
- data/ext/scrypt/sha256.h +84 -50
- data/ext/scrypt/warnp.c +76 -0
- data/ext/scrypt/warnp.h +59 -0
- data/lib/scrypt/security_utils.rb +23 -0
- data/lib/scrypt/version.rb +1 -1
- data/lib/scrypt.rb +4 -3
- data/scrypt.gemspec +4 -3
- data/spec/scrypt/engine_spec.rb +23 -21
- data/spec/scrypt/password_spec.rb +25 -25
- data/spec/scrypt/utils_spec.rb +12 -0
- data.tar.gz.sig +0 -0
- metadata +38 -22
- metadata.gz.sig +2 -2
data/ext/scrypt/sha256.h
CHANGED
@@ -1,61 +1,95 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*
|
5
|
-
* Redistribution and use in source and binary forms, with or without
|
6
|
-
* modification, are permitted provided that the following conditions
|
7
|
-
* are met:
|
8
|
-
* 1. Redistributions of source code must retain the above copyright
|
9
|
-
* notice, this list of conditions and the following disclaimer.
|
10
|
-
* 2. Redistributions in binary form must reproduce the above copyright
|
11
|
-
* notice, this list of conditions and the following disclaimer in the
|
12
|
-
* documentation and/or other materials provided with the distribution.
|
13
|
-
*
|
14
|
-
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
15
|
-
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
17
|
-
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
18
|
-
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
19
|
-
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
20
|
-
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
21
|
-
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
22
|
-
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
23
|
-
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
24
|
-
* SUCH DAMAGE.
|
25
|
-
*
|
26
|
-
* $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $
|
27
|
-
*/
|
28
|
-
|
29
|
-
#ifndef _scrypt_SHA256_H_
|
30
|
-
#define _scrypt_SHA256_H_
|
31
|
-
|
32
|
-
#include <sys/types.h>
|
1
|
+
#ifndef _SHA256_H_
|
2
|
+
#define _SHA256_H_
|
33
3
|
|
4
|
+
#include <stddef.h>
|
34
5
|
#include <stdint.h>
|
35
6
|
|
36
|
-
|
7
|
+
/*
|
8
|
+
* Use #defines in order to avoid namespace collisions with anyone else's
|
9
|
+
* SHA256 code (e.g., the code in OpenSSL).
|
10
|
+
*/
|
11
|
+
#define SHA256_Init libcperciva_SHA256_Init
|
12
|
+
#define SHA256_Update libcperciva_SHA256_Update
|
13
|
+
#define SHA256_Final libcperciva_SHA256_Final
|
14
|
+
#define SHA256_Buf libcperciva_SHA256_Buf
|
15
|
+
#define SHA256_CTX libcperciva_SHA256_CTX
|
16
|
+
#define HMAC_SHA256_Init libcperciva_HMAC_SHA256_Init
|
17
|
+
#define HMAC_SHA256_Update libcperciva_HMAC_SHA256_Update
|
18
|
+
#define HMAC_SHA256_Final libcperciva_HMAC_SHA256_Final
|
19
|
+
#define HMAC_SHA256_Buf libcperciva_HMAC_SHA256_Buf
|
20
|
+
#define HMAC_SHA256_CTX libcperciva_HMAC_SHA256_CTX
|
21
|
+
|
22
|
+
/* Context structure for SHA256 operations. */
|
23
|
+
typedef struct {
|
37
24
|
uint32_t state[8];
|
38
|
-
|
39
|
-
|
40
|
-
}
|
25
|
+
uint64_t count;
|
26
|
+
uint8_t buf[64];
|
27
|
+
} SHA256_CTX;
|
41
28
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
29
|
+
/**
|
30
|
+
* SHA256_Init(ctx):
|
31
|
+
* Initialize the SHA256 context ${ctx}.
|
32
|
+
*/
|
33
|
+
void SHA256_Init(SHA256_CTX *);
|
34
|
+
|
35
|
+
/**
|
36
|
+
* SHA256_Update(ctx, in, len):
|
37
|
+
* Input ${len} bytes from ${in} into the SHA256 context ${ctx}.
|
38
|
+
*/
|
39
|
+
void SHA256_Update(SHA256_CTX *, const void *, size_t);
|
40
|
+
|
41
|
+
/**
|
42
|
+
* SHA256_Final(digest, ctx):
|
43
|
+
* Output the SHA256 hash of the data input to the context ${ctx} into the
|
44
|
+
* buffer ${digest}.
|
45
|
+
*/
|
46
|
+
void SHA256_Final(uint8_t[32], SHA256_CTX *);
|
47
|
+
|
48
|
+
/**
|
49
|
+
* SHA256_Buf(in, len, digest):
|
50
|
+
* Compute the SHA256 hash of ${len} bytes from $in} and write it to ${digest}.
|
51
|
+
*/
|
52
|
+
void SHA256_Buf(const void *, size_t, uint8_t[32]);
|
46
53
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
/* Context structure for HMAC-SHA256 operations. */
|
55
|
+
typedef struct {
|
56
|
+
SHA256_CTX ictx;
|
57
|
+
SHA256_CTX octx;
|
58
|
+
} HMAC_SHA256_CTX;
|
59
|
+
|
60
|
+
/**
|
61
|
+
* HMAC_SHA256_Init(ctx, K, Klen):
|
62
|
+
* Initialize the HMAC-SHA256 context ${ctx} with ${Klen} bytes of key from
|
63
|
+
* ${K}.
|
64
|
+
*/
|
65
|
+
void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t);
|
66
|
+
|
67
|
+
/**
|
68
|
+
* HMAC_SHA256_Update(ctx, in, len):
|
69
|
+
* Input ${len} bytes from ${in} into the HMAC-SHA256 context ${ctx}.
|
70
|
+
*/
|
71
|
+
void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t);
|
72
|
+
|
73
|
+
/**
|
74
|
+
* HMAC_SHA256_Final(digest, ctx):
|
75
|
+
* Output the HMAC-SHA256 of the data input to the context ${ctx} into the
|
76
|
+
* buffer ${digest}.
|
77
|
+
*/
|
78
|
+
void HMAC_SHA256_Final(uint8_t[32], HMAC_SHA256_CTX *);
|
79
|
+
|
80
|
+
/**
|
81
|
+
* HMAC_SHA256_Buf(K, Klen, in, len, digest):
|
82
|
+
* Compute the HMAC-SHA256 of ${len} bytes from ${in} using the key ${K} of
|
83
|
+
* length ${Klen}, and write the result to ${digest}.
|
84
|
+
*/
|
85
|
+
void HMAC_SHA256_Buf(const void *, size_t, const void *, size_t, uint8_t[32]);
|
53
86
|
|
54
87
|
/**
|
55
|
-
*
|
56
|
-
* Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-
|
88
|
+
* PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
|
89
|
+
* Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
|
57
90
|
* write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
|
58
91
|
*/
|
59
|
-
void
|
92
|
+
void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t,
|
93
|
+
uint64_t, uint8_t *, size_t);
|
60
94
|
|
61
|
-
#endif /* !
|
95
|
+
#endif /* !_SHA256_H_ */
|
data/ext/scrypt/warnp.c
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#include <errno.h>
|
2
|
+
#include <stdarg.h>
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <string.h>
|
6
|
+
|
7
|
+
#include "warnp.h"
|
8
|
+
|
9
|
+
static int initialized = 0;
|
10
|
+
static char * name = NULL;
|
11
|
+
|
12
|
+
/* Free the name string. */
|
13
|
+
static void
|
14
|
+
done(void)
|
15
|
+
{
|
16
|
+
|
17
|
+
free(name);
|
18
|
+
name = NULL;
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* warnp_setprogname(progname):
|
23
|
+
* Set the program name to be used by warn() and warnx() to ${progname}.
|
24
|
+
*/
|
25
|
+
void
|
26
|
+
warnp_setprogname(const char * progname)
|
27
|
+
{
|
28
|
+
const char * p;
|
29
|
+
|
30
|
+
/* Free the name if we already have one. */
|
31
|
+
free(name);
|
32
|
+
|
33
|
+
/* Find the last segment of the program name. */
|
34
|
+
for (p = progname; progname[0] != '\0'; progname++)
|
35
|
+
if (progname[0] == '/')
|
36
|
+
p = progname + 1;
|
37
|
+
|
38
|
+
/* Copy the name string. */
|
39
|
+
name = strdup(p);
|
40
|
+
|
41
|
+
/* If we haven't already done so, register our exit handler. */
|
42
|
+
if (initialized == 0) {
|
43
|
+
atexit(done);
|
44
|
+
initialized = 1;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
void
|
49
|
+
warn(const char * fmt, ...)
|
50
|
+
{
|
51
|
+
va_list ap;
|
52
|
+
|
53
|
+
va_start(ap, fmt);
|
54
|
+
fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)");
|
55
|
+
if (fmt != NULL) {
|
56
|
+
fprintf(stderr, ": ");
|
57
|
+
vfprintf(stderr, fmt, ap);
|
58
|
+
}
|
59
|
+
fprintf(stderr, ": %s\n", strerror(errno));
|
60
|
+
va_end(ap);
|
61
|
+
}
|
62
|
+
|
63
|
+
void
|
64
|
+
warnx(const char * fmt, ...)
|
65
|
+
{
|
66
|
+
va_list ap;
|
67
|
+
|
68
|
+
va_start(ap, fmt);
|
69
|
+
fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)");
|
70
|
+
if (fmt != NULL) {
|
71
|
+
fprintf(stderr, ": ");
|
72
|
+
vfprintf(stderr, fmt, ap);
|
73
|
+
}
|
74
|
+
fprintf(stderr, "\n");
|
75
|
+
va_end(ap);
|
76
|
+
}
|
data/ext/scrypt/warnp.h
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#ifndef _WARNP_H_
|
2
|
+
#define _WARNP_H_
|
3
|
+
|
4
|
+
#include <errno.h>
|
5
|
+
|
6
|
+
/* Avoid namespace collisions with BSD <err.h>. */
|
7
|
+
#define warn libcperciva_warn
|
8
|
+
#define warnx libcperciva_warnx
|
9
|
+
|
10
|
+
/**
|
11
|
+
* warnp_setprogname(progname):
|
12
|
+
* Set the program name to be used by warn() and warnx() to ${progname}.
|
13
|
+
*/
|
14
|
+
void warnp_setprogname(const char *);
|
15
|
+
#define WARNP_INIT do { \
|
16
|
+
if (argv[0] != NULL) \
|
17
|
+
warnp_setprogname(argv[0]); \
|
18
|
+
} while (0)
|
19
|
+
|
20
|
+
/* As in BSD <err.h>. */
|
21
|
+
void warn(const char *, ...);
|
22
|
+
void warnx(const char *, ...);
|
23
|
+
|
24
|
+
/*
|
25
|
+
* If compiled with DEBUG defined, print __FILE__ and __LINE__.
|
26
|
+
*/
|
27
|
+
#ifdef DEBUG
|
28
|
+
#define warnline do { \
|
29
|
+
warnx("%s, %d", __FILE__, __LINE__); \
|
30
|
+
} while (0)
|
31
|
+
#else
|
32
|
+
#define warnline
|
33
|
+
#endif
|
34
|
+
|
35
|
+
/*
|
36
|
+
* Call warn(3) or warnx(3) depending upon whether errno == 0; and clear
|
37
|
+
* errno (so that the standard error message isn't repeated later).
|
38
|
+
*/
|
39
|
+
#define warnp(...) do { \
|
40
|
+
warnline; \
|
41
|
+
if (errno != 0) { \
|
42
|
+
warn(__VA_ARGS__); \
|
43
|
+
errno = 0; \
|
44
|
+
} else \
|
45
|
+
warnx(__VA_ARGS__); \
|
46
|
+
} while (0)
|
47
|
+
|
48
|
+
/*
|
49
|
+
* Call warnx(3) and set errno == 0. Unlike warnp, this should be used
|
50
|
+
* in cases where we're reporting a problem which we discover ourselves
|
51
|
+
* rather than one which is reported to us from a library or the kernel.
|
52
|
+
*/
|
53
|
+
#define warn0(...) do { \
|
54
|
+
warnline; \
|
55
|
+
warnx(__VA_ARGS__); \
|
56
|
+
errno = 0; \
|
57
|
+
} while (0)
|
58
|
+
|
59
|
+
#endif /* !_WARNP_H_ */
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# NOTE:: a verbatim copy of https://github.com/rails/rails/blob/c8c660002f4b0e9606de96325f20b95248b6ff2d/activesupport/lib/active_support/security_utils.rb
|
2
|
+
# Please see the Rails license: https://github.com/rails/rails/blob/master/activesupport/MIT-LICENSE
|
3
|
+
|
4
|
+
module SCrypt
|
5
|
+
module SecurityUtils
|
6
|
+
# Constant time string comparison.
|
7
|
+
#
|
8
|
+
# The values compared should be of fixed length, such as strings
|
9
|
+
# that have already been processed by HMAC. This should not be used
|
10
|
+
# on variable length plaintext strings because it could leak length info
|
11
|
+
# via timing attacks.
|
12
|
+
def secure_compare(a, b)
|
13
|
+
return false unless a.bytesize == b.bytesize
|
14
|
+
|
15
|
+
l = a.unpack "C#{a.bytesize}"
|
16
|
+
|
17
|
+
res = 0
|
18
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
19
|
+
res == 0
|
20
|
+
end
|
21
|
+
module_function :secure_compare
|
22
|
+
end
|
23
|
+
end
|
data/lib/scrypt/version.rb
CHANGED
data/lib/scrypt.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# A wrapper for the scrypt algorithm.
|
2
2
|
|
3
3
|
require "scrypt/scrypt_ext"
|
4
|
+
require "scrypt/security_utils"
|
4
5
|
require "openssl"
|
5
6
|
require "scanf"
|
6
7
|
require "ffi"
|
@@ -23,8 +24,8 @@ module SCrypt
|
|
23
24
|
class Engine
|
24
25
|
DEFAULTS = {
|
25
26
|
:key_len => 32,
|
26
|
-
:salt_size =>
|
27
|
-
:max_mem => 1024 * 1024,
|
27
|
+
:salt_size => 32,
|
28
|
+
:max_mem => 16 * 1024 * 1024,
|
28
29
|
:max_memfrac => 0.5,
|
29
30
|
:max_time => 0.2,
|
30
31
|
:cost => nil
|
@@ -246,7 +247,7 @@ module SCrypt
|
|
246
247
|
|
247
248
|
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
|
248
249
|
def ==(secret)
|
249
|
-
|
250
|
+
SecurityUtils.secure_compare(self, SCrypt::Engine.hash_secret(secret, @cost + @salt, self.digest.length / 2))
|
250
251
|
end
|
251
252
|
alias_method :is_password?, :==
|
252
253
|
|
data/scrypt.gemspec
CHANGED
@@ -5,8 +5,9 @@ require "scrypt/version"
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "scrypt"
|
7
7
|
s.version = SCrypt::VERSION
|
8
|
-
s.authors = ["Patrick Hogan", "Stephen von Takach"]
|
9
|
-
s.email = ["pbhogan@gmail.com", "steve@advancedcontrol.com.au"
|
8
|
+
s.authors = ["Patrick Hogan", "Stephen von Takach", "Rene van Paassen" ]
|
9
|
+
s.email = ["pbhogan@gmail.com", "steve@advancedcontrol.com.au",
|
10
|
+
"rene.vanpaassen@gmail.com" ]
|
10
11
|
s.cert_chain = ['certs/stakach.pem']
|
11
12
|
s.license = 'MIT'
|
12
13
|
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
|
@@ -18,7 +19,7 @@ Gem::Specification.new do |s|
|
|
18
19
|
alternative functions such as PBKDF2 or bcrypt.
|
19
20
|
EOF
|
20
21
|
|
21
|
-
s.add_dependency 'ffi-compiler', '>= 0.0
|
22
|
+
s.add_dependency 'ffi-compiler', '>= 1.0.0'
|
22
23
|
s.add_dependency 'rake'
|
23
24
|
s.add_development_dependency "rspec"
|
24
25
|
s.add_development_dependency "rdoc"
|
data/spec/scrypt/engine_spec.rb
CHANGED
@@ -3,34 +3,34 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
|
3
3
|
describe "The SCrypt engine" do
|
4
4
|
it "should calculate a valid cost factor" do
|
5
5
|
first = SCrypt::Engine.calibrate(:max_time => 0.2)
|
6
|
-
SCrypt::Engine.valid_cost?(first).
|
6
|
+
expect(SCrypt::Engine.valid_cost?(first)).to equal(true)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
10
|
|
11
11
|
describe "Generating SCrypt salts" do
|
12
12
|
it "should produce strings" do
|
13
|
-
SCrypt::Engine.generate_salt.
|
13
|
+
expect(SCrypt::Engine.generate_salt).to be_an_instance_of(String)
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should produce random data" do
|
17
|
-
SCrypt::Engine.generate_salt.
|
17
|
+
expect(SCrypt::Engine.generate_salt).not_to equal(SCrypt::Engine.generate_salt)
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should used the saved cost factor" do
|
21
21
|
# Verify cost is different before saving
|
22
22
|
cost = SCrypt::Engine.calibrate(:max_time => 0.01)
|
23
|
-
SCrypt::Engine.generate_salt(:max_time => 30, :max_mem => 64*1024*1024).
|
23
|
+
expect(SCrypt::Engine.generate_salt(:max_time => 30, :max_mem => 64*1024*1024)).not_to start_with(cost)
|
24
24
|
|
25
25
|
cost = SCrypt::Engine.calibrate!(:max_time => 0.01)
|
26
|
-
SCrypt::Engine.generate_salt(:max_time => 30, :max_mem => 64*1024*1024).
|
26
|
+
expect(SCrypt::Engine.generate_salt(:max_time => 30, :max_mem => 64*1024*1024)).to start_with(cost)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
|
31
31
|
describe "Autodetecting of salt cost" do
|
32
32
|
it "should work" do
|
33
|
-
SCrypt::Engine.autodetect_cost("2a$08$c3$randomjunkgoeshere").
|
33
|
+
expect(SCrypt::Engine.autodetect_cost("2a$08$c3$randomjunkgoeshere")).to eq("2a$08$c3$")
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -42,41 +42,43 @@ describe "Generating SCrypt hashes" do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
before :each do
|
45
|
-
@salt = SCrypt::Engine.generate_salt
|
45
|
+
@salt = SCrypt::Engine.generate_salt
|
46
46
|
@password = "woo"
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should produce a string" do
|
50
|
-
SCrypt::Engine.hash_secret(@password, @salt).
|
50
|
+
expect(SCrypt::Engine.hash_secret(@password, @salt)).to be_an_instance_of(String)
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should raise an InvalidSalt error if the salt is invalid" do
|
54
|
-
lambda { SCrypt::Engine.hash_secret(@password, 'nino') }.
|
54
|
+
expect(lambda { SCrypt::Engine.hash_secret(@password, 'nino') }).to raise_error(SCrypt::Errors::InvalidSalt)
|
55
55
|
end
|
56
56
|
|
57
57
|
it "should raise an InvalidSecret error if the secret is invalid" do
|
58
|
-
lambda { SCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.
|
59
|
-
lambda { SCrypt::Engine.hash_secret(nil, @salt) }.
|
60
|
-
lambda { SCrypt::Engine.hash_secret(false, @salt) }.
|
58
|
+
expect(lambda { SCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }).to raise_error(SCrypt::Errors::InvalidSecret)
|
59
|
+
expect(lambda { SCrypt::Engine.hash_secret(nil, @salt) }).to_not raise_error
|
60
|
+
expect(lambda { SCrypt::Engine.hash_secret(false, @salt) }).to_not raise_error
|
61
61
|
end
|
62
62
|
|
63
63
|
it "should call #to_s on the secret and use the return value as the actual secret data" do
|
64
|
-
SCrypt::Engine.hash_secret(false, @salt).
|
64
|
+
expect(SCrypt::Engine.hash_secret(false, @salt)).to eq(SCrypt::Engine.hash_secret("false", @salt))
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
68
|
describe "SCrypt test vectors" do
|
69
69
|
it "should match results of SCrypt function" do
|
70
|
-
|
71
|
-
SCrypt::Engine.scrypt('
|
72
|
-
SCrypt::Engine.scrypt('
|
73
|
-
SCrypt::Engine.scrypt('pleaseletmein', 'SodiumChloride',
|
70
|
+
|
71
|
+
expect(SCrypt::Engine.scrypt('', '', 16, 1, 1, 64).unpack('H*').first).to eq('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')
|
72
|
+
expect(SCrypt::Engine.scrypt('password', 'NaCl', 1024, 8, 16, 64).unpack('H*').first).to eq('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')
|
73
|
+
expect(SCrypt::Engine.scrypt('pleaseletmein', 'SodiumChloride', 16384, 8, 1, 64).unpack('H*').first).to eq('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')
|
74
|
+
# Raspberry is memory limited, and fails on this test
|
75
|
+
# expect(SCrypt::Engine.scrypt('pleaseletmein', 'SodiumChloride', 1048576, 8, 1, 64).unpack('H*').first).to eq('2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4')
|
74
76
|
end
|
75
77
|
|
76
78
|
it "should match equivalent results sent through hash_secret() function" do
|
77
|
-
SCrypt::Engine.hash_secret('', '10$1$1$0000000000000000', 64).
|
78
|
-
SCrypt::Engine.hash_secret('password', '400$8$10$000000004e61436c', 64).
|
79
|
-
SCrypt::Engine.hash_secret('pleaseletmein', '4000$8$1$536f6469756d43686c6f72696465', 64).
|
80
|
-
|
79
|
+
expect(SCrypt::Engine.hash_secret('', '10$1$1$0000000000000000', 64)).to match(/\$77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906$/)
|
80
|
+
expect(SCrypt::Engine.hash_secret('password', '400$8$10$000000004e61436c', 64)).to match(/\$fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640$/)
|
81
|
+
expect(SCrypt::Engine.hash_secret('pleaseletmein', '4000$8$1$536f6469756d43686c6f72696465', 64)).to match(/\$7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887$/)
|
82
|
+
# expect(SCrypt::Engine.hash_secret('pleaseletmein', '100000$8$1$536f6469756d43686c6f72696465', 64)).to match(/\$2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4$/)
|
81
83
|
end
|
82
84
|
end
|
@@ -6,23 +6,23 @@ describe "Creating a hashed password" do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
it "should return a SCrypt::Password" do
|
9
|
-
@password.
|
9
|
+
expect(@password).to be_an_instance_of(SCrypt::Password)
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should return a valid password" do
|
13
|
-
lambda { SCrypt::Password.new(@password) }.
|
13
|
+
expect(lambda { SCrypt::Password.new(@password) }).to_not raise_error
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should behave normally if the secret is not a string" do
|
17
|
-
lambda { SCrypt::Password.create(nil) }.
|
18
|
-
lambda { SCrypt::Password.create({:woo => "yeah"}) }.
|
19
|
-
lambda { SCrypt::Password.create(false) }.
|
17
|
+
expect(lambda { SCrypt::Password.create(nil) }).to_not raise_error
|
18
|
+
expect(lambda { SCrypt::Password.create({:woo => "yeah"}) }).to_not raise_error
|
19
|
+
expect(lambda { SCrypt::Password.create(false) }).to_not raise_error
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should tolerate empty string secrets" do
|
23
|
-
lambda { SCrypt::Password.create( "\n".chop ) }.
|
24
|
-
lambda { SCrypt::Password.create( "" ) }.
|
25
|
-
lambda { SCrypt::Password.create( String.new ) }.
|
23
|
+
expect(lambda { SCrypt::Password.create( "\n".chop ) }).to_not raise_error
|
24
|
+
expect(lambda { SCrypt::Password.create( "" ) }).to_not raise_error
|
25
|
+
expect(lambda { SCrypt::Password.create( String.new ) }).to_not raise_error
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -35,13 +35,13 @@ describe "Reading a hashed password" do
|
|
35
35
|
|
36
36
|
it "should read the cost, salt, and hash" do
|
37
37
|
password = SCrypt::Password.new(@hash)
|
38
|
-
password.cost.
|
39
|
-
password.salt.
|
40
|
-
password.to_s.
|
38
|
+
expect(password.cost).to eq("400$8$d$")
|
39
|
+
expect(password.salt).to eq("173a8189751c095a29b933789560b73bf17b2e01")
|
40
|
+
expect(password.to_s).to eq(@hash)
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should raise an InvalidHashError when given an invalid hash" do
|
44
|
-
lambda { SCrypt::Password.new('not a valid hash') }.
|
44
|
+
expect(lambda { SCrypt::Password.new('not a valid hash') }).to raise_error(SCrypt::Errors::InvalidHash)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -52,11 +52,11 @@ describe "Comparing a hashed password with a secret" do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should compare successfully to the original secret" do
|
55
|
-
(@password == @secret).
|
55
|
+
expect((@password == @secret)).to be(true)
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should compare unsuccessfully to anything besides original secret" do
|
59
|
-
(@password == "@secret").
|
59
|
+
expect((@password == "@secret")).to be(false)
|
60
60
|
end
|
61
61
|
|
62
62
|
end
|
@@ -68,27 +68,27 @@ describe "non-default salt sizes" do
|
|
68
68
|
|
69
69
|
it "should enforce a minimum salt of 8 bytes" do
|
70
70
|
@password = SCrypt::Password.create(@secret, :salt_size => 7)
|
71
|
-
@password.salt.length.
|
71
|
+
expect(@password.salt.length).to eq(8 * 2)
|
72
72
|
end
|
73
73
|
|
74
74
|
it "should allow a salt of 32 bytes" do
|
75
75
|
@password = SCrypt::Password.create(@secret, :salt_size => 32)
|
76
|
-
@password.salt.length.
|
76
|
+
expect(@password.salt.length).to eq(32 * 2)
|
77
77
|
end
|
78
78
|
|
79
79
|
it "should enforce a maximum salt of 32 bytes" do
|
80
80
|
@password = SCrypt::Password.create(@secret, :salt_size => 33)
|
81
|
-
@password.salt.length.
|
81
|
+
expect(@password.salt.length).to eq(32 * 2)
|
82
82
|
end
|
83
83
|
|
84
84
|
it "should pad a 20-byte salt to not look like a 20-byte SHA1" do
|
85
85
|
@password = SCrypt::Password.create(@secret, :salt_size => 20)
|
86
|
-
@password.salt.length.
|
86
|
+
expect(@password.salt.length).to eq(41)
|
87
87
|
end
|
88
88
|
|
89
89
|
it "should properly compare a non-standard salt hash" do
|
90
90
|
@password = SCrypt::Password.create(@secret, :salt_size => 20)
|
91
|
-
(SCrypt::Password.new(@password.to_s) == @secret).
|
91
|
+
expect((SCrypt::Password.new(@password.to_s) == @secret)).to be(true)
|
92
92
|
end
|
93
93
|
|
94
94
|
end
|
@@ -100,22 +100,22 @@ describe "non-default key lengths" do
|
|
100
100
|
|
101
101
|
it "should enforce a minimum keylength of 16 bytes" do
|
102
102
|
@password = SCrypt::Password.create(@secret, :key_len => 15)
|
103
|
-
@password.digest.length.
|
103
|
+
expect(@password.digest.length).to eq(16 * 2)
|
104
104
|
end
|
105
105
|
|
106
106
|
it "should allow a keylength of 512 bytes" do
|
107
107
|
@password = SCrypt::Password.create(@secret, :key_len => 512)
|
108
|
-
@password.digest.length.
|
108
|
+
expect(@password.digest.length).to eq(512 * 2)
|
109
109
|
end
|
110
110
|
|
111
111
|
it "should enforce a maximum keylength of 512 bytes" do
|
112
112
|
@password = SCrypt::Password.create(@secret, :key_len => 513)
|
113
|
-
@password.digest.length.
|
113
|
+
expect(@password.digest.length).to eq(512 * 2)
|
114
114
|
end
|
115
115
|
|
116
116
|
it "should properly compare a non-standard hash" do
|
117
117
|
@password = SCrypt::Password.create(@secret, :key_len => 512)
|
118
|
-
(SCrypt::Password.new(@password.to_s) == @secret).
|
118
|
+
expect((SCrypt::Password.new(@password.to_s) == @secret)).to be(true)
|
119
119
|
end
|
120
120
|
|
121
121
|
end
|
@@ -127,13 +127,13 @@ describe "Old-style hashes" do
|
|
127
127
|
end
|
128
128
|
|
129
129
|
it "should compare successfully" do
|
130
|
-
(SCrypt::Password.new(@hash) == @secret).
|
130
|
+
expect((SCrypt::Password.new(@hash) == @secret)).to be(true)
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
134
|
describe "Respecting standard ruby behaviors" do
|
135
135
|
it 'should hash as a fixnum' do
|
136
136
|
password = SCrypt::Password.create('')
|
137
|
-
password.hash.
|
137
|
+
expect(password.hash).to be_kind_of(Fixnum)
|
138
138
|
end
|
139
139
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe "Security Utils" do
|
4
|
+
it "should perform a string comparison" do
|
5
|
+
expect(SCrypt::SecurityUtils.secure_compare('a', 'a')).to equal(true)
|
6
|
+
expect(SCrypt::SecurityUtils.secure_compare('a', 'b')).to equal(false)
|
7
|
+
expect(SCrypt::SecurityUtils.secure_compare('aa', 'aa')).to equal(true)
|
8
|
+
expect(SCrypt::SecurityUtils.secure_compare('aa', 'ab')).to equal(false)
|
9
|
+
expect(SCrypt::SecurityUtils.secure_compare('aa', 'aaa')).to equal(false)
|
10
|
+
expect(SCrypt::SecurityUtils.secure_compare('aaa', 'aa')).to equal(false)
|
11
|
+
end
|
12
|
+
end
|
data.tar.gz.sig
CHANGED
Binary file
|