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.
@@ -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_ */
@@ -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_ */
@@ -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
@@ -0,0 +1,3 @@
1
+ module SCrypt
2
+ VERSION = "1.0.0"
3
+ end
@@ -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