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 */
15 VALUE mPKey;
16 VALUE cPKey;
17 VALUE ePKeyError;
18 static ID id_private_q;
19
20 /*
21 * callback for generating keys
22 */
23 static VALUE
call_check_ints0(VALUE arg)24 call_check_ints0(VALUE arg)
25 {
26 rb_thread_check_ints();
27 return Qnil;
28 }
29
30 static void *
call_check_ints(void * arg)31 call_check_ints(void *arg)
32 {
33 int state;
34 rb_protect(call_check_ints0, Qnil, &state);
35 return (void *)(VALUE)state;
36 }
37
38 int
ossl_generate_cb_2(int p,int n,BN_GENCB * cb)39 ossl_generate_cb_2(int p, int n, BN_GENCB *cb)
40 {
41 VALUE ary;
42 struct ossl_generate_cb_arg *arg;
43 int state;
44
45 arg = (struct ossl_generate_cb_arg *)BN_GENCB_get_arg(cb);
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 */
54 rb_protect(rb_yield, ary, &state);
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
71 void
ossl_generate_cb_stop(void * ptr)72 ossl_generate_cb_stop(void *ptr)
73 {
74 struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr;
75 arg->interrupted = 1;
76 }
77
78 static void
ossl_evp_pkey_free(void * ptr)79 ossl_evp_pkey_free(void *ptr)
80 {
81 EVP_PKEY_free(ptr);
82 }
83
84 /*
85 * Public
86 */
87 const rb_data_type_t ossl_evp_pkey_type = {
88 "OpenSSL/EVP_PKEY",
89 {
90 0, ossl_evp_pkey_free,
91 },
92 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
93 };
94
95 static VALUE
pkey_new0(EVP_PKEY * pkey)96 pkey_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
128 VALUE
ossl_pkey_new(EVP_PKEY * pkey)129 ossl_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 */
159 static VALUE
ossl_pkey_new_from_data(int argc,VALUE * argv,VALUE self)160 ossl_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
188 void
ossl_pkey_check_public_key(const EVP_PKEY * pkey)189 ossl_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
228 EVP_PKEY *
GetPKeyPtr(VALUE obj)229 GetPKeyPtr(VALUE obj)
230 {
231 EVP_PKEY *pkey;
232
233 GetPKey(obj, pkey);
234
235 return pkey;
236 }
237
238 EVP_PKEY *
GetPrivPKeyPtr(VALUE obj)239 GetPrivPKeyPtr(VALUE obj)
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
251 EVP_PKEY *
DupPKeyPtr(VALUE obj)252 DupPKeyPtr(VALUE obj)
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 */
265 static VALUE
ossl_pkey_alloc(VALUE klass)266 ossl_pkey_alloc(VALUE klass)
267 {
268 EVP_PKEY *pkey;
269 VALUE obj;
270
271 obj = NewPKey(klass);
272 if (!(pkey = EVP_PKEY_new())) {
273 ossl_raise(ePKeyError, NULL);
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 */
287 static VALUE
ossl_pkey_initialize(VALUE self)288 ossl_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 */
313 static VALUE
ossl_pkey_sign(VALUE self,VALUE digest,VALUE data)314 ossl_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 */
369 static VALUE
ossl_pkey_verify(VALUE self,VALUE digest,VALUE sig,VALUE data)370 ossl_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);
378 ossl_pkey_check_public_key(pkey);
379 md = ossl_evp_get_digestbyname(digest);
380 StringValue(sig);
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:
399 ossl_clear_error();
400 return Qfalse;
401 case 1:
402 return Qtrue;
403 default:
404 ossl_raise(ePKeyError, "EVP_VerifyFinal");
405 }
406 }
407
408 /*
409 * INIT
410 */
411 void
Init_ossl_pkey(void)412 Init_ossl_pkey(void)
413 {
414 #undef rb_intern
415 #if 0
416 mOSSL = rb_define_module("OpenSSL");
417 eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
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 */
471 mPKey = rb_define_module_under(mOSSL, "PKey");
472
473 /* Document-class: OpenSSL::PKey::PKeyError
474 *
475 *Raised when errors occur during PKey#sign or PKey#verify.
476 */
477 ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError);
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 */
488 cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject);
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 */
503 Init_ossl_rsa();
504 Init_ossl_dsa();
505 Init_ossl_dh();
506 Init_ossl_ec();
507 }
508