scrypt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|