lib/extensions/openssl/ext/ossl_pkey_ec.c in rhodes-5.5.18 vs lib/extensions/openssl/ext/ossl_pkey_ec.c in rhodes-6.0.11

- old
+ new

@@ -18,76 +18,78 @@ #define EXPORT_PEM 0 #define EXPORT_DER 1 +static const rb_data_type_t ossl_ec_group_type; +static const rb_data_type_t ossl_ec_point_type; #define GetPKeyEC(obj, pkey) do { \ - GetPKey(obj, pkey); \ - if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) { \ + GetPKey((obj), (pkey)); \ + if (EVP_PKEY_type((pkey)->type) != EVP_PKEY_EC) { \ ossl_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!"); \ } \ } while (0) #define SafeGet_ec_group(obj, group) do { \ - OSSL_Check_Kind(obj, cEC_GROUP); \ - Data_Get_Struct(obj, ossl_ec_group, group); \ + OSSL_Check_Kind((obj), cEC_GROUP); \ + TypedData_Get_Struct((obj), ossl_ec_group, &ossl_ec_group_type, (group)); \ } while(0) #define Get_EC_KEY(obj, key) do { \ EVP_PKEY *pkey; \ - GetPKeyEC(obj, pkey); \ - key = pkey->pkey.ec; \ + GetPKeyEC((obj), pkey); \ + (key) = pkey->pkey.ec; \ } while(0) #define Require_EC_KEY(obj, key) do { \ - Get_EC_KEY(obj, key); \ - if (key == NULL) \ - rb_raise(eECError, "EC_KEY is not initialized"); \ + Get_EC_KEY((obj), (key)); \ + if ((key) == NULL) \ + ossl_raise(eECError, "EC_KEY is not initialized"); \ } while(0) #define SafeRequire_EC_KEY(obj, key) do { \ - OSSL_Check_Kind(obj, cEC); \ - Require_EC_KEY(obj, key); \ + OSSL_Check_Kind((obj), cEC); \ + Require_EC_KEY((obj), (key)); \ } while (0) #define Get_EC_GROUP(obj, g) do { \ ossl_ec_group *ec_group; \ - Data_Get_Struct(obj, ossl_ec_group, ec_group); \ + TypedData_Get_Struct((obj), ossl_ec_group, &ossl_ec_group_type, ec_group); \ if (ec_group == NULL) \ - rb_raise(eEC_GROUP, "missing ossl_ec_group structure"); \ - g = ec_group->group; \ + ossl_raise(eEC_GROUP, "missing ossl_ec_group structure"); \ + (g) = ec_group->group; \ } while(0) #define Require_EC_GROUP(obj, group) do { \ - Get_EC_GROUP(obj, group); \ - if (group == NULL) \ - rb_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ + Get_EC_GROUP((obj), (group)); \ + if ((group) == NULL) \ + ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ } while(0) #define SafeRequire_EC_GROUP(obj, group) do { \ - OSSL_Check_Kind(obj, cEC_GROUP); \ - Require_EC_GROUP(obj, group); \ + OSSL_Check_Kind((obj), cEC_GROUP); \ + Require_EC_GROUP((obj), (group)); \ } while(0) #define Get_EC_POINT(obj, p) do { \ ossl_ec_point *ec_point; \ - Data_Get_Struct(obj, ossl_ec_point, ec_point); \ + TypedData_Get_Struct((obj), ossl_ec_point, &ossl_ec_point_type, ec_point); \ if (ec_point == NULL) \ - rb_raise(eEC_POINT, "missing ossl_ec_point structure"); \ - p = ec_point->point; \ + ossl_raise(eEC_POINT, "missing ossl_ec_point structure"); \ + (p) = ec_point->point; \ } while(0) #define Require_EC_POINT(obj, point) do { \ - Get_EC_POINT(obj, point); \ - if (point == NULL) \ - rb_raise(eEC_POINT, "EC_POINT is not initialized"); \ + Get_EC_POINT((obj), (point)); \ + if ((point) == NULL) \ + ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ } while(0) #define SafeRequire_EC_POINT(obj, point) do { \ - OSSL_Check_Kind(obj, cEC_POINT); \ - Require_EC_POINT(obj, point); \ + OSSL_Check_Kind((obj), cEC_POINT); \ + Require_EC_POINT((obj), (point)); \ } while(0) VALUE cEC; VALUE eECError; VALUE cEC_GROUP; @@ -112,18 +114,19 @@ VALUE obj; if (!ec) { return Qfalse; } + obj = NewPKey(klass); if (!(pkey = EVP_PKEY_new())) { return Qfalse; } if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) { EVP_PKEY_free(pkey); return Qfalse; } - WrapPKey(klass, obj, pkey); + SetPKey(obj, pkey); return obj; } VALUE ossl_ec_new(EVP_PKEY *pkey) @@ -131,14 +134,15 @@ VALUE obj; if (!pkey) { obj = ec_instance(cEC, EC_KEY_new()); } else { + obj = NewPKey(cEC); if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) { ossl_raise(rb_eTypeError, "Not a EC key!"); } - WrapPKey(cEC, obj, pkey); + SetPKey(obj, pkey); } if (obj == Qfalse) { ossl_raise(eECError, NULL); } @@ -150,10 +154,11 @@ * OpenSSL::PKey::EC.new() * OpenSSL::PKey::EC.new(ec_key) * OpenSSL::PKey::EC.new(ec_group) * OpenSSL::PKey::EC.new("secp112r1") * OpenSSL::PKey::EC.new(pem_string) + * OpenSSL::PKey::EC.new(pem_string [, pwd]) * OpenSSL::PKey::EC.new(der_string) * * See the OpenSSL documentation for: * EC_KEY_* */ @@ -161,14 +166,15 @@ { EVP_PKEY *pkey; EC_KEY *ec = NULL; VALUE arg, pass; VALUE group = Qnil; + char *passwd = NULL; GetPKey(self, pkey); if (pkey->pkey.ec) - rb_raise(eECError, "EC_KEY already initialized"); + ossl_raise(eECError, "EC_KEY already initialized"); rb_scan_args(argc, argv, "02", &arg, &pass); if (NIL_P(arg)) { ec = EC_KEY_new(); @@ -182,24 +188,24 @@ ec = EC_KEY_new(); group = arg; } else { BIO *in = ossl_obj2bio(arg); - ec = PEM_read_bio_ECPrivateKey(in, NULL, NULL, NULL); + if (!NIL_P(pass)) { + passwd = StringValuePtr(pass); + } + ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); if (!ec) { - (void)BIO_reset(in); - (void)ERR_get_error(); - ec = PEM_read_bio_EC_PUBKEY(in, NULL, NULL, NULL); + OSSL_BIO_reset(in); + ec = PEM_read_bio_EC_PUBKEY(in, NULL, ossl_pem_passwd_cb, passwd); } if (!ec) { - (void)BIO_reset(in); - (void)ERR_get_error(); + OSSL_BIO_reset(in); ec = d2i_ECPrivateKey_bio(in, NULL); } if (!ec) { - (void)BIO_reset(in); - (void)ERR_get_error(); + OSSL_BIO_reset(in); ec = d2i_EC_PUBKEY_bio(in, NULL); } BIO_free(in); @@ -365,11 +371,11 @@ VALUE obj; const EC_GROUP *group; ossl_ec_point *new_point; obj = rb_obj_alloc(cEC_POINT); - Data_Get_Struct(obj, ossl_ec_point, new_point); + TypedData_Get_Struct(obj, ossl_ec_point, &ossl_ec_point_type, new_point); SafeRequire_EC_GROUP(group_v, group); new_point->point = EC_POINT_dup(point, group); if (new_point->point == NULL) @@ -459,77 +465,64 @@ Require_EC_KEY(self, ec); return (EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse); } -static VALUE ossl_ec_key_to_string(VALUE self, int format) +static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int format) { EC_KEY *ec; BIO *out; int i = -1; int private = 0; -#if 0 /* unused now */ - EVP_CIPHER *cipher = NULL; char *password = NULL; -#endif VALUE str; + const EVP_CIPHER *cipher = NULL; Require_EC_KEY(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) - rb_raise(eECError, "can't export - no public key set"); + ossl_raise(eECError, "can't export - no public key set"); if (EC_KEY_check_key(ec) != 1) ossl_raise(eECError, "can't export - EC_KEY_check_key failed"); if (EC_KEY_get0_private_key(ec)) private = 1; + if (!NIL_P(ciph)) { + cipher = GetCipherPtr(ciph); + if (!NIL_P(pass)) { + StringValue(pass); + if (RSTRING_LENINT(pass) < OSSL_MIN_PWD_LEN) + ossl_raise(eOSSLError, "OpenSSL requires passwords to be at least four characters long"); + password = RSTRING_PTR(pass); + } + } + if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eECError, "BIO_new(BIO_s_mem())"); switch(format) { case EXPORT_PEM: if (private) { -#if 0 /* unused now */ - if (cipher || password) -/* BUG: finish cipher/password key export */ - rb_notimplement(); i = PEM_write_bio_ECPrivateKey(out, ec, cipher, NULL, 0, NULL, password); -#endif - i = PEM_write_bio_ECPrivateKey(out, ec, NULL, NULL, 0, NULL, NULL); } else { -#if 0 /* unused now */ - if (cipher || password) - rb_raise(rb_eArgError, "encryption is not supported when exporting this key type"); -#endif - i = PEM_write_bio_EC_PUBKEY(out, ec); } break; case EXPORT_DER: if (private) { -#if 0 /* unused now */ - if (cipher || password) - rb_raise(rb_eArgError, "encryption is not supported when exporting this key type"); -#endif - i = i2d_ECPrivateKey_bio(out, ec); } else { -#if 0 /* unused now */ - if (cipher || password) - rb_raise(rb_eArgError, "encryption is not supported when exporting this key type"); -#endif - i = i2d_EC_PUBKEY_bio(out, ec); } break; default: BIO_free(out); - rb_raise(rb_eRuntimeError, "unknown format (internal error)"); + ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); } if (i != 1) { BIO_free(out); ossl_raise(eECError, "outlen=%d", i); @@ -540,28 +533,36 @@ return str; } /* * call-seq: - * key.to_pem => String + * key.export([cipher, pass_phrase]) => String + * key.to_pem([cipher, pass_phrase]) => String * - * See the OpenSSL documentation for PEM_write_bio_ECPrivateKey() + * Outputs the EC key in PEM encoding. If +cipher+ and +pass_phrase+ are + * given they will be used to encrypt the key. +cipher+ must be an + * OpenSSL::Cipher::Cipher instance. Note that encryption will only be + * effective for a private key, public keys will always be encoded in plain + * text. + * */ -static VALUE ossl_ec_key_to_pem(VALUE self) +static VALUE ossl_ec_key_export(int argc, VALUE *argv, VALUE self) { - return ossl_ec_key_to_string(self, EXPORT_PEM); + VALUE cipher, passwd; + rb_scan_args(argc, argv, "02", &cipher, &passwd); + return ossl_ec_key_to_string(self, cipher, passwd, EXPORT_PEM); } /* * call-seq: * key.to_der => String * * See the OpenSSL documentation for i2d_ECPrivateKey_bio() */ static VALUE ossl_ec_key_to_der(VALUE self) { - return ossl_ec_key_to_string(self, EXPORT_DER); + return ossl_ec_key_to_string(self, Qnil, Qnil, EXPORT_DER); } /* * call-seq: * key.to_text => String @@ -673,11 +674,11 @@ if (EC_KEY_get0_private_key(ec) == NULL) ossl_raise(eECError, "Private EC key needed!"); str = rb_str_new(0, ECDSA_size(ec) + 16); - if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LEN(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1) + if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1) ossl_raise(eECError, "ECDSA_sign"); rb_str_resize(str, buf_len); return str; @@ -695,32 +696,43 @@ Require_EC_KEY(self, ec); StringValue(data); StringValue(sig); - switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LEN(data), (unsigned char *) RSTRING_PTR(sig), RSTRING_LEN(sig), ec)) { + switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(sig), (int)RSTRING_LEN(sig), ec)) { case 1: return Qtrue; case 0: return Qfalse; default: break; } ossl_raise(eECError, "ECDSA_verify"); + + UNREACHABLE; } -static void ossl_ec_group_free(ossl_ec_group *ec_group) +static void ossl_ec_group_free(void *ptr) { + ossl_ec_group *ec_group = ptr; if (!ec_group->dont_free && ec_group->group) EC_GROUP_clear_free(ec_group->group); ruby_xfree(ec_group); } +static const rb_data_type_t ossl_ec_group_type = { + "OpenSSL/ec_group", + { + 0, ossl_ec_group_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + static VALUE ossl_ec_group_alloc(VALUE klass) { ossl_ec_group *ec_group; VALUE obj; - obj = Data_Make_Struct(klass, ossl_ec_group, 0, ossl_ec_group_free, ec_group); + obj = TypedData_Make_Struct(klass, ossl_ec_group, &ossl_ec_group_type, ec_group); return obj; } /* call-seq: @@ -743,13 +755,13 @@ { VALUE arg1, arg2, arg3, arg4; ossl_ec_group *ec_group; EC_GROUP *group = NULL; - Data_Get_Struct(self, ossl_ec_group, ec_group); + TypedData_Get_Struct(self, ossl_ec_group, &ossl_ec_group_type, ec_group); if (ec_group->group != NULL) - rb_raise(rb_eRuntimeError, "EC_GROUP is already initialized"); + ossl_raise(rb_eRuntimeError, "EC_GROUP is already initialized"); switch (rb_scan_args(argc, argv, "13", &arg1, &arg2, &arg3, &arg4)) { case 1: if (SYMBOL_P(arg1)) { const EC_METHOD *method = NULL; @@ -759,19 +771,21 @@ method = EC_GFp_simple_method(); } else if (id == s_GFp_mont) { method = EC_GFp_mont_method(); } else if (id == s_GFp_nist) { method = EC_GFp_nist_method(); +#if !defined(OPENSSL_NO_EC2M) } else if (id == s_GF2m_simple) { method = EC_GF2m_simple_method(); +#endif } if (method) { if ((group = EC_GROUP_new(method)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_new"); } else { - rb_raise(rb_eArgError, "unknown symbol, must be :GFp_simple, :GFp_mont, :GFp_nist or :GF2m_simple"); + ossl_raise(rb_eArgError, "unknown symbol, must be :GFp_simple, :GFp_mont, :GFp_nist or :GF2m_simple"); } } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { const EC_GROUP *arg1_group; SafeRequire_EC_GROUP(arg1, arg1_group); @@ -780,20 +794,21 @@ } else { BIO *in = ossl_obj2bio(arg1); group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL); if (!group) { - (void)BIO_reset(in); + OSSL_BIO_reset(in); group = d2i_ECPKParameters_bio(in, NULL); } BIO_free(in); if (!group) { const char *name = StringValueCStr(arg1); int nid = OBJ_sn2nid(name); + (void)ERR_get_error(); if (nid == NID_undef) ossl_raise(eEC_GROUP, "unknown curve name (%s)", name); group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) @@ -813,25 +828,27 @@ const BIGNUM *a = GetBNPtr(arg3); const BIGNUM *b = GetBNPtr(arg4); if (id == s_GFp) { new_curve = EC_GROUP_new_curve_GFp; +#if !defined(OPENSSL_NO_EC2M) } else if (id == s_GF2m) { new_curve = EC_GROUP_new_curve_GF2m; +#endif } else { - rb_raise(rb_eArgError, "unknown symbol, must be :GFp or :GF2m"); + ossl_raise(rb_eArgError, "unknown symbol, must be :GFp or :GF2m"); } if ((group = new_curve(p, a, b, ossl_bn_ctx)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_new_by_GF*"); } else { - rb_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); + ossl_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); } break; default: - rb_raise(rb_eArgError, "wrong number of arguments"); + ossl_raise(rb_eArgError, "wrong number of arguments"); } if (group == NULL) ossl_raise(eEC_GROUP, ""); @@ -839,10 +856,11 @@ return self; } /* call-seq: + * group1.eql?(group2) => true | false * group1 == group2 => true | false * */ static VALUE ossl_ec_group_eql(VALUE a, VALUE b) { @@ -967,11 +985,11 @@ */ static VALUE ossl_s_builtin_curves(VALUE self) { EC_builtin_curve *curves = NULL; int n; - int crv_len = EC_get_builtin_curves(NULL, 0); + int crv_len = rb_long2int(EC_get_builtin_curves(NULL, 0)); VALUE ary, ret; curves = ALLOCA_N(EC_builtin_curve, crv_len); if (curves == NULL) return Qnil; @@ -1043,11 +1061,11 @@ 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; - default: rb_raise(eEC_GROUP, "unsupported point conversion form: %d, this module should be updated", form); + default: ossl_raise(eEC_GROUP, "unsupported point conversion form: %d, this module should be updated", form); } return ID2SYM(ret); } @@ -1069,11 +1087,11 @@ } else if (form_id == ID_compressed) { form = POINT_CONVERSION_COMPRESSED; } else if (form_id == ID_hybrid) { form = POINT_CONVERSION_HYBRID; } else { - rb_raise(rb_eArgError, "form must be :compressed, :uncompressed, or :hybrid"); + ossl_raise(rb_eArgError, "form must be :compressed, :uncompressed, or :hybrid"); } EC_GROUP_set_point_conversion_form(group, form); return form_v; @@ -1109,11 +1127,11 @@ EC_GROUP *group = NULL; Require_EC_GROUP(self, group); StringValue(seed); - if (EC_GROUP_set_seed(group, (unsigned char *)RSTRING_PTR(seed), RSTRING_LEN(seed)) != RSTRING_LEN(seed)) + if (EC_GROUP_set_seed(group, (unsigned char *)RSTRING_PTR(seed), RSTRING_LEN(seed)) != (size_t)RSTRING_LEN(seed)) ossl_raise(eEC_GROUP, "EC_GROUP_set_seed"); return seed; } @@ -1152,11 +1170,11 @@ case EXPORT_DER: i = i2d_ECPKParameters_bio(out, group); break; default: BIO_free(out); - rb_raise(rb_eRuntimeError, "unknown format (internal error)"); + ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); } if (i != 1) { BIO_free(out); ossl_raise(eECError, NULL); @@ -1210,23 +1228,32 @@ return str; } -static void ossl_ec_point_free(ossl_ec_point *ec_point) +static void ossl_ec_point_free(void *ptr) { + ossl_ec_point *ec_point = ptr; if (!ec_point->dont_free && ec_point->point) EC_POINT_clear_free(ec_point->point); ruby_xfree(ec_point); } +static const rb_data_type_t ossl_ec_point_type = { + "OpenSSL/ec_point", + { + 0, ossl_ec_point_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + static VALUE ossl_ec_point_alloc(VALUE klass) { ossl_ec_point *ec_point; VALUE obj; - obj = Data_Make_Struct(klass, ossl_ec_point, 0, ossl_ec_point_free, ec_point); + obj = TypedData_Make_Struct(klass, ossl_ec_point, &ossl_ec_point_type, ec_point); return obj; } /* @@ -1243,13 +1270,13 @@ EC_POINT *point = NULL; VALUE arg1, arg2; VALUE group_v = Qnil; const EC_GROUP *group = NULL; - Data_Get_Struct(self, ossl_ec_point, ec_point); + TypedData_Get_Struct(self, ossl_ec_point, &ossl_ec_point_type, ec_point); if (ec_point->point) - rb_raise(eEC_POINT, "EC_POINT already initialized"); + ossl_raise(eEC_POINT, "EC_POINT already initialized"); switch (rb_scan_args(argc, argv, "11", &arg1, &arg2)) { case 1: if (rb_obj_is_kind_of(arg1, cEC_POINT)) { const EC_POINT *arg_point; @@ -1263,17 +1290,17 @@ group_v = arg1; SafeRequire_EC_GROUP(group_v, group); point = EC_POINT_new(group); } else { - rb_raise(eEC_POINT, "wrong argument type: must be OpenSSL::PKey::EC::Point or OpenSSL::Pkey::EC::Group"); + ossl_raise(eEC_POINT, "wrong argument type: must be OpenSSL::PKey::EC::Point or OpenSSL::Pkey::EC::Group"); } break; case 2: if (!rb_obj_is_kind_of(arg1, cEC_GROUP)) - rb_raise(rb_eArgError, "1st argument must be OpenSSL::PKey::EC::Group"); + ossl_raise(rb_eArgError, "1st argument must be OpenSSL::PKey::EC::Group"); group_v = arg1; SafeRequire_EC_GROUP(group_v, group); if (rb_obj_is_kind_of(arg2, cBN)) { const BIGNUM *bn = GetBNPtr(arg2); @@ -1290,28 +1317,29 @@ ossl_raise(eEC_POINT, "unknown type for 2nd arg"); } } break; default: - rb_raise(rb_eArgError, "wrong number of arguments"); + ossl_raise(rb_eArgError, "wrong number of arguments"); } if (point == NULL) ossl_raise(eEC_POINT, NULL); if (NIL_P(group_v)) - rb_raise(rb_eRuntimeError, "missing group (internal error)"); + ossl_raise(rb_eRuntimeError, "missing group (internal error)"); ec_point->point = point; rb_iv_set(self, "@group", group_v); return self; } /* * call-seq: + * point1.eql?(point2) => true | false * point1 == point2 => true | false * */ static VALUE ossl_ec_point_eql(VALUE a, VALUE b) { @@ -1350,10 +1378,12 @@ 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"); } + + UNREACHABLE; } /* * call-seq: * point.on_curve? => true | false @@ -1371,10 +1401,12 @@ 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"); } + + UNREACHABLE; } /* * call-seq: * point.make_affine! => self @@ -1462,19 +1494,91 @@ ossl_raise(eEC_POINT, "EC_POINT_point2bn"); return bn_obj; } +/* + * call-seq: + * point.mul(bn) => point + * point.mul(bn, bn) => point + * point.mul([bn], [point]) => point + * point.mul([bn], [point], bn) => point + */ +static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) +{ + EC_POINT *point1, *point2; + const EC_GROUP *group; + VALUE group_v = rb_iv_get(self, "@group"); + VALUE bn_v1, bn_v2, r, points_v; + BIGNUM *bn1 = NULL, *bn2 = NULL; + + Require_EC_POINT(self, point1); + SafeRequire_EC_GROUP(group_v, group); + + r = rb_obj_alloc(cEC_POINT); + ossl_ec_point_initialize(1, &group_v, r); + Require_EC_POINT(r, point2); + + argc = rb_scan_args(argc, argv, "12", &bn_v1, &points_v, &bn_v2); + + if (rb_obj_is_kind_of(bn_v1, cBN)) { + bn1 = GetBNPtr(bn_v1); + if (argc >= 2) { + bn2 = GetBNPtr(points_v); + } + if (EC_POINT_mul(group, point2, bn2, point1, bn1, ossl_bn_ctx) != 1) + ossl_raise(eEC_POINT, "Multiplication failed"); + } else { + size_t i, points_len, bignums_len; + const EC_POINT **points; + const BIGNUM **bignums; + + Check_Type(bn_v1, T_ARRAY); + bignums_len = RARRAY_LEN(bn_v1); + bignums = (const BIGNUM **)OPENSSL_malloc(bignums_len * (int)sizeof(BIGNUM *)); + + for (i = 0; i < bignums_len; ++i) { + bignums[i] = GetBNPtr(rb_ary_entry(bn_v1, i)); + } + + if (!rb_obj_is_kind_of(points_v, rb_cArray)) { + OPENSSL_free((void *)bignums); + rb_raise(rb_eTypeError, "Argument2 must be an array"); + } + + rb_ary_unshift(points_v, self); + points_len = RARRAY_LEN(points_v); + points = (const EC_POINT **)OPENSSL_malloc(points_len * (int)sizeof(EC_POINT *)); + + for (i = 0; i < points_len; ++i) { + Get_EC_POINT(rb_ary_entry(points_v, i), points[i]); + } + + if (argc >= 3) { + bn2 = GetBNPtr(bn_v2); + } + if (EC_POINTs_mul(group, point2, bn2, points_len, points, bignums, ossl_bn_ctx) != 1) { + OPENSSL_free((void *)bignums); + OPENSSL_free((void *)points); + ossl_raise(eEC_POINT, "Multiplication failed"); + } + OPENSSL_free((void *)bignums); + OPENSSL_free((void *)points); + } + + return r; +} + static void no_copy(VALUE klass) { rb_undef_method(klass, "copy"); rb_undef_method(klass, "clone"); rb_undef_method(klass, "dup"); rb_undef_method(klass, "initialize_copy"); } -void Init_ossl_ec() +void Init_ossl_ec(void) { #ifdef DONT_NEED_RDOC_WORKAROUND mOSSL = rb_define_module("OpenSSL"); mPKey = rb_define_module_under(mOSSL, "PKey"); #endif @@ -1528,11 +1632,12 @@ 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 */ - rb_define_method(cEC, "to_pem", ossl_ec_key_to_pem, 0); + rb_define_method(cEC, "export", ossl_ec_key_export, -1); + rb_define_alias(cEC, "to_pem", "export"); rb_define_method(cEC, "to_der", ossl_ec_key_to_der, 0); rb_define_method(cEC, "to_text", ossl_ec_key_to_text, 0); rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); @@ -1582,16 +1687,17 @@ 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); + rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1); no_copy(cEC); no_copy(cEC_GROUP); no_copy(cEC_POINT); } #else /* defined NO_EC */ -void Init_ossl_ec() +void Init_ossl_ec(void) { } #endif /* NO_EC */