Ruby  1.9.3p429(2013-05-15revision40747)
ossl_pkey_dsa.c
Go to the documentation of this file.
1 /*
2  * $Id: ossl_pkey_dsa.c 32344 2011-06-30 20:20:32Z nobu $
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 #if !defined(OPENSSL_NO_DSA)
12 
13 #include "ossl.h"
14 
15 #define GetPKeyDSA(obj, pkey) do { \
16  GetPKey((obj), (pkey)); \
17  if (EVP_PKEY_type((pkey)->type) != EVP_PKEY_DSA) { /* PARANOIA? */ \
18  ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \
19  } \
20 } while (0)
21 
22 #define DSA_HAS_PRIVATE(dsa) ((dsa)->priv_key)
23 #define DSA_PRIVATE(obj,dsa) (DSA_HAS_PRIVATE(dsa)||OSSL_PKEY_IS_PRIVATE(obj))
24 
25 /*
26  * Classes
27  */
30 
31 /*
32  * Public
33  */
34 static VALUE
35 dsa_instance(VALUE klass, DSA *dsa)
36 {
37  EVP_PKEY *pkey;
38  VALUE obj;
39 
40  if (!dsa) {
41  return Qfalse;
42  }
43  if (!(pkey = EVP_PKEY_new())) {
44  return Qfalse;
45  }
46  if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
47  EVP_PKEY_free(pkey);
48  return Qfalse;
49  }
50  WrapPKey(klass, obj, pkey);
51 
52  return obj;
53 }
54 
55 VALUE
56 ossl_dsa_new(EVP_PKEY *pkey)
57 {
58  VALUE obj;
59 
60  if (!pkey) {
61  obj = dsa_instance(cDSA, DSA_new());
62  } else {
63  if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) {
64  ossl_raise(rb_eTypeError, "Not a DSA key!");
65  }
66  WrapPKey(cDSA, obj, pkey);
67  }
68  if (obj == Qfalse) {
70  }
71 
72  return obj;
73 }
74 
75 /*
76  * Private
77  */
78 static DSA *
80 {
81  DSA *dsa;
82  unsigned char seed[20];
83  int seed_len = 20, counter;
84  unsigned long h;
85 
86  if (!RAND_bytes(seed, seed_len)) {
87  return 0;
88  }
89  dsa = DSA_generate_parameters(size, seed, seed_len, &counter, &h,
91  NULL);
92  if(!dsa) return 0;
93 
94  if (!DSA_generate_key(dsa)) {
95  DSA_free(dsa);
96  return 0;
97  }
98 
99  return dsa;
100 }
101 
102 /*
103  * call-seq:
104  * DSA.generate(size) -> dsa
105  *
106  * Creates a new DSA instance by generating a private/public key pair
107  * from scratch.
108  *
109  * === Parameters
110  * * +size+ is an integer representing the desired key size.
111  *
112  */
113 static VALUE
115 {
116  DSA *dsa = dsa_generate(NUM2INT(size)); /* err handled by dsa_instance */
117  VALUE obj = dsa_instance(klass, dsa);
118 
119  if (obj == Qfalse) {
120  DSA_free(dsa);
122  }
123 
124  return obj;
125 }
126 
127 /*
128  * call-seq:
129  * DSA.new([size | string [, pass]) -> dsa
130  *
131  * Creates a new DSA instance by reading an existing key from +string+.
132  *
133  * === Parameters
134  * * +size+ is an integer representing the desired key size.
135  * * +string+ contains a DER or PEM encoded key.
136  * * +pass+ is a string that contains an optional password.
137  *
138  * === Examples
139  * DSA.new -> dsa
140  * DSA.new(1024) -> dsa
141  * DSA.new(File.read('dsa.pem')) -> dsa
142  * DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa
143  *
144  */
145 static VALUE
147 {
148  EVP_PKEY *pkey;
149  DSA *dsa;
150  BIO *in;
151  char *passwd = NULL;
152  VALUE arg, pass;
153 
154  GetPKey(self, pkey);
155  if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) {
156  dsa = DSA_new();
157  }
158  else if (FIXNUM_P(arg)) {
159  if (!(dsa = dsa_generate(FIX2INT(arg)))) {
161  }
162  }
163  else {
164  if (!NIL_P(pass)) passwd = StringValuePtr(pass);
165  arg = ossl_to_der_if_possible(arg);
166  in = ossl_obj2bio(arg);
167  dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd);
168  if (!dsa) {
169  OSSL_BIO_reset(in);
170  dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL);
171  }
172  if (!dsa) {
173  OSSL_BIO_reset(in);
174  dsa = d2i_DSAPrivateKey_bio(in, NULL);
175  }
176  if (!dsa) {
177  OSSL_BIO_reset(in);
178  dsa = d2i_DSA_PUBKEY_bio(in, NULL);
179  }
180  if (!dsa) {
181  OSSL_BIO_reset(in);
183  }
184  BIO_free(in);
185  if (!dsa) {
186  ERR_clear_error();
187  ossl_raise(eDSAError, "Neither PUB key nor PRIV key:");
188  }
189  }
190  if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
191  DSA_free(dsa);
193  }
194 
195  return self;
196 }
197 
198 /*
199  * call-seq:
200  * dsa.public? -> true | false
201  *
202  * Indicates whether this DSA instance has a public key associated with it or
203  * not. The public key may be retrieved with DSA#public_key.
204  */
205 static VALUE
207 {
208  EVP_PKEY *pkey;
209 
210  GetPKeyDSA(self, pkey);
211 
212  return (pkey->pkey.dsa->pub_key) ? Qtrue : Qfalse;
213 }
214 
215 /*
216  * call-seq:
217  * dsa.private? -> true | false
218  *
219  * Indicates whether this DSA instance has a private key associated with it or
220  * not. The private key may be retrieved with DSA#private_key.
221  */
222 static VALUE
224 {
225  EVP_PKEY *pkey;
226 
227  GetPKeyDSA(self, pkey);
228 
229  return (DSA_PRIVATE(self, pkey->pkey.dsa)) ? Qtrue : Qfalse;
230 }
231 
232 /*
233  * call-seq:
234  * dsa.to_pem([cipher, password]) -> aString
235  *
236  * Encodes this DSA to its PEM encoding.
237  *
238  * === Parameters
239  * * +cipher+ is an OpenSSL::Cipher.
240  * * +password+ is a string containing your password.
241  *
242  * === Examples
243  * DSA.to_pem -> aString
244  * DSA.to_pem(cipher, 'mypassword') -> aString
245  *
246  */
247 static VALUE
249 {
250  EVP_PKEY *pkey;
251  BIO *out;
252  const EVP_CIPHER *ciph = NULL;
253  char *passwd = NULL;
254  VALUE cipher, pass, str;
255 
256  GetPKeyDSA(self, pkey);
257  rb_scan_args(argc, argv, "02", &cipher, &pass);
258  if (!NIL_P(cipher)) {
259  ciph = GetCipherPtr(cipher);
260  if (!NIL_P(pass)) {
261  passwd = StringValuePtr(pass);
262  }
263  }
264  if (!(out = BIO_new(BIO_s_mem()))) {
266  }
267  if (DSA_HAS_PRIVATE(pkey->pkey.dsa)) {
268  if (!PEM_write_bio_DSAPrivateKey(out, pkey->pkey.dsa, ciph,
269  NULL, 0, ossl_pem_passwd_cb, passwd)){
270  BIO_free(out);
272  }
273  } else {
274  if (!PEM_write_bio_DSA_PUBKEY(out, pkey->pkey.dsa)) {
275  BIO_free(out);
277  }
278  }
279  str = ossl_membio2str(out);
280 
281  return str;
282 }
283 
284 /*
285  * call-seq:
286  * dsa.to_der -> aString
287  *
288  * Encodes this DSA to its DER encoding.
289  *
290  */
291 static VALUE
293 {
294  EVP_PKEY *pkey;
295  int (*i2d_func)_((DSA*, unsigned char**));
296  unsigned char *p;
297  long len;
298  VALUE str;
299 
300  GetPKeyDSA(self, pkey);
301  if(DSA_HAS_PRIVATE(pkey->pkey.dsa))
302  i2d_func = (int(*)_((DSA*,unsigned char**)))i2d_DSAPrivateKey;
303  else
304  i2d_func = i2d_DSA_PUBKEY;
305  if((len = i2d_func(pkey->pkey.dsa, NULL)) <= 0)
307  str = rb_str_new(0, len);
308  p = (unsigned char *)RSTRING_PTR(str);
309  if(i2d_func(pkey->pkey.dsa, &p) < 0)
311  ossl_str_adjust(str, p);
312 
313  return str;
314 }
315 
316 /*
317  * call-seq:
318  * dsa.params -> hash
319  *
320  * Stores all parameters of key to the hash
321  * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
322  * Don't use :-)) (I's up to you)
323  */
324 static VALUE
326 {
327  EVP_PKEY *pkey;
328  VALUE hash;
329 
330  GetPKeyDSA(self, pkey);
331 
332  hash = rb_hash_new();
333 
334  rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dsa->p));
335  rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.dsa->q));
336  rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dsa->g));
337  rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dsa->pub_key));
338  rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dsa->priv_key));
339 
340  return hash;
341 }
342 
343 /*
344  * call-seq:
345  * dsa.to_text -> aString
346  *
347  * Prints all parameters of key to buffer
348  * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
349  * Don't use :-)) (I's up to you)
350  */
351 static VALUE
353 {
354  EVP_PKEY *pkey;
355  BIO *out;
356  VALUE str;
357 
358  GetPKeyDSA(self, pkey);
359  if (!(out = BIO_new(BIO_s_mem()))) {
361  }
362  if (!DSA_print(out, pkey->pkey.dsa, 0)) { /* offset = 0 */
363  BIO_free(out);
365  }
366  str = ossl_membio2str(out);
367 
368  return str;
369 }
370 
371 /*
372  * call-seq:
373  * dsa.public_key -> aDSA
374  *
375  * Returns a new DSA instance that carries just the public key information.
376  * If the current instance has also private key information, this will no
377  * longer be present in the new instance. This feature is helpful for
378  * publishing the public key information without leaking any of the private
379  * information.
380  *
381  * === Example
382  * dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information
383  * pub_key = dsa.public_key # has only the public part available
384  * pub_key_der = pub_key.to_der # it's safe to publish this
385  *
386  *
387  */
388 static VALUE
390 {
391  EVP_PKEY *pkey;
392  DSA *dsa;
393  VALUE obj;
394 
395  GetPKeyDSA(self, pkey);
396  /* err check performed by dsa_instance */
397  dsa = DSAPublicKey_dup(pkey->pkey.dsa);
398  obj = dsa_instance(CLASS_OF(self), dsa);
399  if (obj == Qfalse) {
400  DSA_free(dsa);
402  }
403  return obj;
404 }
405 
406 #define ossl_dsa_buf_size(pkey) (DSA_size((pkey)->pkey.dsa)+16)
407 
408 /*
409  * call-seq:
410  * dsa.syssign(string) -> aString
411  *
412  * Computes and returns the DSA signature of +string+, where +string+ is
413  * expected to be an already-computed message digest of the original input
414  * data. The signature is issued using the private key of this DSA instance.
415  *
416  * === Parameters
417  * * +string+ is a message digest of the original input data to be signed
418  *
419  * === Example
420  * dsa = OpenSSL::PKey::DSA.new(2048)
421  * doc = "Sign me"
422  * digest = OpenSSL::Digest::SHA1.digest(doc)
423  * sig = dsa.syssign(digest)
424  *
425  *
426  */
427 static VALUE
429 {
430  EVP_PKEY *pkey;
431  unsigned int buf_len;
432  VALUE str;
433 
434  GetPKeyDSA(self, pkey);
435  StringValue(data);
436  if (!DSA_PRIVATE(self, pkey->pkey.dsa)) {
437  ossl_raise(eDSAError, "Private DSA key needed!");
438  }
439  str = rb_str_new(0, ossl_dsa_buf_size(pkey));
440  if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
441  (unsigned char *)RSTRING_PTR(str),
442  &buf_len, pkey->pkey.dsa)) { /* type is ignored (0) */
444  }
445  rb_str_set_len(str, buf_len);
446 
447  return str;
448 }
449 
450 /*
451  * call-seq:
452  * dsa.sysverify(digest, sig) -> true | false
453  *
454  * Verifies whether the signature is valid given the message digest input. It
455  * does so by validating +sig+ using the public key of this DSA instance.
456  *
457  * === Parameters
458  * * +digest+ is a message digest of the original input data to be signed
459  * * +sig+ is a DSA signature value
460  *
461  * === Example
462  * dsa = OpenSSL::PKey::DSA.new(2048)
463  * doc = "Sign me"
464  * digest = OpenSSL::Digest::SHA1.digest(doc)
465  * sig = dsa.syssign(digest)
466  * puts dsa.sysverify(digest, sig) # => true
467  *
468  */
469 static VALUE
470 ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig)
471 {
472  EVP_PKEY *pkey;
473  int ret;
474 
475  GetPKeyDSA(self, pkey);
476  StringValue(digest);
477  StringValue(sig);
478  /* type is ignored (0) */
479  ret = DSA_verify(0, (unsigned char *)RSTRING_PTR(digest), RSTRING_LENINT(digest),
480  (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey->pkey.dsa);
481  if (ret < 0) {
483  }
484  else if (ret == 1) {
485  return Qtrue;
486  }
487 
488  return Qfalse;
489 }
490 
491 OSSL_PKEY_BN(dsa, p)
492 OSSL_PKEY_BN(dsa, q)
493 OSSL_PKEY_BN(dsa, g)
494 OSSL_PKEY_BN(dsa, pub_key)
495 OSSL_PKEY_BN(dsa, priv_key)
496 
497 /*
498  * INIT
499  */
500 void
502 {
503 #if 0
504  mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL and mPKey */
506 #endif
507 
508  /* Document-class: OpenSSL::PKey::DSAError
509  *
510  * Generic exception that is raised if an operation on a DSA PKey
511  * fails unexpectedly or in case an instantiation of an instance of DSA
512  * fails due to non-conformant input data.
513  */
515 
516  /* Document-class: OpenSSL::PKey::DSA
517  *
518  * DSA, the Digital Signature Algorithm, is specified in NIST's
519  * FIPS 186-3. It is an asymmetric public key algorithm that may be used
520  * similar to e.g. RSA.
521  * Please note that for OpenSSL versions prior to 1.0.0 the digest
522  * algorithms OpenSSL::Digest::DSS (equivalent to SHA) or
523  * OpenSSL::Digest::DSS1 (equivalent to SHA-1) must be used for issuing
524  * signatures with a DSA key using OpenSSL::PKey#sign.
525  * Starting with OpenSSL 1.0.0, digest algorithms are no longer restricted,
526  * any Digest may be used for signing.
527  */
529 
531  rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
532 
533  rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0);
534  rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0);
535  rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0);
536  rb_define_method(cDSA, "export", ossl_dsa_export, -1);
537  rb_define_alias(cDSA, "to_pem", "export");
538  rb_define_alias(cDSA, "to_s", "export");
539  rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0);
540  rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0);
541  rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
542  rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);
543 
544  DEF_OSSL_PKEY_BN(cDSA, dsa, p);
545  DEF_OSSL_PKEY_BN(cDSA, dsa, q);
546  DEF_OSSL_PKEY_BN(cDSA, dsa, g);
547  DEF_OSSL_PKEY_BN(cDSA, dsa, pub_key);
548  DEF_OSSL_PKEY_BN(cDSA, dsa, priv_key);
549 
551 }
552 
553 #else /* defined NO_DSA */
554 void
556 {
557 }
558 #endif /* NO_DSA */
559