lib/rbnacl/hmac/sha256.rb in rbnacl-5.0.0 vs lib/rbnacl/hmac/sha256.rb in rbnacl-6.0.0

- old
+ new

@@ -20,25 +20,90 @@ sodium_type :auth sodium_primitive :hmacsha256 sodium_constant :BYTES sodium_constant :KEYBYTES - sodium_function :auth_hmacsha256, - :crypto_auth_hmacsha256, - [:pointer, :pointer, :ulong_long, :pointer] + sodium_function :auth_hmacsha256_init, + :crypto_auth_hmacsha256_init, + %i[pointer pointer size_t] - sodium_function :auth_hmacsha256_verify, - :crypto_auth_hmacsha256_verify, - [:pointer, :pointer, :ulong_long, :pointer] + sodium_function :auth_hmacsha256_update, + :crypto_auth_hmacsha256_update, + %i[pointer pointer ulong_long] + sodium_function :auth_hmacsha256_final, + :crypto_auth_hmacsha256_final, + %i[pointer pointer] + + # Create instance without checking key length + # + # RFC 2104 HMAC + # The key for HMAC can be of any length. + # + # see https://tools.ietf.org/html/rfc2104#section-3 + def initialize(key) + @key = Util.check_hmac_key(key, "#{self.class} key") + @state = State.new + @authenticator = Util.zeros(tag_bytes) + + self.class.auth_hmacsha256_init(@state, key, key.bytesize) + end + + # Compute authenticator for message + # + # @params [#to_str] message message to construct an authenticator for + def update(message) + self.class.auth_hmacsha256_update(@state, message, message.bytesize) + self.class.auth_hmacsha256_final(@state.clone, @authenticator) + + hexdigest + end + + # Return the authenticator, as raw bytes + # + # @return [String] The authenticator, as raw bytes + def digest + @authenticator + end + + # Return the authenticator, as hex string + # + # @return [String] The authenticator, as hex string + def hexdigest + @authenticator.unpack("H*").last + end + private def compute_authenticator(authenticator, message) - self.class.auth_hmacsha256(authenticator, message, message.bytesize, key) + state = State.new + + self.class.auth_hmacsha256_init(state, key, key.bytesize) + self.class.auth_hmacsha256_update(state, message, message.bytesize) + self.class.auth_hmacsha256_final(state, authenticator) end + # libsodium crypto_auth_hmacsha256_verify works only for 32 byte keys + # ref: https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_auth/hmacsha256/auth_hmacsha256.c#L109 def verify_message(authenticator, message) - self.class.auth_hmacsha256_verify(authenticator, message, message.bytesize, key) + correct = Util.zeros(BYTES) + compute_authenticator(correct, message) + Util.verify32(correct, authenticator) end + end + + # The crypto_auth_hmacsha256_state struct representation + # ref: jedisct1/libsodium/src/libsodium/include/sodium/crypto_auth_hmacsha256.h + class SHA256State < FFI::Struct + layout :state, [:uint32, 8], + :count, :uint64, + :buf, [:uint8, 64] + end + + # The crypto_hash_sha256_state struct representation + # ref: jedisct1/libsodium/src/libsodium/include/sodium/crypto_hash_sha256.h + class State < FFI::Struct + layout :ictx, SHA256State, + :octx, SHA256State end end end