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

- old
+ new

@@ -4,93 +4,55 @@ #include "ossl.h" #if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) -typedef struct { - EC_GROUP *group; - int dont_free; -} ossl_ec_group; - -typedef struct { - EC_POINT *point; - int dont_free; -} ossl_ec_point; - - #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) { \ + if (EVP_PKEY_base_id(pkey) != 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); \ - 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; \ -} while(0) - -#define Require_EC_KEY(obj, key) do { \ - 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)); \ +#define GetEC(obj, key) do { \ + EVP_PKEY *_pkey; \ + GetPKeyEC(obj, _pkey); \ + (key) = EVP_PKEY_get0_EC_KEY(_pkey); \ } while (0) +#define SafeGetEC(obj, key) do { \ + OSSL_Check_Kind(obj, cEC); \ + GetEC(obj, key); \ +} while (0) -#define Get_EC_GROUP(obj, g) do { \ - ossl_ec_group *ec_group; \ - TypedData_Get_Struct((obj), ossl_ec_group, &ossl_ec_group_type, ec_group); \ - if (ec_group == NULL) \ - 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)); \ +#define GetECGroup(obj, group) do { \ + TypedData_Get_Struct(obj, EC_GROUP, &ossl_ec_group_type, group); \ if ((group) == NULL) \ - ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ -} while(0) - -#define SafeRequire_EC_GROUP(obj, group) do { \ + ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ +} while (0) +#define SafeGetECGroup(obj, group) do { \ OSSL_Check_Kind((obj), cEC_GROUP); \ - Require_EC_GROUP((obj), (group)); \ -} while(0) + GetECGroup(obj, group); \ +} while (0) -#define Get_EC_POINT(obj, p) do { \ - ossl_ec_point *ec_point; \ - TypedData_Get_Struct((obj), ossl_ec_point, &ossl_ec_point_type, ec_point); \ - if (ec_point == NULL) \ - 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)); \ +#define GetECPoint(obj, point) do { \ + TypedData_Get_Struct(obj, EC_POINT, &ossl_ec_point_type, point); \ if ((point) == NULL) \ - ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ -} while(0) - -#define SafeRequire_EC_POINT(obj, point) do { \ + ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ +} while (0) +#define SafeGetECPoint(obj, point) do { \ OSSL_Check_Kind((obj), cEC_POINT); \ - Require_EC_POINT((obj), (point)); \ + GetECPoint(obj, point); \ } while(0) +#define GetECPointGroup(obj, group) do { \ + VALUE _group = rb_attr_get(obj, id_i_group); \ + SafeGetECGroup(_group, group); \ +} while (0) VALUE cEC; VALUE eECError; VALUE cEC_GROUP; VALUE eEC_GROUP; @@ -106,10 +68,15 @@ static ID ID_uncompressed; static ID ID_compressed; static ID ID_hybrid; +static ID id_i_group; + +static VALUE ec_group_new(const EC_GROUP *group); +static VALUE ec_point_new(const EC_POINT *point, const EC_GROUP *group); + static VALUE ec_instance(VALUE klass, EC_KEY *ec) { EVP_PKEY *pkey; VALUE obj; @@ -135,11 +102,11 @@ if (!pkey) { obj = ec_instance(cEC, EC_KEY_new()); } else { obj = NewPKey(cEC); - if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) { + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { ossl_raise(rb_eTypeError, "Not a EC key!"); } SetPKey(obj, pkey); } if (obj == Qfalse) { @@ -147,173 +114,201 @@ } return obj; } +/* + * Creates a new EC_KEY on the EC group obj. arg can be an EC::Group or a String + * representing an OID. + */ +static EC_KEY * +ec_key_new_from_group(VALUE arg) +{ + EC_KEY *ec; -/* call-seq: - * 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) + if (rb_obj_is_kind_of(arg, cEC_GROUP)) { + EC_GROUP *group; + + SafeGetECGroup(arg, group); + if (!(ec = EC_KEY_new())) + ossl_raise(eECError, NULL); + + if (!EC_KEY_set_group(ec, group)) { + EC_KEY_free(ec); + ossl_raise(eECError, NULL); + } + } else { + int nid = OBJ_sn2nid(StringValueCStr(arg)); + + if (nid == NID_undef) + ossl_raise(eECError, "invalid curve name"); + + if (!(ec = EC_KEY_new_by_curve_name(nid))) + ossl_raise(eECError, NULL); + + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); + } + + return ec; +} + +/* + * call-seq: + * EC.generate(ec_group) -> ec + * EC.generate(string) -> ec * - * See the OpenSSL documentation for: - * EC_KEY_* + * Creates a new EC instance with a new random private and public key. */ +static VALUE +ossl_ec_key_s_generate(VALUE klass, VALUE arg) +{ + EC_KEY *ec; + VALUE obj; + + ec = ec_key_new_from_group(arg); + + obj = ec_instance(klass, ec); + if (obj == Qfalse) { + EC_KEY_free(ec); + ossl_raise(eECError, NULL); + } + + if (!EC_KEY_generate_key(ec)) + ossl_raise(eECError, "EC_KEY_generate_key"); + + return obj; +} + +/* + * call-seq: + * 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 [, pwd]) + * OpenSSL::PKey::EC.new(der_string) + * + * Creates a new EC object from given arguments. + */ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - EC_KEY *ec = NULL; + EC_KEY *ec; VALUE arg, pass; - VALUE group = Qnil; - char *passwd = NULL; GetPKey(self, pkey); - if (pkey->pkey.ec) + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eECError, "EC_KEY already initialized"); rb_scan_args(argc, argv, "02", &arg, &pass); if (NIL_P(arg)) { - ec = EC_KEY_new(); + if (!(ec = EC_KEY_new())) + ossl_raise(eECError, NULL); + } else if (rb_obj_is_kind_of(arg, cEC)) { + EC_KEY *other_ec = NULL; + + SafeGetEC(arg, other_ec); + if (!(ec = EC_KEY_dup(other_ec))) + ossl_raise(eECError, NULL); + } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) { + ec = ec_key_new_from_group(arg); } else { - if (rb_obj_is_kind_of(arg, cEC)) { - EC_KEY *other_ec = NULL; + BIO *in; - SafeRequire_EC_KEY(arg, other_ec); - ec = EC_KEY_dup(other_ec); - } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) { - ec = EC_KEY_new(); - group = arg; - } else { - BIO *in = ossl_obj2bio(arg); + pass = ossl_pem_passwd_value(pass); + in = ossl_obj2bio(&arg); - if (!NIL_P(pass)) { - passwd = StringValuePtr(pass); - } - ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); - if (!ec) { - OSSL_BIO_reset(in); - ec = PEM_read_bio_EC_PUBKEY(in, NULL, ossl_pem_passwd_cb, passwd); - } - if (!ec) { - OSSL_BIO_reset(in); - ec = d2i_ECPrivateKey_bio(in, NULL); - } - if (!ec) { - OSSL_BIO_reset(in); - ec = d2i_EC_PUBKEY_bio(in, NULL); - } + ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); + if (!ec) { + OSSL_BIO_reset(in); + ec = PEM_read_bio_EC_PUBKEY(in, NULL, ossl_pem_passwd_cb, (void *)pass); + } + if (!ec) { + OSSL_BIO_reset(in); + ec = d2i_ECPrivateKey_bio(in, NULL); + } + if (!ec) { + OSSL_BIO_reset(in); + ec = d2i_EC_PUBKEY_bio(in, NULL); + } + BIO_free(in); - BIO_free(in); - - if (ec == NULL) { - const char *name = StringValueCStr(arg); - int nid = OBJ_sn2nid(name); - - (void)ERR_get_error(); - if (nid == NID_undef) - ossl_raise(eECError, "unknown curve name (%s)\n", name); - - 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); - } - } + if (!ec) { + ossl_clear_error(); + ec = ec_key_new_from_group(arg); + } } - if (ec == NULL) - ossl_raise(eECError, NULL); - if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) { EC_KEY_free(ec); ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); } - rb_iv_set(self, "@group", Qnil); + return self; +} - if (!NIL_P(group)) - rb_funcall(self, rb_intern("group="), 1, arg); +static VALUE +ossl_ec_key_initialize_copy(VALUE self, VALUE other) +{ + EVP_PKEY *pkey; + EC_KEY *ec, *ec_new; + GetPKey(self, pkey); + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) + ossl_raise(eECError, "EC already initialized"); + SafeGetEC(other, ec); + + ec_new = EC_KEY_dup(ec); + if (!ec_new) + ossl_raise(eECError, "EC_KEY_dup"); + if (!EVP_PKEY_assign_EC_KEY(pkey, ec_new)) { + EC_KEY_free(ec_new); + ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); + } + return self; } /* - * call-seq: - * key.group => group + * call-seq: + * key.group => group * - * Returns a constant <code>OpenSSL::EC::Group</code> that is tied to the key. - * Modifying the returned group can make the key invalid. + * Returns the EC::Group that the key is associated with. Modifying the returned + * group does not affect +key+. */ -static VALUE ossl_ec_key_get_group(VALUE self) +static VALUE +ossl_ec_key_get_group(VALUE self) { - VALUE group_v; EC_KEY *ec; - ossl_ec_group *ec_group; - EC_GROUP *group; + const EC_GROUP *group; - Require_EC_KEY(self, ec); + GetEC(self, ec); + group = EC_KEY_get0_group(ec); + if (!group) + return Qnil; - group_v = rb_iv_get(self, "@group"); - if (!NIL_P(group_v)) - return group_v; - - if ((group = (EC_GROUP *)EC_KEY_get0_group(ec)) != NULL) { - group_v = rb_obj_alloc(cEC_GROUP); - SafeGet_ec_group(group_v, ec_group); - ec_group->group = group; - ec_group->dont_free = 1; - rb_iv_set(group_v, "@key", self); - rb_iv_set(self, "@group", group_v); - return group_v; - } - - return Qnil; + return ec_group_new(group); } /* - * call-seq: - * key.group = group => group + * call-seq: + * key.group = group * - * Returns the same object passed, not the group object associated with the key. - * If you wish to access the group object tied to the key call key.group after setting - * the group. - * - * Setting the group will immediately destroy any previously assigned group object. - * The group is internally copied by OpenSSL. Modifying the original group after - * assignment will not effect the internal key structure. - * (your changes may be lost). BE CAREFUL. - * - * EC_KEY_set_group calls EC_GROUP_free(key->group) then EC_GROUP_dup(), not EC_GROUP_copy. - * This documentation is accurate for OpenSSL 0.9.8b. + * Sets the EC::Group for the key. The group structure is internally copied so + * modification to +group+ after assigning to a key has no effect on the key. */ -static VALUE ossl_ec_key_set_group(VALUE self, VALUE group_v) +static VALUE +ossl_ec_key_set_group(VALUE self, VALUE group_v) { - VALUE old_group_v; EC_KEY *ec; EC_GROUP *group; - Require_EC_KEY(self, ec); - SafeRequire_EC_GROUP(group_v, group); + GetEC(self, ec); + SafeGetECGroup(group_v, group); - old_group_v = rb_iv_get(self, "@group"); - if (!NIL_P(old_group_v)) { - ossl_ec_group *old_ec_group; - SafeGet_ec_group(old_group_v, old_ec_group); - - old_ec_group->group = NULL; - old_ec_group->dont_free = 0; - rb_iv_set(old_group_v, "@key", Qnil); - } - - rb_iv_set(self, "@group", Qnil); - if (EC_KEY_set_group(ec, group) != 1) ossl_raise(eECError, "EC_KEY_set_group"); return group_v; } @@ -327,12 +322,11 @@ static VALUE ossl_ec_key_get_private_key(VALUE self) { EC_KEY *ec; const BIGNUM *bn; - Require_EC_KEY(self, ec); - + GetEC(self, ec); if ((bn = EC_KEY_get0_private_key(ec)) == NULL) return Qnil; return ossl_bn_new(bn); } @@ -346,11 +340,11 @@ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key) { EC_KEY *ec; BIGNUM *bn = NULL; - Require_EC_KEY(self, ec); + GetEC(self, ec); if (!NIL_P(private_key)) bn = GetBNPtr(private_key); switch (EC_KEY_set_private_key(ec, bn)) { case 1: @@ -363,52 +357,26 @@ } return private_key; } - -static VALUE ossl_ec_point_dup(const EC_POINT *point, VALUE group_v) -{ - VALUE obj; - const EC_GROUP *group; - ossl_ec_point *new_point; - - obj = rb_obj_alloc(cEC_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) - ossl_raise(eEC_POINT, "EC_POINT_dup"); - rb_iv_set(obj, "@group", group_v); - - return obj; -} - /* * call-seq: * key.public_key => OpenSSL::PKey::EC::Point * * See the OpenSSL documentation for EC_KEY_get0_public_key() */ static VALUE ossl_ec_key_get_public_key(VALUE self) { EC_KEY *ec; const EC_POINT *point; - VALUE group; - Require_EC_KEY(self, ec); - + GetEC(self, ec); if ((point = EC_KEY_get0_public_key(ec)) == NULL) return Qnil; - group = rb_funcall(self, rb_intern("group"), 0); - if (NIL_P(group)) - ossl_raise(eECError, "EC_KEY_get0_get0_group (has public_key but no group???"); - - return ossl_ec_point_dup(point, group); + return ec_point_new(point, EC_KEY_get0_group(ec)); } /* * call-seq: * key.public_key = ec_point @@ -418,13 +386,13 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) { EC_KEY *ec; EC_POINT *point = NULL; - Require_EC_KEY(self, ec); + GetEC(self, ec); if (!NIL_P(public_key)) - SafeRequire_EC_POINT(public_key, point); + SafeGetECPoint(public_key, point); switch (EC_KEY_set_public_key(ec, point)) { case 1: break; case 0: @@ -437,49 +405,50 @@ return public_key; } /* * call-seq: - * key.public_key? => true or false + * key.public? => true or false * - * Both public_key? and private_key? may return false at the same time unlike other PKey classes. + * Returns whether this EC instance has a public key. The public key + * (EC::Point) can be retrieved with EC#public_key. */ -static VALUE ossl_ec_key_is_public_key(VALUE self) +static VALUE ossl_ec_key_is_public(VALUE self) { EC_KEY *ec; - Require_EC_KEY(self, ec); + GetEC(self, ec); - return (EC_KEY_get0_public_key(ec) ? Qtrue : Qfalse); + return EC_KEY_get0_public_key(ec) ? Qtrue : Qfalse; } /* * call-seq: - * key.private_key? => true or false + * key.private? => true or false * - * Both public_key? and private_key? may return false at the same time unlike other PKey classes. + * Returns whether this EC instance has a private key. The private key (BN) can + * be retrieved with EC#private_key. */ -static VALUE ossl_ec_key_is_private_key(VALUE self) +static VALUE ossl_ec_key_is_private(VALUE self) { EC_KEY *ec; - Require_EC_KEY(self, ec); + GetEC(self, ec); - return (EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse); + return EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse; } 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; - char *password = NULL; VALUE str; const EVP_CIPHER *cipher = NULL; - Require_EC_KEY(self, ec); + GetEC(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) ossl_raise(eECError, "can't export - no public key set"); if (EC_KEY_check_key(ec) != 1) @@ -488,25 +457,20 @@ 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); - } + pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eECError, "BIO_new(BIO_s_mem())"); switch(format) { case EXPORT_PEM: if (private) { - i = PEM_write_bio_ECPrivateKey(out, ec, cipher, NULL, 0, NULL, password); + i = PEM_write_bio_ECPrivateKey(out, ec, cipher, NULL, 0, ossl_pem_passwd_cb, (void *)pass); } else { i = PEM_write_bio_EC_PUBKEY(out, ec); } break; @@ -536,16 +500,14 @@ /* * call-seq: * key.export([cipher, pass_phrase]) => String * key.to_pem([cipher, pass_phrase]) => String * - * 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. - * + * 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 + * 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_export(int argc, VALUE *argv, VALUE self) { VALUE cipher, passwd; rb_scan_args(argc, argv, "02", &cipher, &passwd); @@ -573,11 +535,11 @@ { EC_KEY *ec; BIO *out; VALUE str; - Require_EC_KEY(self, ec); + GetEC(self, ec); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eECError, "BIO_new(BIO_s_mem())"); } if (!EC_KEY_print(out, ec, 0)) { BIO_free(out); @@ -588,20 +550,27 @@ return str; } /* * call-seq: - * key.generate_key => self + * key.generate_key! => self * - * See the OpenSSL documentation for EC_KEY_generate_key() + * Generates a new random private and public key. + * + * See also the OpenSSL documentation for EC_KEY_generate_key() + * + * === Example + * ec = OpenSSL::PKey::EC.new("prime256v1") + * p ec.private_key # => nil + * ec.generate_key! + * p ec.private_key # => #<OpenSSL::BN XXXXXX> */ static VALUE ossl_ec_key_generate_key(VALUE self) { EC_KEY *ec; - Require_EC_KEY(self, ec); - + GetEC(self, ec); if (EC_KEY_generate_key(ec) != 1) ossl_raise(eECError, "EC_KEY_generate_key"); return self; } @@ -616,12 +585,11 @@ */ static VALUE ossl_ec_key_check_key(VALUE self) { EC_KEY *ec; - Require_EC_KEY(self, ec); - + GetEC(self, ec); if (EC_KEY_check_key(ec) != 1) ossl_raise(eECError, "EC_KEY_check_key"); return Qtrue; } @@ -637,12 +605,12 @@ EC_KEY *ec; EC_POINT *point; int buf_len; VALUE str; - Require_EC_KEY(self, ec); - SafeRequire_EC_POINT(pubkey, point); + GetEC(self, ec); + SafeGetECPoint(pubkey, point); /* BUG: need a way to figure out the maximum string size */ buf_len = 1024; str = rb_str_new(0, buf_len); /* BUG: take KDF as a block */ @@ -667,22 +635,21 @@ { EC_KEY *ec; unsigned int buf_len; VALUE str; - Require_EC_KEY(self, ec); + GetEC(self, ec); StringValue(data); if (EC_KEY_get0_private_key(ec) == NULL) ossl_raise(eECError, "Private EC key needed!"); - str = rb_str_new(0, ECDSA_size(ec) + 16); + str = rb_str_new(0, ECDSA_size(ec)); 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"); + ossl_raise(eECError, "ECDSA_sign"); + rb_str_set_len(str, buf_len); - rb_str_resize(str, buf_len); - return str; } /* * call-seq: @@ -692,11 +659,11 @@ */ static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig) { EC_KEY *ec; - Require_EC_KEY(self, ec); + GetEC(self, ec); StringValue(data); StringValue(sig); 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; @@ -707,60 +674,76 @@ ossl_raise(eECError, "ECDSA_verify"); UNREACHABLE; } -static void ossl_ec_group_free(void *ptr) +/* + * OpenSSL::PKey::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); + EC_GROUP_clear_free(ptr); } 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) +static VALUE +ossl_ec_group_alloc(VALUE klass) { - ossl_ec_group *ec_group; + return TypedData_Wrap_Struct(klass, &ossl_ec_group_type, NULL); +} + +static VALUE +ec_group_new(const EC_GROUP *group) +{ VALUE obj; + EC_GROUP *group_new; - obj = TypedData_Make_Struct(klass, ossl_ec_group, &ossl_ec_group_type, ec_group); + obj = ossl_ec_group_alloc(cEC_GROUP); + group_new = EC_GROUP_dup(group); + if (!group_new) + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + RTYPEDDATA_DATA(obj) = group_new; return obj; } -/* call-seq: - * OpenSSL::PKey::EC::Group.new("secp112r1") - * OpenSSL::PKey::EC::Group.new(ec_group) - * OpenSSL::PKey::EC::Group.new(pem_string) - * OpenSSL::PKey::EC::Group.new(der_string) - * OpenSSL::PKey::EC::Group.new(pem_file) - * OpenSSL::PKey::EC::Group.new(der_file) - * OpenSSL::PKey::EC::Group.new(:GFp_simple) - * OpenSSL::PKey::EC::Group.new(:GFp_mult) - * OpenSSL::PKey::EC::Group.new(:GFp_nist) - * OpenSSL::PKey::EC::Group.new(:GF2m_simple) - * OpenSSL::PKey::EC::Group.new(:GFp, bignum_p, bignum_a, bignum_b) - * OpenSSL::PKey::EC::Group.new(:GF2m, bignum_p, bignum_a, bignum_b) +/* + * call-seq: + * OpenSSL::PKey::EC::Group.new(ec_group) + * OpenSSL::PKey::EC::Group.new(pem_or_der_encoded) + * OpenSSL::PKey::EC::Group.new(ec_method) + * OpenSSL::PKey::EC::Group.new(:GFp, bignum_p, bignum_a, bignum_b) + * OpenSSL::PKey::EC::Group.new(:GF2m, bignum_p, bignum_a, bignum_b) * - * See the OpenSSL documentation for EC_GROUP_* + * Creates a new EC::Group object. + * + * +ec_method+ is a symbol that represents an EC_METHOD. Currently the following + * are supported: + * + * * :GFp_simple + * * :GFp_mont + * * :GFp_nist + * * :GF2m_simple + * + * If the first argument is :GFp or :GF2m, creates a new curve with given + * parameters. */ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) { VALUE arg1, arg2, arg3, arg4; - ossl_ec_group *ec_group; - EC_GROUP *group = NULL; + EC_GROUP *group; - TypedData_Get_Struct(self, ossl_ec_group, &ossl_ec_group_type, ec_group); - if (ec_group->group != NULL) + TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group); + if (group) 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)) { @@ -786,15 +769,15 @@ 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); + SafeGetECGroup(arg1, arg1_group); if ((group = EC_GROUP_dup(arg1_group)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_dup"); } else { - BIO *in = ossl_obj2bio(arg1); + BIO *in = ossl_obj2bio(&arg1); group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL); if (!group) { OSSL_BIO_reset(in); group = d2i_ECPKParameters_bio(in, NULL); @@ -804,17 +787,17 @@ if (!group) { const char *name = StringValueCStr(arg1); int nid = OBJ_sn2nid(name); - (void)ERR_get_error(); + ossl_clear_error(); /* ignore errors in d2i_ECPKParameters_bio() */ if (nid == NID_undef) - ossl_raise(eEC_GROUP, "unknown curve name (%s)", name); + ossl_raise(eEC_GROUP, "unknown curve name (%"PRIsVALUE")", arg1); group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) - ossl_raise(eEC_GROUP, "unable to create curve (%s)", name); + ossl_raise(eEC_GROUP, "unable to create curve (%"PRIsVALUE")", arg1); EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); } } @@ -849,141 +832,182 @@ ossl_raise(rb_eArgError, "wrong number of arguments"); } if (group == NULL) ossl_raise(eEC_GROUP, ""); + RTYPEDDATA_DATA(self) = group; - ec_group->group = group; + return self; +} +static VALUE +ossl_ec_group_initialize_copy(VALUE self, VALUE other) +{ + EC_GROUP *group, *group_new; + + TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group_new); + if (group_new) + ossl_raise(eEC_GROUP, "EC::Group already initialized"); + SafeGetECGroup(other, group); + + group_new = EC_GROUP_dup(group); + if (!group_new) + ossl_raise(eEC_GROUP, "EC_GROUP_dup"); + RTYPEDDATA_DATA(self) = group_new; + return self; } -/* call-seq: - * group1.eql?(group2) => true | false - * group1 == group2 => true | false +/* + * call-seq: + * group1.eql?(group2) => true | false + * group1 == group2 => true | false * + * Returns true if the two groups use the same curve and have the same + * parameters, false otherwise. */ 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); + GetECGroup(a, group1); + SafeGetECGroup(b, group2); if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1) return Qfalse; return Qtrue; } -/* call-seq: - * group.generator => ec_point +/* + * call-seq: + * group.generator => ec_point * - * See the OpenSSL documentation for EC_GROUP_get0_generator() + * Returns the generator of the group. + * + * See the OpenSSL documentation for EC_GROUP_get0_generator() */ static VALUE ossl_ec_group_get_generator(VALUE self) { - VALUE point_obj; - EC_GROUP *group = NULL; + EC_GROUP *group; + const EC_POINT *generator; - Require_EC_GROUP(self, group); + GetECGroup(self, group); + generator = EC_GROUP_get0_generator(group); + if (!generator) + return Qnil; - point_obj = ossl_ec_point_dup(EC_GROUP_get0_generator(group), self); - - return point_obj; + return ec_point_new(generator, group); } -/* call-seq: - * group.set_generator(generator, order, cofactor) => self +/* + * call-seq: + * group.set_generator(generator, order, cofactor) => self * - * See the OpenSSL documentation for EC_GROUP_set_generator() + * Sets the curve parameters. +generator+ must be an instance of EC::Point that + * is on the curve. +order+ and +cofactor+ are integers. + * + * See the OpenSSL documentation for EC_GROUP_set_generator() */ static VALUE ossl_ec_group_set_generator(VALUE self, VALUE generator, VALUE order, VALUE cofactor) { EC_GROUP *group = NULL; const EC_POINT *point; const BIGNUM *o, *co; - Require_EC_GROUP(self, group); - SafeRequire_EC_POINT(generator, point); + GetECGroup(self, group); + SafeGetECPoint(generator, point); o = GetBNPtr(order); co = GetBNPtr(cofactor); if (EC_GROUP_set_generator(group, point, o, co) != 1) ossl_raise(eEC_GROUP, "EC_GROUP_set_generator"); return self; } -/* call-seq: - * group.get_order => order_bn +/* + * call-seq: + * group.get_order => order_bn * - * See the OpenSSL documentation for EC_GROUP_get_order() + * Returns the order of the group. + * + * See the OpenSSL documentation for EC_GROUP_get_order() */ static VALUE ossl_ec_group_get_order(VALUE self) { VALUE bn_obj; BIGNUM *bn; EC_GROUP *group = NULL; - Require_EC_GROUP(self, group); + GetECGroup(self, group); bn_obj = ossl_bn_new(NULL); bn = GetBNPtr(bn_obj); if (EC_GROUP_get_order(group, bn, ossl_bn_ctx) != 1) ossl_raise(eEC_GROUP, "EC_GROUP_get_order"); return bn_obj; } -/* call-seq: - * group.get_cofactor => cofactor_bn +/* + * call-seq: + * group.get_cofactor => cofactor_bn * - * See the OpenSSL documentation for EC_GROUP_get_cofactor() + * Returns the cofactor of the group. + * + * See the OpenSSL documentation for EC_GROUP_get_cofactor() */ static VALUE ossl_ec_group_get_cofactor(VALUE self) { VALUE bn_obj; BIGNUM *bn; EC_GROUP *group = NULL; - Require_EC_GROUP(self, group); + GetECGroup(self, group); bn_obj = ossl_bn_new(NULL); bn = GetBNPtr(bn_obj); if (EC_GROUP_get_cofactor(group, bn, ossl_bn_ctx) != 1) ossl_raise(eEC_GROUP, "EC_GROUP_get_cofactor"); return bn_obj; } -/* call-seq: - * group.curve_name => String +/* + * call-seq: + * group.curve_name => String * - * See the OpenSSL documentation for EC_GROUP_get_curve_name() + * Returns the curve name (sn). + * + * See the OpenSSL documentation for EC_GROUP_get_curve_name() */ static VALUE ossl_ec_group_get_curve_name(VALUE self) { EC_GROUP *group = NULL; int nid; - Get_EC_GROUP(self, group); + GetECGroup(self, group); if (group == NULL) return Qnil; nid = EC_GROUP_get_curve_name(group); /* BUG: an nid or asn1 object should be returned, maybe. */ return rb_str_new2(OBJ_nid2sn(nid)); } -/* call-seq: - * EC.builtin_curves => [[name, comment], ...] +/* + * call-seq: + * EC.builtin_curves => [[sn, comment], ...] * - * See the OpenSSL documentation for EC_builtin_curves() + * Obtains a list of all predefined curves by the OpenSSL. Curve names are + * returned as sn. + * + * See the OpenSSL documentation for EC_get_builtin_curves(). */ static VALUE ossl_s_builtin_curves(VALUE self) { EC_builtin_curve *curves = NULL; int n; @@ -1009,56 +1033,68 @@ } return ret; } -/* call-seq: - * group.asn1_flag => Fixnum +/* + * call-seq: + * group.asn1_flag -> Integer * - * See the OpenSSL documentation for EC_GROUP_get_asn1_flag() + * Returns the flags set on the group. + * + * See also #asn1_flag=. */ static VALUE ossl_ec_group_get_asn1_flag(VALUE self) { EC_GROUP *group = NULL; int flag; - Require_EC_GROUP(self, group); - + GetECGroup(self, group); flag = EC_GROUP_get_asn1_flag(group); - return INT2FIX(flag); + return INT2NUM(flag); } -/* call-seq: - * group.asn1_flag = Fixnum => Fixnum +/* + * call-seq: + * group.asn1_flag = flags * - * See the OpenSSL documentation for EC_GROUP_set_asn1_flag() + * Sets flags on the group. The flag value is used to determine how to encode + * the group: encode explicit parameters or named curve using an OID. + * + * The flag value can be either of: + * + * * EC::NAMED_CURVE + * * EC::EXPLICIT_CURVE + * + * See the OpenSSL documentation for EC_GROUP_set_asn1_flag(). */ static VALUE ossl_ec_group_set_asn1_flag(VALUE self, VALUE flag_v) { EC_GROUP *group = NULL; - Require_EC_GROUP(self, group); - + GetECGroup(self, group); EC_GROUP_set_asn1_flag(group, NUM2INT(flag_v)); return flag_v; } -/* call-seq: - * group.point_conversion_form => :uncompressed | :compressed | :hybrid +/* + * call-seq: + * group.point_conversion_form -> Symbol * - * See the OpenSSL documentation for EC_GROUP_get_point_conversion_form() + * Returns the form how EC::Point data is encoded as ASN.1. + * + * See also #point_conversion_form=. */ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self) { EC_GROUP *group = NULL; point_conversion_form_t form; VALUE ret; - Require_EC_GROUP(self, group); - + GetECGroup(self, group); 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; @@ -1067,88 +1103,111 @@ } return ID2SYM(ret); } -/* call-seq: - * group.point_conversion_form = form => form +static point_conversion_form_t +parse_point_conversion_form_symbol(VALUE sym) +{ + ID id = SYM2ID(sym); + + if (id == ID_uncompressed) + return POINT_CONVERSION_UNCOMPRESSED; + else if (id == ID_compressed) + return POINT_CONVERSION_COMPRESSED; + else if (id == ID_hybrid) + return POINT_CONVERSION_HYBRID; + else + ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE + " (expected :compressed, :uncompressed, or :hybrid)", sym); +} + +/* + * call-seq: + * group.point_conversion_form = form * - * See the OpenSSL documentation for EC_GROUP_set_point_conversion_form() + * Sets the form how EC::Point data is encoded as ASN.1 as defined in X9.62. + * + * +format+ can be one of these: + * + * :compressed:: + * Encoded as z||x, where z is an octet indicating which solution of the + * equation y is. z will be 0x02 or 0x03. + * :uncompressed:: + * Encoded as z||x||y, where z is an octet 0x04. + * :hybrid:: + * Encodes as z||x||y, where z is an octet indicating which solution of the + * equation y is. z will be 0x06 or 0x07. + * + * See the OpenSSL documentation for EC_GROUP_set_point_conversion_form() */ -static VALUE ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v) +static VALUE +ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v) { - EC_GROUP *group = NULL; + EC_GROUP *group; point_conversion_form_t form; - ID form_id = SYM2ID(form_v); - Require_EC_GROUP(self, group); + GetECGroup(self, group); + form = parse_point_conversion_form_symbol(form_v); - if (form_id == ID_uncompressed) { - form = POINT_CONVERSION_UNCOMPRESSED; - } else if (form_id == ID_compressed) { - form = POINT_CONVERSION_COMPRESSED; - } else if (form_id == ID_hybrid) { - form = POINT_CONVERSION_HYBRID; - } else { - ossl_raise(rb_eArgError, "form must be :compressed, :uncompressed, or :hybrid"); - } - EC_GROUP_set_point_conversion_form(group, form); return form_v; } -/* call-seq: - * group.seed => String or nil +/* + * call-seq: + * group.seed => String or nil * - * See the OpenSSL documentation for EC_GROUP_get0_seed() + * See the OpenSSL documentation for EC_GROUP_get0_seed() */ static VALUE ossl_ec_group_get_seed(VALUE self) { EC_GROUP *group = NULL; size_t seed_len; - Require_EC_GROUP(self, group); - + GetECGroup(self, group); seed_len = EC_GROUP_get_seed_len(group); if (seed_len == 0) return Qnil; return rb_str_new((const char *)EC_GROUP_get0_seed(group), seed_len); } -/* call-seq: - * group.seed = seed => seed +/* + * call-seq: + * group.seed = seed => seed * - * See the OpenSSL documentation for EC_GROUP_set_seed() + * See the OpenSSL documentation for EC_GROUP_set_seed() */ static VALUE ossl_ec_group_set_seed(VALUE self, VALUE seed) { EC_GROUP *group = NULL; - Require_EC_GROUP(self, group); + GetECGroup(self, group); StringValue(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; } /* get/set curve GFp, GF2m */ -/* call-seq: - * group.degree => Fixnum +/* + * call-seq: + * group.degree => integer * - * See the OpenSSL documentation for EC_GROUP_get_degree() + * See the OpenSSL documentation for EC_GROUP_get_degree() */ static VALUE ossl_ec_group_get_degree(VALUE self) { EC_GROUP *group = NULL; - Require_EC_GROUP(self, group); + GetECGroup(self, group); return INT2NUM(EC_GROUP_get_degree(group)); } static VALUE ossl_ec_group_to_string(VALUE self, int format) @@ -1156,11 +1215,11 @@ EC_GROUP *group; BIO *out; int i = -1; VALUE str; - Get_EC_GROUP(self, group); + GetECGroup(self, group); if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); switch(format) { @@ -1183,42 +1242,45 @@ str = ossl_membio2str(out); return str; } -/* call-seq: - * group.to_pem => String +/* + * call-seq: + * group.to_pem => String * * See the OpenSSL documentation for PEM_write_bio_ECPKParameters() */ static VALUE ossl_ec_group_to_pem(VALUE self) { return ossl_ec_group_to_string(self, EXPORT_PEM); } -/* call-seq: - * group.to_der => String +/* + * call-seq: + * group.to_der => String * - * See the OpenSSL documentation for i2d_ECPKParameters_bio() + * See the OpenSSL documentation for i2d_ECPKParameters_bio() */ static VALUE ossl_ec_group_to_der(VALUE self) { return ossl_ec_group_to_string(self, EXPORT_DER); } -/* call-seq: - * group.to_text => String +/* + * call-seq: + * group.to_text => String * - * See the OpenSSL documentation for ECPKParameters_print() + * See the OpenSSL documentation for ECPKParameters_print() */ static VALUE ossl_ec_group_to_text(VALUE self) { EC_GROUP *group; BIO *out; VALUE str; - Require_EC_GROUP(self, group); + GetECGroup(self, group); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); } if (!ECPKParameters_print(out, group, 0)) { BIO_free(out); @@ -1228,69 +1290,81 @@ return str; } -static void ossl_ec_point_free(void *ptr) +/* + * OpenSSL::PKey::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); + EC_POINT_clear_free(ptr); } static const rb_data_type_t ossl_ec_point_type = { - "OpenSSL/ec_point", + "OpenSSL/EC_POINT", { 0, ossl_ec_point_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; -static VALUE ossl_ec_point_alloc(VALUE klass) +static VALUE +ossl_ec_point_alloc(VALUE klass) { - ossl_ec_point *ec_point; + return TypedData_Wrap_Struct(klass, &ossl_ec_point_type, NULL); +} + +static VALUE +ec_point_new(const EC_POINT *point, const EC_GROUP *group) +{ + EC_POINT *point_new; VALUE obj; - obj = TypedData_Make_Struct(klass, ossl_ec_point, &ossl_ec_point_type, ec_point); + obj = ossl_ec_point_alloc(cEC_POINT); + point_new = EC_POINT_dup(point, group); + if (!point_new) + ossl_raise(eEC_POINT, "EC_POINT_dup"); + RTYPEDDATA_DATA(obj) = point_new; + rb_ivar_set(obj, id_i_group, ec_group_new(group)); return obj; } /* - * call-seq: - * OpenSSL::PKey::EC::Point.new(point) - * OpenSSL::PKey::EC::Point.new(group) - * OpenSSL::PKey::EC::Point.new(group, bn) + * call-seq: + * OpenSSL::PKey::EC::Point.new(point) + * OpenSSL::PKey::EC::Point.new(group) + * OpenSSL::PKey::EC::Point.new(group, bn) * - * See the OpenSSL documentation for EC_POINT_* + * See the OpenSSL documentation for EC_POINT_* */ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) { - ossl_ec_point *ec_point; - EC_POINT *point = NULL; + EC_POINT *point; VALUE arg1, arg2; VALUE group_v = Qnil; const EC_GROUP *group = NULL; - TypedData_Get_Struct(self, ossl_ec_point, &ossl_ec_point_type, ec_point); - if (ec_point->point) + TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point); + if (point) 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; - group_v = rb_iv_get(arg1, "@group"); - SafeRequire_EC_GROUP(group_v, group); - SafeRequire_EC_POINT(arg1, arg_point); + group_v = rb_attr_get(arg1, id_i_group); + SafeGetECGroup(group_v, group); + SafeGetECPoint(arg1, arg_point); point = EC_POINT_dup(arg_point, group); } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { group_v = arg1; - SafeRequire_EC_GROUP(group_v, group); + SafeGetECGroup(group_v, group); point = EC_POINT_new(group); } else { ossl_raise(eEC_POINT, "wrong argument type: must be OpenSSL::PKey::EC::Point or OpenSSL::Pkey::EC::Group"); } @@ -1298,18 +1372,18 @@ break; case 2: if (!rb_obj_is_kind_of(arg1, cEC_GROUP)) ossl_raise(rb_eArgError, "1st argument must be OpenSSL::PKey::EC::Group"); group_v = arg1; - SafeRequire_EC_GROUP(group_v, group); + SafeGetECGroup(group_v, group); if (rb_obj_is_kind_of(arg2, cBN)) { const BIGNUM *bn = GetBNPtr(arg2); point = EC_POINT_bn2point(group, bn, NULL, ossl_bn_ctx); } else { - BIO *in = ossl_obj2bio(arg1); + BIO *in = ossl_obj2bio(&arg1); /* BUG: finish me */ BIO_free(in); @@ -1326,56 +1400,76 @@ ossl_raise(eEC_POINT, NULL); if (NIL_P(group_v)) ossl_raise(rb_eRuntimeError, "missing group (internal error)"); - ec_point->point = point; + RTYPEDDATA_DATA(self) = point; + rb_ivar_set(self, id_i_group, group_v); - rb_iv_set(self, "@group", group_v); + return self; +} +static VALUE +ossl_ec_point_initialize_copy(VALUE self, VALUE other) +{ + EC_POINT *point, *point_new; + EC_GROUP *group; + VALUE group_v; + + TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point_new); + if (point_new) + ossl_raise(eEC_POINT, "EC::Point already initialized"); + SafeGetECPoint(other, point); + + group_v = rb_obj_dup(rb_attr_get(other, id_i_group)); + SafeGetECGroup(group_v, group); + + point_new = EC_POINT_dup(point, group); + if (!point_new) + ossl_raise(eEC_POINT, "EC_POINT_dup"); + RTYPEDDATA_DATA(self) = point_new; + rb_ivar_set(self, id_i_group, group_v); + return self; } /* - * call-seq: - * point1.eql?(point2) => true | false - * point1 == point2 => true | false - * + * call-seq: + * point1.eql?(point2) => true | false + * 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"); + VALUE group_v1 = rb_attr_get(a, id_i_group); + VALUE group_v2 = rb_attr_get(b, id_i_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); + GetECPoint(a, point1); + SafeGetECPoint(b, point2); + SafeGetECGroup(group_v1, group); if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1) return Qfalse; return Qtrue; } /* - * call-seq: - * point.infinity? => true | false - * + * 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); + GetECPoint(self, point); + GetECPointGroup(self, 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"); @@ -1383,22 +1477,20 @@ UNREACHABLE; } /* - * call-seq: - * point.on_curve? => true | false - * + * 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); + GetECPoint(self, point); + GetECPointGroup(self, 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"); @@ -1406,187 +1498,210 @@ UNREACHABLE; } /* - * call-seq: - * point.make_affine! => self - * + * 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); + GetECPoint(self, point); + GetECPointGroup(self, 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 - * + * 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); + GetECPoint(self, point); + GetECPointGroup(self, 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 - * + * 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); + GetECPoint(self, point); + GetECPointGroup(self, 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 + * call-seq: + * point.to_bn(conversion_form = nil) => OpenSSL::BN * - * See the OpenSSL documentation for EC_POINT_point2bn() + * Convert the EC point into an octet string and store in an OpenSSL::BN. If + * +conversion_form+ is given, the point data is converted using the specified + * form. If not given, the default form set in the EC::Group object is used. + * + * See also EC::Point#point_conversion_form=. */ -static VALUE ossl_ec_point_to_bn(VALUE self) +static VALUE +ossl_ec_point_to_bn(int argc, VALUE *argv, VALUE self) { EC_POINT *point; - VALUE bn_obj; - VALUE group_v = rb_iv_get(self, "@group"); + VALUE form_obj, bn_obj; const EC_GROUP *group; point_conversion_form_t form; BIGNUM *bn; - Require_EC_POINT(self, point); - SafeRequire_EC_GROUP(group_v, group); + GetECPoint(self, point); + GetECPointGroup(self, group); + rb_scan_args(argc, argv, "01", &form_obj); + if (NIL_P(form_obj)) + form = EC_GROUP_get_point_conversion_form(group); + else + form = parse_point_conversion_form_symbol(form_obj); - form = EC_GROUP_get_point_conversion_form(group); - bn_obj = rb_obj_alloc(cBN); bn = GetBNPtr(bn_obj); if (EC_POINT_point2bn(group, point, form, bn, ossl_bn_ctx) == NULL) 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 + * call-seq: + * point.mul(bn1 [, bn2]) => point + * point.mul(bns, points [, bn2]) => point + * + * Performs elliptic curve point multiplication. + * + * The first form calculates <tt>bn1 * point + bn2 * G</tt>, where +G+ is the + * generator of the group of +point+. +bn2+ may be omitted, and in that case, + * the result is just <tt>bn1 * point</tt>. + * + * The second form calculates <tt>bns[0] * point + bns[1] * points[0] + ... + * + bns[-1] * points[-1] + bn2 * G</tt>. +bn2+ may be omitted. +bns+ must be + * an array of OpenSSL::BN. +points+ must be an array of + * OpenSSL::PKey::EC::Point. Please note that <tt>points[0]</tt> is not + * multiplied by <tt>bns[0]</tt>, but <tt>bns[1]</tt>. */ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) { - EC_POINT *point1, *point2; + EC_POINT *point_self, *point_result; 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; + VALUE group_v = rb_attr_get(self, id_i_group); + VALUE arg1, arg2, arg3, result; + const BIGNUM *bn_g = NULL; - Require_EC_POINT(self, point1); - SafeRequire_EC_GROUP(group_v, group); + GetECPoint(self, point_self); + SafeGetECGroup(group_v, group); - r = rb_obj_alloc(cEC_POINT); - ossl_ec_point_initialize(1, &group_v, r); - Require_EC_POINT(r, point2); + result = rb_obj_alloc(cEC_POINT); + ossl_ec_point_initialize(1, &group_v, result); + GetECPoint(result, point_result); - argc = rb_scan_args(argc, argv, "12", &bn_v1, &points_v, &bn_v2); + rb_scan_args(argc, argv, "12", &arg1, &arg2, &arg3); + if (!RB_TYPE_P(arg1, T_ARRAY)) { + BIGNUM *bn = GetBNPtr(arg1); - 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"); + if (!NIL_P(arg2)) + bn_g = GetBNPtr(arg2); + if (EC_POINT_mul(group, point_result, bn_g, point_self, bn, ossl_bn_ctx) != 1) + ossl_raise(eEC_POINT, NULL); } else { - size_t i, points_len, bignums_len; - const EC_POINT **points; - const BIGNUM **bignums; + /* + * bignums | arg1[0] | arg1[1] | arg1[2] | ... + * points | self | arg2[0] | arg2[1] | ... + */ + long i, num; + VALUE bns_tmp, tmp_p, tmp_b; + 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 *)); + Check_Type(arg1, T_ARRAY); + Check_Type(arg2, T_ARRAY); + if (RARRAY_LEN(arg1) != RARRAY_LEN(arg2) + 1) /* arg2 must be 1 larger */ + ossl_raise(rb_eArgError, "bns must be 1 longer than points; see the documentation"); - for (i = 0; i < bignums_len; ++i) { - bignums[i] = GetBNPtr(rb_ary_entry(bn_v1, i)); - } + num = RARRAY_LEN(arg1); + bns_tmp = rb_ary_tmp_new(num); + bignums = ALLOCV_N(const BIGNUM *, tmp_b, num); + for (i = 0; i < num; i++) { + VALUE item = RARRAY_AREF(arg1, i); + bignums[i] = GetBNPtr(item); + rb_ary_push(bns_tmp, item); + } - if (!rb_obj_is_kind_of(points_v, rb_cArray)) { - OPENSSL_free((void *)bignums); - rb_raise(rb_eTypeError, "Argument2 must be an array"); - } + points = ALLOCV_N(const EC_POINT *, tmp_p, num); + points[0] = point_self; /* self */ + for (i = 0; i < num - 1; i++) + SafeGetECPoint(RARRAY_AREF(arg2, i), points[i + 1]); - rb_ary_unshift(points_v, self); - points_len = RARRAY_LEN(points_v); - points = (const EC_POINT **)OPENSSL_malloc(points_len * (int)sizeof(EC_POINT *)); + if (!NIL_P(arg3)) + bn_g = GetBNPtr(arg3); - for (i = 0; i < points_len; ++i) { - Get_EC_POINT(rb_ary_entry(points_v, i), points[i]); - } + if (EC_POINTs_mul(group, point_result, bn_g, num, points, bignums, ossl_bn_ctx) != 1) { + ALLOCV_END(tmp_b); + ALLOCV_END(tmp_p); + ossl_raise(eEC_POINT, NULL); + } - 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); + ALLOCV_END(tmp_b); + ALLOCV_END(tmp_p); } - return r; + return result; } -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) { -#ifdef DONT_NEED_RDOC_WORKAROUND - mOSSL = rb_define_module("OpenSSL"); +#if 0 mPKey = rb_define_module_under(mOSSL, "PKey"); + cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); + eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); + ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); #endif eECError = rb_define_class_under(mPKey, "ECError", ePKeyError); + /* + * Document-class: OpenSSL::PKey::EC + * + * OpenSSL::PKey::EC provides access to Elliptic Curve Digital Signature + * Algorithm (ECDSA) and Elliptic Curve Diffie-Hellman (ECDH). + * + * === Key exchange + * ec1 = OpenSSL::PKey::EC.generate("prime256v1") + * ec2 = OpenSSL::PKey::EC.generate("prime256v1") + * # ec1 and ec2 have own private key respectively + * shared_key1 = ec1.dh_compute_key(ec2.public_key) + * shared_key2 = ec2.dh_compute_key(ec1.public_key) + * + * p shared_key1 == shared_key2 #=> true + */ cEC = rb_define_class_under(mPKey, "EC", cPKey); cEC_GROUP = rb_define_class_under(cEC, "Group", rb_cObject); cEC_POINT = rb_define_class_under(cEC, "Point", rb_cObject); eEC_GROUP = rb_define_class_under(cEC_GROUP, "Error", eOSSLError); eEC_POINT = rb_define_class_under(cEC_POINT, "Error", eOSSLError); @@ -1600,35 +1715,41 @@ ID_uncompressed = rb_intern("uncompressed"); 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)); + rb_define_const(cEC, "NAMED_CURVE", INT2NUM(OPENSSL_EC_NAMED_CURVE)); +#if defined(OPENSSL_EC_EXPLICIT_CURVE) + rb_define_const(cEC, "EXPLICIT_CURVE", INT2NUM(OPENSSL_EC_EXPLICIT_CURVE)); #endif rb_define_singleton_method(cEC, "builtin_curves", ossl_s_builtin_curves, 0); + rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1); rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1); + rb_define_copy_func(cEC, ossl_ec_key_initialize_copy); /* copy/dup/cmp */ rb_define_method(cEC, "group", ossl_ec_key_get_group, 0); rb_define_method(cEC, "group=", ossl_ec_key_set_group, 1); rb_define_method(cEC, "private_key", ossl_ec_key_get_private_key, 0); rb_define_method(cEC, "private_key=", ossl_ec_key_set_private_key, 1); rb_define_method(cEC, "public_key", ossl_ec_key_get_public_key, 0); rb_define_method(cEC, "public_key=", ossl_ec_key_set_public_key, 1); - rb_define_method(cEC, "private_key?", ossl_ec_key_is_private_key, 0); - rb_define_method(cEC, "public_key?", ossl_ec_key_is_public_key, 0); + rb_define_method(cEC, "private?", ossl_ec_key_is_private, 0); + rb_define_method(cEC, "public?", ossl_ec_key_is_public, 0); + rb_define_alias(cEC, "private_key?", "private?"); + rb_define_alias(cEC, "public_key?", "public?"); /* rb_define_method(cEC, "", ossl_ec_key_get_, 0); rb_define_method(cEC, "=", ossl_ec_key_set_ 1); set/get enc_flags set/get _conv_from set/get asn1_flag (can use ruby to call self.group.asn1_flag) set/get precompute_mult */ - rb_define_method(cEC, "generate_key", ossl_ec_key_generate_key, 0); + rb_define_method(cEC, "generate_key!", ossl_ec_key_generate_key, 0); + rb_define_alias(cEC, "generate_key", "generate_key!"); 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, 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); @@ -1640,10 +1761,11 @@ rb_define_method(cEC, "to_text", ossl_ec_key_to_text, 0); rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); rb_define_method(cEC_GROUP, "initialize", ossl_ec_group_initialize, -1); + rb_define_copy_func(cEC_GROUP, ossl_ec_group_initialize_copy); 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); @@ -1675,10 +1797,11 @@ rb_define_method(cEC_GROUP, "to_text", ossl_ec_group_to_text, 0); rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc); rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1); + rb_define_copy_func(cEC_POINT, ossl_ec_point_initialize_copy); 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); @@ -1686,15 +1809,13 @@ 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); + rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, -1); rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1); - no_copy(cEC); - no_copy(cEC_GROUP); - no_copy(cEC_POINT); + id_i_group = rb_intern("@group"); } #else /* defined NO_EC */ void Init_ossl_ec(void) {