Skip to content

Commit d3b229c

Browse files
committed
digest: use EVP_MD_fetch() if available
With the introduction of OpenSSL 3 providers, newly implemented algorithms do not necessarily have a corresponding NID assigned. To use such an algorithm, it has to be "fetched" from providers using the new EVP_*_fetch() functions. For digest algorithms, we have to use EVP_MD_fetch() instead of the existing EVP_get_digestbyname(). However, it is not a drop-in replacement because: - EVP_MD_fetch() does not support all algorithm name aliases recognized by EVP_get_digestbyname(). - Both return an EVP_MD, but the one returned by EVP_MD_fetch() is sometimes reference counted and the user has to explicitly release it with EVP_MD_free(). So, keep using EVP_get_digestbyname() for all OpenSSL versions for now, and fall back to EVP_MD_fetch() if it fails. In the latter case, prepare a T_DATA object to manage the fetched EVP_MD's lifetime.
1 parent 7cc5068 commit d3b229c

File tree

16 files changed

+163
-107
lines changed

16 files changed

+163
-107
lines changed

ext/openssl/ossl_cipher.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
264264
{
265265
EVP_CIPHER_CTX *ctx;
266266
const EVP_MD *digest;
267-
VALUE vpass, vsalt, viter, vdigest;
267+
VALUE vpass, vsalt, viter, vdigest, md_holder;
268268
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
269269
int iter;
270270

@@ -279,7 +279,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
279279
iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
280280
if (iter <= 0)
281281
rb_raise(rb_eArgError, "iterations must be a positive integer");
282-
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest);
282+
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_md_fetch(vdigest, &md_holder);
283283
GetCipher(self, ctx);
284284
EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
285285
(unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);

ext/openssl/ossl_digest.c

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
static VALUE cDigest;
2323
static VALUE eDigestError;
24+
static ID id_md_holder;
2425

2526
static VALUE ossl_digest_alloc(VALUE klass);
2627

@@ -38,35 +39,62 @@ static const rb_data_type_t ossl_digest_type = {
3839
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
3940
};
4041

42+
#ifdef OSSL_USE_PROVIDER
43+
static void
44+
ossl_evp_md_free(void *ptr)
45+
{
46+
// This is safe to call against const EVP_MD * returned by
47+
// EVP_get_digestbyname()
48+
EVP_MD_free(ptr);
49+
}
50+
51+
static const rb_data_type_t ossl_evp_md_holder_type = {
52+
"OpenSSL/EVP_MD",
53+
{
54+
.dfree = ossl_evp_md_free,
55+
},
56+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
57+
};
58+
#endif
59+
4160
/*
4261
* Public
4362
*/
4463
const EVP_MD *
45-
ossl_evp_get_digestbyname(VALUE obj)
64+
ossl_evp_md_fetch(VALUE obj, volatile VALUE *holder)
4665
{
47-
const EVP_MD *md;
48-
ASN1_OBJECT *oid = NULL;
49-
50-
if (RB_TYPE_P(obj, T_STRING)) {
51-
const char *name = StringValueCStr(obj);
52-
53-
md = EVP_get_digestbyname(name);
54-
if (!md) {
55-
oid = OBJ_txt2obj(name, 0);
56-
md = EVP_get_digestbyobj(oid);
57-
ASN1_OBJECT_free(oid);
58-
}
59-
if (!md)
60-
ossl_raise(rb_eArgError,
61-
"unsupported digest algorithm: %"PRIsVALUE, obj);
62-
} else {
66+
*holder = Qnil;
67+
if (rb_obj_is_kind_of(obj, cDigest)) {
6368
EVP_MD_CTX *ctx;
64-
6569
GetDigest(obj, ctx);
66-
67-
md = EVP_MD_CTX_get0_md(ctx);
70+
EVP_MD *md = (EVP_MD *)EVP_MD_CTX_get0_md(ctx);
71+
#ifdef OSSL_USE_PROVIDER
72+
*holder = TypedData_Wrap_Struct(0, &ossl_evp_md_holder_type, NULL);
73+
if (!EVP_MD_up_ref(md))
74+
ossl_raise(eDigestError, "EVP_MD_up_ref");
75+
RTYPEDDATA_DATA(*holder) = md;
76+
#endif
77+
return md;
6878
}
6979

80+
const char *name = StringValueCStr(obj);
81+
EVP_MD *md = (EVP_MD *)EVP_get_digestbyname(name);
82+
if (!md) {
83+
ASN1_OBJECT *oid = OBJ_txt2obj(name, 0);
84+
md = (EVP_MD *)EVP_get_digestbyobj(oid);
85+
ASN1_OBJECT_free(oid);
86+
}
87+
#ifdef OSSL_USE_PROVIDER
88+
if (!md) {
89+
ossl_clear_error();
90+
*holder = TypedData_Wrap_Struct(0, &ossl_evp_md_holder_type, NULL);
91+
md = EVP_MD_fetch(NULL, name, NULL);
92+
RTYPEDDATA_DATA(*holder) = md;
93+
}
94+
#endif
95+
if (!md)
96+
ossl_raise(rb_eArgError, "unsupported digest algorithm: %"PRIsVALUE,
97+
obj);
7098
return md;
7199
}
72100

@@ -76,6 +104,9 @@ ossl_digest_new(const EVP_MD *md)
76104
VALUE ret;
77105
EVP_MD_CTX *ctx;
78106

107+
// NOTE: This does not set id_md_holder because this function should
108+
// only be called from ossl_engine.c, which will not use any
109+
// reference-counted digests.
79110
ret = ossl_digest_alloc(cDigest);
80111
ctx = EVP_MD_CTX_new();
81112
if (!ctx)
@@ -122,10 +153,10 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
122153
{
123154
EVP_MD_CTX *ctx;
124155
const EVP_MD *md;
125-
VALUE type, data;
156+
VALUE type, data, md_holder;
126157

127158
rb_scan_args(argc, argv, "11", &type, &data);
128-
md = ossl_evp_get_digestbyname(type);
159+
md = ossl_evp_md_fetch(type, &md_holder);
129160
if (!NIL_P(data)) StringValue(data);
130161

131162
TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx);
@@ -137,6 +168,7 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
137168

138169
if (!EVP_DigestInit_ex(ctx, md, NULL))
139170
ossl_raise(eDigestError, "Digest initialization failed");
171+
rb_ivar_set(self, id_md_holder, md_holder);
140172

141173
if (!NIL_P(data)) return ossl_digest_update(self, data);
142174
return self;
@@ -443,4 +475,6 @@ Init_ossl_digest(void)
443475
rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0);
444476

445477
rb_define_method(cDigest, "name", ossl_digest_name, 0);
478+
479+
id_md_holder = rb_intern_const("EVP_MD_holder");
446480
}

ext/openssl/ossl_digest.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010
#if !defined(_OSSL_DIGEST_H_)
1111
#define _OSSL_DIGEST_H_
1212

13-
const EVP_MD *ossl_evp_get_digestbyname(VALUE);
13+
/*
14+
* Gets EVP_MD from a String or an OpenSSL::Digest instance (discouraged, but
15+
* still supported for compatibility). A holder object is created if the EVP_MD
16+
* is a "fetched" algorithm.
17+
*/
18+
const EVP_MD *ossl_evp_md_fetch(VALUE obj, volatile VALUE *holder);
19+
/*
20+
* This is meant for OpenSSL::Engine#digest. EVP_MD must not be a fetched one.
21+
*/
1422
VALUE ossl_digest_new(const EVP_MD *);
1523
void Init_ossl_digest(void);
1624

ext/openssl/ossl_hmac.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
static VALUE cHMAC;
2525
static VALUE eHMACError;
26+
static ID id_md_holder;
2627

2728
/*
2829
* Public
@@ -94,16 +95,19 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
9495
{
9596
EVP_MD_CTX *ctx;
9697
EVP_PKEY *pkey;
98+
const EVP_MD *md;
99+
VALUE md_holder;
97100

98101
GetHMAC(self, ctx);
99102
StringValue(key);
103+
md = ossl_evp_md_fetch(digest, &md_holder);
104+
rb_ivar_set(self, id_md_holder, md_holder);
100105
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
101106
(unsigned char *)RSTRING_PTR(key),
102107
RSTRING_LENINT(key));
103108
if (!pkey)
104109
ossl_raise(eHMACError, "EVP_PKEY_new_raw_private_key");
105-
if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest),
106-
NULL, pkey) != 1) {
110+
if (EVP_DigestSignInit(ctx, NULL, md, NULL, pkey) != 1) {
107111
EVP_PKEY_free(pkey);
108112
ossl_raise(eHMACError, "EVP_DigestSignInit");
109113
}
@@ -300,4 +304,6 @@ Init_ossl_hmac(void)
300304
rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0);
301305
rb_define_alias(cHMAC, "inspect", "hexdigest");
302306
rb_define_alias(cHMAC, "to_s", "hexdigest");
307+
308+
id_md_holder = rb_intern_const("EVP_MD_holder");
303309
}

ext/openssl/ossl_kdf.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static VALUE mKDF, eKDF;
3535
static VALUE
3636
kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
3737
{
38-
VALUE pass, salt, opts, kwargs[4], str;
38+
VALUE pass, salt, opts, kwargs[4], str, md_holder;
3939
static ID kwargs_ids[4];
4040
int iters, len;
4141
const EVP_MD *md;
@@ -53,7 +53,7 @@ kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self)
5353
salt = StringValue(kwargs[0]);
5454
iters = NUM2INT(kwargs[1]);
5555
len = NUM2INT(kwargs[2]);
56-
md = ossl_evp_get_digestbyname(kwargs[3]);
56+
md = ossl_evp_md_fetch(kwargs[3], &md_holder);
5757

5858
str = rb_str_new(0, len);
5959
if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass),
@@ -172,7 +172,7 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self)
172172
static VALUE
173173
kdf_hkdf(int argc, VALUE *argv, VALUE self)
174174
{
175-
VALUE ikm, salt, info, opts, kwargs[4], str;
175+
VALUE ikm, salt, info, opts, kwargs[4], str, md_holder;
176176
static ID kwargs_ids[4];
177177
int saltlen, ikmlen, infolen;
178178
size_t len;
@@ -197,7 +197,7 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self)
197197
len = (size_t)NUM2LONG(kwargs[2]);
198198
if (len > LONG_MAX)
199199
rb_raise(rb_eArgError, "length must be non-negative");
200-
md = ossl_evp_get_digestbyname(kwargs[3]);
200+
md = ossl_evp_md_fetch(kwargs[3], &md_holder);
201201

202202
str = rb_str_new(NULL, (long)len);
203203
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);

ext/openssl/ossl_ns_spki.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,13 @@ ossl_spki_sign(VALUE self, VALUE key, VALUE digest)
283283
NETSCAPE_SPKI *spki;
284284
EVP_PKEY *pkey;
285285
const EVP_MD *md;
286+
VALUE md_holder;
286287

287288
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
288-
md = ossl_evp_get_digestbyname(digest);
289+
md = ossl_evp_md_fetch(digest, &md_holder);
289290
GetSPKI(self, spki);
290-
if (!NETSCAPE_SPKI_sign(spki, pkey, md)) {
291-
ossl_raise(eSPKIError, NULL);
292-
}
291+
if (!NETSCAPE_SPKI_sign(spki, pkey, md))
292+
ossl_raise(eSPKIError, "NETSCAPE_SPKI_sign");
293293

294294
return self;
295295
}

ext/openssl/ossl_ocsp.c

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ ossl_ocspreq_get_certid(VALUE self)
369369
static VALUE
370370
ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
371371
{
372-
VALUE signer_cert, signer_key, certs, flags, digest;
372+
VALUE signer_cert, signer_key, certs, flags, digest, md_holder;
373373
OCSP_REQUEST *req;
374374
X509 *signer;
375375
EVP_PKEY *key;
@@ -384,18 +384,16 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
384384
key = GetPrivPKeyPtr(signer_key);
385385
if (!NIL_P(flags))
386386
flg = NUM2INT(flags);
387-
if (NIL_P(digest))
388-
md = NULL;
389-
else
390-
md = ossl_evp_get_digestbyname(digest);
387+
md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder);
391388
if (NIL_P(certs))
392389
flg |= OCSP_NOCERTS;
393390
else
394391
x509s = ossl_x509_ary2sk(certs);
395392

396393
ret = OCSP_request_sign(req, signer, key, md, x509s, flg);
397394
sk_X509_pop_free(x509s, X509_free);
398-
if (!ret) ossl_raise(eOCSPError, NULL);
395+
if (!ret)
396+
ossl_raise(eOCSPError, "OCSP_request_sign");
399397

400398
return self;
401399
}
@@ -1000,7 +998,7 @@ ossl_ocspbres_find_response(VALUE self, VALUE target)
1000998
static VALUE
1001999
ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
10021000
{
1003-
VALUE signer_cert, signer_key, certs, flags, digest;
1001+
VALUE signer_cert, signer_key, certs, flags, digest, md_holder;
10041002
OCSP_BASICRESP *bs;
10051003
X509 *signer;
10061004
EVP_PKEY *key;
@@ -1015,18 +1013,16 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
10151013
key = GetPrivPKeyPtr(signer_key);
10161014
if (!NIL_P(flags))
10171015
flg = NUM2INT(flags);
1018-
if (NIL_P(digest))
1019-
md = NULL;
1020-
else
1021-
md = ossl_evp_get_digestbyname(digest);
1016+
md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder);
10221017
if (NIL_P(certs))
10231018
flg |= OCSP_NOCERTS;
10241019
else
10251020
x509s = ossl_x509_ary2sk(certs);
10261021

10271022
ret = OCSP_basic_sign(bs, signer, key, md, x509s, flg);
10281023
sk_X509_pop_free(x509s, X509_free);
1029-
if (!ret) ossl_raise(eOCSPError, NULL);
1024+
if (!ret)
1025+
ossl_raise(eOCSPError, "OCSP_basic_sign");
10301026

10311027
return self;
10321028
}
@@ -1460,10 +1456,11 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
14601456
else {
14611457
X509 *x509s, *x509i;
14621458
const EVP_MD *md;
1459+
VALUE md_holder;
14631460

14641461
x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */
14651462
x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */
1466-
md = !NIL_P(digest) ? ossl_evp_get_digestbyname(digest) : NULL;
1463+
md = NIL_P(digest) ? NULL : ossl_evp_md_fetch(digest, &md_holder);
14671464

14681465
newid = OCSP_cert_to_id(md, x509s, x509i);
14691466
if (!newid)

ext/openssl/ossl_pkcs12.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ static VALUE
271271
pkcs12_set_mac(int argc, VALUE *argv, VALUE self)
272272
{
273273
PKCS12 *p12;
274-
VALUE pass, salt, iter, md_name;
274+
VALUE pass, salt, iter, md_name, md_holder = Qnil;
275275
int iter_i = 0;
276276
const EVP_MD *md_type = NULL;
277277

@@ -285,7 +285,7 @@ pkcs12_set_mac(int argc, VALUE *argv, VALUE self)
285285
if (!NIL_P(iter))
286286
iter_i = NUM2INT(iter);
287287
if (!NIL_P(md_name))
288-
md_type = ossl_evp_get_digestbyname(md_name);
288+
md_type = ossl_evp_md_fetch(md_name, &md_holder);
289289

290290
if (!PKCS12_set_mac(p12, RSTRING_PTR(pass), RSTRING_LENINT(pass),
291291
!NIL_P(salt) ? (unsigned char *)RSTRING_PTR(salt) : NULL,

ext/openssl/ossl_pkcs7.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ static VALUE cPKCS7;
6868
static VALUE cPKCS7Signer;
6969
static VALUE cPKCS7Recipient;
7070
static VALUE ePKCS7Error;
71+
static ID id_md_holder;
7172

7273
static void
7374
ossl_pkcs7_free(void *ptr)
@@ -968,14 +969,15 @@ ossl_pkcs7si_initialize(VALUE self, VALUE cert, VALUE key, VALUE digest)
968969
EVP_PKEY *pkey;
969970
X509 *x509;
970971
const EVP_MD *md;
972+
VALUE md_holder;
971973

972974
pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
973975
x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
974-
md = ossl_evp_get_digestbyname(digest);
976+
md = ossl_evp_md_fetch(digest, &md_holder);
977+
rb_ivar_set(self, id_md_holder, md_holder);
975978
GetPKCS7si(self, p7si);
976-
if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, md))) {
977-
ossl_raise(ePKCS7Error, NULL);
978-
}
979+
if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, md)))
980+
ossl_raise(ePKCS7Error, "PKCS7_SIGNER_INFO_set");
979981

980982
return self;
981983
}
@@ -1161,4 +1163,6 @@ Init_ossl_pkcs7(void)
11611163
DefPKCS7Const(BINARY);
11621164
DefPKCS7Const(NOATTR);
11631165
DefPKCS7Const(NOSMIMECAP);
1166+
1167+
id_md_holder = rb_intern_const("EVP_MD_holder");
11641168
}

0 commit comments

Comments
 (0)