Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
ossl_pkey.c
Go to the documentation of this file.
1/*
2 * 'OpenSSL for Ruby' project
3 * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
4 * All rights reserved.
5 */
6/*
7 * This program is licensed under the same licence as Ruby.
8 * (See the file 'LICENCE'.)
9 */
10#include "ossl.h"
11
12/*
13 * Classes
14 */
18static ID id_private_q;
19
20/*
21 * callback for generating keys
22 */
23static VALUE
24call_check_ints0(VALUE arg)
25{
27 return Qnil;
28}
29
30static void *
31call_check_ints(void *arg)
32{
33 int state;
34 rb_protect(call_check_ints0, Qnil, &state);
35 return (void *)(VALUE)state;
36}
37
38int
39ossl_generate_cb_2(int p, int n, BN_GENCB *cb)
40{
41 VALUE ary;
43 int state;
44
46 if (arg->yield) {
47 ary = rb_ary_new2(2);
48 rb_ary_store(ary, 0, INT2NUM(p));
49 rb_ary_store(ary, 1, INT2NUM(n));
50
51 /*
52 * can be break by raising exception or 'break'
53 */
55 if (state) {
56 arg->state = state;
57 return 0;
58 }
59 }
60 if (arg->interrupted) {
61 arg->interrupted = 0;
62 state = (int)(VALUE)rb_thread_call_with_gvl(call_check_ints, NULL);
63 if (state) {
64 arg->state = state;
65 return 0;
66 }
67 }
68 return 1;
69}
70
71void
73{
75 arg->interrupted = 1;
76}
77
78static void
79ossl_evp_pkey_free(void *ptr)
80{
81 EVP_PKEY_free(ptr);
82}
83
84/*
85 * Public
86 */
88 "OpenSSL/EVP_PKEY",
89 {
90 0, ossl_evp_pkey_free,
91 },
93};
94
95static VALUE
96pkey_new0(EVP_PKEY *pkey)
97{
98 VALUE obj;
99 int type;
100
101 if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE)
102 ossl_raise(rb_eRuntimeError, "pkey is empty");
103
104 switch (type) {
105#if !defined(OPENSSL_NO_RSA)
106 case EVP_PKEY_RSA:
107 return ossl_rsa_new(pkey);
108#endif
109#if !defined(OPENSSL_NO_DSA)
110 case EVP_PKEY_DSA:
111 return ossl_dsa_new(pkey);
112#endif
113#if !defined(OPENSSL_NO_DH)
114 case EVP_PKEY_DH:
115 return ossl_dh_new(pkey);
116#endif
117#if !defined(OPENSSL_NO_EC)
118 case EVP_PKEY_EC:
119 return ossl_ec_new(pkey);
120#endif
121 default:
122 obj = NewPKey(cPKey);
123 SetPKey(obj, pkey);
124 return obj;
125 }
126}
127
128VALUE
129ossl_pkey_new(EVP_PKEY *pkey)
130{
131 VALUE obj;
132 int status;
133
134 obj = rb_protect((VALUE (*)(VALUE))pkey_new0, (VALUE)pkey, &status);
135 if (status) {
136 EVP_PKEY_free(pkey);
137 rb_jump_tag(status);
138 }
139
140 return obj;
141}
142
143/*
144 * call-seq:
145 * OpenSSL::PKey.read(string [, pwd ]) -> PKey
146 * OpenSSL::PKey.read(io [, pwd ]) -> PKey
147 *
148 * Reads a DER or PEM encoded string from _string_ or _io_ and returns an
149 * instance of the appropriate PKey class.
150 *
151 * === Parameters
152 * * _string+ is a DER- or PEM-encoded string containing an arbitrary private
153 * or public key.
154 * * _io_ is an instance of IO containing a DER- or PEM-encoded
155 * arbitrary private or public key.
156 * * _pwd_ is an optional password in case _string_ or _io_ is an encrypted
157 * PEM resource.
158 */
159static VALUE
160ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
161{
162 EVP_PKEY *pkey;
163 BIO *bio;
164 VALUE data, pass;
165
166 rb_scan_args(argc, argv, "11", &data, &pass);
167 pass = ossl_pem_passwd_value(pass);
168
169 bio = ossl_obj2bio(&data);
170 if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) {
171 OSSL_BIO_reset(bio);
172 if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) {
173 OSSL_BIO_reset(bio);
174 if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) {
175 OSSL_BIO_reset(bio);
176 pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, (void *)pass);
177 }
178 }
179 }
180
181 BIO_free(bio);
182 if (!pkey)
183 ossl_raise(ePKeyError, "Could not parse PKey");
184
185 return ossl_pkey_new(pkey);
186}
187
188void
189ossl_pkey_check_public_key(const EVP_PKEY *pkey)
190{
191 void *ptr;
192 const BIGNUM *n, *e, *pubkey;
193
194 if (EVP_PKEY_missing_parameters(pkey))
195 ossl_raise(ePKeyError, "parameters missing");
196
197 /* OpenSSL < 1.1.0 takes non-const pointer */
198 ptr = EVP_PKEY_get0((EVP_PKEY *)pkey);
199 switch (EVP_PKEY_base_id(pkey)) {
200 case EVP_PKEY_RSA:
201 RSA_get0_key(ptr, &n, &e, NULL);
202 if (n && e)
203 return;
204 break;
205 case EVP_PKEY_DSA:
206 DSA_get0_key(ptr, &pubkey, NULL);
207 if (pubkey)
208 return;
209 break;
210 case EVP_PKEY_DH:
211 DH_get0_key(ptr, &pubkey, NULL);
212 if (pubkey)
213 return;
214 break;
215#if !defined(OPENSSL_NO_EC)
216 case EVP_PKEY_EC:
217 if (EC_KEY_get0_public_key(ptr))
218 return;
219 break;
220#endif
221 default:
222 /* unsupported type; assuming ok */
223 return;
224 }
225 ossl_raise(ePKeyError, "public key missing");
226}
227
228EVP_PKEY *
230{
231 EVP_PKEY *pkey;
232
233 GetPKey(obj, pkey);
234
235 return pkey;
236}
237
238EVP_PKEY *
240{
241 EVP_PKEY *pkey;
242
243 if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) {
244 ossl_raise(rb_eArgError, "Private key is needed.");
245 }
246 GetPKey(obj, pkey);
247
248 return pkey;
249}
250
251EVP_PKEY *
253{
254 EVP_PKEY *pkey;
255
256 GetPKey(obj, pkey);
257 EVP_PKEY_up_ref(pkey);
258
259 return pkey;
260}
261
262/*
263 * Private
264 */
265static VALUE
266ossl_pkey_alloc(VALUE klass)
267{
268 EVP_PKEY *pkey;
269 VALUE obj;
270
271 obj = NewPKey(klass);
272 if (!(pkey = EVP_PKEY_new())) {
274 }
275 SetPKey(obj, pkey);
276
277 return obj;
278}
279
280/*
281 * call-seq:
282 * PKeyClass.new -> self
283 *
284 * Because PKey is an abstract class, actually calling this method explicitly
285 * will raise a NotImplementedError.
286 */
287static VALUE
288ossl_pkey_initialize(VALUE self)
289{
290 if (rb_obj_is_instance_of(self, cPKey)) {
291 ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly");
292 }
293 return self;
294}
295
296/*
297 * call-seq:
298 * pkey.sign(digest, data) -> String
299 *
300 * To sign the String _data_, _digest_, an instance of OpenSSL::Digest, must
301 * be provided. The return value is again a String containing the signature.
302 * A PKeyError is raised should errors occur.
303 * Any previous state of the Digest instance is irrelevant to the signature
304 * outcome, the digest instance is reset to its initial state during the
305 * operation.
306 *
307 * == Example
308 * data = 'Sign me!'
309 * digest = OpenSSL::Digest::SHA256.new
310 * pkey = OpenSSL::PKey::RSA.new(2048)
311 * signature = pkey.sign(digest, data)
312 */
313static VALUE
314ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
315{
316 EVP_PKEY *pkey;
317 const EVP_MD *md;
318 EVP_MD_CTX *ctx;
319 unsigned int buf_len;
320 VALUE str;
321 int result;
322
323 pkey = GetPrivPKeyPtr(self);
324 md = ossl_evp_get_digestbyname(digest);
325 StringValue(data);
326 str = rb_str_new(0, EVP_PKEY_size(pkey));
327
328 ctx = EVP_MD_CTX_new();
329 if (!ctx)
330 ossl_raise(ePKeyError, "EVP_MD_CTX_new");
331 if (!EVP_SignInit_ex(ctx, md, NULL)) {
332 EVP_MD_CTX_free(ctx);
333 ossl_raise(ePKeyError, "EVP_SignInit_ex");
334 }
335 if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
336 EVP_MD_CTX_free(ctx);
337 ossl_raise(ePKeyError, "EVP_SignUpdate");
338 }
339 result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey);
340 EVP_MD_CTX_free(ctx);
341 if (!result)
342 ossl_raise(ePKeyError, "EVP_SignFinal");
343 rb_str_set_len(str, buf_len);
344
345 return str;
346}
347
348/*
349 * call-seq:
350 * pkey.verify(digest, signature, data) -> String
351 *
352 * To verify the String _signature_, _digest_, an instance of
353 * OpenSSL::Digest, must be provided to re-compute the message digest of the
354 * original _data_, also a String. The return value is +true+ if the
355 * signature is valid, +false+ otherwise. A PKeyError is raised should errors
356 * occur.
357 * Any previous state of the Digest instance is irrelevant to the validation
358 * outcome, the digest instance is reset to its initial state during the
359 * operation.
360 *
361 * == Example
362 * data = 'Sign me!'
363 * digest = OpenSSL::Digest::SHA256.new
364 * pkey = OpenSSL::PKey::RSA.new(2048)
365 * signature = pkey.sign(digest, data)
366 * pub_key = pkey.public_key
367 * puts pub_key.verify(digest, signature, data) # => true
368 */
369static VALUE
370ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
371{
372 EVP_PKEY *pkey;
373 const EVP_MD *md;
374 EVP_MD_CTX *ctx;
375 int siglen, result;
376
377 GetPKey(self, pkey);
379 md = ossl_evp_get_digestbyname(digest);
381 siglen = RSTRING_LENINT(sig);
382 StringValue(data);
383
384 ctx = EVP_MD_CTX_new();
385 if (!ctx)
386 ossl_raise(ePKeyError, "EVP_MD_CTX_new");
387 if (!EVP_VerifyInit_ex(ctx, md, NULL)) {
388 EVP_MD_CTX_free(ctx);
389 ossl_raise(ePKeyError, "EVP_VerifyInit_ex");
390 }
391 if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
392 EVP_MD_CTX_free(ctx);
393 ossl_raise(ePKeyError, "EVP_VerifyUpdate");
394 }
395 result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey);
396 EVP_MD_CTX_free(ctx);
397 switch (result) {
398 case 0:
400 return Qfalse;
401 case 1:
402 return Qtrue;
403 default:
404 ossl_raise(ePKeyError, "EVP_VerifyFinal");
405 }
406}
407
408/*
409 * INIT
410 */
411void
413{
414#undef rb_intern
415#if 0
416 mOSSL = rb_define_module("OpenSSL");
418#endif
419
420 /* Document-module: OpenSSL::PKey
421 *
422 * == Asymmetric Public Key Algorithms
423 *
424 * Asymmetric public key algorithms solve the problem of establishing and
425 * sharing secret keys to en-/decrypt messages. The key in such an
426 * algorithm consists of two parts: a public key that may be distributed
427 * to others and a private key that needs to remain secret.
428 *
429 * Messages encrypted with a public key can only be decrypted by
430 * recipients that are in possession of the associated private key.
431 * Since public key algorithms are considerably slower than symmetric
432 * key algorithms (cf. OpenSSL::Cipher) they are often used to establish
433 * a symmetric key shared between two parties that are in possession of
434 * each other's public key.
435 *
436 * Asymmetric algorithms offer a lot of nice features that are used in a
437 * lot of different areas. A very common application is the creation and
438 * validation of digital signatures. To sign a document, the signatory
439 * generally uses a message digest algorithm (cf. OpenSSL::Digest) to
440 * compute a digest of the document that is then encrypted (i.e. signed)
441 * using the private key. Anyone in possession of the public key may then
442 * verify the signature by computing the message digest of the original
443 * document on their own, decrypting the signature using the signatory's
444 * public key and comparing the result to the message digest they
445 * previously computed. The signature is valid if and only if the
446 * decrypted signature is equal to this message digest.
447 *
448 * The PKey module offers support for three popular public/private key
449 * algorithms:
450 * * RSA (OpenSSL::PKey::RSA)
451 * * DSA (OpenSSL::PKey::DSA)
452 * * Elliptic Curve Cryptography (OpenSSL::PKey::EC)
453 * Each of these implementations is in fact a sub-class of the abstract
454 * PKey class which offers the interface for supporting digital signatures
455 * in the form of PKey#sign and PKey#verify.
456 *
457 * == Diffie-Hellman Key Exchange
458 *
459 * Finally PKey also features OpenSSL::PKey::DH, an implementation of
460 * the Diffie-Hellman key exchange protocol based on discrete logarithms
461 * in finite fields, the same basis that DSA is built on.
462 * The Diffie-Hellman protocol can be used to exchange (symmetric) keys
463 * over insecure channels without needing any prior joint knowledge
464 * between the participating parties. As the security of DH demands
465 * relatively long "public keys" (i.e. the part that is overtly
466 * transmitted between participants) DH tends to be quite slow. If
467 * security or speed is your primary concern, OpenSSL::PKey::EC offers
468 * another implementation of the Diffie-Hellman protocol.
469 *
470 */
472
473 /* Document-class: OpenSSL::PKey::PKeyError
474 *
475 *Raised when errors occur during PKey#sign or PKey#verify.
476 */
478
479 /* Document-class: OpenSSL::PKey::PKey
480 *
481 * An abstract class that bundles signature creation (PKey#sign) and
482 * validation (PKey#verify) that is common to all implementations except
483 * OpenSSL::PKey::DH
484 * * OpenSSL::PKey::RSA
485 * * OpenSSL::PKey::DSA
486 * * OpenSSL::PKey::EC
487 */
489
490 rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1);
491
492 rb_define_alloc_func(cPKey, ossl_pkey_alloc);
493 rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
494
495 rb_define_method(cPKey, "sign", ossl_pkey_sign, 2);
496 rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
497
498 id_private_q = rb_intern("private?");
499
500 /*
501 * INIT rsa, dsa, dh, ec
502 */
505 Init_ossl_dh();
506 Init_ossl_ec();
507}
struct RIMemo * ptr
Definition: debug.c:65
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
VALUE rb_define_class_under(VALUE, const char *, VALUE)
Defines a class under the namespace of outer.
Definition: class.c:711
VALUE rb_define_module(const char *)
Definition: class.c:785
VALUE rb_define_module_under(VALUE, const char *)
Definition: class.c:810
VALUE rb_cObject
Object class.
Definition: ruby.h:2012
VALUE rb_eStandardError
Definition: error.c:921
VALUE rb_eTypeError
Definition: error.c:924
VALUE rb_protect(VALUE(*)(VALUE), VALUE, int *)
Protects a function call from potential global escapes from the function.
Definition: eval.c:1072
VALUE rb_eRuntimeError
Definition: error.c:922
VALUE rb_eArgError
Definition: error.c:925
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:884
VALUE rb_obj_is_instance_of(VALUE, VALUE)
Determines if obj is an instance of c.
Definition: object.c:675
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:39
#define EVP_PKEY_up_ref(x)
#define EVP_MD_CTX_free
#define EVP_MD_CTX_new
#define BN_GENCB_get_arg(cb)
VALUE mOSSL
Definition: ossl.c:231
int ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_)
Definition: ossl.c:177
VALUE ossl_pem_passwd_value(VALUE pass)
Definition: ossl.c:151
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:293
VALUE eOSSLError
Definition: ossl.c:236
void ossl_clear_error(void)
Definition: ossl.c:304
#define OSSL_BIO_reset(bio)
Definition: ossl.h:115
BIO * ossl_obj2bio(volatile VALUE *pobj)
Definition: ossl_bio.c:13
const EVP_MD * ossl_evp_get_digestbyname(VALUE obj)
Definition: ossl_digest.c:45
EVP_PKEY * GetPrivPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:239
VALUE cPKey
Definition: ossl_pkey.c:16
const rb_data_type_t ossl_evp_pkey_type
Definition: ossl_pkey.c:87
VALUE mPKey
Definition: ossl_pkey.c:15
void ossl_pkey_check_public_key(const EVP_PKEY *pkey)
Definition: ossl_pkey.c:189
int ossl_generate_cb_2(int p, int n, BN_GENCB *cb)
Definition: ossl_pkey.c:39
void ossl_generate_cb_stop(void *ptr)
Definition: ossl_pkey.c:72
EVP_PKEY * DupPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:252
EVP_PKEY * GetPKeyPtr(VALUE obj)
Definition: ossl_pkey.c:229
void Init_ossl_pkey(void)
Definition: ossl_pkey.c:412
VALUE ossl_pkey_new(EVP_PKEY *pkey)
Definition: ossl_pkey.c:129
VALUE ePKeyError
Definition: ossl_pkey.c:17
void Init_ossl_dh(void)
Definition: ossl_pkey_dh.c:576
void Init_ossl_ec(void)
VALUE ossl_dsa_new(EVP_PKEY *)
Definition: ossl_pkey_dsa.c:72
VALUE ossl_ec_new(EVP_PKEY *)
Definition: ossl_pkey_ec.c:87
void Init_ossl_rsa(void)
void Init_ossl_dsa(void)
VALUE ossl_dh_new(EVP_PKEY *)
Definition: ossl_pkey_dh.c:58
#define GetPKey(obj, pkey)
Definition: ossl_pkey.h:31
VALUE ossl_rsa_new(EVP_PKEY *)
Definition: ossl_pkey_rsa.c:73
#define SetPKey(obj, pkey)
Definition: ossl_pkey.h:24
#define NewPKey(klass)
Definition: ossl_pkey.h:22
#define NULL
#define rb_funcallv(recv, mid, argc, argv)
use StringValue() instead")))
#define RSTRING_LEN(str)
const VALUE VALUE obj
#define RSTRING_PTR(str)
#define rb_str_new(str, len)
#define RSTRING_LENINT(str)
const char size_t n
void rb_str_set_len(VALUE, long)
Definition: string.c:2692
unsigned long VALUE
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
#define INT2NUM(x)
void rb_define_module_function(VALUE, const char *, VALUE(*)(), int)
#define RUBY_TYPED_FREE_IMMEDIATELY
#define rb_scan_args(argc, argvp, fmt,...)
void rb_thread_check_ints(void)
Definition: thread.c:1361
#define rb_intern(str)
#define Qtrue
#define Qnil
#define Qfalse
const VALUE * argv
__inline__ int
unsigned long ID
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1237
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
#define rb_ary_new2
void rb_ary_store(VALUE, long, VALUE)
Definition: array.c:1079
RUBY_SYMBOL_EXPORT_BEGIN void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
Definition: thread.c:1661