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.