pbkdf2-ruby 0.2.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/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: []
|