-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3d8c122
Showing
10 changed files
with
315 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.gem | ||
.bundle | ||
Gemfile.lock | ||
pkg/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--order rand | ||
--color |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#!/usr/bin/env bash | ||
|
||
# This is an RVM Project .rvmrc file, used to automatically load the ruby | ||
# development environment upon cd'ing into the directory | ||
|
||
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional, | ||
# Only full ruby name is supported here, for short names use: | ||
# echo "rvm use 1.9.3" > .rvmrc | ||
environment_id="ruby-1.9.3@hkdf" | ||
|
||
# Uncomment the following lines if you want to verify rvm version per project | ||
# rvmrc_rvm_version="1.12.2 (stable)" # 1.10.1 seams as a safe start | ||
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || { | ||
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading." | ||
# return 1 | ||
# } | ||
|
||
# First we attempt to load the desired environment directly from the environment | ||
# file. This is very fast and efficient compared to running through the entire | ||
# CLI and selector. If you want feedback on which environment was used then | ||
# insert the word 'use' after --create as this triggers verbose mode. | ||
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" | ||
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]] | ||
then | ||
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id" | ||
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] && | ||
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true | ||
else | ||
# If the environment file has not yet been created, use the RVM CLI to select. | ||
rvm --create "$environment_id" || { | ||
echo "Failed to create RVM environment '${environment_id}'." | ||
return 1 | ||
} | ||
fi | ||
|
||
# If you use bundler, this might be useful to you: | ||
# if [[ -s Gemfile ]] && { | ||
# ! builtin command -v bundle >/dev/null || | ||
# builtin command -v bundle | grep $rvm_path/bin/bundle >/dev/null | ||
# } | ||
# then | ||
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n" | ||
# gem install bundler | ||
# fi | ||
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null | ||
# then | ||
# bundle install | grep -vE '^Using|Your bundle is complete' | ||
# fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
source "http://rubygems.org" | ||
gemspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require 'bundler/gem_tasks' | ||
require 'rspec/core/rake_task' | ||
|
||
RSpec::Core::RakeTask.new | ||
|
||
desc 'Open an irb session preloaded with the library' | ||
task :console do | ||
sh "irb -rubygems -r hkdf -I lib" | ||
end | ||
|
||
task :default => :spec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Gem::Specification.new do |s| | ||
s.name = 'hkdf' | ||
s.version = '0.1.0' | ||
s.authors = ['John Downey'] | ||
s.email = ['jdowney@gmail.com'] | ||
s.homepage = 'http://github.com/jtdowney/hkdf' | ||
s.summary = %q{HMAC-based Key Derivation Function} | ||
s.description = %q{A ruby implementation of RFC5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF). The goal of HKDF is to take some source key material and generate suitable cryptographic keys from it.} | ||
|
||
s.files = Dir.glob('lib/**/*') | ||
s.test_files = Dir.glob('spec/**/*') | ||
s.require_paths = ['lib'] | ||
|
||
s.add_development_dependency 'rspec' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
require 'openssl' | ||
|
||
class HKDF | ||
def initialize(source, options = {}) | ||
options = {:algorithm => 'SHA256', :info => '', :salt => nil}.merge(options) | ||
|
||
@digest = OpenSSL::Digest.new(options[:algorithm]) | ||
@info = options[:info] | ||
|
||
salt = options[:salt] | ||
salt = 0.chr * @digest.digest_length if salt.nil? #or salt.empty? | ||
|
||
@prk = OpenSSL::HMAC.digest(@digest, salt, source) | ||
@position = 0 | ||
@blocks = [] | ||
@blocks << '' | ||
end | ||
|
||
def algorithm | ||
@digest.name | ||
end | ||
|
||
def max_length | ||
@digest.digest_length * 255 | ||
end | ||
|
||
def seek(position) | ||
raise RangeError.new("cannot seek past #{max_length}") if position > max_length | ||
|
||
@position = position | ||
end | ||
|
||
def rewind | ||
seek(0) | ||
end | ||
|
||
def next_bytes(length) | ||
new_position = length + @position | ||
raise RangeError.new("requested #{length} bytes, only #{max_length} available") if new_position > max_length | ||
|
||
_generate_blocks(new_position) | ||
|
||
start = @position | ||
@position = new_position | ||
|
||
@blocks.join('').slice(start, length) | ||
end | ||
|
||
def next_hex_bytes(length) | ||
next_bytes(length).unpack('H*').first | ||
end | ||
|
||
def _generate_blocks(length) | ||
start = @blocks.size | ||
block_count = (length.to_f / @digest.digest_length).ceil | ||
start.upto(block_count) do |n| | ||
@blocks << OpenSSL::HMAC.digest(@digest, @prk, @blocks[n - 1] + @info + n.chr) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
require 'spec_helper' | ||
|
||
describe HKDF do | ||
before(:each) do | ||
@algorithm = 'SHA256' | ||
@source = 'source' | ||
@hkdf = HKDF.new(@source, :algorithm => @algorithm) | ||
end | ||
|
||
describe 'initialize' do | ||
it 'defaults the algorithm to SHA-256' do | ||
HKDF.new(@source).algorithm.should == 'SHA256' | ||
end | ||
|
||
it 'takes an optional digest algorithm' do | ||
@hkdf = HKDF.new('source', :algorithm => 'SHA1') | ||
@hkdf.algorithm.should == 'SHA1' | ||
end | ||
|
||
it 'defaults salt to all zeros of digest length' do | ||
salt = 0.chr * 32 | ||
|
||
@hkdf_salt = HKDF.new(@source, :algorithm => @algorithm, :salt => salt) | ||
@hkdf_nosalt = HKDF.new(@source, :algorithm => @algorithm) | ||
@hkdf_salt.next_bytes(32) == @hkdf_nosalt.next_bytes(32) | ||
end | ||
|
||
it 'sets salt to all zeros of digest 32 if empty' do | ||
@hkdf_blanksalt = HKDF.new(@source, :algorithm => @algorithm, :salt => '') | ||
@hkdf_nosalt = HKDF.new(@source, :algorithm => @algorithm) | ||
@hkdf_blanksalt.next_bytes(32) == @hkdf_nosalt.next_bytes(32) | ||
end | ||
|
||
it 'defaults info to an empty string' do | ||
@hkdf_info = HKDF.new(@source, :algorithm => @algorithm, :info => '') | ||
@hkdf_noinfo = HKDF.new(@source, :algorithm => @algorithm) | ||
@hkdf_info.next_bytes(32) == @hkdf_noinfo.next_bytes(32) | ||
end | ||
end | ||
|
||
describe 'max_length' do | ||
it 'is 255 times the digest length' do | ||
@hkdf.max_length.should == 255 * 32 | ||
end | ||
end | ||
|
||
describe 'next_bytes' do | ||
it 'raises an error if requested size is > max_length' do | ||
expect { @hkdf.next_bytes(@hkdf.max_length + 1) }.should raise_error(RangeError, /requested \d+ bytes, only \d+ available/) | ||
expect { @hkdf.next_bytes(@hkdf.max_length) }.should_not raise_error(RangeError, /requested \d+ bytes, only \d+ available/) | ||
end | ||
|
||
it 'raises an error if requested size + current position is > max_length' do | ||
expect do | ||
@hkdf.next_bytes(32) | ||
@hkdf.next_bytes(@hkdf.max_length - 31) | ||
end.should raise_error(RangeError, /requested \d+ bytes, only \d+ available/) | ||
end | ||
|
||
it 'advances the stream position' do | ||
@hkdf.next_bytes(32).should_not == @hkdf.next_bytes(32) | ||
end | ||
|
||
test_vectors.each do |name, options| | ||
it "matches output from the '#{name}' test vector" do | ||
options[:algorithm] = options[:Hash] | ||
|
||
hkdf = HKDF.new(options[:IKM], options) | ||
hkdf.next_bytes(options[:L].to_i).should == options[:OKM] | ||
end | ||
end | ||
end | ||
|
||
describe 'next_hex_bytes' do | ||
it 'returns the next bytes as hex' do | ||
@hkdf.next_hex_bytes(20).should == 'fb496612b8cb82cd2297770f83c72b377af16d7b' | ||
end | ||
end | ||
|
||
describe 'seek' do | ||
it 'sets the position anywhere in the stream' do | ||
@hkdf.next_bytes(10) | ||
output = @hkdf.next_bytes(32) | ||
@hkdf.seek(10) | ||
@hkdf.next_bytes(32).should == output | ||
end | ||
|
||
it 'raises an error if requested to seek past end of stream' do | ||
expect { @hkdf.seek(@hkdf.max_length + 1) }.should raise_error(RangeError, /cannot seek past \d+/) | ||
expect { @hkdf.seek(@hkdf.max_length) }.should_not raise_error(RangeError, /cannot seek past \d+/) | ||
end | ||
end | ||
|
||
describe 'rewind' do | ||
it 'resets the stream position to the beginning' do | ||
output = @hkdf.next_bytes(32) | ||
@hkdf.rewind | ||
@hkdf.next_bytes(32).should == output | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
require 'hkdf' | ||
|
||
def test_vectors | ||
test_lines = File.readlines('spec/test_vectors.txt').map(&:strip).reject(&:empty?) | ||
|
||
vectors = {} | ||
test_lines.each_slice(8) do |lines| | ||
name = lines.shift | ||
values = lines.inject({}) do |hash, line| | ||
key, value = line.split('=').map(&:strip) | ||
value = '' unless value | ||
value = [value.slice(2..-1)].pack('H*') if value.start_with?('0x') | ||
hash[key.to_sym] = value | ||
hash | ||
end | ||
vectors[name] = values | ||
end | ||
vectors | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
Basic test case with SHA256 | ||
Hash = SHA256 | ||
IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | ||
salt = 0x000102030405060708090a0b0c | ||
info = 0xf0f1f2f3f4f5f6f7f8f9 | ||
L = 42 | ||
PRK = 0x077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5 | ||
OKM = 0x3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865 | ||
|
||
Test with SHA256 and longer inputs/outputs | ||
Hash = SHA256 | ||
IKM = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f | ||
salt = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf | ||
info = 0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff | ||
L = 82 | ||
PRK = 0x06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244 | ||
OKM = 0xb11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87 | ||
|
||
Test with SHA256 and empty salt/info | ||
Hash = SHA256 | ||
IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | ||
salt = | ||
info = | ||
L = 42 | ||
PRK = 0x19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04 | ||
OKM = 0x8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8 | ||
|
||
Basic test case with SHA1 | ||
Hash = SHA1 | ||
IKM = 0x0b0b0b0b0b0b0b0b0b0b0b | ||
salt = 0x000102030405060708090a0b0c | ||
info = 0xf0f1f2f3f4f5f6f7f8f9 | ||
L = 42 | ||
PRK = 0x9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243 | ||
OKM = 0x085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896 | ||
|
||
Test with SHA1 and longer inputs/outputs | ||
Hash = SHA1 | ||
IKM = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f | ||
salt = 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf | ||
info = 0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff | ||
L = 82 | ||
PRK = 0x8adae09a2a307059478d309b26c4115a224cfaf6 | ||
OKM = 0x0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4 | ||
|
||
Test with SHA1 and empty salt/info | ||
Hash = SHA1 | ||
IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | ||
salt = | ||
info = | ||
L = 42 | ||
PRK = 0xda8c8a73c7fa77288ec6f5e7c297786aa0d32d01 | ||
OKM = 0x0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918 |