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

hmac: migrate from the low-level HMAC API to the EVP API #371

Merged
merged 2 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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