pbkdf2-ruby 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.TXT +21 -0
- data/README.markdown +158 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/benchmark.rb +33 -0
- data/lib/pbkdf2.rb +168 -0
- data/pbkdf2.gemspec +52 -0
- data/spec/pbkdf2_spec.rb +144 -0
- data/spec/rfc3962_spec.rb +195 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +2 -0
- metadata +93 -0
data/LICENSE.TXT
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2008 Sam Quigley <quigley@emerose.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
21
|
+
This license is sometimes referred to as "The MIT License"
|
data/README.markdown
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# PBKDF2 #
|
2
|
+
|
3
|
+
A Ruby implementation of the [Password-Based Key-Derivation Function, uh,
|
4
|
+
2][PBKDF2].
|
5
|
+
|
6
|
+
## Using PBKDF2 ##
|
7
|
+
|
8
|
+
The basic steps are:
|
9
|
+
|
10
|
+
1. Instantiate a `PBKDF2` object
|
11
|
+
2. Call `#bin_string` (or `#hex_string`) on it to recover the binary (hex) form of the key
|
12
|
+
|
13
|
+
To instantiate, you can either pass the required arguments as options in a
|
14
|
+
hash, like so:
|
15
|
+
|
16
|
+
PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>10000)
|
17
|
+
|
18
|
+
or use the (easier and prettier, in my view) builder idiom:
|
19
|
+
|
20
|
+
PBKDF2.new do |p|
|
21
|
+
p.password = "s33krit"
|
22
|
+
p.salt = "nacl"
|
23
|
+
p.iterations = 10000
|
24
|
+
end
|
25
|
+
|
26
|
+
You can also mix-and-match ways of passing arguments, but I don't know why
|
27
|
+
you'd want to do that.
|
28
|
+
|
29
|
+
### Required options ###
|
30
|
+
|
31
|
+
A `PBKDF2` object cannot be instantiated without setting the following options:
|
32
|
+
|
33
|
+
* **`password`**: The passphrase used for the key, passed as a (possibly
|
34
|
+
binary) string.
|
35
|
+
|
36
|
+
This should be kept secret, preferably nowhere but the end-user's memory.
|
37
|
+
|
38
|
+
* **`salt`**: A salt for this passphrase, passed as a (possibly binary)
|
39
|
+
string.
|
40
|
+
|
41
|
+
This does not need to be kept secret, but should be made as long as is
|
42
|
+
reasonable (64-128 bits) to avoid precomputed image ("rainbow table")
|
43
|
+
attacks.
|
44
|
+
|
45
|
+
* **`iterations`**: The number of iterated hashes to calculate, expressed as
|
46
|
+
an integer.
|
47
|
+
|
48
|
+
This does not need to be kept secret, but should be made as large as is
|
49
|
+
reasonable. See below for guidance on choosing a good number for this.
|
50
|
+
|
51
|
+
PBKDF2 objects can also be configured with the following options:
|
52
|
+
|
53
|
+
* **`hash_function`**: The hashing algorithm to be used.
|
54
|
+
|
55
|
+
This option may be expressed in a number of ways:
|
56
|
+
* As a `Class` object, such as `OpenSSL::Digest::SHA512`. Only OpenSSL
|
57
|
+
digest classes are supported at the moment.
|
58
|
+
* As an already-instantiated OpenSSL digest object, such as the result of
|
59
|
+
`OpenSSL::Digest::SHA512.new`. If you use this method, take care that
|
60
|
+
the hash object is in its just-initialized state (or that the same hash
|
61
|
+
object with the same state is used whenever keys are generated/checked).
|
62
|
+
* As a string which is understood by `OpenSSL::Digest::Digest.new()`.
|
63
|
+
Things like "sha1", "md5", "RIPEMD160", etc. all work fine. If the
|
64
|
+
string begins with the text "hmacWith" it will be stripped before
|
65
|
+
passing it to the underlying OpenSSL library, making it possible to use
|
66
|
+
arguments that at least look more like the ASN.1 identifiers in the
|
67
|
+
spec.
|
68
|
+
* A symbol, like `:sha256`, that, when converted to a string, meets the
|
69
|
+
rules for strings above.
|
70
|
+
|
71
|
+
If not specified, SHA-256 will be used. (Note that other implementations
|
72
|
+
may default to SHA-1.)
|
73
|
+
|
74
|
+
* **`key_length`**: The length, in bytes, of the key you wish to generate.
|
75
|
+
|
76
|
+
By default, the key generated will be equal in length to the hash output
|
77
|
+
size. This can be adjusted to any size required, up to `((2**32 - 1) * (hash
|
78
|
+
length)`.
|
79
|
+
|
80
|
+
If a required parameter is missing, or if an invalid parameter is passed to one of these options, an `ArgumentError` exception will be raised.
|
81
|
+
|
82
|
+
## Setting the Number of Iterations ##
|
83
|
+
|
84
|
+
The `iterations` option exposed by PBKDF2 provides a way of controlling the
|
85
|
+
amount of work required to check a candidate passphrase. It can be thought of
|
86
|
+
as a work factor governing the amount of work an attacker must do in order to
|
87
|
+
perform a dictionary or brute-force attack on passwords. Unfortunately, it
|
88
|
+
also governs the amount of work that must be performed on behalf of legitimate
|
89
|
+
users must when checking credentials.
|
90
|
+
|
91
|
+
Choosing the correct value for this parameter is thus a balancing act: it
|
92
|
+
should be set as high as possible, to make attacks as difficult as possible,
|
93
|
+
without making legitimate applications unusably slow. One method for choosing
|
94
|
+
a value is based on estimating an upper bound on the resources an attacker is
|
95
|
+
likely to have available, and then finding an iteration count that makes such
|
96
|
+
attacks unprofitable. A useful example of this sort of reasoning can be found
|
97
|
+
in the [Security Considerations section of RFC 3962][ITERS].
|
98
|
+
|
99
|
+
The other approach for choosing the iterations count is to decide the maximum
|
100
|
+
performance penalty that can be tolerated in the context of the application,
|
101
|
+
and to set the iteration count so that it remains within these bounds. The
|
102
|
+
`PBKDF2` module contains a `benchmark` method for this purpose: to use it,
|
103
|
+
instantiate a `PBKDF2` object as normal, using the `hash_function` and
|
104
|
+
`key_length` you intend to use in the final application. Then, call the
|
105
|
+
`benchmark` method on the object: the result will be the time, in seconds,
|
106
|
+
required to complete one iteration. Divide the maximum performance penalty
|
107
|
+
by this number to find the number of iterations you should choose.
|
108
|
+
|
109
|
+
The first method requires implementors to estimate a number of important
|
110
|
+
variables, including the resources available to attackers, which may be
|
111
|
+
difficult or impossible to do well. The second method is also prone to error,
|
112
|
+
as it can be difficult to predict load characteristics in production
|
113
|
+
conditions, or the impact of a few milliseconds' delay on end-user
|
114
|
+
perceptions. The best approach will necessarily involve trying both
|
115
|
+
approaches and balancing the competing concerns against one-another.
|
116
|
+
|
117
|
+
Note that no default for this option is provided, as a way of forcing
|
118
|
+
implementors to consider this issue in their own contexts. Anyone who, having
|
119
|
+
read and understood the above, is still unsure what the value to choose should
|
120
|
+
just use 5,000 and move on.
|
121
|
+
|
122
|
+
## Relevant Standards ##
|
123
|
+
|
124
|
+
PBKDF2 was originally defined as part of RSA Laboratories' [PKCS #5][PKCS],
|
125
|
+
part of their Public-Key Cryptography Standards series. It has since been
|
126
|
+
republished as [RFC 2898][RFC].
|
127
|
+
|
128
|
+
### Standards Conformance ###
|
129
|
+
|
130
|
+
This implementation conforms to [RFC 2898][RFC], and has been tested using the
|
131
|
+
test vectors in Appendix B of [RFC 3962][3962]. Note, however, that while
|
132
|
+
those specifications use [HMAC][HMAC]-[SHA-1][SHA1], **this implementation
|
133
|
+
defaults to [HMAC][HMAC]-[SHA-256][SHA1]**. (SHA-256 provides a longer bit
|
134
|
+
length. In addition, NIST has [stated][NIST] that SHA-1 should be phased out
|
135
|
+
due to concerns over recent cryptanalytic attacks.)
|
136
|
+
|
137
|
+
## TODO ##
|
138
|
+
|
139
|
+
This version is essentially complete. If ASN.1 weren't such a nightmare, it
|
140
|
+
might be useful to be able to initialize `PBKDF2` objects based on standard
|
141
|
+
OIDs for parameters. It would also be nice to have a standard envelope for
|
142
|
+
serializing sets of {key, salt, options}. Both of these are probably tasks
|
143
|
+
for other modules, however. (YAML fits the bill pretty well already.)
|
144
|
+
|
145
|
+
## License ##
|
146
|
+
|
147
|
+
This software is ©2008 Sam Quigley <quigley@emerose.com>. See the
|
148
|
+
LICENSE.TXT file accompanying this document for the terms under which it may
|
149
|
+
be used and distributed.
|
150
|
+
|
151
|
+
[PBKDF2]: http://en.wikipedia.org/wiki/PBKDF2 "Wikipedia: PBKDF2"
|
152
|
+
[PKCS]: http://www.rsa.com/rsalabs/node.asp?id=2127 "PKCS #5"
|
153
|
+
[RFC]: http://tools.ietf.org/html/rfc2898 "RFC 2898"
|
154
|
+
[3962]: http://tools.ietf.org/html/rfc3962 "RFC 3962"
|
155
|
+
[SHA1]: http://en.wikipedia.org/wiki/SHA-1 "Wikipedia: SHA-1"
|
156
|
+
[HMAC]: http://tools.ietf.org/html/rfc2104 "RFC 2104"
|
157
|
+
[ITERS]: http://tools.ietf.org/html/rfc3962#page-6 "RFC 3962: Section 8"
|
158
|
+
[NIST]: http://csrc.nist.gov/groups/ST/hash/statement.html "NIST Comments on Cryptanalytic Attacks on SHA-1"
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "pbkdf2-ruby"
|
8
|
+
gem.summary = %Q{Password-Based Key Derivation Function 2 - PBKDF2}
|
9
|
+
gem.description = %Q{This implementation conforms to RFC 2898, and has been tested using the test vectors in Appendix B of RFC 3962. Note, however, that while those specifications use HMAC-SHA-1, this implementation defaults to HMAC-SHA-256. (SHA-256 provides a longer bit length. In addition, NIST has stated that SHA-1 should be phased out due to concerns over recent cryptanalytic attacks.)}
|
10
|
+
gem.email = "quigley@emerose.com"
|
11
|
+
gem.homepage = "http://github.com/emerose/pbkdf2-ruby"
|
12
|
+
gem.authors = ["Sam Quigley"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_development_dependency "rdoc", ">= 2.4.2"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rspec/core/rake_task'
|
23
|
+
|
24
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
25
|
+
spec.rspec_opts = ["--color", "--format progress", "-r ./spec/spec_helper.rb"]
|
26
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
27
|
+
end
|
28
|
+
|
29
|
+
task :spec => :check_dependencies
|
30
|
+
|
31
|
+
task :default => :spec
|
32
|
+
|
33
|
+
require 'rdoc/task'
|
34
|
+
RDoc::Task.new do |rdoc|
|
35
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
36
|
+
|
37
|
+
rdoc.rdoc_dir = 'rdoc'
|
38
|
+
rdoc.title = "pbkdf2 #{version}"
|
39
|
+
rdoc.rdoc_files.include('README*')
|
40
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
41
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/benchmark.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'lib/pbkdf2'
|
3
|
+
|
4
|
+
n = 100000
|
5
|
+
#
|
6
|
+
# from active-ldap
|
7
|
+
module Salt
|
8
|
+
CHARS = ['.', '/', '0'..'9', 'A'..'Z', 'a'..'z'].collect do |x|
|
9
|
+
x.to_a
|
10
|
+
end.flatten
|
11
|
+
module_function
|
12
|
+
def generate(length)
|
13
|
+
salt = ""
|
14
|
+
length.times {salt << CHARS[rand(CHARS.length)]}
|
15
|
+
salt
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def next_salt
|
20
|
+
Salt.generate(8)
|
21
|
+
end
|
22
|
+
Benchmark.bm do |x|
|
23
|
+
x.report do
|
24
|
+
1.upto(n) do
|
25
|
+
PBKDF2.new do |p|
|
26
|
+
p.password = "s33krit"
|
27
|
+
p.salt = next_salt
|
28
|
+
p.iterations = 1000
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/lib/pbkdf2.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
class PBKDF2
|
4
|
+
def initialize(opts={})
|
5
|
+
@hash_function = OpenSSL::Digest::Digest.new("sha256")
|
6
|
+
|
7
|
+
# override with options
|
8
|
+
opts.each_key do |k|
|
9
|
+
if self.respond_to?("#{k}=")
|
10
|
+
self.send("#{k}=", opts[k])
|
11
|
+
else
|
12
|
+
raise ArgumentError, "Argument '#{k}' is not allowed"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
yield self if block_given?
|
17
|
+
|
18
|
+
# set this to the default if nothing was given
|
19
|
+
@key_length ||= @hash_function.size
|
20
|
+
|
21
|
+
# make sure the relevant things got set
|
22
|
+
raise ArgumentError, "password not set" if @password.nil?
|
23
|
+
raise ArgumentError, "salt not set" if @salt.nil?
|
24
|
+
raise ArgumentError, "iterations not set" if @iterations.nil?
|
25
|
+
end
|
26
|
+
attr_reader :key_length, :hash_function, :iterations, :salt, :password
|
27
|
+
|
28
|
+
def key_length=(l)
|
29
|
+
raise ArgumentError, "key too short" if l < 1
|
30
|
+
raise ArgumentError, "key too long" if l > ((2**32 - 1) * @hash_function.size)
|
31
|
+
@value = nil
|
32
|
+
@key_length = l
|
33
|
+
end
|
34
|
+
|
35
|
+
def hash_function=(h)
|
36
|
+
@value = nil
|
37
|
+
@hash_function = find_hash(h)
|
38
|
+
end
|
39
|
+
|
40
|
+
def iterations=(i)
|
41
|
+
raise ArgumentError, "iterations can't be less than 1" if i < 1
|
42
|
+
@value = nil
|
43
|
+
@iterations = i
|
44
|
+
end
|
45
|
+
|
46
|
+
def salt=(s)
|
47
|
+
@value = nil
|
48
|
+
@salt = s
|
49
|
+
end
|
50
|
+
|
51
|
+
def password=(p)
|
52
|
+
@value = nil
|
53
|
+
@password = p
|
54
|
+
end
|
55
|
+
|
56
|
+
def value
|
57
|
+
calculate! if @value.nil?
|
58
|
+
@value
|
59
|
+
end
|
60
|
+
|
61
|
+
alias bin_string value
|
62
|
+
|
63
|
+
def hex_string
|
64
|
+
bin_string.unpack("H*").first
|
65
|
+
end
|
66
|
+
|
67
|
+
# return number of milliseconds it takes to complete one iteration
|
68
|
+
def benchmark(iters = 400000)
|
69
|
+
iter_orig = @iterations
|
70
|
+
@iterations=iters
|
71
|
+
start = Time.now
|
72
|
+
calculate!
|
73
|
+
time = Time.now - start
|
74
|
+
@iterations = iter_orig
|
75
|
+
return (time/iters)
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
# finds and instantiates, if necessary, a hash function
|
81
|
+
def find_hash(hash)
|
82
|
+
case hash
|
83
|
+
when Class
|
84
|
+
# allow people to pass in classes to be instantiated
|
85
|
+
# (eg, pass in OpenSSL::Digest::SHA1)
|
86
|
+
hash = find_hash(hash.new)
|
87
|
+
when Symbol
|
88
|
+
# convert symbols to strings and see if OpenSSL::Digest can make sense of
|
89
|
+
hash = find_hash(hash.to_s)
|
90
|
+
when String
|
91
|
+
# if it's a string, first strip off any leading 'hmacWith' (which is implied)
|
92
|
+
hash.gsub!(/^hmacWith/i,'')
|
93
|
+
# see if the OpenSSL lib understands it
|
94
|
+
hash = OpenSSL::Digest::Digest.new(hash)
|
95
|
+
when OpenSSL::Digest
|
96
|
+
when OpenSSL::Digest::Digest
|
97
|
+
# ok
|
98
|
+
else
|
99
|
+
raise TypeError, "Unknown hash type: #{hash.class}"
|
100
|
+
end
|
101
|
+
hash
|
102
|
+
end
|
103
|
+
|
104
|
+
# the pseudo-random function defined in the spec
|
105
|
+
def prf(data)
|
106
|
+
OpenSSL::HMAC.digest(@hash_function, @password, data)
|
107
|
+
end
|
108
|
+
|
109
|
+
# this is a translation of the helper function "F" defined in the spec
|
110
|
+
def calculate_block(block_num)
|
111
|
+
# u_1:
|
112
|
+
u = prf(salt+[block_num].pack("N"))
|
113
|
+
ret = u
|
114
|
+
# u_2 through u_c:
|
115
|
+
2.upto(@iterations) do
|
116
|
+
# calculate u_n
|
117
|
+
u = prf(u)
|
118
|
+
# xor it with the previous results
|
119
|
+
ret = ret^u
|
120
|
+
end
|
121
|
+
ret
|
122
|
+
end
|
123
|
+
|
124
|
+
# the bit that actually does the calculating
|
125
|
+
def calculate!
|
126
|
+
# how many blocks we'll need to calculate (the last may be truncated)
|
127
|
+
blocks_needed = (@key_length.to_f / @hash_function.size).ceil
|
128
|
+
# reset
|
129
|
+
@value = ""
|
130
|
+
# main block-calculating loop:
|
131
|
+
1.upto(blocks_needed) do |block_num|
|
132
|
+
@value << calculate_block(block_num)
|
133
|
+
end
|
134
|
+
# truncate to desired length:
|
135
|
+
@value = @value.slice(0,@key_length)
|
136
|
+
@value
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
class String
|
142
|
+
if RUBY_VERSION >= "1.9"
|
143
|
+
def xor_impl(other)
|
144
|
+
result = "".encode("ASCII-8BIT")
|
145
|
+
o_bytes = other.bytes.to_a
|
146
|
+
bytes.each_with_index do |c, i|
|
147
|
+
result << (c ^ o_bytes[i])
|
148
|
+
end
|
149
|
+
result
|
150
|
+
end
|
151
|
+
else
|
152
|
+
def xor_impl(other)
|
153
|
+
result = (0..self.length-1).collect { |i| self[i] ^ other[i] }
|
154
|
+
result.pack("C*")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private :xor_impl
|
159
|
+
|
160
|
+
def ^(other)
|
161
|
+
raise ArgumentError, "Can't bitwise-XOR a String with a non-String" \
|
162
|
+
unless other.kind_of? String
|
163
|
+
raise ArgumentError, "Can't bitwise-XOR strings of different length" \
|
164
|
+
unless self.length == other.length
|
165
|
+
|
166
|
+
xor_impl(other)
|
167
|
+
end
|
168
|
+
end
|
data/pbkdf2.gemspec
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "pbkdf2"
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sam Quigley"]
|
12
|
+
s.date = "2013-10-23"
|
13
|
+
s.description = "This implementation conforms to RFC 2898, and has been tested using the test vectors in Appendix B of RFC 3962. Note, however, that while those specifications use HMAC-SHA-1, this implementation defaults to HMAC-SHA-256. (SHA-256 provides a longer bit length. In addition, NIST has stated that SHA-1 should be phased out due to concerns over recent cryptanalytic attacks.)"
|
14
|
+
s.email = "quigley@emerose.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.TXT",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE.TXT",
|
21
|
+
"README.markdown",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"benchmark.rb",
|
25
|
+
"lib/pbkdf2.rb",
|
26
|
+
"pbkdf2.gemspec",
|
27
|
+
"spec/pbkdf2_spec.rb",
|
28
|
+
"spec/rfc3962_spec.rb",
|
29
|
+
"spec/spec.opts",
|
30
|
+
"spec/spec_helper.rb"
|
31
|
+
]
|
32
|
+
s.homepage = "http://github.com/emerose/pbkdf2-ruby"
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = "1.8.23"
|
35
|
+
s.summary = "Password-Based Key Derivation Function 2 - PBKDF2"
|
36
|
+
|
37
|
+
if s.respond_to? :specification_version then
|
38
|
+
s.specification_version = 3
|
39
|
+
|
40
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
41
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
42
|
+
s.add_development_dependency(%q<rdoc>, [">= 2.4.2"])
|
43
|
+
else
|
44
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
45
|
+
s.add_dependency(%q<rdoc>, [">= 2.4.2"])
|
46
|
+
end
|
47
|
+
else
|
48
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
49
|
+
s.add_dependency(%q<rdoc>, [">= 2.4.2"])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
data/spec/pbkdf2_spec.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe PBKDF2, "when initializing" do
|
4
|
+
it "should raise an ArgumentError if an unrecognized parameter is passed" do
|
5
|
+
lambda {PBKDF2.new(:foo=>1)}.should raise_error(ArgumentError)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should raise an ArgumentError if a password is not set" do
|
9
|
+
lambda {PBKDF2.new(:salt=>"nacl", :iterations=>1000)}.should raise_error(ArgumentError)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should raise an ArgumentError if a salt is not set" do
|
13
|
+
lambda {PBKDF2.new(:password=>"s33krit", :iterations=>1000)}.should raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should raise an ArgumentError if iterations is not set" do
|
17
|
+
lambda {PBKDF2.new(:password=>"s33krit", :salt=>"nacl")}.should raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow setting options in a block" do
|
21
|
+
PBKDF2.new do |x|
|
22
|
+
x.password = "s33krit"
|
23
|
+
x.salt = "nacl"
|
24
|
+
x.iterations = 1000
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe PBKDF2, "when configuring a hash function" do
|
30
|
+
it "should default to SHA256" do
|
31
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000)
|
32
|
+
p.hash_function.name.should == "SHA256"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should support at least SHA1, SHA512, and MD5" do
|
36
|
+
%w{SHA1 SHA512 MD5}.each do |alg|
|
37
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>alg)
|
38
|
+
p.hash_function.name.should == alg
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should allow setting by symbols" do
|
43
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>:sha512)
|
44
|
+
p.hash_function.name.should == "SHA512"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should allow setting by strings" do
|
48
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>"sha512")
|
49
|
+
p.hash_function.name.should == "SHA512"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should allow setting by PKCS-style 'hmacWith' strings" do
|
53
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>"hmacWithSHA512")
|
54
|
+
p.hash_function.name.should == "SHA512"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should allow setting by classes in OpenSSL::Digest" do
|
58
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>OpenSSL::Digest::SHA512)
|
59
|
+
p.hash_function.name.should == "SHA512"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should allow setting by an instantiated object of type OpenSSL::Digest::Digest" do
|
63
|
+
hfunc = OpenSSL::Digest::SHA512.new
|
64
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>hfunc)
|
65
|
+
p.hash_function.name.should == "SHA512"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe PBKDF2, "when setting a key length" do
|
70
|
+
it "should default to the size of the hash function used" do
|
71
|
+
%w{SHA1 SHA512 MD5}.each do |alg|
|
72
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :hash_function=>alg)
|
73
|
+
p.key_length.should == OpenSSL::Digest::Digest.new(alg).size
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should throw an ArgumentError if a negative length is set" do
|
78
|
+
lambda {p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1000, :key_length=>-1)}.should raise_error(ArgumentError)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should throw an ArgumentError if too long a length is set" do
|
82
|
+
not_too_long = ((2**32 - 1) * OpenSSL::Digest::SHA256.new.size)
|
83
|
+
too_long = not_too_long + 1
|
84
|
+
lambda {p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1, :key_length=>not_too_long, :hash_function=>:sha256)}.should_not raise_error
|
85
|
+
lambda {p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1, :key_length=>too_long, :hash_function=>:sha256)}.should raise_error(ArgumentError)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should ensure that the derived key really is that long" do
|
89
|
+
length = 123
|
90
|
+
p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>1, :key_length=>length)
|
91
|
+
p.bin_string.length.should == length
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe PBKDF2, "when setting the number of iterations" do
|
96
|
+
it "should throw an ArgumentError if a number less than 1 is passed" do
|
97
|
+
lambda {p = PBKDF2.new(:password=>"s33krit", :salt=>"nacl", :iterations=>0)}.should raise_error(ArgumentError)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe PBKDF2, "once created" do
|
102
|
+
before do
|
103
|
+
@p = PBKDF2.new do |x|
|
104
|
+
x.password = "s33krit"
|
105
|
+
x.salt = "nacl"
|
106
|
+
x.iterations = 1
|
107
|
+
end
|
108
|
+
@val = @p.hex_string
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should have the same hex value as another, identically derived key" do
|
112
|
+
q = PBKDF2.new do |x|
|
113
|
+
x.password = "s33krit"
|
114
|
+
x.salt = "nacl"
|
115
|
+
x.iterations = 1
|
116
|
+
end
|
117
|
+
@p.hex_string.should == q.hex_string
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should recalculate the value if the password changes" do
|
121
|
+
@p.password = "foo"
|
122
|
+
@p.hex_string.should_not == @val
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should recalculate the value if the salt changes" do
|
126
|
+
@p.salt = "foo"
|
127
|
+
@p.hex_string.should_not == @val
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should recalculate the value if the number of iterations changes" do
|
131
|
+
@p.iterations = 2
|
132
|
+
@p.hex_string.should_not == @val
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should recalculate the value if the hash function changes" do
|
136
|
+
@p.hash_function = :md5
|
137
|
+
@p.hex_string.should_not == @val
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should recalculate the value if the key length changes" do
|
141
|
+
@p.key_length = 10
|
142
|
+
@p.hex_string.should_not == @val
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe PBKDF2, "when deriving keys" do
|
4
|
+
# see http://www.rfc-archive.org/getrfc.php?rfc=3962
|
5
|
+
# all examples there use HMAC-SHA1
|
6
|
+
it "should match the first test case in appendix B of RFC 3962" do
|
7
|
+
# Iteration count = 1
|
8
|
+
# Pass phrase = "password"
|
9
|
+
# Salt = "ATHENA.MIT.EDUraeburn"
|
10
|
+
# 128-bit PBKDF2 output:
|
11
|
+
# cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15
|
12
|
+
# 256-bit PBKDF2 output:
|
13
|
+
# cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15
|
14
|
+
# 0a d1 f7 a0 4b b9 f3 a3 33 ec c0 e2 e1 f7 08 37
|
15
|
+
p = PBKDF2.new do |p|
|
16
|
+
p.hash_function = :sha1
|
17
|
+
p.iterations = 1
|
18
|
+
p.password = "password"
|
19
|
+
p.salt = "ATHENA.MIT.EDUraeburn"
|
20
|
+
p.key_length = 128/8
|
21
|
+
end
|
22
|
+
|
23
|
+
expected = "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15"
|
24
|
+
p.hex_string.should == expected.gsub(' ','')
|
25
|
+
|
26
|
+
expected = "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15" +
|
27
|
+
"0a d1 f7 a0 4b b9 f3 a3 33 ec c0 e2 e1 f7 08 37"
|
28
|
+
|
29
|
+
p.key_length = 256/8
|
30
|
+
p.hex_string.should == expected.gsub(' ','')
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should match the second test case in appendix B of RFC 3962" do
|
34
|
+
# Iteration count = 2
|
35
|
+
# Pass phrase = "password"
|
36
|
+
# Salt="ATHENA.MIT.EDUraeburn"
|
37
|
+
# 128-bit PBKDF2 output:
|
38
|
+
# 01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d
|
39
|
+
# 256-bit PBKDF2 output:
|
40
|
+
# 01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d
|
41
|
+
# a0 53 78 b9 32 44 ec 8f 48 a9 9e 61 ad 79 9d 86
|
42
|
+
p = PBKDF2.new do |p|
|
43
|
+
p.hash_function = :sha1
|
44
|
+
p.iterations = 2
|
45
|
+
p.password = "password"
|
46
|
+
p.salt = "ATHENA.MIT.EDUraeburn"
|
47
|
+
p.key_length = 128/8
|
48
|
+
end
|
49
|
+
|
50
|
+
expected = "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d"
|
51
|
+
p.hex_string.should == expected.gsub(' ','')
|
52
|
+
|
53
|
+
expected = "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d" +
|
54
|
+
"a0 53 78 b9 32 44 ec 8f 48 a9 9e 61 ad 79 9d 86"
|
55
|
+
p.key_length = 256/8
|
56
|
+
p.hex_string.should == expected.gsub(' ','')
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should match the third test case in appendix B of RFC 3962" do
|
60
|
+
# Iteration count = 1200
|
61
|
+
# Pass phrase = "password"
|
62
|
+
# Salt = "ATHENA.MIT.EDUraeburn"
|
63
|
+
# 128-bit PBKDF2 output:
|
64
|
+
# 5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b
|
65
|
+
# 256-bit PBKDF2 output:
|
66
|
+
# 5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b
|
67
|
+
# a7 e5 2d db c5 e5 14 2f 70 8a 31 e2 e6 2b 1e 13
|
68
|
+
p = PBKDF2.new do |p|
|
69
|
+
p.hash_function = :sha1
|
70
|
+
p.iterations = 1200
|
71
|
+
p.password = "password"
|
72
|
+
p.salt = "ATHENA.MIT.EDUraeburn"
|
73
|
+
p.key_length = 128/8
|
74
|
+
end
|
75
|
+
|
76
|
+
expected = "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b"
|
77
|
+
p.hex_string.should == expected.gsub(' ','')
|
78
|
+
|
79
|
+
expected = "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b" +
|
80
|
+
"a7 e5 2d db c5 e5 14 2f 70 8a 31 e2 e6 2b 1e 13"
|
81
|
+
p.key_length = 256/8
|
82
|
+
p.hex_string.should == expected.gsub(' ','')
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should match the fourth test case in appendix B of RFC 3962" do
|
86
|
+
# Iteration count = 5
|
87
|
+
# Pass phrase = "password"
|
88
|
+
# Salt=0x1234567878563412
|
89
|
+
# 128-bit PBKDF2 output:
|
90
|
+
# d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49
|
91
|
+
# 256-bit PBKDF2 output:
|
92
|
+
# d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49
|
93
|
+
# 3f 98 d2 03 e6 be 49 a6 ad f4 fa 57 4b 6e 64 ee
|
94
|
+
p = PBKDF2.new do |p|
|
95
|
+
p.hash_function = :sha1
|
96
|
+
p.iterations = 5
|
97
|
+
p.password = "password"
|
98
|
+
p.salt = [0x1234567878563412].pack("Q")
|
99
|
+
p.key_length = 128/8
|
100
|
+
end
|
101
|
+
|
102
|
+
expected = "d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49"
|
103
|
+
p.hex_string.should == expected.gsub(' ','')
|
104
|
+
|
105
|
+
expected = "d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49" +
|
106
|
+
"3f 98 d2 03 e6 be 49 a6 ad f4 fa 57 4b 6e 64 ee"
|
107
|
+
p.key_length = 256/8
|
108
|
+
p.hex_string.should == expected.gsub(' ','')
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should match the fifth test case in appendix B of RFC 3962" do
|
112
|
+
# Iteration count = 1200
|
113
|
+
# Pass phrase = (64 characters)
|
114
|
+
# "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
115
|
+
# Salt="pass phrase equals block size"
|
116
|
+
# 128-bit PBKDF2 output:
|
117
|
+
# 13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9
|
118
|
+
# 256-bit PBKDF2 output:
|
119
|
+
# 13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9
|
120
|
+
# c5 ec 59 f1 a4 52 f5 cc 9a d9 40 fe a0 59 8e d1
|
121
|
+
p = PBKDF2.new do |p|
|
122
|
+
p.hash_function = :sha1
|
123
|
+
p.iterations = 1200
|
124
|
+
p.password = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
125
|
+
p.salt = "pass phrase equals block size"
|
126
|
+
p.key_length = 128/8
|
127
|
+
end
|
128
|
+
|
129
|
+
expected = "13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9"
|
130
|
+
p.hex_string.should == expected.gsub(' ','')
|
131
|
+
|
132
|
+
expected = "13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9" +
|
133
|
+
"c5 ec 59 f1 a4 52 f5 cc 9a d9 40 fe a0 59 8e d1"
|
134
|
+
p.key_length = 256/8
|
135
|
+
p.hex_string.should == expected.gsub(' ','')
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should match the sixth test case in appendix B of RFC 3962" do
|
139
|
+
# Iteration count = 1200
|
140
|
+
# Pass phrase = (65 characters)
|
141
|
+
# "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
142
|
+
# Salt = "pass phrase exceeds block size"
|
143
|
+
# 128-bit PBKDF2 output:
|
144
|
+
# 9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61
|
145
|
+
# 256-bit PBKDF2 output:
|
146
|
+
# 9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61
|
147
|
+
# 1a 8b 4d 28 26 01 db 3b 36 be 92 46 91 5e c8 2a
|
148
|
+
p = PBKDF2.new do |p|
|
149
|
+
p.hash_function = :sha1
|
150
|
+
p.iterations = 1200
|
151
|
+
p.password = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
152
|
+
p.salt = "pass phrase exceeds block size"
|
153
|
+
p.key_length = 128/8
|
154
|
+
end
|
155
|
+
|
156
|
+
expected = "9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61"
|
157
|
+
p.hex_string.should == expected.gsub(' ','')
|
158
|
+
|
159
|
+
expected = "9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61" +
|
160
|
+
"1a 8b 4d 28 26 01 db 3b 36 be 92 46 91 5e c8 2a"
|
161
|
+
p.key_length = 256/8
|
162
|
+
p.hex_string.should == expected.gsub(' ','')
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should match the seventh test case in appendix B of RFC 3962" do
|
166
|
+
# Iteration count = 50
|
167
|
+
# Pass phrase = g-clef (0xf09d849e)
|
168
|
+
# Salt = "EXAMPLE.COMpianist"
|
169
|
+
# 128-bit PBKDF2 output:
|
170
|
+
# 6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39
|
171
|
+
# 256-bit PBKDF2 output:
|
172
|
+
# 6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39
|
173
|
+
# e7 fe 37 a0 c4 1e 02 c2 81 ff 30 69 e1 e9 4f 52
|
174
|
+
p = PBKDF2.new do |p|
|
175
|
+
p.hash_function = :sha1
|
176
|
+
p.iterations = 50
|
177
|
+
# this is a gorram horrible test case. it took me quite a while to
|
178
|
+
# track down why 0xf09d849e should be interpreted as "\360\235\204\236"
|
179
|
+
# (which is what other code uses for this example). the mysterious
|
180
|
+
# "g-clef" annotation didn't help (turns out to be a Unicode character
|
181
|
+
# in UTF8 -- ie, 0xf0 0x9d 0x84 0x9e)
|
182
|
+
p.password = [0xf09d849e].pack("N")
|
183
|
+
p.salt = "EXAMPLE.COMpianist"
|
184
|
+
p.key_length = 128/8
|
185
|
+
end
|
186
|
+
|
187
|
+
expected = "6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39"
|
188
|
+
p.hex_string.should == expected.gsub(' ','')
|
189
|
+
|
190
|
+
expected = "6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39" +
|
191
|
+
"e7 fe 37 a0 c4 1e 02 c2 81 ff 30 69 e1 e9 4f 52"
|
192
|
+
p.key_length = 256/8
|
193
|
+
p.hex_string.should == expected.gsub(' ','')
|
194
|
+
end
|
195
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pbkdf2-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sam Quigley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.2.9
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.2.9
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.4.2
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.4.2
|
46
|
+
description: This implementation conforms to RFC 2898, and has been tested using the
|
47
|
+
test vectors in Appendix B of RFC 3962. Note, however, that while those specifications
|
48
|
+
use HMAC-SHA-1, this implementation defaults to HMAC-SHA-256. (SHA-256 provides
|
49
|
+
a longer bit length. In addition, NIST has stated that SHA-1 should be phased out
|
50
|
+
due to concerns over recent cryptanalytic attacks.)
|
51
|
+
email: quigley@emerose.com
|
52
|
+
executables: []
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files:
|
55
|
+
- LICENSE.TXT
|
56
|
+
- README.markdown
|
57
|
+
files:
|
58
|
+
- LICENSE.TXT
|
59
|
+
- README.markdown
|
60
|
+
- Rakefile
|
61
|
+
- VERSION
|
62
|
+
- benchmark.rb
|
63
|
+
- lib/pbkdf2.rb
|
64
|
+
- pbkdf2.gemspec
|
65
|
+
- spec/pbkdf2_spec.rb
|
66
|
+
- spec/rfc3962_spec.rb
|
67
|
+
- spec/spec.opts
|
68
|
+
- spec/spec_helper.rb
|
69
|
+
homepage: http://github.com/emerose/pbkdf2-ruby
|
70
|
+
licenses: []
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 1.8.23
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: Password-Based Key Derivation Function 2 - PBKDF2
|
93
|
+
test_files: []
|