Skip to content

Commit ad1fdd4

Browse files
committed
digest: let #finish raise if the output size is unknown
In OpenSSL 3.4 or later, SHAKE digests do not have a default output length and EVP_MD_CTX_size() returns -1. Raise OpenSSL::Digest::DigestError if the function returns a negative number. Fortunately, this is currently caught by a sanity check in rb_str_new() and leads to ArgumentError with the message "negative string size (or size too big)". Still, the caller should check it. Also, override #inspect to rescue an exception raised in #hexdigest.
1 parent 6e5dd1f commit ad1fdd4

3 files changed

Lines changed: 25 additions & 1 deletion

File tree

ext/openssl/ossl_digest.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,20 @@ ossl_digest_finish(VALUE self)
283283
{
284284
EVP_MD_CTX *ctx;
285285
VALUE str;
286+
int size;
286287

287288
GetDigest(self, ctx);
288-
str = rb_str_new(NULL, EVP_MD_CTX_size(ctx));
289+
size = EVP_MD_CTX_size(ctx);
290+
if (size <= 0) {
291+
#ifdef EVP_MD_FLAG_XOF /* Not in LibreSSL 4.3 (latest) */
292+
if (EVP_MD_flags(EVP_MD_CTX_get0_md(ctx)) & EVP_MD_FLAG_XOF)
293+
ossl_raise(eDigestError,
294+
"output length not set for XOF; " \
295+
"use OpenSSL::Digest#squeeze instead");
296+
#endif
297+
ossl_raise(eDigestError, "EVP_MD_CTX_size");
298+
}
299+
str = rb_str_new(NULL, size);
289300
if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL))
290301
ossl_raise(eDigestError, "EVP_DigestFinal_ex");
291302

lib/openssl/digest.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ def hexdigest(data)
5252
class Digest < Digest; end # :nodoc:
5353
deprecate_constant :Digest
5454

55+
def inspect
56+
"#<#{self.class}: #{name} #{hexdigest rescue $!.inspect}>"
57+
end
58+
5559
end # Digest
5660

5761
# Returns a Digest subclass by _name_

test/openssl/test_digest.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ def test_xof_squeeze
158158
assert_raise(OpenSSL::Digest::DigestError) { digest.update("a") }
159159
end
160160

161+
def test_xof_digest
162+
# SHAKE had a default output length in OpenSSL < 3.4
163+
return unless openssl? && openssl?(3, 4, 0)
164+
165+
digest = OpenSSL::Digest.new("SHAKE128")
166+
assert_raise(OpenSSL::Digest::DigestError) { digest.digest }
167+
assert_match(/#<OpenSSL::Digest: SHAKE128/, digest.inspect)
168+
end
169+
161170
def test_openssl_digest
162171
assert_equal OpenSSL::Digest::MD5, OpenSSL::Digest("MD5")
163172

0 commit comments

Comments
 (0)