1 /*
2  * 'OpenSSL for Ruby' project
3  * Copyright (C) 2001-2002  Technorama team <oss-ruby@technorama.net>
4  * All rights reserved.
5  */
6 /*
7  * This program is licensed under the same licence as Ruby.
8  * (See the file 'LICENCE'.)
9  */
10 /* modified by Michal Rokos <m.rokos@sh.cvut.cz> */
11 #include "ossl.h"
12 
13 #define NewBN(klass) \
14   TypedData_Wrap_Struct((klass), &ossl_bn_type, 0)
15 #define SetBN(obj, bn) do { \
16   if (!(bn)) { \
17     ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \
18   } \
19   RTYPEDDATA_DATA(obj) = (bn); \
20 } while (0)
21 
22 #define GetBN(obj, bn) do { \
23   TypedData_Get_Struct((obj), BIGNUM, &ossl_bn_type, (bn)); \
24   if (!(bn)) { \
25     ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \
26   } \
27 } while (0)
28 
29 static void
ossl_bn_free(void * ptr)30 ossl_bn_free(void *ptr)
31 {
32     BN_clear_free(ptr);
33 }
34 
35 static const rb_data_type_t ossl_bn_type = {
36     "OpenSSL/BN",
37     {
38 	0, ossl_bn_free,
39     },
40     0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
41 };
42 
43 /*
44  * Classes
45  */
46 VALUE cBN;
47 
48 /* Document-class: OpenSSL::BNError
49  *
50  * Generic Error for all of OpenSSL::BN (big num)
51  */
52 VALUE eBNError;
53 
54 /*
55  * Public
56  */
57 VALUE
ossl_bn_new(const BIGNUM * bn)58 ossl_bn_new(const BIGNUM *bn)
59 {
60     BIGNUM *newbn;
61     VALUE obj;
62 
63     obj = NewBN(cBN);
64     newbn = bn ? BN_dup(bn) : BN_new();
65     if (!newbn) {
66 	ossl_raise(eBNError, NULL);
67     }
68     SetBN(obj, newbn);
69 
70     return obj;
71 }
72 
73 static BIGNUM *
integer_to_bnptr(VALUE obj,BIGNUM * orig)74 integer_to_bnptr(VALUE obj, BIGNUM *orig)
75 {
76     BIGNUM *bn;
77 
78     if (FIXNUM_P(obj)) {
79 	long i;
80 	unsigned char bin[sizeof(long)];
81 	long n = FIX2LONG(obj);
82 	unsigned long un = labs(n);
83 
84 	for (i = sizeof(long) - 1; 0 <= i; i--) {
85 	    bin[i] = un & 0xff;
86 	    un >>= 8;
87 	}
88 
89 	bn = BN_bin2bn(bin, sizeof(bin), orig);
90 	if (!bn)
91 	    ossl_raise(eBNError, "BN_bin2bn");
92 	if (n < 0)
93 	    BN_set_negative(bn, 1);
94     }
95     else { /* assuming Bignum */
96 	size_t len = rb_absint_size(obj, NULL);
97 	unsigned char *bin;
98 	VALUE buf;
99 	int sign;
100 
101 	if (INT_MAX < len) {
102 	    rb_raise(eBNError, "bignum too long");
103 	}
104 	bin = (unsigned char*)ALLOCV_N(unsigned char, buf, len);
105 	sign = rb_integer_pack(obj, bin, len, 1, 0, INTEGER_PACK_BIG_ENDIAN);
106 
107 	bn = BN_bin2bn(bin, (int)len, orig);
108 	ALLOCV_END(buf);
109 	if (!bn)
110 	    ossl_raise(eBNError, "BN_bin2bn");
111 	if (sign < 0)
112 	    BN_set_negative(bn, 1);
113     }
114 
115     return bn;
116 }
117 
118 static VALUE
try_convert_to_bn(VALUE obj)119 try_convert_to_bn(VALUE obj)
120 {
121     BIGNUM *bn;
122     VALUE newobj = Qnil;
123 
124     if (rb_obj_is_kind_of(obj, cBN))
125 	return obj;
126     if (RB_INTEGER_TYPE_P(obj)) {
127 	newobj = NewBN(cBN); /* Handle potential mem leaks */
128 	bn = integer_to_bnptr(obj, NULL);
129 	SetBN(newobj, bn);
130     }
131 
132     return newobj;
133 }
134 
135 BIGNUM *
ossl_bn_value_ptr(volatile VALUE * ptr)136 ossl_bn_value_ptr(volatile VALUE *ptr)
137 {
138     VALUE tmp;
139     BIGNUM *bn;
140 
141     tmp = try_convert_to_bn(*ptr);
142     if (NIL_P(tmp))
143 	ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN");
144     GetBN(tmp, bn);
145     *ptr = tmp;
146 
147     return bn;
148 }
149 
150 /*
151  * Private
152  */
153 /*
154  * BN_CTX - is used in more difficult math. ops
155  * (Why just 1? Because Ruby itself isn't thread safe,
156  *  we don't need to care about threads)
157  */
158 BN_CTX *ossl_bn_ctx;
159 
160 static VALUE
ossl_bn_alloc(VALUE klass)161 ossl_bn_alloc(VALUE klass)
162 {
163     BIGNUM *bn;
164     VALUE obj = NewBN(klass);
165 
166     if (!(bn = BN_new())) {
167 	ossl_raise(eBNError, NULL);
168     }
169     SetBN(obj, bn);
170 
171     return obj;
172 }
173 
174 /*
175  * call-seq:
176  *    OpenSSL::BN.new => aBN
177  *    OpenSSL::BN.new(bn) => aBN
178  *    OpenSSL::BN.new(integer) => aBN
179  *    OpenSSL::BN.new(string) => aBN
180  *    OpenSSL::BN.new(string, 0 | 2 | 10 | 16) => aBN
181  *
182  * Construct a new OpenSSL BIGNUM object.
183  */
184 static VALUE
ossl_bn_initialize(int argc,VALUE * argv,VALUE self)185 ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
186 {
187     BIGNUM *bn;
188     VALUE str, bs;
189     int base = 10;
190     char *ptr;
191 
192     if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) {
193 	base = NUM2INT(bs);
194     }
195 
196     if (RB_INTEGER_TYPE_P(str)) {
197 	GetBN(self, bn);
198 	integer_to_bnptr(str, bn);
199 
200 	return self;
201     }
202 
203     if (RTEST(rb_obj_is_kind_of(str, cBN))) {
204 	BIGNUM *other;
205 
206 	GetBN(self, bn);
207 	GetBN(str, other); /* Safe - we checked kind_of? above */
208 	if (!BN_copy(bn, other)) {
209 	    ossl_raise(eBNError, NULL);
210 	}
211 	return self;
212     }
213 
214     GetBN(self, bn);
215     switch (base) {
216     case 0:
217         ptr = StringValuePtr(str);
218         if (!BN_mpi2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) {
219 	    ossl_raise(eBNError, NULL);
220 	}
221 	break;
222     case 2:
223         ptr = StringValuePtr(str);
224         if (!BN_bin2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) {
225 	    ossl_raise(eBNError, NULL);
226 	}
227 	break;
228     case 10:
229 	if (!BN_dec2bn(&bn, StringValueCStr(str))) {
230 	    ossl_raise(eBNError, NULL);
231 	}
232 	break;
233     case 16:
234 	if (!BN_hex2bn(&bn, StringValueCStr(str))) {
235 	    ossl_raise(eBNError, NULL);
236 	}
237 	break;
238     default:
239 	ossl_raise(rb_eArgError, "invalid radix %d", base);
240     }
241     return self;
242 }
243 
244 /*
245  * call-seq:
246  *    bn.to_s => string
247  *    bn.to_s(base) => string
248  *
249  * === Parameters
250  * * _base_ - Integer
251  *   Valid values:
252  *   * 0 - MPI
253  *   * 2 - binary
254  *   * 10 - the default
255  *   * 16 - hex
256  */
257 static VALUE
ossl_bn_to_s(int argc,VALUE * argv,VALUE self)258 ossl_bn_to_s(int argc, VALUE *argv, VALUE self)
259 {
260     BIGNUM *bn;
261     VALUE str, bs;
262     int base = 10, len;
263     char *buf;
264 
265     if (rb_scan_args(argc, argv, "01", &bs) == 1) {
266 	base = NUM2INT(bs);
267     }
268     GetBN(self, bn);
269     switch (base) {
270     case 0:
271 	len = BN_bn2mpi(bn, NULL);
272         str = rb_str_new(0, len);
273 	if (BN_bn2mpi(bn, (unsigned char *)RSTRING_PTR(str)) != len)
274 	    ossl_raise(eBNError, NULL);
275 	break;
276     case 2:
277 	len = BN_num_bytes(bn);
278         str = rb_str_new(0, len);
279 	if (BN_bn2bin(bn, (unsigned char *)RSTRING_PTR(str)) != len)
280 	    ossl_raise(eBNError, NULL);
281 	break;
282     case 10:
283 	if (!(buf = BN_bn2dec(bn))) ossl_raise(eBNError, NULL);
284 	str = ossl_buf2str(buf, rb_long2int(strlen(buf)));
285 	break;
286     case 16:
287 	if (!(buf = BN_bn2hex(bn))) ossl_raise(eBNError, NULL);
288 	str = ossl_buf2str(buf, rb_long2int(strlen(buf)));
289 	break;
290     default:
291 	ossl_raise(rb_eArgError, "invalid radix %d", base);
292     }
293 
294     return str;
295 }
296 
297 /*
298  * call-seq:
299  *    bn.to_i => integer
300  */
301 static VALUE
ossl_bn_to_i(VALUE self)302 ossl_bn_to_i(VALUE self)
303 {
304     BIGNUM *bn;
305     char *txt;
306     VALUE num;
307 
308     GetBN(self, bn);
309 
310     if (!(txt = BN_bn2hex(bn))) {
311 	ossl_raise(eBNError, NULL);
312     }
313     num = rb_cstr_to_inum(txt, 16, Qtrue);
314     OPENSSL_free(txt);
315 
316     return num;
317 }
318 
319 static VALUE
ossl_bn_to_bn(VALUE self)320 ossl_bn_to_bn(VALUE self)
321 {
322     return self;
323 }
324 
325 static VALUE
ossl_bn_coerce(VALUE self,VALUE other)326 ossl_bn_coerce(VALUE self, VALUE other)
327 {
328     switch(TYPE(other)) {
329     case T_STRING:
330 	self = ossl_bn_to_s(0, NULL, self);
331 	break;
332     case T_FIXNUM:
333     case T_BIGNUM:
334 	self = ossl_bn_to_i(self);
335 	break;
336     default:
337 	if (!RTEST(rb_obj_is_kind_of(other, cBN))) {
338 	    ossl_raise(rb_eTypeError, "Don't know how to coerce");
339 	}
340     }
341     return rb_assoc_new(other, self);
342 }
343 
344 #define BIGNUM_BOOL1(func)				\
345     static VALUE					\
346     ossl_bn_##func(VALUE self)				\
347     {							\
348 	BIGNUM *bn;					\
349 	GetBN(self, bn);				\
350 	if (BN_##func(bn)) {				\
351 	    return Qtrue;				\
352 	}						\
353 	return Qfalse;					\
354     }
355 
356 /*
357  * Document-method: OpenSSL::BN#zero?
358  * call-seq:
359  *   bn.zero? => true | false
360  */
361 BIGNUM_BOOL1(is_zero)
362 
363 /*
364  * Document-method: OpenSSL::BN#one?
365  * call-seq:
366  *   bn.one? => true | false
367  */
BIGNUM_BOOL1(is_one)368 BIGNUM_BOOL1(is_one)
369 
370 /*
371  * Document-method: OpenSSL::BN#odd?
372  * call-seq:
373  *   bn.odd? => true | false
374  */
375 BIGNUM_BOOL1(is_odd)
376 
377 /*
378  * call-seq:
379  *   bn.negative? => true | false
380  */
381 static VALUE
382 ossl_bn_is_negative(VALUE self)
383 {
384     BIGNUM *bn;
385 
386     GetBN(self, bn);
387     if (BN_is_zero(bn))
388 	return Qfalse;
389     return BN_is_negative(bn) ? Qtrue : Qfalse;
390 }
391 
392 #define BIGNUM_1c(func)					\
393     static VALUE					\
394     ossl_bn_##func(VALUE self)				\
395     {							\
396 	BIGNUM *bn, *result;				\
397 	VALUE obj;					\
398 	GetBN(self, bn);				\
399 	obj = NewBN(rb_obj_class(self));		\
400 	if (!(result = BN_new())) {			\
401 	    ossl_raise(eBNError, NULL);			\
402 	}						\
403 	if (!BN_##func(result, bn, ossl_bn_ctx)) {	\
404 	    BN_free(result);				\
405 	    ossl_raise(eBNError, NULL);			\
406 	}						\
407 	SetBN(obj, result);				\
408 	return obj;					\
409     }
410 
411 /*
412  * Document-method: OpenSSL::BN#sqr
413  * call-seq:
414  *   bn.sqr => aBN
415  */
416 BIGNUM_1c(sqr)
417 
418 #define BIGNUM_2(func)					\
419     static VALUE					\
420     ossl_bn_##func(VALUE self, VALUE other)		\
421     {							\
422 	BIGNUM *bn1, *bn2 = GetBNPtr(other), *result;	\
423 	VALUE obj;					\
424 	GetBN(self, bn1);				\
425 	obj = NewBN(rb_obj_class(self));		\
426 	if (!(result = BN_new())) {			\
427 	    ossl_raise(eBNError, NULL);			\
428 	}						\
429 	if (!BN_##func(result, bn1, bn2)) {		\
430 	    BN_free(result);				\
431 	    ossl_raise(eBNError, NULL);			\
432 	}						\
433 	SetBN(obj, result);				\
434 	return obj;					\
435     }
436 
437 /*
438  * Document-method: OpenSSL::BN#+
439  * call-seq:
440  *   bn + bn2 => aBN
441  */
BIGNUM_2(add)442 BIGNUM_2(add)
443 
444 /*
445  * Document-method: OpenSSL::BN#-
446  * call-seq:
447  *   bn - bn2 => aBN
448  */
449 BIGNUM_2(sub)
450 
451 #define BIGNUM_2c(func)						\
452     static VALUE						\
453     ossl_bn_##func(VALUE self, VALUE other)			\
454     {								\
455 	BIGNUM *bn1, *bn2 = GetBNPtr(other), *result;		\
456 	VALUE obj;						\
457 	GetBN(self, bn1);					\
458 	obj = NewBN(rb_obj_class(self));			\
459 	if (!(result = BN_new())) {				\
460 	    ossl_raise(eBNError, NULL);				\
461 	}							\
462 	if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) {	\
463 	    BN_free(result);					\
464 	    ossl_raise(eBNError, NULL);				\
465 	}							\
466 	SetBN(obj, result);					\
467 	return obj;						\
468     }
469 
470 /*
471  * Document-method: OpenSSL::BN#*
472  * call-seq:
473  *   bn * bn2 => aBN
474  */
475 BIGNUM_2c(mul)
476 
477 /*
478  * Document-method: OpenSSL::BN#%
479  * call-seq:
480  *   bn % bn2 => aBN
481  */
482 BIGNUM_2c(mod)
483 
484 /*
485  * Document-method: OpenSSL::BN#**
486  * call-seq:
487  *   bn ** bn2 => aBN
488  */
489 BIGNUM_2c(exp)
490 
491 /*
492  * Document-method: OpenSSL::BN#gcd
493  * call-seq:
494  *   bn.gcd(bn2) => aBN
495  */
496 BIGNUM_2c(gcd)
497 
498 /*
499  * Document-method: OpenSSL::BN#mod_sqr
500  * call-seq:
501  *   bn.mod_sqr(bn2) => aBN
502  */
503 BIGNUM_2c(mod_sqr)
504 
505 /*
506  * Document-method: OpenSSL::BN#mod_inverse
507  * call-seq:
508  *   bn.mod_inverse(bn2) => aBN
509  */
510 BIGNUM_2c(mod_inverse)
511 
512 /*
513  * call-seq:
514  *    bn1 / bn2 => [result, remainder]
515  *
516  * Division of OpenSSL::BN instances
517  */
518 static VALUE
519 ossl_bn_div(VALUE self, VALUE other)
520 {
521     BIGNUM *bn1, *bn2 = GetBNPtr(other), *r1, *r2;
522     VALUE klass, obj1, obj2;
523 
524     GetBN(self, bn1);
525 
526     klass = rb_obj_class(self);
527     obj1 = NewBN(klass);
528     obj2 = NewBN(klass);
529     if (!(r1 = BN_new())) {
530 	ossl_raise(eBNError, NULL);
531     }
532     if (!(r2 = BN_new())) {
533 	BN_free(r1);
534 	ossl_raise(eBNError, NULL);
535     }
536     if (!BN_div(r1, r2, bn1, bn2, ossl_bn_ctx)) {
537 	BN_free(r1);
538 	BN_free(r2);
539 	ossl_raise(eBNError, NULL);
540     }
541     SetBN(obj1, r1);
542     SetBN(obj2, r2);
543 
544     return rb_ary_new3(2, obj1, obj2);
545 }
546 
547 #define BIGNUM_3c(func)						\
548     static VALUE						\
549     ossl_bn_##func(VALUE self, VALUE other1, VALUE other2)	\
550     {								\
551 	BIGNUM *bn1, *bn2 = GetBNPtr(other1);			\
552 	BIGNUM *bn3 = GetBNPtr(other2), *result;		\
553 	VALUE obj;						\
554 	GetBN(self, bn1);					\
555 	obj = NewBN(rb_obj_class(self));			\
556 	if (!(result = BN_new())) {				\
557 	    ossl_raise(eBNError, NULL);				\
558 	}							\
559 	if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) {	\
560 	    BN_free(result);					\
561 	    ossl_raise(eBNError, NULL);				\
562 	}							\
563 	SetBN(obj, result);					\
564 	return obj;						\
565     }
566 
567 /*
568  * Document-method: OpenSSL::BN#mod_add
569  * call-seq:
570  *   bn.mod_add(bn1, bn2) -> aBN
571  */
572 BIGNUM_3c(mod_add)
573 
574 /*
575  * Document-method: OpenSSL::BN#mod_sub
576  * call-seq:
577  *   bn.mod_sub(bn1, bn2) -> aBN
578  */
BIGNUM_3c(mod_sub)579 BIGNUM_3c(mod_sub)
580 
581 /*
582  * Document-method: OpenSSL::BN#mod_mul
583  * call-seq:
584  *   bn.mod_mul(bn1, bn2) -> aBN
585  */
586 BIGNUM_3c(mod_mul)
587 
588 /*
589  * Document-method: OpenSSL::BN#mod_exp
590  * call-seq:
591  *   bn.mod_exp(bn1, bn2) -> aBN
592  */
593 BIGNUM_3c(mod_exp)
594 
595 #define BIGNUM_BIT(func)				\
596     static VALUE					\
597     ossl_bn_##func(VALUE self, VALUE bit)		\
598     {							\
599 	BIGNUM *bn;					\
600 	GetBN(self, bn);				\
601 	if (!BN_##func(bn, NUM2INT(bit))) {		\
602 	    ossl_raise(eBNError, NULL);			\
603 	}						\
604 	return self;					\
605     }
606 
607 /*
608  * Document-method: OpenSSL::BN#set_bit!
609  * call-seq:
610  *   bn.set_bit!(bit) -> self
611  */
612 BIGNUM_BIT(set_bit)
613 
614 /*
615  * Document-method: OpenSSL::BN#clear_bit!
616  * call-seq:
617  *   bn.clear_bit!(bit) -> self
618  */
619 BIGNUM_BIT(clear_bit)
620 
621 /*
622  * Document-method: OpenSSL::BN#mask_bit!
623  * call-seq:
624  *   bn.mask_bit!(bit) -> self
625  */
626 BIGNUM_BIT(mask_bits)
627 
628 /*
629  * call-seq:
630  *   bn.bit_set?(bit) => true | false
631  *
632  * Tests bit _bit_ in _bn_ and returns +true+ if set, +false+ if not set.
633  */
634 static VALUE
635 ossl_bn_is_bit_set(VALUE self, VALUE bit)
636 {
637     int b;
638     BIGNUM *bn;
639 
640     b = NUM2INT(bit);
641     GetBN(self, bn);
642     if (BN_is_bit_set(bn, b)) {
643 	return Qtrue;
644     }
645     return Qfalse;
646 }
647 
648 #define BIGNUM_SHIFT(func)				\
649     static VALUE					\
650     ossl_bn_##func(VALUE self, VALUE bits)		\
651     {							\
652 	BIGNUM *bn, *result;				\
653 	int b;						\
654 	VALUE obj;					\
655 	b = NUM2INT(bits);				\
656 	GetBN(self, bn);				\
657 	obj = NewBN(rb_obj_class(self));		\
658 	if (!(result = BN_new())) {			\
659 		ossl_raise(eBNError, NULL);		\
660 	}						\
661 	if (!BN_##func(result, bn, b)) {		\
662 		BN_free(result);			\
663 		ossl_raise(eBNError, NULL);		\
664 	}						\
665 	SetBN(obj, result);				\
666 	return obj;					\
667     }
668 
669 /*
670  * Document-method: OpenSSL::BN#<<
671  * call-seq:
672  *   bn << bits -> aBN
673  */
674 BIGNUM_SHIFT(lshift)
675 
676 /*
677  * Document-method: OpenSSL::BN#>>
678  * call-seq:
679  *   bn >> bits -> aBN
680  */
BIGNUM_SHIFT(rshift)681 BIGNUM_SHIFT(rshift)
682 
683 #define BIGNUM_SELF_SHIFT(func)				\
684     static VALUE					\
685     ossl_bn_self_##func(VALUE self, VALUE bits)		\
686     {							\
687 	BIGNUM *bn;					\
688 	int b;						\
689 	b = NUM2INT(bits);				\
690 	GetBN(self, bn);				\
691 	if (!BN_##func(bn, bn, b))			\
692 		ossl_raise(eBNError, NULL);		\
693 	return self;					\
694     }
695 
696 /*
697  * Document-method: OpenSSL::BN#lshift!
698  * call-seq:
699  *   bn.lshift!(bits) -> self
700  */
701 BIGNUM_SELF_SHIFT(lshift)
702 
703 /*
704  * Document-method: OpenSSL::BN#rshift!
705  * call-seq:
706  *   bn.rshift!(bits) -> self
707  */
708 BIGNUM_SELF_SHIFT(rshift)
709 
710 #define BIGNUM_RAND(func)					\
711     static VALUE						\
712     ossl_bn_s_##func(int argc, VALUE *argv, VALUE klass)	\
713     {								\
714 	BIGNUM *result;						\
715 	int bottom = 0, top = 0, b;				\
716 	VALUE bits, fill, odd, obj;				\
717 								\
718 	switch (rb_scan_args(argc, argv, "12", &bits, &fill, &odd)) {	\
719 	case 3:							\
720 	    bottom = (odd == Qtrue) ? 1 : 0;			\
721 	    /* FALLTHROUGH */					\
722 	case 2:							\
723 	    top = NUM2INT(fill);				\
724 	}							\
725 	b = NUM2INT(bits);					\
726 	obj = NewBN(klass);					\
727 	if (!(result = BN_new())) {				\
728 	    ossl_raise(eBNError, NULL);				\
729 	}							\
730 	if (!BN_##func(result, b, top, bottom)) {		\
731 	    BN_free(result);					\
732 	    ossl_raise(eBNError, NULL);				\
733 	}							\
734 	SetBN(obj, result);					\
735 	return obj;						\
736     }
737 
738 /*
739  * Document-method: OpenSSL::BN.rand
740  *   BN.rand(bits [, fill [, odd]]) -> aBN
741  */
742 BIGNUM_RAND(rand)
743 
744 /*
745  * Document-method: OpenSSL::BN.pseudo_rand
746  *   BN.pseudo_rand(bits [, fill [, odd]]) -> aBN
747  */
748 BIGNUM_RAND(pseudo_rand)
749 
750 #define BIGNUM_RAND_RANGE(func)					\
751     static VALUE						\
752     ossl_bn_s_##func##_range(VALUE klass, VALUE range)		\
753     {								\
754 	BIGNUM *bn = GetBNPtr(range), *result;			\
755 	VALUE obj = NewBN(klass);				\
756 	if (!(result = BN_new())) {				\
757 	    ossl_raise(eBNError, NULL);				\
758 	}							\
759 	if (!BN_##func##_range(result, bn)) {			\
760 	    BN_free(result);					\
761 	    ossl_raise(eBNError, NULL);				\
762 	}							\
763 	SetBN(obj, result);					\
764 	return obj;						\
765     }
766 
767 /*
768  * Document-method: OpenSSL::BN.rand_range
769  * call-seq:
770  *   BN.rand_range(range) -> aBN
771  *
772  */
773 BIGNUM_RAND_RANGE(rand)
774 
775 /*
776  * Document-method: OpenSSL::BN.pseudo_rand_range
777  * call-seq:
778  *   BN.pseudo_rand_range(range) -> aBN
779  *
780  */
781 BIGNUM_RAND_RANGE(pseudo_rand)
782 
783 /*
784  * call-seq:
785  *    BN.generate_prime(bits, [, safe [, add [, rem]]]) => bn
786  *
787  * Generates a random prime number of bit length _bits_. If _safe_ is set to
788  * +true+, generates a safe prime. If _add_ is specified, generates a prime that
789  * fulfills condition <tt>p % add = rem</tt>.
790  *
791  * === Parameters
792  * * _bits_ - integer
793  * * _safe_ - boolean
794  * * _add_ - BN
795  * * _rem_ - BN
796  */
797 static VALUE
798 ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass)
799 {
800     BIGNUM *add = NULL, *rem = NULL, *result;
801     int safe = 1, num;
802     VALUE vnum, vsafe, vadd, vrem, obj;
803 
804     rb_scan_args(argc, argv, "13", &vnum, &vsafe, &vadd, &vrem);
805 
806     num = NUM2INT(vnum);
807 
808     if (vsafe == Qfalse) {
809 	safe = 0;
810     }
811     if (!NIL_P(vadd)) {
812 	add = GetBNPtr(vadd);
813 	rem = NIL_P(vrem) ? NULL : GetBNPtr(vrem);
814     }
815     obj = NewBN(klass);
816     if (!(result = BN_new())) {
817 	ossl_raise(eBNError, NULL);
818     }
819     if (!BN_generate_prime_ex(result, num, safe, add, rem, NULL)) {
820 	BN_free(result);
821 	ossl_raise(eBNError, NULL);
822     }
823     SetBN(obj, result);
824 
825     return obj;
826 }
827 
828 #define BIGNUM_NUM(func)			\
829     static VALUE 				\
830     ossl_bn_##func(VALUE self)			\
831     {						\
832 	BIGNUM *bn;				\
833 	GetBN(self, bn);			\
834 	return INT2NUM(BN_##func(bn));		\
835     }
836 
837 /*
838  * Document-method: OpenSSL::BN#num_bytes
839  * call-seq:
840  *   bn.num_bytes => integer
841  */
842 BIGNUM_NUM(num_bytes)
843 
844 /*
845  * Document-method: OpenSSL::BN#num_bits
846  * call-seq:
847  *   bn.num_bits => integer
848  */
BIGNUM_NUM(num_bits)849 BIGNUM_NUM(num_bits)
850 
851 static VALUE
852 ossl_bn_copy(VALUE self, VALUE other)
853 {
854     BIGNUM *bn1, *bn2;
855 
856     rb_check_frozen(self);
857 
858     if (self == other) return self;
859 
860     GetBN(self, bn1);
861     bn2 = GetBNPtr(other);
862 
863     if (!BN_copy(bn1, bn2)) {
864 	ossl_raise(eBNError, NULL);
865     }
866     return self;
867 }
868 
869 /*
870  * call-seq:
871  *   +bn -> aBN
872  */
873 static VALUE
ossl_bn_uplus(VALUE self)874 ossl_bn_uplus(VALUE self)
875 {
876     return self;
877 }
878 
879 /*
880  * call-seq:
881  *   -bn -> aBN
882  */
883 static VALUE
ossl_bn_uminus(VALUE self)884 ossl_bn_uminus(VALUE self)
885 {
886     VALUE obj;
887     BIGNUM *bn1, *bn2;
888 
889     GetBN(self, bn1);
890     obj = NewBN(cBN);
891     bn2 = BN_dup(bn1);
892     if (!bn2)
893 	ossl_raise(eBNError, "BN_dup");
894     SetBN(obj, bn2);
895     BN_set_negative(bn2, !BN_is_negative(bn2));
896 
897     return obj;
898 }
899 
900 #define BIGNUM_CMP(func)				\
901     static VALUE					\
902     ossl_bn_##func(VALUE self, VALUE other)		\
903     {							\
904 	BIGNUM *bn1, *bn2 = GetBNPtr(other);		\
905 	GetBN(self, bn1);				\
906 	return INT2NUM(BN_##func(bn1, bn2));		\
907     }
908 
909 /*
910  * Document-method: OpenSSL::BN#cmp
911  * call-seq:
912  *   bn.cmp(bn2) => integer
913  */
914 /*
915  * Document-method: OpenSSL::BN#<=>
916  * call-seq:
917  *   bn <=> bn2 => integer
918  */
919 BIGNUM_CMP(cmp)
920 
921 /*
922  * Document-method: OpenSSL::BN#ucmp
923  * call-seq:
924  *   bn.ucmp(bn2) => integer
925  */
BIGNUM_CMP(ucmp)926 BIGNUM_CMP(ucmp)
927 
928 /*
929  *  call-seq:
930  *     bn == obj => true or false
931  *
932  *  Returns +true+ only if _obj_ has the same value as _bn_. Contrast this
933  *  with OpenSSL::BN#eql?, which requires obj to be OpenSSL::BN.
934  */
935 static VALUE
936 ossl_bn_eq(VALUE self, VALUE other)
937 {
938     BIGNUM *bn1, *bn2;
939 
940     GetBN(self, bn1);
941     other = try_convert_to_bn(other);
942     if (NIL_P(other))
943 	return Qfalse;
944     GetBN(other, bn2);
945 
946     if (!BN_cmp(bn1, bn2)) {
947 	return Qtrue;
948     }
949     return Qfalse;
950 }
951 
952 /*
953  *  call-seq:
954  *     bn.eql?(obj) => true or false
955  *
956  *  Returns <code>true</code> only if <i>obj</i> is a
957  *  <code>OpenSSL::BN</code> with the same value as <i>bn</i>. Contrast this
958  *  with OpenSSL::BN#==, which performs type conversions.
959  */
960 static VALUE
ossl_bn_eql(VALUE self,VALUE other)961 ossl_bn_eql(VALUE self, VALUE other)
962 {
963     BIGNUM *bn1, *bn2;
964 
965     if (!rb_obj_is_kind_of(other, cBN))
966 	return Qfalse;
967     GetBN(self, bn1);
968     GetBN(other, bn2);
969 
970     return BN_cmp(bn1, bn2) ? Qfalse : Qtrue;
971 }
972 
973 /*
974  *  call-seq:
975  *     bn.hash => Integer
976  *
977  *  Returns a hash code for this object.
978  *
979  *  See also Object#hash.
980  */
981 static VALUE
ossl_bn_hash(VALUE self)982 ossl_bn_hash(VALUE self)
983 {
984     BIGNUM *bn;
985     VALUE tmp, hash;
986     unsigned char *buf;
987     int len;
988 
989     GetBN(self, bn);
990     len = BN_num_bytes(bn);
991     buf = ALLOCV(tmp, len);
992     if (BN_bn2bin(bn, buf) != len) {
993 	ALLOCV_END(tmp);
994 	ossl_raise(eBNError, "BN_bn2bin");
995     }
996 
997     hash = ST2FIX(rb_memhash(buf, len));
998     ALLOCV_END(tmp);
999 
1000     return hash;
1001 }
1002 
1003 /*
1004  * call-seq:
1005  *    bn.prime? => true | false
1006  *    bn.prime?(checks) => true | false
1007  *
1008  * Performs a Miller-Rabin probabilistic primality test with _checks_
1009  * iterations. If _checks_ is not specified, a number of iterations is used
1010  * that yields a false positive rate of at most 2^-80 for random input.
1011  *
1012  * === Parameters
1013  * * _checks_ - integer
1014  */
1015 static VALUE
ossl_bn_is_prime(int argc,VALUE * argv,VALUE self)1016 ossl_bn_is_prime(int argc, VALUE *argv, VALUE self)
1017 {
1018     BIGNUM *bn;
1019     VALUE vchecks;
1020     int checks = BN_prime_checks;
1021 
1022     if (rb_scan_args(argc, argv, "01", &vchecks) == 1) {
1023 	checks = NUM2INT(vchecks);
1024     }
1025     GetBN(self, bn);
1026     switch (BN_is_prime_ex(bn, checks, ossl_bn_ctx, NULL)) {
1027     case 1:
1028 	return Qtrue;
1029     case 0:
1030 	return Qfalse;
1031     default:
1032 	ossl_raise(eBNError, NULL);
1033     }
1034     /* not reachable */
1035     return Qnil;
1036 }
1037 
1038 /*
1039  * call-seq:
1040  *    bn.prime_fasttest? => true | false
1041  *    bn.prime_fasttest?(checks) => true | false
1042  *    bn.prime_fasttest?(checks, trial_div) => true | false
1043  *
1044  * Performs a Miller-Rabin primality test. This is same as #prime? except this
1045  * first attempts trial divisions with some small primes.
1046  *
1047  * === Parameters
1048  * * _checks_ - integer
1049  * * _trial_div_ - boolean
1050  */
1051 static VALUE
ossl_bn_is_prime_fasttest(int argc,VALUE * argv,VALUE self)1052 ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self)
1053 {
1054     BIGNUM *bn;
1055     VALUE vchecks, vtrivdiv;
1056     int checks = BN_prime_checks, do_trial_division = 1;
1057 
1058     rb_scan_args(argc, argv, "02", &vchecks, &vtrivdiv);
1059 
1060     if (!NIL_P(vchecks)) {
1061 	checks = NUM2INT(vchecks);
1062     }
1063     GetBN(self, bn);
1064     /* handle true/false */
1065     if (vtrivdiv == Qfalse) {
1066 	do_trial_division = 0;
1067     }
1068     switch (BN_is_prime_fasttest_ex(bn, checks, ossl_bn_ctx, do_trial_division, NULL)) {
1069     case 1:
1070 	return Qtrue;
1071     case 0:
1072 	return Qfalse;
1073     default:
1074 	ossl_raise(eBNError, NULL);
1075     }
1076     /* not reachable */
1077     return Qnil;
1078 }
1079 
1080 /*
1081  * INIT
1082  * (NOTE: ordering of methods is the same as in 'man bn')
1083  */
1084 void
Init_ossl_bn(void)1085 Init_ossl_bn(void)
1086 {
1087 #if 0
1088     mOSSL = rb_define_module("OpenSSL");
1089     eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
1090 #endif
1091 
1092     if (!(ossl_bn_ctx = BN_CTX_new())) {
1093 	ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX");
1094     }
1095 
1096     eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError);
1097 
1098     cBN = rb_define_class_under(mOSSL, "BN", rb_cObject);
1099 
1100     rb_define_alloc_func(cBN, ossl_bn_alloc);
1101     rb_define_method(cBN, "initialize", ossl_bn_initialize, -1);
1102 
1103     rb_define_method(cBN, "initialize_copy", ossl_bn_copy, 1);
1104     rb_define_method(cBN, "copy", ossl_bn_copy, 1);
1105 
1106     /* swap (=coerce?) */
1107 
1108     rb_define_method(cBN, "num_bytes", ossl_bn_num_bytes, 0);
1109     rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0);
1110     /* num_bits_word */
1111 
1112     rb_define_method(cBN, "+@", ossl_bn_uplus, 0);
1113     rb_define_method(cBN, "-@", ossl_bn_uminus, 0);
1114 
1115     rb_define_method(cBN, "+", ossl_bn_add, 1);
1116     rb_define_method(cBN, "-", ossl_bn_sub, 1);
1117     rb_define_method(cBN, "*", ossl_bn_mul, 1);
1118     rb_define_method(cBN, "sqr", ossl_bn_sqr, 0);
1119     rb_define_method(cBN, "/", ossl_bn_div, 1);
1120     rb_define_method(cBN, "%", ossl_bn_mod, 1);
1121     /* nnmod */
1122 
1123     rb_define_method(cBN, "mod_add", ossl_bn_mod_add, 2);
1124     rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2);
1125     rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2);
1126     rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1);
1127     rb_define_method(cBN, "**", ossl_bn_exp, 1);
1128     rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2);
1129     rb_define_method(cBN, "gcd", ossl_bn_gcd, 1);
1130 
1131     /* add_word
1132      * sub_word
1133      * mul_word
1134      * div_word
1135      * mod_word */
1136 
1137     rb_define_method(cBN, "cmp", ossl_bn_cmp, 1);
1138     rb_define_alias(cBN, "<=>", "cmp");
1139     rb_define_method(cBN, "ucmp", ossl_bn_ucmp, 1);
1140     rb_define_method(cBN, "eql?", ossl_bn_eql, 1);
1141     rb_define_method(cBN, "hash", ossl_bn_hash, 0);
1142     rb_define_method(cBN, "==", ossl_bn_eq, 1);
1143     rb_define_alias(cBN, "===", "==");
1144     rb_define_method(cBN, "zero?", ossl_bn_is_zero, 0);
1145     rb_define_method(cBN, "one?", ossl_bn_is_one, 0);
1146     /* is_word */
1147     rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0);
1148     rb_define_method(cBN, "negative?", ossl_bn_is_negative, 0);
1149 
1150     /* zero
1151      * one
1152      * value_one - DON'T IMPL.
1153      * set_word
1154      * get_word */
1155 
1156     rb_define_singleton_method(cBN, "rand", ossl_bn_s_rand, -1);
1157     rb_define_singleton_method(cBN, "pseudo_rand", ossl_bn_s_pseudo_rand, -1);
1158     rb_define_singleton_method(cBN, "rand_range", ossl_bn_s_rand_range, 1);
1159     rb_define_singleton_method(cBN, "pseudo_rand_range", ossl_bn_s_pseudo_rand_range, 1);
1160 
1161     rb_define_singleton_method(cBN, "generate_prime", ossl_bn_s_generate_prime, -1);
1162     rb_define_method(cBN, "prime?", ossl_bn_is_prime, -1);
1163     rb_define_method(cBN, "prime_fasttest?", ossl_bn_is_prime_fasttest, -1);
1164 
1165     rb_define_method(cBN, "set_bit!", ossl_bn_set_bit, 1);
1166     rb_define_method(cBN, "clear_bit!", ossl_bn_clear_bit, 1);
1167     rb_define_method(cBN, "bit_set?", ossl_bn_is_bit_set, 1);
1168     rb_define_method(cBN, "mask_bits!", ossl_bn_mask_bits, 1);
1169     rb_define_method(cBN, "<<", ossl_bn_lshift, 1);
1170     rb_define_method(cBN, ">>", ossl_bn_rshift, 1);
1171     rb_define_method(cBN, "lshift!", ossl_bn_self_lshift, 1);
1172     rb_define_method(cBN, "rshift!", ossl_bn_self_rshift, 1);
1173     /* lshift1 - DON'T IMPL. */
1174     /* rshift1 - DON'T IMPL. */
1175 
1176     /*
1177      * bn2bin
1178      * bin2bn
1179      * bn2hex
1180      * bn2dec
1181      * hex2bn
1182      * dec2bn - all these are implemented in ossl_bn_initialize, and ossl_bn_to_s
1183      * print - NOT IMPL.
1184      * print_fp - NOT IMPL.
1185      * bn2mpi
1186      * mpi2bn
1187      */
1188     rb_define_method(cBN, "to_s", ossl_bn_to_s, -1);
1189     rb_define_method(cBN, "to_i", ossl_bn_to_i, 0);
1190     rb_define_alias(cBN, "to_int", "to_i");
1191     rb_define_method(cBN, "to_bn", ossl_bn_to_bn, 0);
1192     rb_define_method(cBN, "coerce", ossl_bn_coerce, 1);
1193 
1194     /*
1195      * TODO:
1196      * But how to: from_bin, from_mpi? PACK?
1197      * to_bin
1198      * to_mpi
1199      */
1200 
1201     rb_define_method(cBN, "mod_inverse", ossl_bn_mod_inverse, 1);
1202 
1203     /* RECiProcal
1204      * MONTgomery */
1205 }
1206