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