Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose lower level of blake2b API #159

Merged
merged 5 commits into from
Jun 13, 2017

Conversation

fudanchii
Copy link
Contributor

Hi,

Wondering if we could expose the low-level API of blake2b.
This would be really useful in the case of hashing really large file or data stream.

Thanks!

This will support arbitrary length of message to be hashed
without having to load all the message into memory beforehand.
@tarcieri
Copy link
Contributor

tarcieri commented Jun 2, 2017

Unfortunately thanks to the existing API, this is less than ideal.

The standard Ruby ::Digest Initialize-Update-Finalize API looks like:

  • reset: Matches yours
  • update and << (aliases): Matches yours
  • digest: Computes final digest. Already taken by a different method.

My takeaway is the existing API is kind of not great, and that the existing digest method should be a class method.

So, perhaps we should do that, then we could call your final_digest method digest (and match the core Ruby API).

I think it's very confusing to have both digest and final_digest.

This would be a breaking API change, and would necessitate a major version bump. But I think it's the "right" way to handle this, and I'm not opposed to doing that.

@tarcieri
Copy link
Contributor

tarcieri commented Jun 2, 2017

Note we still support Ruby 2.2, and the &. syntax is not available on these versions

@fudanchii
Copy link
Contributor Author

fudanchii commented Jun 2, 2017

I think it's very confusing to have both digest and final_digest.

Agree, previously I was about to use the existing digest but then it would break the arity, I'll try the class method as suggested.

Note we still support Ruby 2.2, and the &. syntax is not available on these versions

ok, will suppress rubocop rule on this.

Thank you.

so we have similar API with ruby's ::Digest.

Also this has minimal breaking changes, in which #digest
essentially still has 1 arity and will be only callable without params
when the initial state for hash calculation is initialized (after
calling #reset).

Added new tests to define the new behavior.
@fudanchii
Copy link
Contributor Author

@tarcieri I end up breaking the arity :D

@@ -0,0 +1,17 @@
# encoding: binary
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would suggest just defining this inside blake2b.rb instead of making a separate file

end

# Calculate a Blake2b digest
#
# @param [String] message Message to be hashed
#
# @return [String] Blake2b digest of the string as raw bytes
def digest(message)
def digest(*message)
return final_digest if message.empty?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, I don't like this. I'd rather this method not have two responsibilities. I'd suggest just renaming final_digest to digest, and making the rest of this method a completely separate class method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I taken out the current digest into class method, I also extract the validation from initialize, since I don't think I want to instantiate the object and add attr_reader just to read the options back. Is this looks ok for you? I will rebase these commits later if needed

This is effectively changing #digest behavior, but thanks to the upper
RbNaCl::Hash.blake2b API, the invocation is essentially the same.

And extract options validation from #initialize, so we don't have
to instantiate Blake2b from self.digest.

Also put Blake2bState together with Blake2b definition.
# @raise [RbNaCl::LengthError] Invalid length specified for one or more options
#
# @return [Hash] opts Configuration hash with sanitized values
def self.validate_opts(opts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make this method private? e.g.

class << self
  private :validate_opts
end

Copy link
Contributor Author

@fudanchii fudanchii Jun 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about something like this?

index 1265ec3..703bce7 100644
--- a/lib/rbnacl/hash/blake2b.rb
+++ b/lib/rbnacl/hash/blake2b.rb
@@ -103,6 +103,13 @@ module RbNaCl
         opts
       end

+      private_class_method :validate_opts
+
+      def self.new(opts = {})
+        opts = validate_opts(opts)
+        super
+      end
+
       # Create a new Blake2b hash object
       #
       # @param [Hash] opts Blake2b configuration
@@ -117,7 +124,6 @@ module RbNaCl
       #
       # @return [RbNaCl::Hash::Blake2b] A Blake2b hasher object
       def initialize(opts = {})
-        opts         = self.class.validate_opts(opts)
         @key         = opts[:key]
         @key_size    = opts[:key_size]
         @digest_size = opts[:digest_size]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

raise(CryptoError, "Hashing failed!")
end

def <<(message)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use alias for this?


# The crypto_generichash_blake2b_state struct representation
# ref: jedisct1/libsodium/src/libsodium/include/sodium/crypto_generichash_blake2b.h#L23
class Blake2bState < FFI::Struct
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be called State, since it's already in the RbNaCl::Blake2b namespace

but then also need to move its invocation to self.new.

Blake2bState is internal within Blake2b so adapt accordingly.

Use alias instead of defining new method for `<<`.
@tarcieri
Copy link
Contributor

tarcieri commented Jun 4, 2017

This looks good to me, but I'll wait a bit for merging in case @namelessjon would like to opine.

Note this is a breaking change and will require a major version bump.

@tarcieri tarcieri merged commit 4addc76 into RubyCrypto:master Jun 13, 2017
@tarcieri
Copy link
Contributor

Okay, merged, will bump the version to 5.0.0 and release this

@tarcieri
Copy link
Contributor

Released in RbNaCl 5.0.0 🎉

@namelessjon
Copy link
Contributor

Nice, thanks!

netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Mar 17, 2018
## 5.0.0 (2017-06-13)

* [#159](RubyCrypto/rbnacl#159)
  Support the BLAKE2b Initialize-Update-Finalize API.
  ([@fudanchii])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants