From 46c571c9a994603c4ff538b7c307592bd830ee0c Mon Sep 17 00:00:00 2001 From: arrtchiu Date: Mon, 28 Jul 2014 18:18:33 +0800 Subject: [PATCH] add timing-safe hmac verify method --- ext/openssl/ossl_hmac.c | 41 +++++++++++++++++++++++++++++++++++++++++ test/openssl/test_hmac.rb | 6 ++++++ 2 files changed, 47 insertions(+) diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index b1e853d..27c2fed 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -324,6 +324,46 @@ ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data) } /* + * call-seq: + * hmac.verify(bytes) -> aBoolean + * + * Compares the receiver's digest to another digest in a timing-safe way. + * +bytes+ must be a binary string containing a digest, for example by calling +#digest+ on another + * instance of +OpenSSL::Digest+. + */ +VALUE +ossl_hmac_verify(VALUE self, VALUE other) +{ + HMAC_CTX *ctx; + unsigned char *buf; + unsigned int buf_len; + unsigned char result; + unsigned int cur_idx; + unsigned char *other_buf; + + if (!RB_TYPE_P(other, T_STRING)) + rb_raise(rb_eArgError, "Must supply binary string or instance of OpenSSL::HMAC"); + + GetHMAC(self, ctx); + hmac_final(ctx, &buf, &buf_len); + + if (!buf_len || buf_len != RSTRING_LEN(other)) + return Qfalse; + + other_buf = (unsigned char *)RSTRING_PTR(other); + + result = 0; + for (cur_idx = 0; cur_idx < buf_len; cur_idx++) { + result |= buf[cur_idx] ^ other_buf[cur_idx]; + } + + if (result == 0) + return Qtrue; + + return Qfalse; +} + +/* * INIT */ void @@ -352,6 +392,7 @@ Init_ossl_hmac() rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0); rb_define_alias(cHMAC, "inspect", "hexdigest"); rb_define_alias(cHMAC, "to_s", "hexdigest"); + rb_define_method(cHMAC, "verify", ossl_hmac_verify, 1); } #else /* NO_HMAC */ diff --git a/test/openssl/test_hmac.rb b/test/openssl/test_hmac.rb index f1e4536..bd2e9d3 100644 --- a/test/openssl/test_hmac.rb +++ b/test/openssl/test_hmac.rb @@ -24,6 +24,12 @@ class OpenSSL::TestHMAC < Test::Unit::TestCase assert_equal(OpenSSL::HMAC.digest("MD5", @key, @data), @h2.digest, "digest") assert_equal(OpenSSL::HMAC.hexdigest("MD5", @key, @data), @h2.hexdigest, "hexdigest") + + assert_same(false, @h1.verify("\x9EPYl\x0F\xA1\x19\x7F\x85\x87D:\x94-\x8A\xFD")) + assert_same(true, @h1.verify("\x9EPYl\x0F\xA1\x19\x7F\x85\x87D:\x94-\x8A\xFC")) + end + + def test_hmac_verify end def test_dup -- 1.8.5.2 (Apple Git-48)