Skip to content

Commit

Permalink
Merge pull request #57 from cryptosphere/blake2b-keyed
Browse files Browse the repository at this point in the history
Support for Blake2b in keyed mode
  • Loading branch information
tarcieri committed Jun 25, 2013
2 parents 42120e3 + 4fd18d9 commit 52a931b
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 12 deletions.
1 change: 1 addition & 0 deletions lib/rbnacl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class IncorrectPrimitiveError < ArgumentError; end
require "rbnacl/box"
require "rbnacl/secret_box"
require "rbnacl/hash"
require "rbnacl/hash/blake2b"
require "rbnacl/util"
require "rbnacl/auth"
require "rbnacl/hmac/sha512256"
Expand Down
30 changes: 18 additions & 12 deletions lib/rbnacl/hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ module Hash
#
# @return [String] The SHA-256 hash as raw bytes (Or encoded as per the second argument)
def self.sha256(data, encoding = :raw)
hash = Util.zeros(NaCl::SHA256BYTES)
NaCl.crypto_hash_sha256(hash, data, data.bytesize) || raise(CryptoError, "Hashing failed!")
Encoder[encoding].encode(hash)
digest = Util.zeros(NaCl::SHA256BYTES)
NaCl.crypto_hash_sha256(digest, data, data.bytesize) || raise(CryptoError, "Hashing failed!")
Encoder[encoding].encode(digest)
end

# Returns the SHA-512 hash of the given data
Expand All @@ -40,28 +40,34 @@ def self.sha256(data, encoding = :raw)
#
# @return [String] The SHA-512 hash as raw bytes (Or encoded as per the second argument)
def self.sha512(data, encoding = :raw)
hash = Util.zeros(NaCl::SHA512BYTES)
NaCl.crypto_hash_sha512(hash, data, data.bytesize) || raise(CryptoError, "Hashing failed!")
Encoder[encoding].encode(hash)
digest = Util.zeros(NaCl::SHA512BYTES)
NaCl.crypto_hash_sha512(digest, data, data.bytesize) || raise(CryptoError, "Hashing failed!")
Encoder[encoding].encode(digest)
end

if NaCl.supported_version? :libsodium, '0.4.0'
# Returns the Blake2b hash of the given data
#
# There's no streaming done, just pass in the data and be done with it.
# This method returns a 64-byte hash.
# This method returns a 64-byte hash by default.
#
# @param [String] data The data, as a collection of bytes
# @param [#to_sym] encoding Encoding of the returned hash.
# @option options [Fixnum] digest_size Size in bytes (1-64, default 64)
# @option options [String] key 64-byte (or less) key for keyed mode
# @option options [Symbol] encoding Output encoding format (default raw)
#
# @raise [CryptoError] If the hashing fails for some reason.
#
# @return [String] The blake2b hash as raw bytes (Or encoded as per the second argument)
def self.blake2b(data, encoding = :raw)
hash = Util.zeros(NaCl::BLAKE2B_OUTBYTES)
NaCl.crypto_hash_blake2b(hash, NaCl::BLAKE2B_OUTBYTES, data, data.bytesize, nil, 0) || raise(CryptoError, "Hashing failed!")
Encoder[encoding].encode(hash)
def self.blake2b(data, options = {})
key = options[:key]
digest_size = options[:digest_size] || NaCl::BLAKE2B_OUTBYTES
encoding = options[:encoding] || :raw

digest = Blake2b.new(options).hash(data)
Encoder[encoding].encode(digest)
end

else
def self.blake2b(data, encoding = :raw)
raise NotImplementedError, "not supported by this version of libsodium"
Expand Down
44 changes: 44 additions & 0 deletions lib/rbnacl/hash/blake2b.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Crypto
module Hash
# The Blake2b hash function
#
# Blake2b is based on Blake, a SHA3 finalist which was snubbed in favor of
# Keccak, a much slower hash function but one sufficiently different from
# SHA2 to let the SHA3 judges panel sleep easy. Back in the real world,
# it'd be great if we can calculate hashes quickly if possible.
#
# Blake2b provides for up to 64-bit digests and also supports a keyed mode
# similar to HMAC
class Blake2b
# Create a new Blake2b hash object
#
# @param [Hash] opts Blake2b configuration
# @option opts [String] :key for Blake2b keyed mode
# @option opts [Integer] :digest_size size of output digest in bytes
#
# @raise [Crypto::LengthError] Invalid length specified for one or more options
#
# @return [Crypto::Hash::Blake2b] A Blake2b hasher object
def initialize(opts = {})
@key = opts.fetch(:key, nil)
@key_size = @key ? @key.bytesize : 0
raise LengthError, "key too long" if @key_size > NaCl::BLAKE2B_KEYBYTES

@digest_size = opts.fetch(:digest_size, NaCl::BLAKE2B_OUTBYTES)
raise LengthError, "invalid digest size" if @digest_size < 1 || @digest_size > NaCl::BLAKE2B_OUTBYTES
end

# Calculate a Blake2b hash
#
# @param [String] message Message to be hashed
# @param [#to_sym] encoding Encoding of the returned hash
#
# @return [String] Blake2b digest of the string as raw bytes
def hash(message, encoding = :raw)
digest = Util.zeros(@digest_size)
NaCl.crypto_hash_blake2b(digest, @digest_size, message, message.bytesize, @key, @key_size) || raise(CryptoError, "Hashing failed!")
Encoder[encoding].encode(digest)
end
end
end
end
13 changes: 13 additions & 0 deletions spec/rbnacl/hash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@
it "calculates the correct hash for an empty string" do
Crypto::Hash.blake2b("").should eq empty_string_hash
end

context "keyed" do
let(:reference_string_hex) { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe" }
let(:reference_string) { hex2bytes reference_string_hex }
let(:reference_string_hash_hex) { "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" }
let(:reference_string_hash) { hex2bytes reference_string_hash_hex }
let(:reference_key_hex) { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" }
let(:reference_key) { hex2bytes reference_key_hex }

it "calculates keyed hashes correctly" do
Crypto::Hash.blake2b(reference_string, :key => reference_key).should eq reference_string_hash
end
end
end
end
end

0 comments on commit 52a931b

Please sign in to comment.