Crystal <= 0.34 backport `final` methods from crystal 0.35.

Crystal = 0.35 use new Digest::Base interface.
Crystal > 0.35 futureport `hexfinal` method.
master
Didactic Drunk 2020-05-18 23:13:50 -07:00
parent 7155cee26c
commit a8f716de09
4 changed files with 76 additions and 48 deletions

View File

@ -199,11 +199,11 @@ data = "data".to_slice
# output_size, key, salt, and personal are optional. # output_size, key, salt, and personal are optional.
digest = Sodium::Digest::Blake2b.new out_size, key: key, salt: salt, personal: personal digest = Sodium::Digest::Blake2b.new out_size, key: key, salt: salt, personal: personal
digest.update data digest.update data
output = d.hexdigest output = d.hexfinal
digest.reset # Reuse existing object to hash again. digest.reset # Reuse existing object to hash again.
digest.update data digest.update data
output = d.hexdigest output = d.hexfinal
``` ```
### Key derivation ### Key derivation

View File

@ -15,14 +15,14 @@ Benchmark.ips(warmup: 0.5) do |bm|
bm.report "blake2b new obj per iter #{size}" do bm.report "blake2b new obj per iter #{size}" do
d = Sodium::Digest::Blake2b.new 64 d = Sodium::Digest::Blake2b.new 64
d.update bufs[i] d.update bufs[i]
d.digest d.final
end end
d = Sodium::Digest::Blake2b.new output_size d = Sodium::Digest::Blake2b.new output_size
bm.report "blake2b reset per iter #{size}" do bm.report "blake2b reset per iter #{size}" do
d.reset d.reset
d.update bufs[i] d.update bufs[i]
d.digest d.final
end end
d = Sodium::Digest::Blake2b.new output_size d = Sodium::Digest::Blake2b.new output_size
@ -30,7 +30,7 @@ Benchmark.ips(warmup: 0.5) do |bm|
bm.report "blake2b reset reusing buffer per iter #{size}" do bm.report "blake2b reset reusing buffer per iter #{size}" do
d.reset d.reset
d.update bufs[i] d.update bufs[i]
d.finish dst d.final dst
end end
end end
@ -39,17 +39,23 @@ Benchmark.ips(warmup: 0.5) do |bm|
bm.report "#{arg} new obj per iter #{size}" do bm.report "#{arg} new obj per iter #{size}" do
d = OpenSSL::Digest.new arg d = OpenSSL::Digest.new arg
d.update bufs[i] d.update bufs[i]
d.digest d.final
end end
d = OpenSSL::Digest.new arg d = OpenSSL::Digest.new arg
bm.report "#{arg} reset per iter #{size}" do bm.report "#{arg} reset per iter #{size}" do
d.reset d.reset
d.update bufs[i] d.update bufs[i]
d.digest d.final
end end
# OpenSSL::Digest doesn't have a public .finish (yet) d = OpenSSL::Digest.new arg
dst = Bytes.new d.digest_size
bm.report "#{arg} reset reusing buffer per iterr #{size}" do
d.reset
d.update bufs[i]
d.final dst
end
end end
end end
end end

View File

@ -34,7 +34,7 @@ describe Sodium::Digest::Blake2b do
test_vectors.each do |vec| test_vectors.each do |vec|
d = Sodium::Digest::Blake2b.new 64, key: vec[:key].hexbytes d = Sodium::Digest::Blake2b.new 64, key: vec[:key].hexbytes
d.update vec[:input].hexbytes d.update vec[:input].hexbytes
d.hexdigest.should eq vec[:output] d.hexfinal.should eq vec[:output]
end end
more_vectors.each do |vec| more_vectors.each do |vec|
@ -42,7 +42,7 @@ describe Sodium::Digest::Blake2b do
personal = vec[:personal].empty? ? nil : vec[:personal].hexbytes personal = vec[:personal].empty? ? nil : vec[:personal].hexbytes
d = Sodium::Digest::Blake2b.new vec[:out_len], key: vec[:key].hexbytes, salt: salt, personal: personal d = Sodium::Digest::Blake2b.new vec[:out_len], key: vec[:key].hexbytes, salt: salt, personal: personal
d.update vec[:input].hexbytes d.update vec[:input].hexbytes
d.hexdigest.should eq vec[:output] d.hexfinal.should eq vec[:output]
end end
end end
@ -58,15 +58,15 @@ describe Sodium::Digest::Blake2b do
d = Sodium::Digest::Blake2b.new key: key, salt: salt, personal: personal d = Sodium::Digest::Blake2b.new key: key, salt: salt, personal: personal
d.update "foo".to_slice d.update "foo".to_slice
output = d.hexdigest output = d.hexfinal
d = Sodium::Digest::Blake2b.new key: key, salt: salt2, personal: personal d = Sodium::Digest::Blake2b.new key: key, salt: salt2, personal: personal
d.update "foo".to_slice d.update "foo".to_slice
saltout = d.hexdigest saltout = d.hexfinal
d = Sodium::Digest::Blake2b.new key: key, salt: salt, personal: personal2 d = Sodium::Digest::Blake2b.new key: key, salt: salt, personal: personal2
d.update "foo".to_slice d.update "foo".to_slice
personalout = d.hexdigest personalout = d.hexfinal
output.should_not eq saltout output.should_not eq saltout
output.should_not eq personalout output.should_not eq personalout

View File

@ -1,6 +1,7 @@
require "../lib_sodium" require "../lib_sodium"
require "../wipe" require "../wipe"
require "../secure_buffer" require "../secure_buffer"
require "digest/base"
require "openssl/digest/digest_base" require "openssl/digest/digest_base"
module Sodium::Digest module Sodium::Digest
@ -13,11 +14,9 @@ module Sodium::Digest
# digest = Blake2b.new # digest = Blake2b.new
# digest.update data # digest.update data
# digest.update data # digest.update data
# digest.hexdigest => String # digest.hexfinal => String
# ``` # ```
class Blake2b class Blake2b < ::Digest::Base
# provides copying digest/hexdigest methods
include OpenSSL::DigestBase
include Wipe include Wipe
# 32 # 32
@ -40,11 +39,11 @@ module Sodium::Digest
# 64 # 64
OUT_SIZE_MAX = LibSodium.crypto_generichash_blake2b_bytes_max.to_i32 OUT_SIZE_MAX = LibSodium.crypto_generichash_blake2b_bytes_max.to_i32
getter digest_size getter digest_size : Int32
@[Wipe::Var] @[Wipe::Var]
@state = StaticArray(UInt8, 384).new 0 @state = StaticArray(UInt8, 384).new 0
@key_size = 0 getter key_size = 0
# implemented as static array's so clone works without jumping through hoops. # implemented as static array's so clone works without jumping through hoops.
@[Wipe::Var] @[Wipe::Var]
@ -57,7 +56,7 @@ module Sodium::Digest
# digest_size is selectable. Use 32 for Blake2b256 (libsodium default), 64 for Blake2b512 # digest_size is selectable. Use 32 for Blake2b256 (libsodium default), 64 for Blake2b512
# or any value between OUT_SIZE_MIN and OUT_SIZE_MAX. Many libsodium bindings only support [256] or [256 and 512] bit output. # or any value between OUT_SIZE_MIN and OUT_SIZE_MAX. Many libsodium bindings only support [256] or [256 and 512] bit output.
# #
# `key`, `salt`, and `personal` are all optional. Most other libsodium bindings don't support them. # `key`, `salt`, and `personal` are all optional. Many other libsodium bindings don't support them.
# Check the other implementation(s) you need to interoperate with before using. # Check the other implementation(s) you need to interoperate with before using.
def initialize(@digest_size : Int32 = OUT_SIZE, key : Bytes? | SecureBuffer? = nil, salt : Bytes? = nil, personal : Bytes? = nil) def initialize(@digest_size : Int32 = OUT_SIZE, key : Bytes? | SecureBuffer? = nil, salt : Bytes? = nil, personal : Bytes? = nil)
if k = key if k = key
@ -80,7 +79,57 @@ module Sodium::Digest
reset reset
end end
def reset # Compatibility with Crystal <= 0.35?(TBD)
{% unless @type.has_method?(:hexfinal) %}
def hexfinal : String
final.hexstring
end
{% end %}
# Compatibility with Crystal <= 0.32
{% unless @type.has_method?(:final) %}
# provides copying digest/hexdigest methods
include OpenSSL::DigestBase
def update(data : Bytes) : self
update_impl data
self
end
def reset : self
reset_impl
self
end
# Destructive operation. Assumes you know what you are doing.
# Use .digest or .hexdigest instead.
def final
dst = Bytes.new @digest_size
final_impl dst
dst
end
# Used by OpenSSL::DigestBase for #digest and #hexdigest
# :nodoc:
protected def finish
final
end
{% end %}
def update_impl(data : Bytes) : Nil
if LibSodium.crypto_generichash_blake2b_update(@state.to_slice, data, data.bytesize) != 0
raise Sodium::Error.new("crypto_generichash_blake2b_update")
end
end
def final_impl(dst : Bytes) : Nil
ret = LibSodium.crypto_generichash_blake2b_final(@state.to_slice, dst, dst.bytesize)
if ret != 0
raise Sodium::Error.new("crypto_generichash_blake2b_final #{ret.inspect}")
end
end
def reset_impl : Nil
key = @key.to_unsafe key = @key.to_unsafe
salt = @salt.to_unsafe salt = @salt.to_unsafe
personal = @personal.to_unsafe personal = @personal.to_unsafe
@ -90,33 +139,6 @@ module Sodium::Digest
end end
end end
def update(data : Bytes)
if LibSodium.crypto_generichash_blake2b_update(@state.to_slice, data, data.bytesize) != 0
raise Sodium::Error.new("crypto_generichash_blake2b_update")
end
self
end
# Destructive operation. Assumes you know what you are doing.
# Use .digest or .hexdigest instead.
def finish
dst = Bytes.new @digest_size
finish dst
dst
end
# Destructive operation. Assumes you know what you are doing.
# Use .digest or .hexdigest instead.
def finish(dst : Bytes) : Bytes
ret = LibSodium.crypto_generichash_blake2b_final(@state.to_slice, dst, dst.bytesize)
if ret != 0
raise Sodium::Error.new("crypto_generichash_blake2b_final #{ret.inspect}")
end
dst
end
def clone def clone
dup dup
end end