diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index 757754cd9..2ac2e5c6c 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -84,20 +84,12 @@ ossl_hmac_alloc(VALUE klass) * * === A note about comparisons * - * Two instances won't be equal when they're compared, even if they have the - * same value. For example: + * Two instances can be securely compared with #== in constant time: * * other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f - * instance - * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance == other_instance - * #=> false - * - * Use #digest and compare in constant time: - * - * OpenSSL.fixed_length_secure_compare(instance.digest, other_instance.digest) - * #=> true + * #=> true * */ static VALUE diff --git a/lib/openssl.rb b/lib/openssl.rb index ec143bc74..be2969557 100644 --- a/lib/openssl.rb +++ b/lib/openssl.rb @@ -17,6 +17,7 @@ require_relative 'openssl/cipher' require_relative 'openssl/config' require_relative 'openssl/digest' +require_relative 'openssl/hmac' require_relative 'openssl/x509' require_relative 'openssl/ssl' require_relative 'openssl/pkcs5' diff --git a/lib/openssl/hmac.rb b/lib/openssl/hmac.rb new file mode 100644 index 000000000..3d4427611 --- /dev/null +++ b/lib/openssl/hmac.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module OpenSSL + class HMAC + # Securely compare with another HMAC instance in constant time. + def ==(other) + return false unless HMAC === other + return false unless self.digest.bytesize == other.digest.bytesize + + OpenSSL.fixed_length_secure_compare(self.digest, other.digest) + end + end +end diff --git a/test/test_hmac.rb b/test/test_hmac.rb index 831a5b6b3..4cd177b86 100644 --- a/test/test_hmac.rb +++ b/test/test_hmac.rb @@ -39,6 +39,16 @@ def test_reset_keep_key second = h1.update("test").hexdigest assert_equal first, second end + + def test_eq + h1 = OpenSSL::HMAC.new("KEY", "MD5") + h2 = OpenSSL::HMAC.new("KEY", OpenSSL::Digest.new("MD5")) + h3 = OpenSSL::HMAC.new("FOO", "MD5") + + assert_equal h1, h2 + refute_equal h1, h2.digest + refute_equal h1, h3 + end end end