1/*
2 * $Id: ossl_pkey.c 45868 2014-05-07 16:59:18Z usa $
3 * 'OpenSSL for Ruby' project
4 * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
5 * All rights reserved.
6 */
7/*
8 * This program is licenced under the same licence as Ruby.
9 * (See the file 'LICENCE'.)
10 */
11#include "ossl.h"
12
13/*
14 * Classes
15 */
16VALUE mPKey;
17VALUE cPKey;
18VALUE ePKeyError;
19ID id_private_q;
20
21/*
22 * callback for generating keys
23 */
24void
25ossl_generate_cb(int p, int n, void *arg)
26{
27    VALUE ary;
28
29    ary = rb_ary_new2(2);
30    rb_ary_store(ary, 0, INT2NUM(p));
31    rb_ary_store(ary, 1, INT2NUM(n));
32
33    rb_yield(ary);
34}
35
36#if HAVE_BN_GENCB
37/* OpenSSL 2nd version of GN generation callback */
38int
39ossl_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 *)cb->arg;
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->stop = 1;
57	    arg->state = state;
58	}
59    }
60    if (arg->stop) return 0;
61    return 1;
62}
63
64void
65ossl_generate_cb_stop(void *ptr)
66{
67    struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr;
68    arg->stop = 1;
69}
70#endif
71
72/*
73 * Public
74 */
75VALUE
76ossl_pkey_new(EVP_PKEY *pkey)
77{
78    if (!pkey) {
79	ossl_raise(ePKeyError, "Cannot make new key from NULL.");
80    }
81    switch (EVP_PKEY_type(pkey->type)) {
82#if !defined(OPENSSL_NO_RSA)
83    case EVP_PKEY_RSA:
84	return ossl_rsa_new(pkey);
85#endif
86#if !defined(OPENSSL_NO_DSA)
87    case EVP_PKEY_DSA:
88	return ossl_dsa_new(pkey);
89#endif
90#if !defined(OPENSSL_NO_DH)
91    case EVP_PKEY_DH:
92	return ossl_dh_new(pkey);
93#endif
94#if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL)
95    case EVP_PKEY_EC:
96	return ossl_ec_new(pkey);
97#endif
98    default:
99	ossl_raise(ePKeyError, "unsupported key type");
100    }
101
102    UNREACHABLE;
103}
104
105VALUE
106ossl_pkey_new_from_file(VALUE filename)
107{
108    FILE *fp;
109    EVP_PKEY *pkey;
110
111    SafeStringValue(filename);
112    if (!(fp = fopen(RSTRING_PTR(filename), "r"))) {
113	ossl_raise(ePKeyError, "%s", strerror(errno));
114    }
115    rb_fd_fix_cloexec(fileno(fp));
116
117    pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL);
118    fclose(fp);
119    if (!pkey) {
120	ossl_raise(ePKeyError, NULL);
121    }
122
123    return ossl_pkey_new(pkey);
124}
125
126/*
127 *  call-seq:
128 *     OpenSSL::PKey.read(string [, pwd ] ) -> PKey
129 *     OpenSSL::PKey.read(file [, pwd ]) -> PKey
130 *
131 * === Parameters
132 * * +string+ is a DER- or PEM-encoded string containing an arbitrary private
133 * or public key.
134 * * +file+ is an instance of +File+ containing a DER- or PEM-encoded
135 * arbitrary private or public key.
136 * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted
137 * PEM resource.
138 */
139static VALUE
140ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self)
141{
142     EVP_PKEY *pkey;
143     BIO *bio;
144     VALUE data, pass;
145     char *passwd = NULL;
146
147     rb_scan_args(argc, argv, "11", &data, &pass);
148
149     bio = ossl_obj2bio(data);
150     if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) {
151	OSSL_BIO_reset(bio);
152	if (!NIL_P(pass)) {
153	    passwd = StringValuePtr(pass);
154	}
155	if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, passwd))) {
156	    OSSL_BIO_reset(bio);
157	    if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) {
158		OSSL_BIO_reset(bio);
159		if (!NIL_P(pass)) {
160		    passwd = StringValuePtr(pass);
161		}
162		pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, passwd);
163	    }
164	}
165    }
166
167    BIO_free(bio);
168    if (!pkey)
169	ossl_raise(rb_eArgError, "Could not parse PKey");
170    return ossl_pkey_new(pkey);
171}
172
173EVP_PKEY *
174GetPKeyPtr(VALUE obj)
175{
176    EVP_PKEY *pkey;
177
178    SafeGetPKey(obj, pkey);
179
180    return pkey;
181}
182
183EVP_PKEY *
184GetPrivPKeyPtr(VALUE obj)
185{
186    EVP_PKEY *pkey;
187
188    if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) {
189	ossl_raise(rb_eArgError, "Private key is needed.");
190    }
191    SafeGetPKey(obj, pkey);
192
193    return pkey;
194}
195
196EVP_PKEY *
197DupPKeyPtr(VALUE obj)
198{
199    EVP_PKEY *pkey;
200
201    SafeGetPKey(obj, pkey);
202    CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
203
204    return pkey;
205}
206
207EVP_PKEY *
208DupPrivPKeyPtr(VALUE obj)
209{
210    EVP_PKEY *pkey;
211
212    if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) {
213	ossl_raise(rb_eArgError, "Private key is needed.");
214    }
215    SafeGetPKey(obj, pkey);
216    CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
217
218    return pkey;
219}
220
221/*
222 * Private
223 */
224static VALUE
225ossl_pkey_alloc(VALUE klass)
226{
227    EVP_PKEY *pkey;
228    VALUE obj;
229
230    if (!(pkey = EVP_PKEY_new())) {
231	ossl_raise(ePKeyError, NULL);
232    }
233    WrapPKey(klass, obj, pkey);
234
235    return obj;
236}
237
238/*
239 *  call-seq:
240 *      PKeyClass.new -> self
241 *
242 * Because PKey is an abstract class, actually calling this method explicitly
243 * will raise a +NotImplementedError+.
244 */
245static VALUE
246ossl_pkey_initialize(VALUE self)
247{
248    if (rb_obj_is_instance_of(self, cPKey)) {
249	ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class.");
250    }
251    return self;
252}
253
254/*
255 *  call-seq:
256 *      pkey.sign(digest, data) -> String
257 *
258 * To sign the +String+ +data+, +digest+, an instance of OpenSSL::Digest, must
259 * be provided. The return value is again a +String+ containing the signature.
260 * A PKeyError is raised should errors occur.
261 * Any previous state of the +Digest+ instance is irrelevant to the signature
262 * outcome, the digest instance is reset to its initial state during the
263 * operation.
264 *
265 * == Example
266 *   data = 'Sign me!'
267 *   digest = OpenSSL::Digest::SHA256.new
268 *   pkey = OpenSSL::PKey::RSA.new(2048)
269 *   signature = pkey.sign(digest, data)
270 */
271static VALUE
272ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
273{
274    EVP_PKEY *pkey;
275    EVP_MD_CTX ctx;
276    unsigned int buf_len;
277    VALUE str;
278
279    if (rb_funcall(self, id_private_q, 0, NULL) != Qtrue) {
280	ossl_raise(rb_eArgError, "Private key is needed.");
281    }
282    GetPKey(self, pkey);
283    EVP_SignInit(&ctx, GetDigestPtr(digest));
284    StringValue(data);
285    EVP_SignUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data));
286    str = rb_str_new(0, EVP_PKEY_size(pkey)+16);
287    if (!EVP_SignFinal(&ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey))
288	ossl_raise(ePKeyError, NULL);
289    assert((long)buf_len <= RSTRING_LEN(str));
290    rb_str_set_len(str, buf_len);
291
292    return str;
293}
294
295/*
296 *  call-seq:
297 *      pkey.verify(digest, signature, data) -> String
298 *
299 * To verify the +String+ +signature+, +digest+, an instance of
300 * OpenSSL::Digest, must be provided to re-compute the message digest of the
301 * original +data+, also a +String+. The return value is +true+ if the
302 * signature is valid, +false+ otherwise. A PKeyError is raised should errors
303 * occur.
304 * Any previous state of the +Digest+ instance is irrelevant to the validation
305 * outcome, the digest instance is reset to its initial state during the
306 * operation.
307 *
308 * == Example
309 *   data = 'Sign me!'
310 *   digest = OpenSSL::Digest::SHA256.new
311 *   pkey = OpenSSL::PKey::RSA.new(2048)
312 *   signature = pkey.sign(digest, data)
313 *   pub_key = pkey.public_key
314 *   puts pub_key.verify(digest, signature, data) # => true
315 */
316static VALUE
317ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
318{
319    EVP_PKEY *pkey;
320    EVP_MD_CTX ctx;
321    int result;
322
323    GetPKey(self, pkey);
324    StringValue(sig);
325    StringValue(data);
326    EVP_VerifyInit(&ctx, GetDigestPtr(digest));
327    EVP_VerifyUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data));
328    result = EVP_VerifyFinal(&ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey);
329    EVP_MD_CTX_cleanup(&ctx);
330    switch (result) {
331    case 0:
332	return Qfalse;
333    case 1:
334	return Qtrue;
335    default:
336	ossl_raise(ePKeyError, NULL);
337    }
338    return Qnil; /* dummy */
339}
340
341/*
342 * INIT
343 */
344void
345Init_ossl_pkey()
346{
347#if 0
348    mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
349#endif
350
351    /* Document-module: OpenSSL::PKey
352     *
353     * == Asymmetric Public Key Algorithms
354     *
355     * Asymmetric public key algorithms solve the problem of establishing and
356     * sharing secret keys to en-/decrypt messages. The key in such an
357     * algorithm consists of two parts: a public key that may be distributed
358     * to others and a private key that needs to remain secret.
359     *
360     * Messages encrypted with a public key can only be encrypted by
361     * recipients that are in possession of the associated private key.
362     * Since public key algorithms are considerably slower than symmetric
363     * key algorithms (cf. OpenSSL::Cipher) they are often used to establish
364     * a symmetric key shared between two parties that are in possession of
365     * each other's public key.
366     *
367     * Asymmetric algorithms offer a lot of nice features that are used in a
368     * lot of different areas. A very common application is the creation and
369     * validation of digital signatures. To sign a document, the signatory
370     * generally uses a message digest algorithm (cf. OpenSSL::Digest) to
371     * compute a digest of the document that is then encrypted (i.e. signed)
372     * using the private key. Anyone in possession of the public key may then
373     * verify the signature by computing the message digest of the original
374     * document on their own, decrypting the signature using the signatory's
375     * public key and comparing the result to the message digest they
376     * previously computed. The signature is valid if and only if the
377     * decrypted signature is equal to this message digest.
378     *
379     * The PKey module offers support for three popular public/private key
380     * algorithms:
381     * * RSA (OpenSSL::PKey::RSA)
382     * * DSA (OpenSSL::PKey::DSA)
383     * * Elliptic Curve Cryptography (OpenSSL::PKey::EC)
384     * Each of these implementations is in fact a sub-class of the abstract
385     * PKey class which offers the interface for supporting digital signatures
386     * in the form of PKey#sign and PKey#verify.
387     *
388     * == Diffie-Hellman Key Exchange
389     *
390     * Finally PKey also features OpenSSL::PKey::DH, an implementation of
391     * the Diffie-Hellman key exchange protocol based on discrete logarithms
392     * in finite fields, the same basis that DSA is built on.
393     * The Diffie-Hellman protocol can be used to exchange (symmetric) keys
394     * over insecure channels without needing any prior joint knowledge
395     * between the participating parties. As the security of DH demands
396     * relatively long "public keys" (i.e. the part that is overtly
397     * transmitted between participants) DH tends to be quite slow. If
398     * security or speed is your primary concern, OpenSSL::PKey::EC offers
399     * another implementation of the Diffie-Hellman protocol.
400     *
401     */
402    mPKey = rb_define_module_under(mOSSL, "PKey");
403
404    /* Document-class: OpenSSL::PKey::PKeyError
405     *
406     *Raised when errors occur during PKey#sign or PKey#verify.
407     */
408    ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError);
409
410    /* Document-class: OpenSSL::PKey::PKey
411     *
412     * An abstract class that bundles signature creation (PKey#sign) and
413     * validation (PKey#verify) that is common to all implementations except
414     * OpenSSL::PKey::DH
415     * * OpenSSL::PKey::RSA
416     * * OpenSSL::PKey::DSA
417     * * OpenSSL::PKey::EC
418     */
419    cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject);
420
421    rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1);
422
423    rb_define_alloc_func(cPKey, ossl_pkey_alloc);
424    rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
425
426    rb_define_method(cPKey, "sign", ossl_pkey_sign, 2);
427    rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
428
429    id_private_q = rb_intern("private?");
430
431    /*
432     * INIT rsa, dsa, dh, ec
433     */
434    Init_ossl_rsa();
435    Init_ossl_dsa();
436    Init_ossl_dh();
437    Init_ossl_ec();
438}
439
440