95 lines
3.3 KiB
Crystal
95 lines
3.3 KiB
Crystal
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
|
|
|
|
|