Ruby  3.1.0dev(2021-09-10revisionb76ad15ed0da636161de0243c547ee1e6fc95681)
ossl_pkey_dh.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 #if !defined(OPENSSL_NO_DH)
13 
14 #define GetPKeyDH(obj, pkey) do { \
15  GetPKey((obj), (pkey)); \
16  if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) { /* PARANOIA? */ \
17  ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \
18  } \
19 } while (0)
20 #define GetDH(obj, dh) do { \
21  EVP_PKEY *_pkey; \
22  GetPKeyDH((obj), _pkey); \
23  (dh) = EVP_PKEY_get0_DH(_pkey); \
24 } while (0)
25 
26 /*
27  * Classes
28  */
31 
32 /*
33  * Private
34  */
35 /*
36  * call-seq:
37  * DH.new -> dh
38  * DH.new(string) -> dh
39  * DH.new(size [, generator]) -> dh
40  *
41  * Creates a new instance of OpenSSL::PKey::DH.
42  *
43  * If called without arguments, an empty instance without any parameter or key
44  * components is created. Use #set_pqg to manually set the parameters afterwards
45  * (and optionally #set_key to set private and public key components).
46  *
47  * If a String is given, tries to parse it as a DER- or PEM- encoded parameters.
48  * See also OpenSSL::PKey.read which can parse keys of any kinds.
49  *
50  * The DH.new(size [, generator]) form is an alias of DH.generate.
51  *
52  * +string+::
53  * A String that contains the DER or PEM encoded key.
54  * +size+::
55  * See DH.generate.
56  * +generator+::
57  * See DH.generate.
58  *
59  * Examples:
60  * # Creating an instance from scratch
61  * dh = DH.new
62  * dh.set_pqg(bn_p, nil, bn_g)
63  *
64  * # Generating a parameters and a key pair
65  * dh = DH.new(2048) # An alias of DH.generate(2048)
66  *
67  * # Reading DH parameters
68  * dh = DH.new(File.read('parameters.pem')) # -> dh, but no public/private key yet
69  * dh.generate_key! # -> dh with public and private key
70  */
71 static VALUE
72 ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
73 {
74  EVP_PKEY *pkey;
75  DH *dh;
76  BIO *in;
77  VALUE arg;
78 
79  GetPKey(self, pkey);
80  /* The DH.new(size, generator) form is handled by lib/openssl/pkey.rb */
81  if (rb_scan_args(argc, argv, "01", &arg) == 0) {
82  dh = DH_new();
83  if (!dh)
84  ossl_raise(eDHError, "DH_new");
85  }
86  else {
87  arg = ossl_to_der_if_possible(arg);
88  in = ossl_obj2bio(&arg);
89  dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
90  if (!dh){
91  OSSL_BIO_reset(in);
92  dh = d2i_DHparams_bio(in, NULL);
93  }
94  BIO_free(in);
95  if (!dh) {
97  }
98  }
99  if (!EVP_PKEY_assign_DH(pkey, dh)) {
100  DH_free(dh);
102  }
103  return self;
104 }
105 
106 static VALUE
107 ossl_dh_initialize_copy(VALUE self, VALUE other)
108 {
109  EVP_PKEY *pkey;
110  DH *dh, *dh_other;
111  const BIGNUM *pub, *priv;
112 
113  GetPKey(self, pkey);
114  if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
115  ossl_raise(eDHError, "DH already initialized");
116  GetDH(other, dh_other);
117 
118  dh = DHparams_dup(dh_other);
119  if (!dh)
120  ossl_raise(eDHError, "DHparams_dup");
121  EVP_PKEY_assign_DH(pkey, dh);
122 
123  DH_get0_key(dh_other, &pub, &priv);
124  if (pub) {
125  BIGNUM *pub2 = BN_dup(pub);
126  BIGNUM *priv2 = BN_dup(priv);
127 
128  if (!pub2 || (priv && !priv2)) {
129  BN_clear_free(pub2);
130  BN_clear_free(priv2);
131  ossl_raise(eDHError, "BN_dup");
132  }
133  DH_set0_key(dh, pub2, priv2);
134  }
135 
136  return self;
137 }
138 
139 /*
140  * call-seq:
141  * dh.public? -> true | false
142  *
143  * Indicates whether this DH instance has a public key associated with it or
144  * not. The public key may be retrieved with DH#pub_key.
145  */
146 static VALUE
147 ossl_dh_is_public(VALUE self)
148 {
149  DH *dh;
150  const BIGNUM *bn;
151 
152  GetDH(self, dh);
153  DH_get0_key(dh, &bn, NULL);
154 
155  return bn ? Qtrue : Qfalse;
156 }
157 
158 /*
159  * call-seq:
160  * dh.private? -> true | false
161  *
162  * Indicates whether this DH instance has a private key associated with it or
163  * not. The private key may be retrieved with DH#priv_key.
164  */
165 static VALUE
166 ossl_dh_is_private(VALUE self)
167 {
168  DH *dh;
169  const BIGNUM *bn;
170 
171  GetDH(self, dh);
172  DH_get0_key(dh, NULL, &bn);
173 
174 #if !defined(OPENSSL_NO_ENGINE)
175  return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse;
176 #else
177  return bn ? Qtrue : Qfalse;
178 #endif
179 }
180 
181 /*
182  * call-seq:
183  * dh.export -> aString
184  * dh.to_pem -> aString
185  * dh.to_s -> aString
186  *
187  * Encodes this DH to its PEM encoding. Note that any existing per-session
188  * public/private keys will *not* get encoded, just the Diffie-Hellman
189  * parameters will be encoded.
190  */
191 static VALUE
192 ossl_dh_export(VALUE self)
193 {
194  DH *dh;
195  BIO *out;
196  VALUE str;
197 
198  GetDH(self, dh);
199  if (!(out = BIO_new(BIO_s_mem()))) {
201  }
202  if (!PEM_write_bio_DHparams(out, dh)) {
203  BIO_free(out);
205  }
206  str = ossl_membio2str(out);
207 
208  return str;
209 }
210 
211 /*
212  * call-seq:
213  * dh.to_der -> aString
214  *
215  * Encodes this DH to its DER encoding. Note that any existing per-session
216  * public/private keys will *not* get encoded, just the Diffie-Hellman
217  * parameters will be encoded.
218 
219  */
220 static VALUE
221 ossl_dh_to_der(VALUE self)
222 {
223  DH *dh;
224  unsigned char *p;
225  long len;
226  VALUE str;
227 
228  GetDH(self, dh);
229  if((len = i2d_DHparams(dh, NULL)) <= 0)
231  str = rb_str_new(0, len);
232  p = (unsigned char *)RSTRING_PTR(str);
233  if(i2d_DHparams(dh, &p) < 0)
235  ossl_str_adjust(str, p);
236 
237  return str;
238 }
239 
240 /*
241  * call-seq:
242  * dh.params -> hash
243  *
244  * Stores all parameters of key to the hash
245  * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
246  * Don't use :-)) (I's up to you)
247  */
248 static VALUE
249 ossl_dh_get_params(VALUE self)
250 {
251  DH *dh;
252  VALUE hash;
253  const BIGNUM *p, *q, *g, *pub_key, *priv_key;
254 
255  GetDH(self, dh);
256  DH_get0_pqg(dh, &p, &q, &g);
257  DH_get0_key(dh, &pub_key, &priv_key);
258 
259  hash = rb_hash_new();
260  rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p));
261  rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q));
262  rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g));
263  rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key));
264  rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key));
265 
266  return hash;
267 }
268 
269 /*
270  * call-seq:
271  * dh.params_ok? -> true | false
272  *
273  * Validates the Diffie-Hellman parameters associated with this instance.
274  * It checks whether a safe prime and a suitable generator are used. If this
275  * is not the case, +false+ is returned.
276  *
277  * See also the man page EVP_PKEY_param_check(3).
278  */
279 static VALUE
280 ossl_dh_check_params(VALUE self)
281 {
282  int ret;
283 #ifdef HAVE_EVP_PKEY_CHECK
284  EVP_PKEY *pkey;
285  EVP_PKEY_CTX *pctx;
286 
287  GetPKey(self, pkey);
288  pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
289  if (!pctx)
290  ossl_raise(eDHError, "EVP_PKEY_CTX_new");
291  ret = EVP_PKEY_param_check(pctx);
292  EVP_PKEY_CTX_free(pctx);
293 #else
294  DH *dh;
295  int codes;
296 
297  GetDH(self, dh);
298  ret = DH_check(dh, &codes) == 1 && codes == 0;
299 #endif
300 
301  if (ret == 1)
302  return Qtrue;
303  else {
304  /* DH_check_ex() will put error entry on failure */
306  return Qfalse;
307  }
308 }
309 
310 /*
311  * Document-method: OpenSSL::PKey::DH#set_pqg
312  * call-seq:
313  * dh.set_pqg(p, q, g) -> self
314  *
315  * Sets _p_, _q_, _g_ to the DH instance.
316  */
317 OSSL_PKEY_BN_DEF3(dh, DH, pqg, p, q, g)
318 /*
319  * Document-method: OpenSSL::PKey::DH#set_key
320  * call-seq:
321  * dh.set_key(pub_key, priv_key) -> self
322  *
323  * Sets _pub_key_ and _priv_key_ for the DH instance. _priv_key_ may be +nil+.
324  */
326 
327 /*
328  * INIT
329  */
330 void
332 {
333 #if 0
337 #endif
338 
339  /* Document-class: OpenSSL::PKey::DHError
340  *
341  * Generic exception that is raised if an operation on a DH PKey
342  * fails unexpectedly or in case an instantiation of an instance of DH
343  * fails due to non-conformant input data.
344  */
346  /* Document-class: OpenSSL::PKey::DH
347  *
348  * An implementation of the Diffie-Hellman key exchange protocol based on
349  * discrete logarithms in finite fields, the same basis that DSA is built
350  * on.
351  *
352  * === Accessor methods for the Diffie-Hellman parameters
353  * DH#p::
354  * The prime (an OpenSSL::BN) of the Diffie-Hellman parameters.
355  * DH#g::
356  * The generator (an OpenSSL::BN) g of the Diffie-Hellman parameters.
357  * DH#pub_key::
358  * The per-session public key (an OpenSSL::BN) matching the private key.
359  * This needs to be passed to DH#compute_key.
360  * DH#priv_key::
361  * The per-session private key, an OpenSSL::BN.
362  *
363  * === Example of a key exchange
364  * # you may send the parameters (der) and own public key (pub1) publicly
365  * # to the participating party
366  * dh1 = OpenSSL::PKey::DH.new(2048)
367  * der = dh1.to_der
368  * pub1 = dh1.pub_key
369  *
370  * # the other party generates its per-session key pair
371  * dhparams = OpenSSL::PKey::DH.new(der)
372  * dh2 = OpenSSL::PKey.generate_key(dhparams)
373  * pub2 = dh2.pub_key
374  *
375  * symm_key1 = dh1.compute_key(pub2)
376  * symm_key2 = dh2.compute_key(pub1)
377  * puts symm_key1 == symm_key2 # => true
378  */
380  rb_define_method(cDH, "initialize", ossl_dh_initialize, -1);
381  rb_define_method(cDH, "initialize_copy", ossl_dh_initialize_copy, 1);
382  rb_define_method(cDH, "public?", ossl_dh_is_public, 0);
383  rb_define_method(cDH, "private?", ossl_dh_is_private, 0);
384  rb_define_method(cDH, "export", ossl_dh_export, 0);
385  rb_define_alias(cDH, "to_pem", "export");
386  rb_define_alias(cDH, "to_s", "export");
387  rb_define_method(cDH, "to_der", ossl_dh_to_der, 0);
388  rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0);
389 
390  DEF_OSSL_PKEY_BN(cDH, dh, p);
391  DEF_OSSL_PKEY_BN(cDH, dh, q);
392  DEF_OSSL_PKEY_BN(cDH, dh, g);
395  rb_define_method(cDH, "set_pqg", ossl_dh_set_pqg, 3);
396  rb_define_method(cDH, "set_key", ossl_dh_set_key, 2);
397 
398  rb_define_method(cDH, "params", ossl_dh_get_params, 0);
399 }
400 
401 #else /* defined NO_DH */
402 void
403 Init_ossl_dh(void)
404 {
405 }
406 #endif /* NO_DH */
OSSL_PKEY_BN_DEF3
#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3)
Definition: ossl_pkey.h:176
priv_key
priv_key
Definition: openssl_missing.h:145
rb_hash_new
VALUE rb_hash_new(void)
Definition: hash.c:1526
rb_define_module_under
VALUE rb_define_module_under(VALUE outer, const char *name)
Definition: class.c:914
ossl_to_der_if_possible
VALUE ossl_to_der_if_possible(VALUE obj)
Definition: ossl.c:261
ossl_membio2str
VALUE ossl_membio2str(BIO *bio)
Definition: ossl_bio.c:29
ePKeyError
VALUE ePKeyError
Definition: ossl_pkey.c:17
pub_key
pub_key
Definition: openssl_missing.h:145
ossl_obj2bio
BIO * ossl_obj2bio(volatile VALUE *pobj)
Definition: ossl_bio.c:13
OSSL_BIO_reset
#define OSSL_BIO_reset(bio)
Definition: ossl.h:115
ossl.h
argv
char ** argv
Definition: ruby.c:243
ossl_clear_error
void ossl_clear_error(void)
Definition: ossl.c:310
cDH
VALUE cDH
Definition: ossl_pkey_dh.c:29
cPKey
VALUE cPKey
Definition: ossl_pkey.c:16
ossl_str_adjust
#define ossl_str_adjust(str, p)
Definition: ossl.h:88
pkey_blocking_generate_arg::pkey
EVP_PKEY * pkey
Definition: ossl_pkey.c:180
OSSL_PKEY_BN_DEF2
#define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2)
Definition: ossl_pkey.h:180
rb_define_alias
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition: class.c:2050
rb_str_new
#define rb_str_new(str, len)
Definition: string.h:213
DEF_OSSL_PKEY_BN
#define DEF_OSSL_PKEY_BN(class, keytype, name)
Definition: ossl_pkey.h:184
mOSSL
VALUE mOSSL
Definition: ossl.c:237
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:50
Qfalse
#define Qfalse
Definition: special_consts.h:50
GetPKey
#define GetPKey(obj, pkey)
Definition: ossl_pkey.h:31
ossl_bn_new
VALUE ossl_bn_new(const BIGNUM *bn)
Definition: ossl_bn.c:62
len
uint8_t len
Definition: escape.c:17
GetDH
#define GetDH(obj, dh)
Definition: ossl_pkey_dh.c:20
ossl_raise
void ossl_raise(VALUE exc, const char *fmt,...)
Definition: ossl.c:299
NULL
#define NULL
Definition: regenc.h:69
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2347
key
key
Definition: openssl_missing.h:145
Qtrue
#define Qtrue
Definition: special_consts.h:52
VALUE
unsigned long VALUE
Definition: value.h:38
mPKey
VALUE mPKey
Definition: ossl_pkey.c:15
RSTRING_PTR
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
ret
return ret
Definition: memory.h:232
rb_hash_aset
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:2892
argc
int argc
Definition: ruby.c:242
rb_str_new2
#define rb_str_new2
Definition: string.h:276
eDHError
VALUE eDHError
Definition: ossl_pkey_dh.c:30
eOSSLError
VALUE eOSSLError
Definition: ossl.c:242
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:809
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:655
Init_ossl_dh
void Init_ossl_dh(void)
Definition: ossl_pkey_dh.c:331