Skip to content

Commit

Permalink
hmac: migrate from the low-level HMAC API to the EVP API
Browse files Browse the repository at this point in the history
Use the EVP API instead of the low-level HMAC API. Use of the HMAC API
has been discouraged and is being marked as deprecated starting from
OpenSSL 3.0.0.

The two singleton methods OpenSSL::HMAC, HMAC.digest and HMAC.hexdigest
are now in lib/openssl/hmac.rb.
  • Loading branch information
rhenium committed May 18, 2020
1 parent 93213b2 commit c659774
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 170 deletions.
3 changes: 1 addition & 2 deletions ext/openssl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ def find_openssl_library
have_func("BN_GENCB_get_arg")
have_func("EVP_MD_CTX_new")
have_func("EVP_MD_CTX_free")
have_func("HMAC_CTX_new")
have_func("HMAC_CTX_free")
have_func("EVP_MD_CTX_pkey_ctx")
have_func("X509_STORE_get_ex_data")
have_func("X509_STORE_set_ex_data")
have_func("X509_STORE_get_ex_new_index")
Expand Down
26 changes: 0 additions & 26 deletions ext/openssl/openssl_missing.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
#if !defined(OPENSSL_NO_ENGINE)
# include <openssl/engine.h>
#endif
#if !defined(OPENSSL_NO_HMAC)
# include <openssl/hmac.h>
#endif
#include <openssl/x509_vfy.h>

#include "openssl_missing.h"
Expand Down Expand Up @@ -58,29 +55,6 @@ ossl_EC_curve_nist2nid(const char *name)
#endif

/*** added in 1.1.0 ***/
#if !defined(HAVE_HMAC_CTX_NEW)
HMAC_CTX *
ossl_HMAC_CTX_new(void)
{
HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
if (!ctx)
return NULL;
HMAC_CTX_init(ctx);
return ctx;
}
#endif

#if !defined(HAVE_HMAC_CTX_FREE)
void
ossl_HMAC_CTX_free(HMAC_CTX *ctx)
{
if (ctx) {
HMAC_CTX_cleanup(ctx);
OPENSSL_free(ctx);
}
}
#endif

#if !defined(HAVE_X509_CRL_GET0_SIGNATURE)
void
ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig,
Expand Down
10 changes: 2 additions & 8 deletions ext/openssl/openssl_missing.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,8 @@ int ossl_EC_curve_nist2nid(const char *);
# define EVP_MD_CTX_free EVP_MD_CTX_destroy
#endif

#if !defined(HAVE_HMAC_CTX_NEW)
HMAC_CTX *ossl_HMAC_CTX_new(void);
# define HMAC_CTX_new ossl_HMAC_CTX_new
#endif

#if !defined(HAVE_HMAC_CTX_FREE)
void ossl_HMAC_CTX_free(HMAC_CTX *);
# define HMAC_CTX_free ossl_HMAC_CTX_free
#if !defined(HAVE_EVP_MD_CTX_PKEY_CTX)
# define EVP_MD_CTX_pkey_ctx(x) (x)->pctx
#endif

#if !defined(HAVE_X509_STORE_GET_EX_DATA)
Expand Down
1 change: 0 additions & 1 deletion ext/openssl/ossl.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <openssl/ssl.h>
#include <openssl/pkcs12.h>
#include <openssl/pkcs7.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/conf.h>
#ifndef OPENSSL_NO_TS
Expand Down
179 changes: 46 additions & 133 deletions ext/openssl/ossl_hmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
* This program is licensed under the same licence as Ruby.
* (See the file 'LICENCE'.)
*/
#if !defined(OPENSSL_NO_HMAC)

#include "ossl.h"

#define NewHMAC(klass) \
TypedData_Wrap_Struct((klass), &ossl_hmac_type, 0)
#define GetHMAC(obj, ctx) do { \
TypedData_Get_Struct((obj), HMAC_CTX, &ossl_hmac_type, (ctx)); \
TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_hmac_type, (ctx)); \
if (!(ctx)) { \
ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \
} \
Expand All @@ -36,7 +34,7 @@ VALUE eHMACError;
static void
ossl_hmac_free(void *ctx)
{
HMAC_CTX_free(ctx);
EVP_MD_CTX_free(ctx);
}

static const rb_data_type_t ossl_hmac_type = {
Expand All @@ -51,12 +49,12 @@ static VALUE
ossl_hmac_alloc(VALUE klass)
{
VALUE obj;
HMAC_CTX *ctx;
EVP_MD_CTX *ctx;

obj = NewHMAC(klass);
ctx = HMAC_CTX_new();
ctx = EVP_MD_CTX_new();
if (!ctx)
ossl_raise(eHMACError, NULL);
ossl_raise(eHMACError, "EVP_MD_CTX");
RTYPEDDATA_DATA(obj) = ctx;

return obj;
Expand All @@ -76,8 +74,7 @@ ossl_hmac_alloc(VALUE klass)
* === Example
*
* key = 'key'
* digest = OpenSSL::Digest.new('sha1')
* instance = OpenSSL::HMAC.new(key, digest)
* instance = OpenSSL::HMAC.new(key, 'SHA1')
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
* instance.class
* #=> OpenSSL::HMAC
Expand All @@ -86,7 +83,7 @@ ossl_hmac_alloc(VALUE klass)
*
* Two instances can be securely compared with #== in constant time:
*
* other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
* other_instance = OpenSSL::HMAC.new('key', 'SHA1')
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
* instance == other_instance
* #=> true
Expand All @@ -95,29 +92,39 @@ ossl_hmac_alloc(VALUE klass)
static VALUE
ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
{
HMAC_CTX *ctx;
EVP_MD_CTX *ctx;
EVP_PKEY *pkey;

StringValue(key);
GetHMAC(self, ctx);
HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key),
ossl_evp_get_digestbyname(digest), NULL);
StringValue(key);
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL,
(unsigned char *)RSTRING_PTR(key),
RSTRING_LENINT(key));
if (!pkey)
ossl_raise(eHMACError, "EVP_PKEY_new_mac_key");
if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest),
NULL, pkey) != 1) {
EVP_PKEY_free(pkey);
ossl_raise(eHMACError, "EVP_DigestSignInit");
}
/* Decrement reference counter; EVP_MD_CTX still keeps it */
EVP_PKEY_free(pkey);

return self;
}

static VALUE
ossl_hmac_copy(VALUE self, VALUE other)
{
HMAC_CTX *ctx1, *ctx2;
EVP_MD_CTX *ctx1, *ctx2;

rb_check_frozen(self);
if (self == other) return self;

GetHMAC(self, ctx1);
GetHMAC(other, ctx2);

if (!HMAC_CTX_copy(ctx1, ctx2))
ossl_raise(eHMACError, "HMAC_CTX_copy");
if (EVP_MD_CTX_copy(ctx1, ctx2) != 1)
ossl_raise(eHMACError, "EVP_MD_CTX_copy");
return self;
}

Expand All @@ -142,57 +149,41 @@ ossl_hmac_copy(VALUE self, VALUE other)
static VALUE
ossl_hmac_update(VALUE self, VALUE data)
{
HMAC_CTX *ctx;
EVP_MD_CTX *ctx;

StringValue(data);
GetHMAC(self, ctx);
HMAC_Update(ctx, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data));
if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
ossl_raise(eHMACError, "EVP_DigestSignUpdate");

return self;
}

static void
hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len)
{
HMAC_CTX *final;

final = HMAC_CTX_new();
if (!final)
ossl_raise(eHMACError, "HMAC_CTX_new");

if (!HMAC_CTX_copy(final, ctx)) {
HMAC_CTX_free(final);
ossl_raise(eHMACError, "HMAC_CTX_copy");
}

HMAC_Final(final, buf, buf_len);
HMAC_CTX_free(final);
}

/*
* call-seq:
* hmac.digest -> string
*
* Returns the authentication code an instance represents as a binary string.
*
* === Example
* instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
* instance = OpenSSL::HMAC.new('key', 'SHA1')
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
* instance.digest
* #=> "\xF4+\xB0\xEE\xB0\x18\xEB\xBDE\x97\xAEr\x13q\x1E\xC6\a`\x84?"
*/
static VALUE
ossl_hmac_digest(VALUE self)
{
HMAC_CTX *ctx;
unsigned int buf_len;
EVP_MD_CTX *ctx;
size_t buf_len;
VALUE ret;

GetHMAC(self, ctx);
ret = rb_str_new(NULL, EVP_MAX_MD_SIZE);
hmac_final(ctx, (unsigned char *)RSTRING_PTR(ret), &buf_len);
assert(buf_len <= EVP_MAX_MD_SIZE);
rb_str_set_len(ret, buf_len);
if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(ret),
&buf_len) != 1)
ossl_raise(eHMACError, "EVP_DigestSignFinal");
rb_str_set_len(ret, (long)buf_len);

return ret;
}
Expand All @@ -207,13 +198,14 @@ ossl_hmac_digest(VALUE self)
static VALUE
ossl_hmac_hexdigest(VALUE self)
{
HMAC_CTX *ctx;
EVP_MD_CTX *ctx;
unsigned char buf[EVP_MAX_MD_SIZE];
unsigned int buf_len;
size_t buf_len;
VALUE ret;

GetHMAC(self, ctx);
hmac_final(ctx, buf, &buf_len);
if (EVP_DigestSignFinal(ctx, buf, &buf_len) != 1)
ossl_raise(eHMACError, "EVP_DigestSignFinal");
ret = rb_str_new(NULL, buf_len * 2);
ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len);

Expand All @@ -230,7 +222,7 @@ ossl_hmac_hexdigest(VALUE self)
* === Example
*
* data = "The quick brown fox jumps over the lazy dog"
* instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
* instance = OpenSSL::HMAC.new('key', 'SHA1')
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
*
* instance.update(data)
Expand All @@ -242,84 +234,17 @@ ossl_hmac_hexdigest(VALUE self)
static VALUE
ossl_hmac_reset(VALUE self)
{
HMAC_CTX *ctx;
EVP_MD_CTX *ctx;
EVP_PKEY *pkey;

GetHMAC(self, ctx);
HMAC_Init_ex(ctx, NULL, 0, NULL, NULL);
pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx));
if (EVP_DigestSignInit(ctx, NULL, EVP_MD_CTX_md(ctx), NULL, pkey) != 1)
ossl_raise(eHMACError, "EVP_DigestSignInit");

return self;
}

/*
* call-seq:
* HMAC.digest(digest, key, data) -> aString
*
* Returns the authentication code as a binary string. The _digest_ parameter
* specifies the digest algorithm to use. This may be a String representing
* the algorithm name or an instance of OpenSSL::Digest.
*
* === Example
*
* key = 'key'
* data = 'The quick brown fox jumps over the lazy dog'
*
* hmac = OpenSSL::HMAC.digest('sha1', key, data)
* #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9"
*
*/
static VALUE
ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
{
unsigned char *buf;
unsigned int buf_len;

StringValue(key);
StringValue(data);
buf = HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
RSTRING_LEN(data), NULL, &buf_len);

return rb_str_new((const char *)buf, buf_len);
}

/*
* call-seq:
* HMAC.hexdigest(digest, key, data) -> aString
*
* Returns the authentication code as a hex-encoded string. The _digest_
* parameter specifies the digest algorithm to use. This may be a String
* representing the algorithm name or an instance of OpenSSL::Digest.
*
* === Example
*
* key = 'key'
* data = 'The quick brown fox jumps over the lazy dog'
*
* hmac = OpenSSL::HMAC.hexdigest('sha1', key, data)
* #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
*
*/
static VALUE
ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data)
{
unsigned char buf[EVP_MAX_MD_SIZE];
unsigned int buf_len;
VALUE ret;

StringValue(key);
StringValue(data);

if (!HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
RSTRING_LEN(data), buf, &buf_len))
ossl_raise(eHMACError, "HMAC");

ret = rb_str_new(NULL, buf_len * 2);
ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len);

return ret;
}

/*
* INIT
*/
Expand Down Expand Up @@ -353,8 +278,7 @@ Init_ossl_hmac(void)
* data1 = File.binread("file1")
* data2 = File.binread("file2")
* key = "key"
* digest = OpenSSL::Digest.new('SHA256')
* hmac = OpenSSL::HMAC.new(key, digest)
* hmac = OpenSSL::HMAC.new(key, 'SHA256')
* hmac << data1
* hmac << data2
* mac = hmac.digest
Expand All @@ -364,8 +288,6 @@ Init_ossl_hmac(void)
cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject);

rb_define_alloc_func(cHMAC, ossl_hmac_alloc);
rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3);
rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3);

rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2);
rb_define_method(cHMAC, "initialize_copy", ossl_hmac_copy, 1);
Expand All @@ -378,12 +300,3 @@ Init_ossl_hmac(void)
rb_define_alias(cHMAC, "inspect", "hexdigest");
rb_define_alias(cHMAC, "to_s", "hexdigest");
}

#else /* NO_HMAC */
# warning >>> OpenSSL is compiled without HMAC support <<<
void
Init_ossl_hmac(void)
{
rb_warning("HMAC is not available: OpenSSL is compiled without HMAC.");
}
#endif /* NO_HMAC */
Loading

0 comments on commit c659774

Please sign in to comment.