ext/rubysl/openssl/ossl_pkey.c in rubysl-openssl-2.10 vs ext/rubysl/openssl/ossl_pkey.c in rubysl-openssl-2.11

- old
+ new

@@ -13,62 +13,69 @@ * Classes */ VALUE mPKey; VALUE cPKey; VALUE ePKeyError; -ID id_private_q; +static ID id_private_q; /* * callback for generating keys */ -void -ossl_generate_cb(int p, int n, void *arg) +static VALUE +call_check_ints0(VALUE arg) { - VALUE ary; + rb_thread_check_ints(); + return Qnil; +} - ary = rb_ary_new2(2); - rb_ary_store(ary, 0, INT2NUM(p)); - rb_ary_store(ary, 1, INT2NUM(n)); - - rb_yield(ary); +static void * +call_check_ints(void *arg) +{ + int state; + rb_protect(call_check_ints0, Qnil, &state); + return (void *)(VALUE)state; } -#if HAVE_BN_GENCB -/* OpenSSL 2nd version of GN generation callback */ int ossl_generate_cb_2(int p, int n, BN_GENCB *cb) { VALUE ary; struct ossl_generate_cb_arg *arg; int state; - arg = (struct ossl_generate_cb_arg *)cb->arg; + arg = (struct ossl_generate_cb_arg *)BN_GENCB_get_arg(cb); if (arg->yield) { ary = rb_ary_new2(2); rb_ary_store(ary, 0, INT2NUM(p)); rb_ary_store(ary, 1, INT2NUM(n)); /* * can be break by raising exception or 'break' */ rb_protect(rb_yield, ary, &state); if (state) { - arg->stop = 1; arg->state = state; + return 0; } } - if (arg->stop) return 0; + if (arg->interrupted) { + arg->interrupted = 0; + state = (int)(VALUE)rb_thread_call_with_gvl(call_check_ints, NULL); + if (state) { + arg->state = state; + return 0; + } + } return 1; } void ossl_generate_cb_stop(void *ptr) { struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr; - arg->stop = 1; + arg->interrupted = 1; } -#endif static void ossl_evp_pkey_free(void *ptr) { EVP_PKEY_free(ptr); @@ -83,17 +90,20 @@ 0, ossl_evp_pkey_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; -VALUE -ossl_pkey_new(EVP_PKEY *pkey) +static VALUE +pkey_new0(EVP_PKEY *pkey) { - if (!pkey) { - ossl_raise(ePKeyError, "Cannot make new key from NULL."); - } - switch (EVP_PKEY_type(pkey->type)) { + VALUE obj; + int type; + + if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE) + ossl_raise(rb_eRuntimeError, "pkey is empty"); + + switch (type) { #if !defined(OPENSSL_NO_RSA) case EVP_PKEY_RSA: return ossl_rsa_new(pkey); #endif #if !defined(OPENSSL_NO_DSA) @@ -107,84 +117,116 @@ #if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) case EVP_PKEY_EC: return ossl_ec_new(pkey); #endif default: - ossl_raise(ePKeyError, "unsupported key type"); + obj = NewPKey(cPKey); + SetPKey(obj, pkey); + return obj; } - - UNREACHABLE; } VALUE -ossl_pkey_new_from_file(VALUE filename) +ossl_pkey_new(EVP_PKEY *pkey) { - FILE *fp; - EVP_PKEY *pkey; + VALUE obj; + int status; - SafeStringValue(filename); - if (!(fp = fopen(RSTRING_PTR(filename), "r"))) { - ossl_raise(ePKeyError, "%s", strerror(errno)); + obj = rb_protect((VALUE (*)(VALUE))pkey_new0, (VALUE)pkey, &status); + if (status) { + EVP_PKEY_free(pkey); + rb_jump_tag(status); } - rb_fd_fix_cloexec(fileno(fp)); - pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL); - fclose(fp); - if (!pkey) { - ossl_raise(ePKeyError, NULL); - } - - return ossl_pkey_new(pkey); + return obj; } /* * call-seq: - * OpenSSL::PKey.read(string [, pwd ] ) -> PKey - * OpenSSL::PKey.read(file [, pwd ]) -> PKey + * OpenSSL::PKey.read(string [, pwd ]) -> PKey + * OpenSSL::PKey.read(io [, pwd ]) -> PKey * + * Reads a DER or PEM encoded string from +string+ or +io+ and returns an + * instance of the appropriate PKey class. + * * === Parameters * * +string+ is a DER- or PEM-encoded string containing an arbitrary private - * or public key. - * * +file+ is an instance of +File+ containing a DER- or PEM-encoded - * arbitrary private or public key. + * or public key. + * * +io+ is an instance of +IO+ containing a DER- or PEM-encoded + * arbitrary private or public key. * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted - * PEM resource. + * PEM resource. */ static VALUE ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) { - EVP_PKEY *pkey; - BIO *bio; - VALUE data, pass; - char *passwd = NULL; + EVP_PKEY *pkey; + BIO *bio; + VALUE data, pass; - rb_scan_args(argc, argv, "11", &data, &pass); + rb_scan_args(argc, argv, "11", &data, &pass); + pass = ossl_pem_passwd_value(pass); - bio = ossl_obj2bio(data); - if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) { + bio = ossl_obj2bio(&data); + if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) { OSSL_BIO_reset(bio); - if (!NIL_P(pass)) { - passwd = StringValuePtr(pass); - } - if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, passwd))) { + if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) { OSSL_BIO_reset(bio); if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) { OSSL_BIO_reset(bio); - if (!NIL_P(pass)) { - passwd = StringValuePtr(pass); - } - pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, passwd); + pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, (void *)pass); } } } BIO_free(bio); if (!pkey) - ossl_raise(rb_eArgError, "Could not parse PKey"); + ossl_raise(ePKeyError, "Could not parse PKey"); + return ossl_pkey_new(pkey); } +void +ossl_pkey_check_public_key(const EVP_PKEY *pkey) +{ + void *ptr; + const BIGNUM *n, *e, *pubkey; + + if (EVP_PKEY_missing_parameters(pkey)) + ossl_raise(ePKeyError, "parameters missing"); + + /* OpenSSL < 1.1.0 takes non-const pointer */ + ptr = EVP_PKEY_get0((EVP_PKEY *)pkey); + switch (EVP_PKEY_base_id(pkey)) { + case EVP_PKEY_RSA: + RSA_get0_key(ptr, &n, &e, NULL); + if (n && e) + return; + break; + case EVP_PKEY_DSA: + DSA_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; + case EVP_PKEY_DH: + DH_get0_key(ptr, &pubkey, NULL); + if (pubkey) + return; + break; +#if !defined(OPENSSL_NO_EC) + case EVP_PKEY_EC: + if (EC_KEY_get0_public_key(ptr)) + return; + break; +#endif + default: + /* unsupported type; assuming ok */ + return; + } + ossl_raise(ePKeyError, "public key missing"); +} + EVP_PKEY * GetPKeyPtr(VALUE obj) { EVP_PKEY *pkey; @@ -210,29 +252,15 @@ DupPKeyPtr(VALUE obj) { EVP_PKEY *pkey; SafeGetPKey(obj, pkey); - CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); + EVP_PKEY_up_ref(pkey); return pkey; } -EVP_PKEY * -DupPrivPKeyPtr(VALUE obj) -{ - EVP_PKEY *pkey; - - if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) { - ossl_raise(rb_eArgError, "Private key is needed."); - } - SafeGetPKey(obj, pkey); - CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); - - return pkey; -} - /* * Private */ static VALUE ossl_pkey_alloc(VALUE klass) @@ -258,11 +286,11 @@ */ static VALUE ossl_pkey_initialize(VALUE self) { if (rb_obj_is_instance_of(self, cPKey)) { - ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class."); + ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); } return self; } /* @@ -284,28 +312,36 @@ */ static VALUE ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) { EVP_PKEY *pkey; - EVP_MD_CTX ctx; + const EVP_MD *md; + EVP_MD_CTX *ctx; unsigned int buf_len; VALUE str; int result; - if (rb_funcallv(self, id_private_q, 0, NULL) != Qtrue) { - ossl_raise(rb_eArgError, "Private key is needed."); - } - GetPKey(self, pkey); - EVP_SignInit(&ctx, GetDigestPtr(digest)); + pkey = GetPrivPKeyPtr(self); + md = GetDigestPtr(digest); StringValue(data); - EVP_SignUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data)); - str = rb_str_new(0, EVP_PKEY_size(pkey)+16); - result = EVP_SignFinal(&ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey); - EVP_MD_CTX_cleanup(&ctx); + str = rb_str_new(0, EVP_PKEY_size(pkey)); + + ctx = EVP_MD_CTX_new(); + if (!ctx) + ossl_raise(ePKeyError, "EVP_MD_CTX_new"); + if (!EVP_SignInit_ex(ctx, md, NULL)) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_SignInit_ex"); + } + if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_SignUpdate"); + } + result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey); + EVP_MD_CTX_free(ctx); if (!result) - ossl_raise(ePKeyError, NULL); - assert((long)buf_len <= RSTRING_LEN(str)); + ossl_raise(ePKeyError, "EVP_SignFinal"); rb_str_set_len(str, buf_len); return str; } @@ -332,39 +368,55 @@ */ static VALUE ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) { EVP_PKEY *pkey; - EVP_MD_CTX ctx; - int result; + const EVP_MD *md; + EVP_MD_CTX *ctx; + int siglen, result; GetPKey(self, pkey); + ossl_pkey_check_public_key(pkey); + md = GetDigestPtr(digest); StringValue(sig); + siglen = RSTRING_LENINT(sig); StringValue(data); - EVP_VerifyInit(&ctx, GetDigestPtr(digest)); - EVP_VerifyUpdate(&ctx, RSTRING_PTR(data), RSTRING_LEN(data)); - result = EVP_VerifyFinal(&ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), pkey); - EVP_MD_CTX_cleanup(&ctx); + + ctx = EVP_MD_CTX_new(); + if (!ctx) + ossl_raise(ePKeyError, "EVP_MD_CTX_new"); + if (!EVP_VerifyInit_ex(ctx, md, NULL)) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_VerifyInit_ex"); + } + if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_VerifyUpdate"); + } + result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey); + EVP_MD_CTX_free(ctx); switch (result) { case 0: + ossl_clear_error(); return Qfalse; case 1: return Qtrue; default: - ossl_raise(ePKeyError, NULL); + ossl_raise(ePKeyError, "EVP_VerifyFinal"); } - return Qnil; /* dummy */ } /* * INIT */ void Init_ossl_pkey(void) { +#undef rb_intern #if 0 - mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */ + mOSSL = rb_define_module("OpenSSL"); + eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* Document-module: OpenSSL::PKey * * == Asymmetric Public Key Algorithms @@ -372,10 +424,10 @@ * Asymmetric public key algorithms solve the problem of establishing and * sharing secret keys to en-/decrypt messages. The key in such an * algorithm consists of two parts: a public key that may be distributed * to others and a private key that needs to remain secret. * - * Messages encrypted with a public key can only be encrypted by + * Messages encrypted with a public key can only be decrypted by * recipients that are in possession of the associated private key. * Since public key algorithms are considerably slower than symmetric * key algorithms (cf. OpenSSL::Cipher) they are often used to establish * a symmetric key shared between two parties that are in possession of * each other's public key.