Add Blake2b.
This commit is contained in:
parent
484847d57f
commit
6670b22ca1
89
spec/cox/blake2b_spec.cr
Normal file
89
spec/cox/blake2b_spec.cr
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
libsodium_comparisons = [
|
||||||
|
{
|
||||||
|
key: nil,
|
||||||
|
input: "",
|
||||||
|
output: "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8",
|
||||||
|
out_size: 32,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# from https://github.com/BLAKE2/BLAKE2/tree/master/testvectors
|
||||||
|
test_vectors = [
|
||||||
|
{
|
||||||
|
key: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||||
|
input: "",
|
||||||
|
output: "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
|
||||||
|
input: "00",
|
||||||
|
output: "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
describe Cox::Blake2b do
|
||||||
|
it "libsodium comparisons" do
|
||||||
|
libsodium_comparisons.each do |vec|
|
||||||
|
d = Cox::Blake2b.new vec[:out_size], key: vec[:key].try(&.hexbytes)
|
||||||
|
d.update vec[:input].hexbytes
|
||||||
|
d.hexdigest.should eq vec[:output]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "test vectors" do
|
||||||
|
test_vectors.each do |vec|
|
||||||
|
d = Cox::Blake2b.new 64, key: vec[:key].hexbytes
|
||||||
|
d.update vec[:input].hexbytes
|
||||||
|
d.hexdigest.should eq vec[:output]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "produces different output with different salt or personal params" do
|
||||||
|
key = Bytes.new Cox::Blake2b::KEY_SIZE
|
||||||
|
salt = Bytes.new Cox::Blake2b::SALT_SIZE
|
||||||
|
salt2 = Bytes.new Cox::Blake2b::SALT_SIZE
|
||||||
|
salt2 = salt.dup
|
||||||
|
salt2[0] = 1
|
||||||
|
personal = Bytes.new Cox::Blake2b::PERSONAL_SIZE
|
||||||
|
personal2 = personal.dup
|
||||||
|
personal2[0] = 1
|
||||||
|
|
||||||
|
|
||||||
|
d = Cox::Blake2b.new key: key, salt: salt, personal: personal
|
||||||
|
d.update "foo".to_slice
|
||||||
|
output = d.hexdigest
|
||||||
|
|
||||||
|
d = Cox::Blake2b.new key: key, salt: salt2, personal: personal
|
||||||
|
d.update "foo".to_slice
|
||||||
|
saltout = d.hexdigest
|
||||||
|
|
||||||
|
d = Cox::Blake2b.new key: key, salt: salt, personal: personal2
|
||||||
|
d.update "foo".to_slice
|
||||||
|
personalout = d.hexdigest
|
||||||
|
|
||||||
|
output.should_not eq saltout
|
||||||
|
output.should_not eq personalout
|
||||||
|
saltout.should_not eq personalout
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises on invalid " do
|
||||||
|
expect_raises ArgumentError do
|
||||||
|
Cox::Blake2b.new key: Bytes.new(128)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect_raises ArgumentError do
|
||||||
|
Cox::Blake2b.new salt: Bytes.new(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect_raises ArgumentError do
|
||||||
|
Cox::Blake2b.new salt: Bytes.new(128)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect_raises ArgumentError do
|
||||||
|
Cox::Blake2b.new personal: Bytes.new(128)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
95
src/cox/blake2b.cr
Normal file
95
src/cox/blake2b.cr
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
require "openssl/digest/digest_base"
|
||||||
|
|
||||||
|
module Cox
|
||||||
|
class Blake2b
|
||||||
|
# provides copying digest/hexdigest methods
|
||||||
|
include OpenSSL::DigestBase
|
||||||
|
|
||||||
|
KEY_SIZE = LibSodium.crypto_generichash_blake2b_keybytes
|
||||||
|
KEY_SIZE_MIN = LibSodium.crypto_generichash_blake2b_keybytes_min
|
||||||
|
KEY_SIZE_MAX = LibSodium.crypto_generichash_blake2b_keybytes_max
|
||||||
|
|
||||||
|
SALT_SIZE = LibSodium.crypto_generichash_blake2b_saltbytes
|
||||||
|
|
||||||
|
PERSONAL_SIZE = LibSodium.crypto_generichash_blake2b_personalbytes
|
||||||
|
|
||||||
|
OUT_SIZE = LibSodium.crypto_generichash_blake2b_bytes.to_i32
|
||||||
|
OUT_SIZE_MIN = LibSodium.crypto_generichash_blake2b_bytes_min.to_i32
|
||||||
|
OUT_SIZE_MAX = LibSodium.crypto_generichash_blake2b_bytes_max.to_i32
|
||||||
|
|
||||||
|
@state = StaticArray(UInt8, 384).new 0
|
||||||
|
@key_size = 0
|
||||||
|
@have_salt = false
|
||||||
|
@have_personal = false
|
||||||
|
|
||||||
|
# implemented as static array's so clone works without jumping through hoops.
|
||||||
|
@key = StaticArray(UInt8, 64).new 0
|
||||||
|
@salt = StaticArray(UInt8, 16).new 0
|
||||||
|
@personal = StaticArray(UInt8, 16).new 0
|
||||||
|
|
||||||
|
def initialize(@out_size : Int32 = OUT_SIZE, key : Bytes? = nil, salt : Bytes? = nil, personal : Bytes? = nil)
|
||||||
|
if k = key
|
||||||
|
raise ArgumentError.new("key larger than KEY_SIZE_MAX, got #{k.bytesize}") if k.bytesize > KEY_SIZE_MAX
|
||||||
|
@key_size = k.bytesize
|
||||||
|
k.copy_to @key.to_slice
|
||||||
|
end
|
||||||
|
|
||||||
|
if sa = salt
|
||||||
|
raise ArgumentError.new("salt must be SALT_SIZE bytes, got #{sa.bytesize}") if sa.bytesize != SALT_SIZE
|
||||||
|
sa.copy_to @salt.to_slice
|
||||||
|
@have_salt = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if pe = personal
|
||||||
|
raise ArgumentError.new("personal must be PERSONAL_SIZE bytes, got #{pe.bytesize}") if pe.bytesize != PERSONAL_SIZE
|
||||||
|
pe.copy_to @personal.to_slice
|
||||||
|
@have_personal = true
|
||||||
|
end
|
||||||
|
|
||||||
|
reset
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
key = @key_size > 0 ? @key.to_unsafe : nil
|
||||||
|
salt = @have_salt ? @salt.to_unsafe : nil
|
||||||
|
personal = @have_personal ? @personal.to_unsafe : nil
|
||||||
|
|
||||||
|
if LibSodium.crypto_generichash_blake2b_init_salt_personal(@state, key, @key_size, @out_size, salt, personal) != 0
|
||||||
|
raise Cox::Error.new("blake2b_init_key_salt_personal")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(data : Bytes)
|
||||||
|
if LibSodium.crypto_generichash_blake2b_update(@state, data, data.bytesize) != 0
|
||||||
|
raise Cox::Error.new("crypto_generichash_blake2b_update")
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish
|
||||||
|
data = Bytes.new @out_size
|
||||||
|
if LibSodium.crypto_generichash_blake2b_final(@state, data, data.bytesize) != 0
|
||||||
|
raise Cox::Error.new("crypto_generichash_blake2b_final")
|
||||||
|
end
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone
|
||||||
|
dup
|
||||||
|
end
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
def __validate_sizes__
|
||||||
|
state_size = LibSodium.crypto_generichash_blake2b_statebytes
|
||||||
|
abort "@state.bytesize doesn't match library version #{@state.to_slice.bytesize} #{state_size}" if @state.to_slice.bytesize < state_size
|
||||||
|
abort "@key.bytesize doesn't match library version" if @key.to_slice.bytesize != KEY_SIZE_MAX
|
||||||
|
abort "@salt.bytesize doesn't match library version #{@salt.to_slice.bytesize} #{SALT_SIZE}" if @salt.to_slice.bytesize != SALT_SIZE
|
||||||
|
abort "@personal.bytesize doesn't match library version #{@personal.to_slice.bytesize} #{PERSONAL_SIZE}" if @personal.to_slice.bytesize != SALT_SIZE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Blake2b.new.__validate_sizes__
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -21,6 +21,15 @@ module Cox
|
|||||||
fun crypto_pwhash_opslimit_sensitive() : LibC::SizeT
|
fun crypto_pwhash_opslimit_sensitive() : LibC::SizeT
|
||||||
fun crypto_pwhash_opslimit_max() : LibC::SizeT
|
fun crypto_pwhash_opslimit_max() : LibC::SizeT
|
||||||
fun crypto_pwhash_strbytes() : LibC::SizeT
|
fun crypto_pwhash_strbytes() : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_statebytes : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_bytes : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_bytes_min : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_bytes_max : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_keybytes : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_keybytes_min : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_keybytes_max : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_saltbytes : LibC::SizeT
|
||||||
|
fun crypto_generichash_blake2b_personalbytes : LibC::SizeT
|
||||||
|
|
||||||
PUBLIC_KEY_BYTES = crypto_box_publickeybytes()
|
PUBLIC_KEY_BYTES = crypto_box_publickeybytes()
|
||||||
SECRET_KEY_BYTES = crypto_box_secretkeybytes()
|
SECRET_KEY_BYTES = crypto_box_secretkeybytes()
|
||||||
@ -119,5 +128,26 @@ module Cox
|
|||||||
optslimit : LibC::ULongLong,
|
optslimit : LibC::ULongLong,
|
||||||
memlimit : LibC::SizeT,
|
memlimit : LibC::SizeT,
|
||||||
) : LibC::Int
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_generichash_blake2b_init_salt_personal(
|
||||||
|
state : Pointer(LibC::UChar),
|
||||||
|
key : Pointer(LibC::UChar),
|
||||||
|
key_len : UInt8,
|
||||||
|
out_len : UInt8,
|
||||||
|
salt : Pointer(LibC::UChar),
|
||||||
|
personal : Pointer(LibC::UChar),
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_generichash_blake2b_update(
|
||||||
|
state : Pointer(LibC::UChar),
|
||||||
|
in : Pointer(LibC::UChar),
|
||||||
|
in_len : UInt64,
|
||||||
|
) : LibC::Int
|
||||||
|
|
||||||
|
fun crypto_generichash_blake2b_final(
|
||||||
|
state : Pointer(LibC::UChar),
|
||||||
|
output : Pointer(LibC::UChar),
|
||||||
|
output_len : UInt64,
|
||||||
|
) : LibC::Int
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user