diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index d5e0470ce..de5a0a191 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -174,6 +174,11 @@ def find_openssl_library have_func("EVP_PBE_scrypt") have_func("SSL_CTX_set_post_handshake_auth") +# added in 1.1.1 +have_func("EC_POINT_get_affine_coordinates") +have_func("EC_POINT_set_affine_coordinates") +have_func("EC_POINT_set_compressed_coordinates") + Logging::message "=== Checking done. ===\n" create_header diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 025307894..da8cf2a57 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -119,8 +119,7 @@ integer_to_bnptr(VALUE obj, BIGNUM *orig) return bn; } -static VALUE -try_convert_to_bn(VALUE obj) +VALUE ossl_try_convert_to_bn(VALUE obj) { BIGNUM *bn; VALUE newobj = Qnil; @@ -142,7 +141,7 @@ ossl_bn_value_ptr(volatile VALUE *ptr) VALUE tmp; BIGNUM *bn; - tmp = try_convert_to_bn(*ptr); + tmp = ossl_try_convert_to_bn(*ptr); if (NIL_P(tmp)) ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); GetBN(tmp, bn); @@ -449,6 +448,22 @@ BIGNUM_BOOL1(is_one) */ BIGNUM_BOOL1(is_odd) +static VALUE +ossl_bn_is_even(VALUE self) +{ + VALUE is_odd; + + is_odd = ossl_bn_is_odd(self); + if (is_odd == Qtrue) { + return Qfalse; + } + else if (is_odd == Qfalse) { + return Qtrue; + } + + rb_raise(eBNError, "ossl_bn_is_odd didn't return a boolean"); +} + /* * call-seq: * bn.negative? => true | false @@ -464,6 +479,21 @@ ossl_bn_is_negative(VALUE self) return BN_is_negative(bn) ? Qtrue : Qfalse; } +/* + * call-seq: + * bn.positive? => true | false + */ +static VALUE +ossl_bn_is_positive(VALUE self) +{ + BIGNUM *bn; + + GetBN(self, bn); + if (BN_is_zero(bn)) + return Qfalse; + return BN_is_negative(bn) ? Qfalse : Qtrue; +} + #define BIGNUM_1c(func) \ static VALUE \ ossl_bn_##func(VALUE self) \ @@ -953,10 +983,11 @@ ossl_bn_copy(VALUE self, VALUE other) /* * call-seq: - * +bn -> aBN + * bn.dup -> aBN + * +bn -> aBN */ static VALUE -ossl_bn_uplus(VALUE self) +ossl_bn_dup(VALUE self) { VALUE obj; BIGNUM *bn1, *bn2; @@ -1006,7 +1037,7 @@ ossl_bn_abs(VALUE self) return ossl_bn_uminus(self); } else { - return ossl_bn_uplus(self); + return ossl_bn_dup(self); } } @@ -1051,7 +1082,7 @@ ossl_bn_eq(VALUE self, VALUE other) BIGNUM *bn1, *bn2; GetBN(self, bn1); - other = try_convert_to_bn(other); + other = ossl_try_convert_to_bn(other); if (NIL_P(other)) return Qfalse; GetBN(other, bn2); @@ -1217,6 +1248,7 @@ Init_ossl_bn(void) rb_define_method(cBN, "initialize_copy", ossl_bn_copy, 1); rb_define_method(cBN, "copy", ossl_bn_copy, 1); + rb_define_method(cBN, "dup", ossl_bn_dup, 0); /* swap (=coerce?) */ @@ -1224,7 +1256,7 @@ Init_ossl_bn(void) rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0); /* num_bits_word */ - rb_define_method(cBN, "+@", ossl_bn_uplus, 0); + rb_define_alias(cBN, "+@", "dup"); rb_define_method(cBN, "-@", ossl_bn_uminus, 0); rb_define_method(cBN, "abs", ossl_bn_abs, 0); @@ -1261,7 +1293,9 @@ Init_ossl_bn(void) rb_define_method(cBN, "one?", ossl_bn_is_one, 0); /* is_word */ rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0); + rb_define_method(cBN, "even?", ossl_bn_is_even, 0); rb_define_method(cBN, "negative?", ossl_bn_is_negative, 0); + rb_define_method(cBN, "positive?", ossl_bn_is_positive, 0); /* zero * one diff --git a/ext/openssl/ossl_bn.h b/ext/openssl/ossl_bn.h index 1cc041fc2..6bb4ec18f 100644 --- a/ext/openssl/ossl_bn.h +++ b/ext/openssl/ossl_bn.h @@ -19,6 +19,7 @@ BN_CTX *ossl_bn_ctx_get(void); #define GetBNPtr(obj) ossl_bn_value_ptr(&(obj)) VALUE ossl_bn_new(const BIGNUM *); +VALUE ossl_try_convert_to_bn(VALUE obj); BIGNUM *ossl_bn_value_ptr(volatile VALUE *); void Init_ossl_bn(void); diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index deca4f43b..12eacf59d 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -47,11 +47,13 @@ VALUE eEC_GROUP; VALUE cEC_POINT; VALUE eEC_POINT; -static ID s_GFp, s_GF2m; +static ID id_GFp, id_GF2m; -static ID ID_uncompressed; -static ID ID_compressed; -static ID ID_hybrid; +static ID id_odd, id_even; + +static ID id_uncompressed; +static ID id_compressed; +static ID id_hybrid; static ID id_i_group; @@ -637,10 +639,10 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) const BIGNUM *a = GetBNPtr(arg3); const BIGNUM *b = GetBNPtr(arg4); - if (id == s_GFp) { + if (id == id_GFp) { new_curve = EC_GROUP_new_curve_GFp; #if !defined(OPENSSL_NO_EC2M) - } else if (id == s_GF2m) { + } else if (id == id_GF2m) { new_curve = EC_GROUP_new_curve_GF2m; #endif } else { @@ -922,9 +924,9 @@ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self) form = EC_GROUP_get_point_conversion_form(group); switch (form) { - case POINT_CONVERSION_UNCOMPRESSED: ret = ID_uncompressed; break; - case POINT_CONVERSION_COMPRESSED: ret = ID_compressed; break; - case POINT_CONVERSION_HYBRID: ret = ID_hybrid; break; + case POINT_CONVERSION_UNCOMPRESSED: ret = id_uncompressed; break; + case POINT_CONVERSION_COMPRESSED: ret = id_compressed; break; + case POINT_CONVERSION_HYBRID: ret = id_hybrid; break; default: ossl_raise(eEC_GROUP, "unsupported point conversion form: %d, this module should be updated", form); } @@ -936,11 +938,11 @@ parse_point_conversion_form_symbol(VALUE sym) { ID id = SYM2ID(sym); - if (id == ID_uncompressed) + if (id == id_uncompressed) return POINT_CONVERSION_UNCOMPRESSED; - else if (id == ID_compressed) + else if (id == id_compressed) return POINT_CONVERSION_COMPRESSED; - else if (id == ID_hybrid) + else if (id == id_hybrid) return POINT_CONVERSION_HYBRID; else ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE @@ -1243,6 +1245,14 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other) return self; } +static VALUE +ossl_ec_point_dup(VALUE self) +{ + VALUE other; + other = ossl_ec_point_alloc(eEC_POINT); + return ossl_ec_point_initialize_copy(other, self); +} + /* * call-seq: * point1.eql?(point2) => true | false @@ -1364,6 +1374,118 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self) return self; } +#ifdef HAVE_EC_POINT_GET_AFFINE_COORDINATES + +/* + * call-seq: + * point.affine_coords => [ xBN, yBN ] + */ +static VALUE +ossl_ec_point_get_affine_coords(VALUE self) +{ + VALUE x, y, result; + EC_POINT *point; + BIGNUM *xBN, *yBN; + const EC_GROUP *group; + + GetECPoint(self, point); + GetECPointGroup(self, group); + + if (point == NULL || group == NULL) { + rb_raise(eEC_POINT, "unable to get point and group"); + } + + x = ossl_bn_new(NULL); + y = ossl_bn_new(NULL); + + xBN = GetBNPtr(x); + yBN = GetBNPtr(y); + + if (!EC_POINT_get_affine_coordinates(group, point, xBN, yBN, NULL)) { + ossl_raise(eEC_POINT, "EC_POINT_get_affine_coordinates"); + } + + result = rb_ary_new_from_args(2, x, y) + + return result; +} +#endif + +#ifdef HAVE_EC_POINT_SET_AFFINE_COORDINATES +/* + * call-seq: + * point.affine_coords = [ xBN, yBN ] + */ +static VALUE +ossl_ec_point_set_affine_coords(VALUE self, VALUE coords) +{ + VALUE x, y; + EC_POINT *point; + BIGNUM *xBN, *yBN; + const EC_GROUP *group; + + GetECPoint(self, point); + GetECPointGroup(self, group); + + if (point == NULL || group == NULL) { + rb_raise(eEC_POINT, "unable to get point and group"); + } + + if (TYPE(coords) != T_ARRAY || RARRAY_LEN(coords) != 2) { + rb_raise(eEC_POINT, "argument must be an array of [ x, y ]"); + } + + x = ossl_try_convert_to_bn(RARRAY_AREF(coords, 0)); + y = ossl_try_convert_to_bn(RARRAY_AREF(coords, 1)); + + xBN = GetBNPtr(x); + yBN = GetBNPtr(y); + + if (!EC_POINT_set_affine_coordinates(group, point, xBN, yBN, NULL)) { + ossl_raise(eEC_POINT, "EC_POINT_set_affine_coordinates"); + } + + return self; +} +#endif + +#ifdef HAVE_EC_POINT_SET_COMPRESSED_COORDINATES +/* + * call-seq: + * point.set_compressed_coords x, y_bit + */ +static VALUE +ossl_ec_point_set_compressed_coords(VALUE self, VALUE x, VALUE y_bit) +{ + EC_POINT *point; + BIGNUM *xBN; + const EC_GROUP *group; + int y; + + GetECPoint(self, point); + GetECPointGroup(self, group); + + if (point == NULL || group == NULL) { + rb_raise(eEC_POINT, "unable to get point and group"); + } + + if (RB_INTEGER_TYPE_P(y_bit)) { + y = NUM2INT(y_bit); + } else { + rb_raise(eEC_POINT, "y_bit must be Integer"); + } + + x = ossl_try_convert_to_bn(x); + xBN = GetBNPtr(x); + + if (!EC_POINT_set_compressed_coordinates(group, point, xBN, y, NULL)) { + ossl_raise(eEC_POINT, "EC_POINT_set_affine_coordinates"); + } + + return self; +} +#endif + /* * call-seq: * point.to_octet_string(conversion_form) -> String @@ -1554,12 +1676,15 @@ void Init_ossl_ec(void) eEC_GROUP = rb_define_class_under(cEC_GROUP, "Error", eOSSLError); eEC_POINT = rb_define_class_under(cEC_POINT, "Error", eOSSLError); - s_GFp = rb_intern("GFp"); - s_GF2m = rb_intern("GF2m"); + id_GFp = rb_intern("GFp"); + id_GF2m = rb_intern("GF2m"); - ID_uncompressed = rb_intern("uncompressed"); - ID_compressed = rb_intern("compressed"); - ID_hybrid = rb_intern("hybrid"); + id_even = rb_intern("even"); + id_odd = rb_intern("odd"); + + id_uncompressed = rb_intern("uncompressed"); + id_compressed = rb_intern("compressed"); + id_hybrid = rb_intern("hybrid"); rb_define_const(cEC, "NAMED_CURVE", INT2NUM(OPENSSL_EC_NAMED_CURVE)); #if defined(OPENSSL_EC_EXPLICIT_CURVE) @@ -1643,7 +1768,10 @@ void Init_ossl_ec(void) rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc); rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1); rb_define_method(cEC_POINT, "initialize_copy", ossl_ec_point_initialize_copy, 1); + rb_define_method(cEC_POINT, "dup", ossl_ec_point_dup, 0); + rb_attr(cEC_POINT, rb_intern("group"), 1, 0, 0); + rb_define_method(cEC_POINT, "eql?", ossl_ec_point_eql, 1); rb_define_alias(cEC_POINT, "==", "eql?"); @@ -1652,7 +1780,16 @@ void Init_ossl_ec(void) rb_define_method(cEC_POINT, "make_affine!", ossl_ec_point_make_affine, 0); rb_define_method(cEC_POINT, "invert!", ossl_ec_point_invert, 0); rb_define_method(cEC_POINT, "set_to_infinity!", ossl_ec_point_set_to_infinity, 0); -/* all the other methods */ + +#ifdef HAVE_EC_POINT_GET_AFFINE_COORDINATES + rb_define_method(cEC_POINT, "affine_coords", ossl_ec_point_get_affine_coords, 0); +#endif +#ifdef HAVE_EC_POINT_SET_AFFINE_COORDINATES + rb_define_method(cEC_POINT, "affine_coords=", ossl_ec_point_set_affine_coords, 1); +#endif +#ifdef HAVE_EC_POINT_SET_COMPRESSED_COORDINATES + rb_define_method(cEC_POINT, "set_compressed_coords", ossl_ec_point_set_compressed_coords, 2); +#endif rb_define_method(cEC_POINT, "to_octet_string", ossl_ec_point_to_octet_string, 1); rb_define_method(cEC_POINT, "add", ossl_ec_point_add, 1); diff --git a/test/openssl/test_bn.rb b/test/openssl/test_bn.rb index 1ed4bbeec..f47583717 100644 --- a/test/openssl/test_bn.rb +++ b/test/openssl/test_bn.rb @@ -103,12 +103,23 @@ def test_odd_p assert_equal(false, 2.to_bn.odd?) end + def test_even_p + assert_equal(false, 1.to_bn.even?) + assert_equal(true, 2.to_bn.even?) + end + def test_negative_p assert_equal(false, 0.to_bn.negative?) assert_equal(false, @e1.negative?) assert_equal(true, @e2.negative?) end + def test_positive_p + assert_equal(false, 0.to_bn.positive?) + assert_equal(true, @e1.positive?) + assert_equal(false, @e2.positive?) + end + def test_sqr assert_equal(1, 1.to_bn.sqr) assert_equal(100, 10.to_bn.sqr) diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb index 80ae9ffdf..6df2f4d9e 100644 --- a/test/openssl/test_pkey_ec.rb +++ b/test/openssl/test_pkey_ec.rb @@ -261,6 +261,54 @@ def test_ec_point assert_equal point.to_octet_string(:uncompressed), point2.to_octet_string(:uncompressed) + point2_dup = point2.dup + assert_equal point2, point2_dup + assert_equal false, point2.equal?(point2_dup) + + point2_string = point2.to_octet_string(:uncompressed) + assert_equal 65, point2_string.size + assert_equal point2_string[0], "\x04" + point2_x_str = point2_string[1..32] + point2_y_str = point2_string[33..-1] + + assert_not_equal(nil, point2_x_str) + assert_not_equal(nil, point2_y_str) + assert_equal(32, point2_x_str.size) + assert_equal(32, point2_y_str.size) + + point2_x_bn = OpenSSL::BN.new point2_x_str, 2 + point2_y_bn = OpenSSL::BN.new point2_y_str, 2 + + if point2.respond_to? :affine_coords + point2_x, point2_y = point2.affine_coords + + assert_equal(point2_x_bn, point2_x) + assert_equal(point2_y_bn, point2_y) + end + + if point2.respond_to? :affine_coords= + point2_explicit_set = OpenSSL::PKey::EC::Point.new group + point2_explicit_set.affine_coords = [point2_x_bn, point2_y_bn] + + assert_equal(point2, point2_explicit_set) + assert_equal(point2_string, point2_explicit_set.to_octet_string(:uncompressed)) + end + + if point2.respond_to? :set_compressed_coords + point2_x, point2_y = point2.affine_coords + + y_bit = point2_y.even? ? 0 : 1 + inverse_y_bit = point2_y.odd? ? 0 : 1 + + point2_from_compressed = OpenSSL::PKey::EC::Point.new group + point2_from_compressed.set_compressed_coords point2_x, y_bit + assert_equal(point2, point2_from_compressed) + + point2_from_compressed_inverse = OpenSSL::PKey::EC::Point.new group + point2_from_compressed_inverse.set_compressed_coords point2_x, inverse_y_bit + assert_not_equal(point2, point2_from_compressed_inverse) + end + point3 = OpenSSL::PKey::EC::Point.new(group, point.to_octet_string(:uncompressed)) assert_equal point, point3