diff --git a/ChangeLog b/ChangeLog index 862edb82cb..051e67102a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Mon Jun 18 08:47:54 2007 Technorama Ltd. + * ext/openssl/{extconf.rb,ossl_ssl_session.c}: + Fix ruby-Bugs-11513. + + * ext/openssl/ossl_pkey_ec.c + New methods EC::Point.[eql,make_affine!,invert!,on_curve?,infinity?] + By default output the same key form as the openssl command. + + * ext/openssl/ossl_rand.c + New method Random.status? + + * test/openssl/test_ec.rb + New tests. + Mon Jun 18 17:04:07 2007 Nobuyoshi Nakada * eval_load.c (rb_require_safe, ruby_init_ext): load with ruby level diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 9356853e1d..37053848ef 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -96,6 +96,7 @@ have_func("X509_CRL_sort") have_func("X509_STORE_get_ex_data") have_func("X509_STORE_set_ex_data") have_func("OBJ_NAME_do_all_sorted") +have_func("SSL_SESSION_get_id") have_func("OPENSSL_cleanse") if try_compile("#define FOO(a, ...) foo(a, ##__VA_ARGS__)\n int x(){FOO(1);FOO(1,2);FOO(1,2,3);}\n") $defs.push("-DHAVE_VA_ARGS_MACRO") diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 18a1db1022..c3a15e722b 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -209,6 +209,9 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL) ossl_raise(eECError, "unable to create curve (%s)\n", name); + + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); } } } @@ -569,23 +572,6 @@ static VALUE ossl_ec_key_to_text(VALUE self) return str; } -static VALUE ossl_ec_key_to_public_key(VALUE self) -{ - EC_KEY *ec; - - VALUE new_obj; - - Require_EC_KEY(self, ec); - - new_obj = rb_obj_alloc(cEC); - -/* BUG: finish .to_public_key */ -rb_notimplement(); - - - return new_obj; -} - /* * call-seq: * key.generate_key => self @@ -664,7 +650,7 @@ static VALUE ossl_ec_key_dh_compute_key(VALUE self, VALUE pubkey) static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data) { EC_KEY *ec; - int buf_len; + unsigned int buf_len; VALUE str; Require_EC_KEY(self, ec); @@ -674,7 +660,7 @@ static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data) ossl_raise(eECError, "Private EC key needed!"); str = rb_str_new(0, ECDSA_size(ec) + 16); - if (ECDSA_sign(0, RSTRING_PTR(data), RSTRING_LEN(data), RSTRING_PTR(str), &buf_len, ec) != 1) + if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LEN(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1) ossl_raise(eECError, "ECDSA_sign"); rb_str_resize(str, buf_len); @@ -696,7 +682,7 @@ static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig) StringValue(data); StringValue(sig); - switch (ECDSA_verify(0, RSTRING_PTR(data), RSTRING_LEN(data), RSTRING_PTR(sig), RSTRING_LEN(sig), ec)) { + switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LEN(data), (unsigned char *) RSTRING_PTR(sig), RSTRING_LEN(sig), ec)) { case 1: return Qtrue; case 0: return Qfalse; default: break; @@ -788,15 +774,18 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) BIO_free(in); if (!group) { - const char *name = STR2CSTR(arg1); - int nid = OBJ_sn2nid(name); + const char *name = STR2CSTR(arg1); + int nid = OBJ_sn2nid(name); - if (nid == NID_undef) - ossl_raise(eEC_GROUP, "unknown curve name (%s)", name); + if (nid == NID_undef) + ossl_raise(eEC_GROUP, "unknown curve name (%s)", name); group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) ossl_raise(eEC_GROUP, "unable to create curve (%s)", name); + + EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); + EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); } } @@ -836,6 +825,23 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* call-seq: + * group1 == group2 => true | false + * + */ +static VALUE ossl_ec_group_eql(VALUE a, VALUE b) +{ + EC_GROUP *group1 = NULL, *group2 = NULL; + + Require_EC_GROUP(a, group1); + SafeRequire_EC_GROUP(b, group2); + + if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1) + return Qfalse; + + return Qtrue; +} + /* call-seq: * group.generator => ec_point * @@ -1287,6 +1293,133 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) return self; } +/* + * call-seq: + * point1 == point2 => true | false + * + */ +static VALUE ossl_ec_point_eql(VALUE a, VALUE b) +{ + EC_POINT *point1, *point2; + VALUE group_v1 = rb_iv_get(a, "@group"); + VALUE group_v2 = rb_iv_get(b, "@group"); + const EC_GROUP *group; + + if (ossl_ec_group_eql(group_v1, group_v2) == Qfalse) + return Qfalse; + + Require_EC_POINT(a, point1); + SafeRequire_EC_POINT(b, point2); + SafeRequire_EC_GROUP(group_v1, group); + + if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1) + return Qfalse; + + return Qtrue; +} + +/* + * call-seq: + * point.infinity? => true | false + * + */ +static VALUE ossl_ec_point_is_at_infinity(VALUE self) +{ + EC_POINT *point; + VALUE group_v = rb_iv_get(self, "@group"); + const EC_GROUP *group; + + Require_EC_POINT(self, point); + SafeRequire_EC_GROUP(group_v, group); + + switch (EC_POINT_is_at_infinity(group, point)) { + case 1: return Qtrue; + case 0: return Qfalse; + default: ossl_raise(cEC_POINT, "EC_POINT_is_at_infinity"); + } +} + +/* + * call-seq: + * point.on_curve? => true | false + * + */ +static VALUE ossl_ec_point_is_on_curve(VALUE self) +{ + EC_POINT *point; + VALUE group_v = rb_iv_get(self, "@group"); + const EC_GROUP *group; + + Require_EC_POINT(self, point); + SafeRequire_EC_GROUP(group_v, group); + + switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) { + case 1: return Qtrue; + case 0: return Qfalse; + default: ossl_raise(cEC_POINT, "EC_POINT_is_on_curve"); + } +} + +/* + * call-seq: + * point.make_affine! => self + * + */ +static VALUE ossl_ec_point_make_affine(VALUE self) +{ + EC_POINT *point; + VALUE group_v = rb_iv_get(self, "@group"); + const EC_GROUP *group; + + Require_EC_POINT(self, point); + SafeRequire_EC_GROUP(group_v, group); + + if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1) + ossl_raise(cEC_POINT, "EC_POINT_make_affine"); + + return self; +} + +/* + * call-seq: + * point.invert! => self + * + */ +static VALUE ossl_ec_point_invert(VALUE self) +{ + EC_POINT *point; + VALUE group_v = rb_iv_get(self, "@group"); + const EC_GROUP *group; + + Require_EC_POINT(self, point); + SafeRequire_EC_GROUP(group_v, group); + + if (EC_POINT_invert(group, point, ossl_bn_ctx) != 1) + ossl_raise(cEC_POINT, "EC_POINT_invert"); + + return self; +} + +/* + * call-seq: + * point.set_to_infinity! => self + * + */ +static VALUE ossl_ec_point_set_to_infinity(VALUE self) +{ + EC_POINT *point; + VALUE group_v = rb_iv_get(self, "@group"); + const EC_GROUP *group; + + Require_EC_POINT(self, point); + SafeRequire_EC_GROUP(group_v, group); + + if (EC_POINT_set_to_infinity(group, point) != 1) + ossl_raise(cEC_POINT, "EC_POINT_set_to_infinity"); + + return self; +} + /* * call-seq: * point.to_bn => OpenSSL::BN @@ -1350,6 +1483,10 @@ void Init_ossl_ec() ID_compressed = rb_intern("compressed"); ID_hybrid = rb_intern("hybrid"); +#ifdef OPENSSL_EC_NAMED_CURVE + rb_define_const(cEC, "NAMED_CURVE", ULONG2NUM(OPENSSL_EC_NAMED_CURVE)); +#endif + rb_define_singleton_method(cEC, "builtin_curves", ossl_s_builtin_curves, 0); rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1); @@ -1373,7 +1510,7 @@ void Init_ossl_ec() rb_define_method(cEC, "generate_key", ossl_ec_key_generate_key, 0); rb_define_method(cEC, "check_key", ossl_ec_key_check_key, 0); - rb_define_method(cEC, "dh_compute_key", ossl_ec_key_dh_compute_key, 2); + rb_define_method(cEC, "dh_compute_key", ossl_ec_key_dh_compute_key, 1); rb_define_method(cEC, "dsa_sign_asn1", ossl_ec_key_dsa_sign_asn1, 1); rb_define_method(cEC, "dsa_verify_asn1", ossl_ec_key_dsa_verify_asn1, 2); /* do_sign/do_verify */ @@ -1385,6 +1522,8 @@ void Init_ossl_ec() rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); rb_define_method(cEC_GROUP, "initialize", ossl_ec_group_initialize, -1); + rb_define_method(cEC_GROUP, "eql?", ossl_ec_group_eql, 1); + rb_define_alias(cEC_GROUP, "==", "eql?"); /* copy/dup/cmp */ rb_define_method(cEC_GROUP, "generator", ossl_ec_group_get_generator, 0); @@ -1419,6 +1558,14 @@ void Init_ossl_ec() rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc); rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1); 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?"); + + rb_define_method(cEC_POINT, "infinity?", ossl_ec_point_is_at_infinity, 0); + rb_define_method(cEC_POINT, "on_curve?", ossl_ec_point_is_on_curve, 0); + 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 */ rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, 0); diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index ca8c9a5ae9..3b6eaf3f47 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -135,6 +135,18 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) return Qtrue; } +/* + * call-seq: + * status? => true | false + * + * Return true if the PRNG has been seeded with enough data, false otherwise. + */ +static VALUE +ossl_rand_status(VALUE self) +{ + return RAND_status() ? Qtrue : Qfalse; +} + #define DEFMETH(class, name, func, argc) \ rb_define_method(class, name, func, argc); \ rb_define_singleton_method(class, name, func, argc); @@ -160,5 +172,6 @@ Init_ossl_rand() DEFMETH(mRandom, "pseudo_bytes", ossl_rand_pseudo_bytes, 1); DEFMETH(mRandom, "egd", ossl_rand_egd, 1); DEFMETH(mRandom, "egd_bytes", ossl_rand_egd_bytes, 2); + DEFMETH(mRandom, "status?", ossl_rand_status, 0) } diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index b21cd1535e..b347f2c1b2 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -157,6 +157,7 @@ static VALUE ossl_ssl_session_get_timeout(VALUE self) SSLSESSION_SET_TIME(time) SSLSESSION_SET_TIME(timeout) +#ifdef HAVE_SSL_SESSION_GET_ID /* * call-seq: * session.id -> aString @@ -175,6 +176,7 @@ static VALUE ossl_ssl_session_get_id(VALUE self) return rb_str_new(p, i); } +#endif /* * call-seq: @@ -285,7 +287,9 @@ void Init_ossl_ssl_session(void) rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); +#ifdef HAVE_SSL_SESSION_GET_ID rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); +#endif rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); diff --git a/test/openssl/test_ec.rb b/test/openssl/test_ec.rb new file mode 100644 index 0000000000..671901ca36 --- /dev/null +++ b/test/openssl/test_ec.rb @@ -0,0 +1,113 @@ +begin + require "openssl" + require File.join(File.dirname(__FILE__), "utils.rb") +rescue LoadError +end +require "test/unit" + +if defined?(OpenSSL::PKey::EC) + +class OpenSSL::TestEC < Test::Unit::TestCase + def setup + @data1 = 'foo' + @data2 = 'bar' * 1000 # data too long for DSA sig + + @group1 = OpenSSL::PKey::EC::Group.new('secp112r1') + @group2 = OpenSSL::PKey::EC::Group.new('sect163k1') + + @key1 = OpenSSL::PKey::EC.new + @key1.group = @group1 + @key1.generate_key + + @key2 = OpenSSL::PKey::EC.new(@group2.curve_name) + @key2.generate_key + + @groups = [@group1, @group2] + @keys = [@key1, @key2] + end + + def compare_keys(k1, k2) + assert_equal(k1.to_pem, k2.to_pem) + end + + def test_curve_names + @groups.each_with_index do |group, idx| + key = @keys[idx] + assert_equal(group.curve_name, key.group.curve_name) + end + end + + def test_check_key + for key in @keys + assert_equal(key.check_key, true) + assert_equal(key.private_key?, true) + assert_equal(key.public_key?, true) + end + end + + def test_encoding + for group in @groups + for meth in [:to_der, :to_pem] + txt = group.send(meth) + gr = OpenSSL::PKey::EC::Group.new(txt) + assert_equal(txt, gr.send(meth)) + + assert_equal(group.generator.to_bn, gr.generator.to_bn) + assert_equal(group.cofactor, gr.cofactor) + assert_equal(group.order, gr.order) + assert_equal(group.seed, gr.seed) + assert_equal(group.degree, gr.degree) + end + end + + for key in @keys + group = key.group + + for meth in [:to_der, :to_pem] + txt = key.send(meth) + assert_equal(txt, OpenSSL::PKey::EC.new(txt).send(meth)) + end + + bn = key.public_key.to_bn + assert_equal(bn, OpenSSL::PKey::EC::Point.new(group, bn).to_bn) + end + end + + def test_set_keys + for key in @keys + k = OpenSSL::PKey::EC.new + k.group = key.group + k.private_key = key.private_key + k.public_key = key.public_key + + compare_keys(key, k) + end + end + + def test_dsa_sign_verify + for key in @keys + sig = key.dsa_sign_asn1(@data1) + assert_equal(key.dsa_verify_asn1(@data1, sig), true) + + assert_raises(OpenSSL::PKey::ECError) { key.dsa_sign_asn1(@data2) } + end + end + + def test_dh_compute_key + for key in @keys + k = OpenSSL::PKey::EC.new(key.group) + k.generate_key + + puba = key.public_key + pubb = k.public_key + a = key.dh_compute_key(pubb) + b = k.dh_compute_key(puba) + assert_equal(a, b) + end + end + +# test Group: asn1_flag, point_conversion + +end + +end