scrypt 1.0.0
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.
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/CHANGELOG +3 -0
- data/COPYING +28 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +24 -0
- data/README +19 -0
- data/Rakefile +42 -0
- data/Rakefile.old +104 -0
- data/autotest/discover.rb +1 -0
- data/ext/mri/Makefile +157 -0
- data/ext/mri/crypto_scrypt-ref.c +284 -0
- data/ext/mri/crypto_scrypt.h +46 -0
- data/ext/mri/extconf.rb +4 -0
- data/ext/mri/memlimit.c +302 -0
- data/ext/mri/memlimit.h +42 -0
- data/ext/mri/scrypt_calibrate.c +104 -0
- data/ext/mri/scrypt_calibrate.h +19 -0
- data/ext/mri/scrypt_ext.bundle +0 -0
- data/ext/mri/scrypt_ext.c +126 -0
- data/ext/mri/scrypt_platform.h +4 -0
- data/ext/mri/scryptenc_cpuperf.c +185 -0
- data/ext/mri/scryptenc_cpuperf.h +39 -0
- data/ext/mri/sha256.c +412 -0
- data/ext/mri/sha256.h +62 -0
- data/ext/mri/sysendian.h +140 -0
- data/lib/scrypt.rb +171 -0
- data/lib/scrypt/version.rb +3 -0
- data/scrypt.gemspec +27 -0
- data/spec/scrypt/engine_spec.rb +58 -0
- data/spec/scrypt/password_spec.rb +62 -0
- data/spec/spec_helper.rb +4 -0
- metadata +101 -0
data/ext/mri/sha256.h
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
/*-
|
2
|
+
* Copyright 2005,2007,2009 Colin Percival
|
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 _SHA256_H_
|
30
|
+
#define _SHA256_H_
|
31
|
+
|
32
|
+
#include <sys/types.h>
|
33
|
+
|
34
|
+
#include <stdint.h>
|
35
|
+
|
36
|
+
typedef struct SHA256Context {
|
37
|
+
uint32_t state[8];
|
38
|
+
uint32_t count[2];
|
39
|
+
unsigned char buf[64];
|
40
|
+
} SHA256_CTX;
|
41
|
+
|
42
|
+
typedef struct HMAC_SHA256Context {
|
43
|
+
SHA256_CTX ictx;
|
44
|
+
SHA256_CTX octx;
|
45
|
+
} HMAC_SHA256_CTX;
|
46
|
+
|
47
|
+
void SHA256_Init(SHA256_CTX *);
|
48
|
+
void SHA256_Update(SHA256_CTX *, const void *, size_t);
|
49
|
+
void SHA256_Final(unsigned char [32], SHA256_CTX *);
|
50
|
+
void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t);
|
51
|
+
void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t);
|
52
|
+
void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *);
|
53
|
+
|
54
|
+
/**
|
55
|
+
* PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen):
|
56
|
+
* Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and
|
57
|
+
* write the output to buf. The value dkLen must be at most 32 * (2^32 - 1).
|
58
|
+
*/
|
59
|
+
void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t,
|
60
|
+
uint64_t, uint8_t *, size_t);
|
61
|
+
|
62
|
+
#endif /* !_SHA256_H_ */
|
data/ext/mri/sysendian.h
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
/*-
|
2
|
+
* Copyright 2007-2009 Colin Percival
|
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
|
+
* This file was originally written by Colin Percival as part of the Tarsnap
|
27
|
+
* online backup system.
|
28
|
+
*/
|
29
|
+
#ifndef _SYSENDIAN_H_
|
30
|
+
#define _SYSENDIAN_H_
|
31
|
+
|
32
|
+
#include "scrypt_platform.h"
|
33
|
+
|
34
|
+
/* If we don't have be64enc, the <sys/endian.h> we have isn't usable. */
|
35
|
+
#if !HAVE_DECL_BE64ENC
|
36
|
+
#undef HAVE_SYS_ENDIAN_H
|
37
|
+
#endif
|
38
|
+
|
39
|
+
#ifdef HAVE_SYS_ENDIAN_H
|
40
|
+
|
41
|
+
#include <sys/endian.h>
|
42
|
+
|
43
|
+
#else
|
44
|
+
|
45
|
+
#include <stdint.h>
|
46
|
+
|
47
|
+
static inline uint32_t
|
48
|
+
be32dec(const void *pp)
|
49
|
+
{
|
50
|
+
const uint8_t *p = (uint8_t const *)pp;
|
51
|
+
|
52
|
+
return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) +
|
53
|
+
((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24));
|
54
|
+
}
|
55
|
+
|
56
|
+
static inline void
|
57
|
+
be32enc(void *pp, uint32_t x)
|
58
|
+
{
|
59
|
+
uint8_t * p = (uint8_t *)pp;
|
60
|
+
|
61
|
+
p[3] = x & 0xff;
|
62
|
+
p[2] = (x >> 8) & 0xff;
|
63
|
+
p[1] = (x >> 16) & 0xff;
|
64
|
+
p[0] = (x >> 24) & 0xff;
|
65
|
+
}
|
66
|
+
|
67
|
+
static inline uint64_t
|
68
|
+
be64dec(const void *pp)
|
69
|
+
{
|
70
|
+
const uint8_t *p = (uint8_t const *)pp;
|
71
|
+
|
72
|
+
return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) +
|
73
|
+
((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) +
|
74
|
+
((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) +
|
75
|
+
((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56));
|
76
|
+
}
|
77
|
+
|
78
|
+
static inline void
|
79
|
+
be64enc(void *pp, uint64_t x)
|
80
|
+
{
|
81
|
+
uint8_t * p = (uint8_t *)pp;
|
82
|
+
|
83
|
+
p[7] = x & 0xff;
|
84
|
+
p[6] = (x >> 8) & 0xff;
|
85
|
+
p[5] = (x >> 16) & 0xff;
|
86
|
+
p[4] = (x >> 24) & 0xff;
|
87
|
+
p[3] = (x >> 32) & 0xff;
|
88
|
+
p[2] = (x >> 40) & 0xff;
|
89
|
+
p[1] = (x >> 48) & 0xff;
|
90
|
+
p[0] = (x >> 56) & 0xff;
|
91
|
+
}
|
92
|
+
|
93
|
+
static inline uint32_t
|
94
|
+
le32dec(const void *pp)
|
95
|
+
{
|
96
|
+
const uint8_t *p = (uint8_t const *)pp;
|
97
|
+
|
98
|
+
return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) +
|
99
|
+
((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24));
|
100
|
+
}
|
101
|
+
|
102
|
+
static inline void
|
103
|
+
le32enc(void *pp, uint32_t x)
|
104
|
+
{
|
105
|
+
uint8_t * p = (uint8_t *)pp;
|
106
|
+
|
107
|
+
p[0] = x & 0xff;
|
108
|
+
p[1] = (x >> 8) & 0xff;
|
109
|
+
p[2] = (x >> 16) & 0xff;
|
110
|
+
p[3] = (x >> 24) & 0xff;
|
111
|
+
}
|
112
|
+
|
113
|
+
static inline uint64_t
|
114
|
+
le64dec(const void *pp)
|
115
|
+
{
|
116
|
+
const uint8_t *p = (uint8_t const *)pp;
|
117
|
+
|
118
|
+
return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) +
|
119
|
+
((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) +
|
120
|
+
((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) +
|
121
|
+
((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56));
|
122
|
+
}
|
123
|
+
|
124
|
+
static inline void
|
125
|
+
le64enc(void *pp, uint64_t x)
|
126
|
+
{
|
127
|
+
uint8_t * p = (uint8_t *)pp;
|
128
|
+
|
129
|
+
p[0] = x & 0xff;
|
130
|
+
p[1] = (x >> 8) & 0xff;
|
131
|
+
p[2] = (x >> 16) & 0xff;
|
132
|
+
p[3] = (x >> 24) & 0xff;
|
133
|
+
p[4] = (x >> 32) & 0xff;
|
134
|
+
p[5] = (x >> 40) & 0xff;
|
135
|
+
p[6] = (x >> 48) & 0xff;
|
136
|
+
p[7] = (x >> 56) & 0xff;
|
137
|
+
}
|
138
|
+
#endif /* !HAVE_SYS_ENDIAN_H */
|
139
|
+
|
140
|
+
#endif /* !_SYSENDIAN_H_ */
|
data/lib/scrypt.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
# A wrapper for the scrypt algorithm.
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "ext", "mri")))
|
4
|
+
require "scrypt_ext"
|
5
|
+
require "openssl"
|
6
|
+
require "digest/sha1"
|
7
|
+
require "scanf"
|
8
|
+
|
9
|
+
|
10
|
+
module SCrypt
|
11
|
+
module Errors
|
12
|
+
class InvalidSalt < StandardError; end # The salt parameter provided is invalid.
|
13
|
+
class InvalidHash < StandardError; end # The hash parameter provided is invalid.
|
14
|
+
class InvalidSecret < StandardError; end # The secret parameter provided is invalid.
|
15
|
+
end
|
16
|
+
|
17
|
+
class Engine
|
18
|
+
DEFAULTS = {
|
19
|
+
:max_mem => 1024 * 1024,
|
20
|
+
:max_memfrac => 0.5,
|
21
|
+
:max_time => 0.2
|
22
|
+
}
|
23
|
+
|
24
|
+
private_class_method :__sc_calibrate
|
25
|
+
private_class_method :__sc_crypt
|
26
|
+
|
27
|
+
# Given a secret and a valid salt (see SCrypt::Engine.generate_salt) calculates an scrypt password hash.
|
28
|
+
def self.hash_secret(secret, salt)
|
29
|
+
if valid_secret?(secret)
|
30
|
+
if valid_salt?(salt)
|
31
|
+
cost = autodetect_cost(salt)
|
32
|
+
salt + "$" + Digest::SHA1.hexdigest(__sc_crypt(secret.to_s, salt, cost))
|
33
|
+
else
|
34
|
+
raise Errors::InvalidSalt.new("invalid salt")
|
35
|
+
end
|
36
|
+
else
|
37
|
+
raise Errors::InvalidSecret.new("invalid secret")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Generates a random salt with a given computational cost.
|
42
|
+
def self.generate_salt(options = {})
|
43
|
+
options = DEFAULTS.merge(options)
|
44
|
+
cost = calibrate(options)
|
45
|
+
salt = Digest::SHA1.hexdigest(OpenSSL::Random.random_bytes(32))
|
46
|
+
cost + salt
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns true if +cost+ is a valid cost, false if not.
|
50
|
+
def self.valid_cost?(cost)
|
51
|
+
cost.match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$$/) != nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns true if +salt+ is a valid salt, false if not.
|
55
|
+
def self.valid_salt?(salt)
|
56
|
+
salt.match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]{20,}$/) != nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns true if +secret+ is a valid secret, false if not.
|
60
|
+
def self.valid_secret?(secret)
|
61
|
+
secret.respond_to?(:to_s)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the cost value which will result in computation limits less than the given options.
|
65
|
+
#
|
66
|
+
# Example:
|
67
|
+
#
|
68
|
+
# # should take less than 200ms
|
69
|
+
# SCrypt.calibrate(:max_time => 0.2)
|
70
|
+
#
|
71
|
+
# # should take less than 1000ms
|
72
|
+
# SCrypt.calibrate(:max_time => 1)
|
73
|
+
#
|
74
|
+
def self.calibrate(options = {})
|
75
|
+
options = DEFAULTS.merge(options)
|
76
|
+
__sc_calibrate(options[:max_mem], options[:max_memfrac], options[:max_time])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Computes the memory use of the given +cost+
|
80
|
+
def self.memory_use(cost)
|
81
|
+
n, r, p = cost.scanf("%x$%x$%x$")
|
82
|
+
(128 * r * p) + (256 * r) + (128 * r * n);
|
83
|
+
end
|
84
|
+
|
85
|
+
# Autodetects the cost from the salt string.
|
86
|
+
def self.autodetect_cost(salt)
|
87
|
+
salt[/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$/]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# A password management class which allows you to safely store users' passwords and compare them.
|
92
|
+
#
|
93
|
+
# Example usage:
|
94
|
+
#
|
95
|
+
# include "scrypt"
|
96
|
+
#
|
97
|
+
# # hash a user's password
|
98
|
+
# @password = Password.create("my grand secret")
|
99
|
+
# @password #=> "2000$8$1$f5f2fa5fe5484a7091f1299768fbe92b5a7fbc77$6a385f22c54d92c314b71a4fd5ef33967c93d679"
|
100
|
+
#
|
101
|
+
# # store it safely
|
102
|
+
# @user.update_attribute(:password, @password)
|
103
|
+
#
|
104
|
+
# # read it back
|
105
|
+
# @user.reload!
|
106
|
+
# @db_password = Password.new(@user.password)
|
107
|
+
#
|
108
|
+
# # compare it after retrieval
|
109
|
+
# @db_password == "my grand secret" #=> true
|
110
|
+
# @db_password == "a paltry guess" #=> false
|
111
|
+
#
|
112
|
+
class Password < String
|
113
|
+
# The hash portion of the stored password hash.
|
114
|
+
attr_reader :hash
|
115
|
+
# The salt of the store password hash
|
116
|
+
attr_reader :salt
|
117
|
+
# The cost factor used to create the hash.
|
118
|
+
attr_reader :cost
|
119
|
+
|
120
|
+
class << self
|
121
|
+
# Hashes a secret, returning a SCrypt::Password instance.
|
122
|
+
# Takes three options (optional), which will determine the cost limits of the computation.
|
123
|
+
# <tt>:max_time</tt> specifies the maximum number of seconds the computation should take.
|
124
|
+
# <tt>:max_mem</tt> specifies the maximum number of bytes the computation should take. A value of 0 specifies no upper limit. The minimum is always 1 MB.
|
125
|
+
# <tt>:max_memfrac</tt> specifies the maximum memory in a fraction of available resources to use. Any value equal to 0 or greater than 0.5 will result in 0.5 being used.
|
126
|
+
# The scrypt key derivation function is designed to be far more secure against hardware brute-force attacks than alternative functions such as PBKDF2 or bcrypt.
|
127
|
+
# The designers of scrypt estimate that on modern (2009) hardware, if 5 seconds are spent computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the cost of a similar attack against bcrypt (to find the same password), and 20000 times greater than a similar attack against PBKDF2.
|
128
|
+
# Default options will result in calculation time of approx. 200 ms with 1 MB memory use.
|
129
|
+
#
|
130
|
+
# Example:
|
131
|
+
# @password = SCrypt::Password.create("my secret", :max_time => 0.25)
|
132
|
+
def create(secret, options = {})
|
133
|
+
options = SCrypt::Engine::DEFAULTS.merge(options)
|
134
|
+
salt = SCrypt::Engine.generate_salt(options)
|
135
|
+
hash = SCrypt::Engine.hash_secret(secret, salt)
|
136
|
+
Password.new(hash)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Initializes a SCrypt::Password instance with the data from a stored hash.
|
141
|
+
def initialize(raw_hash)
|
142
|
+
if valid_hash?(raw_hash)
|
143
|
+
self.replace(raw_hash)
|
144
|
+
@cost, @salt, @hash = split_hash(self.to_s)
|
145
|
+
else
|
146
|
+
raise Errors::InvalidHash.new("invalid hash")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
|
151
|
+
def ==(secret)
|
152
|
+
super(SCrypt::Engine.hash_secret(secret, @cost + @salt))
|
153
|
+
end
|
154
|
+
alias_method :is_password?, :==
|
155
|
+
|
156
|
+
private
|
157
|
+
# Returns true if +h+ is a valid hash.
|
158
|
+
def valid_hash?(h)
|
159
|
+
h.match(/^[0-9a-z]+\$[0-9a-z]+\$[0-9a-z]+\$[A-Za-z0-9]{20,}\$[A-Za-z0-9]{20,}$/) != nil
|
160
|
+
end
|
161
|
+
|
162
|
+
# call-seq:
|
163
|
+
# split_hash(raw_hash) -> cost, salt, hash
|
164
|
+
#
|
165
|
+
# Splits +h+ into cost, salt, and hash and returns them in that order.
|
166
|
+
def split_hash(h)
|
167
|
+
n, v, r, salt, hash = h.split('$')
|
168
|
+
return [n, v, r].join('$') + "$", salt, hash
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/scrypt.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "scrypt/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "scrypt"
|
7
|
+
s.version = SCrypt::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Patrick Hogan"]
|
10
|
+
s.email = ["pbhogan@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = "scrypt password hashing algorithm."
|
13
|
+
s.description = <<-EOF
|
14
|
+
The scrypt key derivation function is designed to be far
|
15
|
+
more secure against hardware brute-force attacks than
|
16
|
+
alternative functions such as PBKDF2 or bcrypt.
|
17
|
+
EOF
|
18
|
+
|
19
|
+
s.rubyforge_project = "scrypt"
|
20
|
+
|
21
|
+
s.extensions = ["ext/mri/extconf.rb"]
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe "The SCrypt engine" do
|
4
|
+
it "should calculate a valid cost factor" do
|
5
|
+
first = SCrypt::Engine.calibrate(:max_time => 0.2)
|
6
|
+
SCrypt::Engine.valid_cost?(first).should == true
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "Generating SCrypt salts" do
|
11
|
+
|
12
|
+
it "should produce strings" do
|
13
|
+
SCrypt::Engine.generate_salt.should be_an_instance_of(String)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should produce random data" do
|
17
|
+
SCrypt::Engine.generate_salt.should_not equal(SCrypt::Engine.generate_salt)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "Autodetecting of salt cost" do
|
23
|
+
|
24
|
+
it "should work" do
|
25
|
+
SCrypt::Engine.autodetect_cost("2a$08$c3$randomjunkgoeshere").should == "2a$08$c3$"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "Generating SCrypt hashes" do
|
31
|
+
|
32
|
+
class MyInvalidSecret
|
33
|
+
undef to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
before :each do
|
37
|
+
@salt = SCrypt::Engine.generate_salt()
|
38
|
+
@password = "woo"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should produce a string" do
|
42
|
+
SCrypt::Engine.hash_secret(@password, @salt).should be_an_instance_of(String)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise an InvalidSalt error if the salt is invalid" do
|
46
|
+
lambda { SCrypt::Engine.hash_secret(@password, 'nino') }.should raise_error(SCrypt::Errors::InvalidSalt)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should raise an InvalidSecret error if the secret is invalid" do
|
50
|
+
lambda { SCrypt::Engine.hash_secret(MyInvalidSecret.new, @salt) }.should raise_error(SCrypt::Errors::InvalidSecret)
|
51
|
+
lambda { SCrypt::Engine.hash_secret(nil, @salt) }.should_not raise_error(SCrypt::Errors::InvalidSecret)
|
52
|
+
lambda { SCrypt::Engine.hash_secret(false, @salt) }.should_not raise_error(SCrypt::Errors::InvalidSecret)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should call #to_s on the secret and use the return value as the actual secret data" do
|
56
|
+
SCrypt::Engine.hash_secret(false, @salt).should == SCrypt::Engine.hash_secret("false", @salt)
|
57
|
+
end
|
58
|
+
end
|