Skip to content

Commit 0317e2f

Browse files
committed
hmac: migrate from the low-level HMAC API to the EVP API
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.
1 parent 8253d7c commit 0317e2f

File tree

6 files changed

+89
-170
lines changed

6 files changed

+89
-170
lines changed

ext/openssl/extconf.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ def find_openssl_library
141141
have_func("BN_GENCB_get_arg")
142142
have_func("EVP_MD_CTX_new")
143143
have_func("EVP_MD_CTX_free")
144-
have_func("HMAC_CTX_new")
145-
have_func("HMAC_CTX_free")
144+
have_func("EVP_MD_CTX_pkey_ctx")
146145
have_func("X509_STORE_get_ex_data")
147146
have_func("X509_STORE_set_ex_data")
148147
have_func("X509_STORE_get_ex_new_index")

ext/openssl/openssl_missing.c

-26
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
#if !defined(OPENSSL_NO_ENGINE)
1414
# include <openssl/engine.h>
1515
#endif
16-
#if !defined(OPENSSL_NO_HMAC)
17-
# include <openssl/hmac.h>
18-
#endif
1916
#include <openssl/x509_vfy.h>
2017

2118
#include "openssl_missing.h"
@@ -58,29 +55,6 @@ ossl_EC_curve_nist2nid(const char *name)
5855
#endif
5956

6057
/*** added in 1.1.0 ***/
61-
#if !defined(HAVE_HMAC_CTX_NEW)
62-
HMAC_CTX *
63-
ossl_HMAC_CTX_new(void)
64-
{
65-
HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
66-
if (!ctx)
67-
return NULL;
68-
HMAC_CTX_init(ctx);
69-
return ctx;
70-
}
71-
#endif
72-
73-
#if !defined(HAVE_HMAC_CTX_FREE)
74-
void
75-
ossl_HMAC_CTX_free(HMAC_CTX *ctx)
76-
{
77-
if (ctx) {
78-
HMAC_CTX_cleanup(ctx);
79-
OPENSSL_free(ctx);
80-
}
81-
}
82-
#endif
83-
8458
#if !defined(HAVE_X509_CRL_GET0_SIGNATURE)
8559
void
8660
ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig,

ext/openssl/openssl_missing.h

+2-8
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,8 @@ int ossl_EC_curve_nist2nid(const char *);
5454
# define EVP_MD_CTX_free EVP_MD_CTX_destroy
5555
#endif
5656

57-
#if !defined(HAVE_HMAC_CTX_NEW)
58-
HMAC_CTX *ossl_HMAC_CTX_new(void);
59-
# define HMAC_CTX_new ossl_HMAC_CTX_new
60-
#endif
61-
62-
#if !defined(HAVE_HMAC_CTX_FREE)
63-
void ossl_HMAC_CTX_free(HMAC_CTX *);
64-
# define HMAC_CTX_free ossl_HMAC_CTX_free
57+
#if !defined(HAVE_EVP_MD_CTX_PKEY_CTX)
58+
# define EVP_MD_CTX_pkey_ctx(x) (x)->pctx
6559
#endif
6660

6761
#if !defined(HAVE_X509_STORE_GET_EX_DATA)

ext/openssl/ossl.h

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include <openssl/ssl.h>
2525
#include <openssl/pkcs12.h>
2626
#include <openssl/pkcs7.h>
27-
#include <openssl/hmac.h>
2827
#include <openssl/rand.h>
2928
#include <openssl/conf.h>
3029
#ifndef OPENSSL_NO_TS

ext/openssl/ossl_hmac.c

+46-133
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@
77
* This program is licensed under the same licence as Ruby.
88
* (See the file 'LICENCE'.)
99
*/
10-
#if !defined(OPENSSL_NO_HMAC)
11-
1210
#include "ossl.h"
1311

1412
#define NewHMAC(klass) \
1513
TypedData_Wrap_Struct((klass), &ossl_hmac_type, 0)
1614
#define GetHMAC(obj, ctx) do { \
17-
TypedData_Get_Struct((obj), HMAC_CTX, &ossl_hmac_type, (ctx)); \
15+
TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_hmac_type, (ctx)); \
1816
if (!(ctx)) { \
1917
ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \
2018
} \
@@ -36,7 +34,7 @@ VALUE eHMACError;
3634
static void
3735
ossl_hmac_free(void *ctx)
3836
{
39-
HMAC_CTX_free(ctx);
37+
EVP_MD_CTX_free(ctx);
4038
}
4139

4240
static const rb_data_type_t ossl_hmac_type = {
@@ -51,12 +49,12 @@ static VALUE
5149
ossl_hmac_alloc(VALUE klass)
5250
{
5351
VALUE obj;
54-
HMAC_CTX *ctx;
52+
EVP_MD_CTX *ctx;
5553

5654
obj = NewHMAC(klass);
57-
ctx = HMAC_CTX_new();
55+
ctx = EVP_MD_CTX_new();
5856
if (!ctx)
59-
ossl_raise(eHMACError, NULL);
57+
ossl_raise(eHMACError, "EVP_MD_CTX");
6058
RTYPEDDATA_DATA(obj) = ctx;
6159

6260
return obj;
@@ -76,8 +74,7 @@ ossl_hmac_alloc(VALUE klass)
7674
* === Example
7775
*
7876
* key = 'key'
79-
* digest = OpenSSL::Digest.new('sha1')
80-
* instance = OpenSSL::HMAC.new(key, digest)
77+
* instance = OpenSSL::HMAC.new(key, 'SHA1')
8178
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
8279
* instance.class
8380
* #=> OpenSSL::HMAC
@@ -86,7 +83,7 @@ ossl_hmac_alloc(VALUE klass)
8683
*
8784
* Two instances can be securely compared with #== in constant time:
8885
*
89-
* other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
86+
* other_instance = OpenSSL::HMAC.new('key', 'SHA1')
9087
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
9188
* instance == other_instance
9289
* #=> true
@@ -95,29 +92,39 @@ ossl_hmac_alloc(VALUE klass)
9592
static VALUE
9693
ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
9794
{
98-
HMAC_CTX *ctx;
95+
EVP_MD_CTX *ctx;
96+
EVP_PKEY *pkey;
9997

100-
StringValue(key);
10198
GetHMAC(self, ctx);
102-
HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key),
103-
ossl_evp_get_digestbyname(digest), NULL);
99+
StringValue(key);
100+
pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL,
101+
(unsigned char *)RSTRING_PTR(key),
102+
RSTRING_LENINT(key));
103+
if (!pkey)
104+
ossl_raise(eHMACError, "EVP_PKEY_new_mac_key");
105+
if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest),
106+
NULL, pkey) != 1) {
107+
EVP_PKEY_free(pkey);
108+
ossl_raise(eHMACError, "EVP_DigestSignInit");
109+
}
110+
/* Decrement reference counter; EVP_MD_CTX still keeps it */
111+
EVP_PKEY_free(pkey);
104112

105113
return self;
106114
}
107115

108116
static VALUE
109117
ossl_hmac_copy(VALUE self, VALUE other)
110118
{
111-
HMAC_CTX *ctx1, *ctx2;
119+
EVP_MD_CTX *ctx1, *ctx2;
112120

113121
rb_check_frozen(self);
114122
if (self == other) return self;
115123

116124
GetHMAC(self, ctx1);
117125
GetHMAC(other, ctx2);
118-
119-
if (!HMAC_CTX_copy(ctx1, ctx2))
120-
ossl_raise(eHMACError, "HMAC_CTX_copy");
126+
if (EVP_MD_CTX_copy(ctx1, ctx2) != 1)
127+
ossl_raise(eHMACError, "EVP_MD_CTX_copy");
121128
return self;
122129
}
123130

@@ -142,57 +149,41 @@ ossl_hmac_copy(VALUE self, VALUE other)
142149
static VALUE
143150
ossl_hmac_update(VALUE self, VALUE data)
144151
{
145-
HMAC_CTX *ctx;
152+
EVP_MD_CTX *ctx;
146153

147154
StringValue(data);
148155
GetHMAC(self, ctx);
149-
HMAC_Update(ctx, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data));
156+
if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
157+
ossl_raise(eHMACError, "EVP_DigestSignUpdate");
150158

151159
return self;
152160
}
153161

154-
static void
155-
hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len)
156-
{
157-
HMAC_CTX *final;
158-
159-
final = HMAC_CTX_new();
160-
if (!final)
161-
ossl_raise(eHMACError, "HMAC_CTX_new");
162-
163-
if (!HMAC_CTX_copy(final, ctx)) {
164-
HMAC_CTX_free(final);
165-
ossl_raise(eHMACError, "HMAC_CTX_copy");
166-
}
167-
168-
HMAC_Final(final, buf, buf_len);
169-
HMAC_CTX_free(final);
170-
}
171-
172162
/*
173163
* call-seq:
174164
* hmac.digest -> string
175165
*
176166
* Returns the authentication code an instance represents as a binary string.
177167
*
178168
* === Example
179-
* instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
169+
* instance = OpenSSL::HMAC.new('key', 'SHA1')
180170
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
181171
* instance.digest
182172
* #=> "\xF4+\xB0\xEE\xB0\x18\xEB\xBDE\x97\xAEr\x13q\x1E\xC6\a`\x84?"
183173
*/
184174
static VALUE
185175
ossl_hmac_digest(VALUE self)
186176
{
187-
HMAC_CTX *ctx;
188-
unsigned int buf_len;
177+
EVP_MD_CTX *ctx;
178+
size_t buf_len;
189179
VALUE ret;
190180

191181
GetHMAC(self, ctx);
192182
ret = rb_str_new(NULL, EVP_MAX_MD_SIZE);
193-
hmac_final(ctx, (unsigned char *)RSTRING_PTR(ret), &buf_len);
194-
assert(buf_len <= EVP_MAX_MD_SIZE);
195-
rb_str_set_len(ret, buf_len);
183+
if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(ret),
184+
&buf_len) != 1)
185+
ossl_raise(eHMACError, "EVP_DigestSignFinal");
186+
rb_str_set_len(ret, (long)buf_len);
196187

197188
return ret;
198189
}
@@ -207,13 +198,14 @@ ossl_hmac_digest(VALUE self)
207198
static VALUE
208199
ossl_hmac_hexdigest(VALUE self)
209200
{
210-
HMAC_CTX *ctx;
201+
EVP_MD_CTX *ctx;
211202
unsigned char buf[EVP_MAX_MD_SIZE];
212-
unsigned int buf_len;
203+
size_t buf_len;
213204
VALUE ret;
214205

215206
GetHMAC(self, ctx);
216-
hmac_final(ctx, buf, &buf_len);
207+
if (EVP_DigestSignFinal(ctx, buf, &buf_len) != 1)
208+
ossl_raise(eHMACError, "EVP_DigestSignFinal");
217209
ret = rb_str_new(NULL, buf_len * 2);
218210
ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len);
219211

@@ -230,7 +222,7 @@ ossl_hmac_hexdigest(VALUE self)
230222
* === Example
231223
*
232224
* data = "The quick brown fox jumps over the lazy dog"
233-
* instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
225+
* instance = OpenSSL::HMAC.new('key', 'SHA1')
234226
* #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
235227
*
236228
* instance.update(data)
@@ -242,84 +234,17 @@ ossl_hmac_hexdigest(VALUE self)
242234
static VALUE
243235
ossl_hmac_reset(VALUE self)
244236
{
245-
HMAC_CTX *ctx;
237+
EVP_MD_CTX *ctx;
238+
EVP_PKEY *pkey;
246239

247240
GetHMAC(self, ctx);
248-
HMAC_Init_ex(ctx, NULL, 0, NULL, NULL);
241+
pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx));
242+
if (EVP_DigestSignInit(ctx, NULL, EVP_MD_CTX_md(ctx), NULL, pkey) != 1)
243+
ossl_raise(eHMACError, "EVP_DigestSignInit");
249244

250245
return self;
251246
}
252247

253-
/*
254-
* call-seq:
255-
* HMAC.digest(digest, key, data) -> aString
256-
*
257-
* Returns the authentication code as a binary string. The _digest_ parameter
258-
* specifies the digest algorithm to use. This may be a String representing
259-
* the algorithm name or an instance of OpenSSL::Digest.
260-
*
261-
* === Example
262-
*
263-
* key = 'key'
264-
* data = 'The quick brown fox jumps over the lazy dog'
265-
*
266-
* hmac = OpenSSL::HMAC.digest('sha1', key, data)
267-
* #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9"
268-
*
269-
*/
270-
static VALUE
271-
ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
272-
{
273-
unsigned char *buf;
274-
unsigned int buf_len;
275-
276-
StringValue(key);
277-
StringValue(data);
278-
buf = HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
279-
RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
280-
RSTRING_LEN(data), NULL, &buf_len);
281-
282-
return rb_str_new((const char *)buf, buf_len);
283-
}
284-
285-
/*
286-
* call-seq:
287-
* HMAC.hexdigest(digest, key, data) -> aString
288-
*
289-
* Returns the authentication code as a hex-encoded string. The _digest_
290-
* parameter specifies the digest algorithm to use. This may be a String
291-
* representing the algorithm name or an instance of OpenSSL::Digest.
292-
*
293-
* === Example
294-
*
295-
* key = 'key'
296-
* data = 'The quick brown fox jumps over the lazy dog'
297-
*
298-
* hmac = OpenSSL::HMAC.hexdigest('sha1', key, data)
299-
* #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
300-
*
301-
*/
302-
static VALUE
303-
ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data)
304-
{
305-
unsigned char buf[EVP_MAX_MD_SIZE];
306-
unsigned int buf_len;
307-
VALUE ret;
308-
309-
StringValue(key);
310-
StringValue(data);
311-
312-
if (!HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
313-
RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
314-
RSTRING_LEN(data), buf, &buf_len))
315-
ossl_raise(eHMACError, "HMAC");
316-
317-
ret = rb_str_new(NULL, buf_len * 2);
318-
ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len);
319-
320-
return ret;
321-
}
322-
323248
/*
324249
* INIT
325250
*/
@@ -353,8 +278,7 @@ Init_ossl_hmac(void)
353278
* data1 = File.binread("file1")
354279
* data2 = File.binread("file2")
355280
* key = "key"
356-
* digest = OpenSSL::Digest.new('SHA256')
357-
* hmac = OpenSSL::HMAC.new(key, digest)
281+
* hmac = OpenSSL::HMAC.new(key, 'SHA256')
358282
* hmac << data1
359283
* hmac << data2
360284
* mac = hmac.digest
@@ -364,8 +288,6 @@ Init_ossl_hmac(void)
364288
cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject);
365289

366290
rb_define_alloc_func(cHMAC, ossl_hmac_alloc);
367-
rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3);
368-
rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3);
369291

370292
rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2);
371293
rb_define_method(cHMAC, "initialize_copy", ossl_hmac_copy, 1);
@@ -378,12 +300,3 @@ Init_ossl_hmac(void)
378300
rb_define_alias(cHMAC, "inspect", "hexdigest");
379301
rb_define_alias(cHMAC, "to_s", "hexdigest");
380302
}
381-
382-
#else /* NO_HMAC */
383-
# warning >>> OpenSSL is compiled without HMAC support <<<
384-
void
385-
Init_ossl_hmac(void)
386-
{
387-
rb_warning("HMAC is not available: OpenSSL is compiled without HMAC.");
388-
}
389-
#endif /* NO_HMAC */

0 commit comments

Comments
 (0)