Skip to content

Commit 56dcc5c

Browse files
authored
Merge pull request #958 from rhenium/ky/digest-fetch-md
Add support for "fetched" EVP_MD and EVP_CIPHER
2 parents 274a652 + 0e565a2 commit 56dcc5c

18 files changed

+277
-159
lines changed

ext/openssl/ossl_cipher.c

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
static VALUE cCipher;
3434
static VALUE eCipherError;
3535
static VALUE eAuthTagError;
36-
static ID id_auth_tag_len, id_key_set;
36+
static ID id_auth_tag_len, id_key_set, id_cipher_holder;
3737

3838
static VALUE ossl_cipher_alloc(VALUE klass);
3939
static void ossl_cipher_free(void *ptr);
@@ -46,30 +46,58 @@ static const rb_data_type_t ossl_cipher_type = {
4646
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
4747
};
4848

49+
#ifdef OSSL_USE_PROVIDER
50+
static void
51+
ossl_evp_cipher_free(void *ptr)
52+
{
53+
// This is safe to call against const EVP_CIPHER * returned by
54+
// EVP_get_cipherbyname()
55+
EVP_CIPHER_free(ptr);
56+
}
57+
58+
static const rb_data_type_t ossl_evp_cipher_holder_type = {
59+
"OpenSSL/EVP_CIPHER",
60+
{
61+
.dfree = ossl_evp_cipher_free,
62+
},
63+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
64+
};
65+
#endif
66+
4967
/*
5068
* PUBLIC
5169
*/
5270
const EVP_CIPHER *
53-
ossl_evp_get_cipherbyname(VALUE obj)
71+
ossl_evp_cipher_fetch(VALUE obj, volatile VALUE *holder)
5472
{
73+
*holder = Qnil;
5574
if (rb_obj_is_kind_of(obj, cCipher)) {
56-
EVP_CIPHER_CTX *ctx;
57-
58-
GetCipher(obj, ctx);
59-
60-
return EVP_CIPHER_CTX_cipher(ctx);
75+
EVP_CIPHER_CTX *ctx;
76+
GetCipher(obj, ctx);
77+
EVP_CIPHER *cipher = (EVP_CIPHER *)EVP_CIPHER_CTX_cipher(ctx);
78+
#ifdef OSSL_USE_PROVIDER
79+
*holder = TypedData_Wrap_Struct(0, &ossl_evp_cipher_holder_type, NULL);
80+
if (!EVP_CIPHER_up_ref(cipher))
81+
ossl_raise(eCipherError, "EVP_CIPHER_up_ref");
82+
RTYPEDDATA_DATA(*holder) = cipher;
83+
#endif
84+
return cipher;
6185
}
62-
else {
63-
const EVP_CIPHER *cipher;
64-
65-
StringValueCStr(obj);
66-
cipher = EVP_get_cipherbyname(RSTRING_PTR(obj));
67-
if (!cipher)
68-
ossl_raise(rb_eArgError,
69-
"unsupported cipher algorithm: %"PRIsVALUE, obj);
7086

71-
return cipher;
87+
const char *name = StringValueCStr(obj);
88+
EVP_CIPHER *cipher = (EVP_CIPHER *)EVP_get_cipherbyname(name);
89+
#ifdef OSSL_USE_PROVIDER
90+
if (!cipher) {
91+
ossl_clear_error();
92+
*holder = TypedData_Wrap_Struct(0, &ossl_evp_cipher_holder_type, NULL);
93+
cipher = EVP_CIPHER_fetch(NULL, name, NULL);
94+
RTYPEDDATA_DATA(*holder) = cipher;
7295
}
96+
#endif
97+
if (!cipher)
98+
ossl_raise(eCipherError, "unsupported cipher algorithm: %"PRIsVALUE,
99+
obj);
100+
return cipher;
73101
}
74102

75103
VALUE
@@ -78,6 +106,9 @@ ossl_cipher_new(const EVP_CIPHER *cipher)
78106
VALUE ret;
79107
EVP_CIPHER_CTX *ctx;
80108

109+
// NOTE: This does not set id_cipher_holder because this function should
110+
// only be called from ossl_engine.c, which will not use any
111+
// reference-counted ciphers.
81112
ret = ossl_cipher_alloc(cCipher);
82113
AllocCipher(ret, ctx);
83114
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
@@ -114,19 +145,17 @@ ossl_cipher_initialize(VALUE self, VALUE str)
114145
{
115146
EVP_CIPHER_CTX *ctx;
116147
const EVP_CIPHER *cipher;
117-
char *name;
148+
VALUE cipher_holder;
118149

119-
name = StringValueCStr(str);
120150
GetCipherInit(self, ctx);
121151
if (ctx) {
122152
ossl_raise(rb_eRuntimeError, "Cipher already initialized!");
123153
}
154+
cipher = ossl_evp_cipher_fetch(str, &cipher_holder);
124155
AllocCipher(self, ctx);
125-
if (!(cipher = EVP_get_cipherbyname(name))) {
126-
ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str);
127-
}
128156
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
129-
ossl_raise(eCipherError, NULL);
157+
ossl_raise(eCipherError, "EVP_CipherInit_ex");
158+
rb_ivar_set(self, id_cipher_holder, cipher_holder);
130159

131160
return self;
132161
}
@@ -268,7 +297,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
268297
{
269298
EVP_CIPHER_CTX *ctx;
270299
const EVP_MD *digest;
271-
VALUE vpass, vsalt, viter, vdigest;
300+
VALUE vpass, vsalt, viter, vdigest, md_holder;
272301
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
273302
int iter;
274303

@@ -283,7 +312,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
283312
iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
284313
if (iter <= 0)
285314
rb_raise(rb_eArgError, "iterations must be a positive integer");
286-
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest);
315+
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_md_fetch(vdigest, &md_holder);
287316
GetCipher(self, ctx);
288317
EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
289318
(unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
@@ -1110,4 +1139,5 @@ Init_ossl_cipher(void)
11101139

11111140
id_auth_tag_len = rb_intern_const("auth_tag_len");
11121141
id_key_set = rb_intern_const("key_set");
1142+
id_cipher_holder = rb_intern_const("EVP_CIPHER_holder");
11131143
}

ext/openssl/ossl_cipher.h

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

13-
const EVP_CIPHER *ossl_evp_get_cipherbyname(VALUE);
13+
/*
14+
* Gets EVP_CIPHER from a String or an OpenSSL::Digest instance (discouraged,
15+
* but still supported for compatibility). A holder object is created if the
16+
* EVP_CIPHER is a "fetched" algorithm.
17+
*/
18+
const EVP_CIPHER *ossl_evp_cipher_fetch(VALUE obj, volatile VALUE *holder);
19+
/*
20+
* This is meant for OpenSSL::Engine#cipher. EVP_CIPHER must not be a fetched
21+
* one.
22+
*/
1423
VALUE ossl_cipher_new(const EVP_CIPHER *);
1524
void Init_ossl_cipher(void);
1625

ext/openssl/ossl_digest.c

Lines changed: 56 additions & 21 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,34 +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_eRuntimeError, "Unsupported digest algorithm (%"PRIsVALUE").", obj);
61-
} else {
66+
*holder = Qnil;
67+
if (rb_obj_is_kind_of(obj, cDigest)) {
6268
EVP_MD_CTX *ctx;
63-
6469
GetDigest(obj, ctx);
65-
66-
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;
6778
}
6879

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(eDigestError, "unsupported digest algorithm: %"PRIsVALUE,
97+
obj);
6998
return md;
7099
}
71100

@@ -75,6 +104,9 @@ ossl_digest_new(const EVP_MD *md)
75104
VALUE ret;
76105
EVP_MD_CTX *ctx;
77106

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.
78110
ret = ossl_digest_alloc(cDigest);
79111
ctx = EVP_MD_CTX_new();
80112
if (!ctx)
@@ -121,10 +153,10 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
121153
{
122154
EVP_MD_CTX *ctx;
123155
const EVP_MD *md;
124-
VALUE type, data;
156+
VALUE type, data, md_holder;
125157

126158
rb_scan_args(argc, argv, "11", &type, &data);
127-
md = ossl_evp_get_digestbyname(type);
159+
md = ossl_evp_md_fetch(type, &md_holder);
128160
if (!NIL_P(data)) StringValue(data);
129161

130162
TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx);
@@ -136,6 +168,7 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
136168

137169
if (!EVP_DigestInit_ex(ctx, md, NULL))
138170
ossl_raise(eDigestError, "Digest initialization failed");
171+
rb_ivar_set(self, id_md_holder, md_holder);
139172

140173
if (!NIL_P(data)) return ossl_digest_update(self, data);
141174
return self;
@@ -442,4 +475,6 @@ Init_ossl_digest(void)
442475
rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0);
443476

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

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,19 +95,22 @@ 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);
100104
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
101105
(unsigned char *)RSTRING_PTR(key),
102106
RSTRING_LENINT(key));
103107
if (!pkey)
104108
ossl_raise(eHMACError, "EVP_PKEY_new_raw_private_key");
105-
if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest),
106-
NULL, pkey) != 1) {
109+
if (EVP_DigestSignInit(ctx, NULL, md, NULL, pkey) != 1) {
107110
EVP_PKEY_free(pkey);
108111
ossl_raise(eHMACError, "EVP_DigestSignInit");
109112
}
113+
rb_ivar_set(self, id_md_holder, md_holder);
110114
/* Decrement reference counter; EVP_MD_CTX still keeps it */
111115
EVP_PKEY_free(pkey);
112116

@@ -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
}

0 commit comments

Comments
 (0)