diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2016-05-31 11:31:27 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2016-05-31 11:31:27 +0900 |
commit | 478c74340f9ea91d6b18a1c4836a4edb2357c9dc (patch) | |
tree | 86d78a22548b105273054941c4a5d1daeabc1a5f | |
parent | 1cc7eea683044c0c295a6fdb05e848cc6554e361 (diff) | |
parent | 2293533fec90b4adc5368f2fd1ef0fec0832b2f4 (diff) | |
download | ruby-openssl-478c74340f9ea91d6b18a1c4836a4edb2357c9dc.tar.gz |
Merge changes from Ruby trunk r53718..r55224
* ruby-trunk r53718..r55224: (46 commits)
(r55224) openssl: fix test failure on Fedora 23
(r55219) openssl: use NUM2TIMET() to convert Integer to time_t
(r55214) openssl: add SSLContext#ecdh_curves=
(r55191) openssl: move SSLSocket#initialize to C extension
(r55175) openssl: avoid NULL dereference in {DH,DSA,RSA}_size()
(r55162) openssl: drop OpenSSL 0.9.6/0.9.7 support
(r55161) openssl: remove unnecessary 'extern "C"' blocks from local ..
(r55160) openssl: remove check of OPENSSL_FIPS macro in extconf.rb
(r55152) openssl: add EC.generate
(r55151) openssl: rename EC#generate_key to EC#generate_key!
(r55146) openssl: make Cipher#key= and #iv= reject too long values
(r55145) openssl: avoid deprecated M_ASN1_* macros
(r55135) openssl: Avoid reference to unset global variable in Init_o..
(r55134) openssl: use StringValueCStr() where NUL-terminated string ..
(r55132) openssl: fix incorrect return value check of RAND_* functions
(r55100) openssl: fix possible SEGV on race between SSLSocket#stop a..
(r55099) openssl: fix SSL client example in documentation [ci skip]
(r55098) openssl: add OpenSSL::PKey::EC#private? and #public?
(r55097) openssl: remove impossible EOFError raise in OpenSSL::Buffe..
(r55089) ossl.c: suppress warnings
(r55087) openssl: improve handling of password for encrypted PEM
(r55078) openssl: don't test default session lifetime value
(r55074) openssl: register ex_data index for X509_STORE{_CTX,} respe..
(r55071) openssl: check argument type in OpenSSL::X509::Attribute#va..
(r55060) openssl: fix test failure due to the previous commit
(r55059) openssl: fix test failure of OpenSSL::TestEC#test_ec_point_..
(r55058) test_random.rb: skip old OpenSSL
(r55057) openssl: fix OpenSSL error queue leak in OpenSSL::PKCS12.new
(r55056) openssl/test_random.rb: requires utils.rb
(r55052) ossl_pkey_ec.c: index types
(r55051) openssl: clear OpenSSL error queue before return to Ruby
(r55050) openssl: report errors in OpenSSL error queue when clear it
(r55048) ext/openssl: refactor OpenSSL::PKey::EC::Point#mul
(r55047) openssl: fix equality test methods of OpenSSL::BN
(r55029) Use Integer instead of Fixnum and Bignum.
(r54959) openssl/extconf.rb: NO SSL macros first
(r54950) * ext/openssl/extconf.rb: asume it doesn't have SSLv2 relat..
(r54506) test_ssl.rb: skip unsupported ciphers
(r54498) test_ssl.rb: skip unavailable pkeys
(r54487) test_get_ephemeral_key: use assert_instance_of
(r54485) openssl: Access to ephemeral TLS session key
(r54466) openssl: accept moving write buffer for write_nonblock
(r54258) openssl: fix build when NPN is disabled by OpenSSL
(r54097) document OpenSSL::SSL::SSLContext#setup as MT-unsafe
(r53871) test: use assert_not_*
(r53723) Fix typo on OpenSSL::PKey doc [ci skip]
Sync-with-trunk: r55224
66 files changed, 1471 insertions, 1567 deletions
diff --git a/ext/openssl/deprecation.rb b/ext/openssl/deprecation.rb index d7735367..7dfc87c1 100644 --- a/ext/openssl/deprecation.rb +++ b/ext/openssl/deprecation.rb @@ -16,7 +16,11 @@ module OpenSSL end def self.check_func(func, header) - have_func(func, header, deprecated_warning_flag) and - have_header(header, nil, deprecated_warning_flag) + have_func(func, header, deprecated_warning_flag) + end + + def self.check_func_or_macro(func, header) + check_func(func, header) or + have_macro(func, header) && $defs.push("-DHAVE_#{func.upcase}") end end diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 7bb6cd8f..eeeae448 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -19,12 +19,15 @@ dir_config("kerberos") Logging::message "=== OpenSSL for Ruby configurator ===\n" +# Add -Werror=deprecated-declarations to $warnflags if available +OpenSSL.deprecated_warning_flag + ## # Adds -DOSSL_DEBUG for compilation and some more targets when GCC is used # To turn it on, use: --with-debug or --enable-debug # if with_config("debug") or enable_config("debug") - $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" + $defs.push("-DOSSL_DEBUG") end Logging::message "=== Checking for system dependent stuff... ===\n" @@ -39,7 +42,6 @@ if $mingw end result = pkg_config("openssl") && have_header("openssl/ssl.h") - unless result result = have_header("openssl/ssl.h") result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "OpenSSL_add_all_digests")} @@ -51,107 +53,63 @@ unless result end end -unless have_header("openssl/conf_api.h") - raise "OpenSSL 0.9.6 or later required." +result = checking_for("OpenSSL version is 0.9.8 or later") { + try_static_assert("OPENSSL_VERSION_NUMBER >= 0x00908000L", "openssl/opensslv.h") +} +unless result + raise "OpenSSL 0.9.8 or later required." end + unless OpenSSL.check_func("SSL_library_init()", "openssl/ssl.h") raise "Ignore OpenSSL broken by Apple.\nPlease use another openssl. (e.g. using `configure --with-openssl-dir=/path/to/openssl')" end Logging::message "=== Checking for OpenSSL features... ===\n" -have_func("ERR_peek_last_error") -have_func("ASN1_put_eoc") -have_func("BN_mod_add") -have_func("BN_mod_sqr") -have_func("BN_mod_sub") -have_func("BN_pseudo_rand_range") -have_func("BN_rand_range") -have_func("CONF_get1_default_config_file") +# compile options + +# check OPENSSL_NO_{SSL2,SSL3_METHOD} macro: on some environment, these symbols +# exist even if compiled with no-ssl2 or no-ssl3-method. +unless have_macro("OPENSSL_NO_SSL2", "openssl/opensslconf.h") + have_func("SSLv2_method") +end +unless have_macro("OPENSSL_NO_SSL3_METHOD", "openssl/opensslconf.h") + have_func("SSLv3_method") +end +have_func("TLSv1_1_method") +have_func("TLSv1_2_method") +have_func("RAND_egd") +engines = %w{builtin_engines openbsd_dev_crypto dynamic 4758cca aep atalla chil + cswift nuron sureware ubsec padlock capi gmp gost cryptodev aesni} +engines.each { |name| + OpenSSL.check_func_or_macro("ENGINE_load_#{name}", "openssl/engine.h") +} + +# added in 1.0.0 have_func("EVP_CIPHER_CTX_copy") -have_func("EVP_CIPHER_CTX_set_padding") -have_func("EVP_CipherFinal_ex") -have_func("EVP_CipherInit_ex") -have_func("EVP_DigestFinal_ex") -have_func("EVP_DigestInit_ex") -have_func("EVP_MD_CTX_cleanup") -have_func("EVP_MD_CTX_create") -have_func("EVP_MD_CTX_destroy") -have_func("EVP_MD_CTX_init") -have_func("HMAC_CTX_cleanup") have_func("HMAC_CTX_copy") -have_func("HMAC_CTX_init") -have_func("PEM_def_callback") have_func("PKCS5_PBKDF2_HMAC") -have_func("PKCS5_PBKDF2_HMAC_SHA1") -have_func("RAND_egd") -have_func("X509V3_set_nconf") -have_func("X509V3_EXT_nconf_nid") -have_func("X509_CRL_add0_revoked") -have_func("X509_CRL_set_issuer_name") -have_func("X509_CRL_set_version") -have_func("X509_CRL_sort") have_func("X509_NAME_hash_old") -have_func("X509_STORE_get_ex_data") -have_func("X509_STORE_set_ex_data") -have_func("OBJ_NAME_do_all_sorted") -have_func("SSL_SESSION_get_id") -have_func("SSL_SESSION_cmp") -have_func("OPENSSL_cleanse") -have_func("SSLv2_method") -have_func("SSLv2_server_method") -have_func("SSLv2_client_method") -have_func("SSLv3_method") -have_func("SSLv3_server_method") -have_func("SSLv3_client_method") -have_func("TLSv1_1_method") -have_func("TLSv1_1_server_method") -have_func("TLSv1_1_client_method") -have_func("TLSv1_2_method") -have_func("TLSv1_2_server_method") -have_func("TLSv1_2_client_method") -have_func("SSL_CTX_set_alpn_select_cb") -have_macro("OPENSSL_NPN_NEGOTIATED", ['openssl/ssl.h']) && $defs.push("-DHAVE_OPENSSL_NPN_NEGOTIATED") -unless have_func("SSL_set_tlsext_host_name", ['openssl/ssl.h']) - have_macro("SSL_set_tlsext_host_name", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_SET_TLSEXT_HOST_NAME") -end -if have_header("openssl/engine.h") - have_func("ENGINE_add") - have_func("ENGINE_load_builtin_engines") - have_func("ENGINE_load_openbsd_dev_crypto") - have_func("ENGINE_get_digest") - have_func("ENGINE_get_cipher") - have_func("ENGINE_cleanup") - have_func("ENGINE_load_dynamic") - have_func("ENGINE_load_4758cca") - have_func("ENGINE_load_aep") - have_func("ENGINE_load_atalla") - have_func("ENGINE_load_chil") - have_func("ENGINE_load_cswift") - have_func("ENGINE_load_nuron") - have_func("ENGINE_load_sureware") - have_func("ENGINE_load_ubsec") - have_func("ENGINE_load_padlock") - have_func("ENGINE_load_capi") - have_func("ENGINE_load_gmp") - have_func("ENGINE_load_gost") - have_func("ENGINE_load_cryptodev") - have_func("ENGINE_load_aesni") -end -have_func("DH_generate_parameters_ex") -have_func("DSA_generate_parameters_ex") -have_func("RSA_generate_key_ex") -if checking_for('OpenSSL version is 0.9.7 or later') { - try_static_assert('OPENSSL_VERSION_NUMBER >= 0x00907000L', 'openssl/opensslv.h') - } - have_header("openssl/ocsp.h") -end +have_func("SSL_SESSION_cmp") # removed +OpenSSL.check_func_or_macro("SSL_set_tlsext_host_name", "openssl/ssl.h") have_struct_member("CRYPTO_THREADID", "ptr", "openssl/crypto.h") -have_struct_member("EVP_CIPHER_CTX", "flags", "openssl/evp.h") -have_struct_member("EVP_CIPHER_CTX", "engine", "openssl/evp.h") -have_struct_member("X509_ATTRIBUTE", "single", "openssl/x509.h") -have_macro("OPENSSL_FIPS", ['openssl/opensslconf.h']) && $defs.push("-DHAVE_OPENSSL_FIPS") + +# added in 1.0.1 +have_func("SSL_CTX_set_next_proto_select_cb") have_macro("EVP_CTRL_GCM_GET_TAG", ['openssl/evp.h']) && $defs.push("-DHAVE_AUTHENTICATED_ENCRYPTION") +# added in 1.0.2 +have_func("EC_curve_nist2nid") +have_func("X509_REVOKED_dup") +have_func("SSL_CTX_set_alpn_select_cb") +OpenSSL.check_func_or_macro("SSL_CTX_set1_curves_list", "openssl/ssl.h") +OpenSSL.check_func_or_macro("SSL_CTX_set_ecdh_auto", "openssl/ssl.h") +OpenSSL.check_func_or_macro("SSL_get_server_tmp_key", "openssl/ssl.h") + +# added in 1.1.0 +have_func("X509_STORE_get_ex_data") +have_func("X509_STORE_set_ex_data") +OpenSSL.check_func_or_macro("SSL_CTX_set_tmp_ecdh_callback", "openssl/ssl.h") # removed + Logging::message "=== Checking done. ===\n" create_header diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c index bd8eef5e..796d8aa0 100644 --- a/ext/openssl/openssl_missing.c +++ b/ext/openssl/openssl_missing.c @@ -9,107 +9,18 @@ */ #include RUBY_EXTCONF_H -#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_EVP_CIPHER_CTX_ENGINE) +#include <string.h> /* memcpy() */ +#if !defined(OPENSSL_NO_ENGINE) # include <openssl/engine.h> #endif -#include <openssl/x509_vfy.h> - #if !defined(OPENSSL_NO_HMAC) -#include <string.h> /* memcpy() */ -#include <openssl/hmac.h> - -#include "openssl_missing.h" - -#if !defined(HAVE_HMAC_CTX_COPY) -void -HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in) -{ - if (!out || !in) return; - memcpy(out, in, sizeof(HMAC_CTX)); - - EVP_MD_CTX_copy(&out->md_ctx, &in->md_ctx); - EVP_MD_CTX_copy(&out->i_ctx, &in->i_ctx); - EVP_MD_CTX_copy(&out->o_ctx, &in->o_ctx); -} -#endif /* HAVE_HMAC_CTX_COPY */ -#endif /* NO_HMAC */ - -#if !defined(HAVE_X509_STORE_SET_EX_DATA) -int X509_STORE_set_ex_data(X509_STORE *str, int idx, void *data) -{ - return CRYPTO_set_ex_data(&str->ex_data, idx, data); -} -#endif - -#if !defined(HAVE_X509_STORE_GET_EX_DATA) -void *X509_STORE_get_ex_data(X509_STORE *str, int idx) -{ - return CRYPTO_get_ex_data(&str->ex_data, idx); -} -#endif - -#if !defined(HAVE_EVP_MD_CTX_CREATE) -EVP_MD_CTX * -EVP_MD_CTX_create(void) -{ - EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof(EVP_MD_CTX)); - if (!ctx) return NULL; - - memset(ctx, 0, sizeof(EVP_MD_CTX)); - - return ctx; -} -#endif - -#if !defined(HAVE_EVP_MD_CTX_CLEANUP) -int -EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) -{ - /* FIXME!!! */ - memset(ctx, 0, sizeof(EVP_MD_CTX)); - - return 1; -} -#endif - -#if !defined(HAVE_EVP_MD_CTX_DESTROY) -void -EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) -{ - EVP_MD_CTX_cleanup(ctx); - OPENSSL_free(ctx); -} -#endif - -#if !defined(HAVE_EVP_MD_CTX_INIT) -void -EVP_MD_CTX_init(EVP_MD_CTX *ctx) -{ - memset(ctx, 0, sizeof(EVP_MD_CTX)); -} -#endif - -#if !defined(HAVE_HMAC_CTX_INIT) -void -HMAC_CTX_init(HMAC_CTX *ctx) -{ - EVP_MD_CTX_init(&ctx->i_ctx); - EVP_MD_CTX_init(&ctx->o_ctx); - EVP_MD_CTX_init(&ctx->md_ctx); -} +# include <openssl/hmac.h> #endif +#include <openssl/x509_vfy.h> -#if !defined(HAVE_HMAC_CTX_CLEANUP) -void -HMAC_CTX_cleanup(HMAC_CTX *ctx) -{ - EVP_MD_CTX_cleanup(&ctx->i_ctx); - EVP_MD_CTX_cleanup(&ctx->o_ctx); - EVP_MD_CTX_cleanup(&ctx->md_ctx); - memset(ctx, 0, sizeof(HMAC_CTX)); -} -#endif +#include "openssl_missing.h" +/* added in 1.0.0 */ #if !defined(HAVE_EVP_CIPHER_CTX_COPY) /* * this function does not exist in OpenSSL yet... or ever?. @@ -117,11 +28,11 @@ HMAC_CTX_cleanup(HMAC_CTX *ctx) * tested on 0.9.7d. */ int -EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, EVP_CIPHER_CTX *in) +EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) { memcpy(out, in, sizeof(EVP_CIPHER_CTX)); -#if defined(HAVE_ENGINE_ADD) && defined(HAVE_EVP_CIPHER_CTX_ENGINE) +#if !defined(OPENSSL_NO_ENGINE) if (in->engine) ENGINE_add(out->engine); if (in->cipher_data) { out->cipher_data = OPENSSL_malloc(in->cipher->ctx_size); @@ -133,222 +44,54 @@ EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, EVP_CIPHER_CTX *in) } #endif -#if !defined(HAVE_X509_CRL_SET_VERSION) -int -X509_CRL_set_version(X509_CRL *x, long version) -{ - if (x == NULL || x->crl == NULL) return 0; - if (x->crl->version == NULL) { - x->crl->version = M_ASN1_INTEGER_new(); - if (x->crl->version == NULL) return 0; - } - return ASN1_INTEGER_set(x->crl->version, version); -} -#endif - -#if !defined(HAVE_X509_CRL_SET_ISSUER_NAME) -int -X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name) -{ - if (x == NULL || x->crl == NULL) return 0; - return X509_NAME_set(&x->crl->issuer, name); -} -#endif - -#if !defined(HAVE_X509_CRL_SORT) -int -X509_CRL_sort(X509_CRL *c) -{ - int i; - X509_REVOKED *r; - /* sort the data so it will be written in serial - * number order */ - sk_X509_REVOKED_sort(c->crl->revoked); - for (i=0; i<sk_X509_REVOKED_num(c->crl->revoked); i++) { - r=sk_X509_REVOKED_value(c->crl->revoked, i); - r->sequence=i; - } - return 1; -} -#endif - -#if !defined(HAVE_X509_CRL_ADD0_REVOKED) -static int -OSSL_X509_REVOKED_cmp(const X509_REVOKED * const *a, const X509_REVOKED * const *b) -{ - return(ASN1_STRING_cmp( - (ASN1_STRING *)(*a)->serialNumber, - (ASN1_STRING *)(*b)->serialNumber)); -} - -int -X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev) -{ - X509_CRL_INFO *inf; - - inf = crl->crl; - if (!inf->revoked) - inf->revoked = sk_X509_REVOKED_new(OSSL_X509_REVOKED_cmp); - if (!inf->revoked || !sk_X509_REVOKED_push(inf->revoked, rev)) - return 0; - return 1; -} -#endif - -#if !defined(HAVE_BN_MOD_SQR) -int -BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx) -{ - if (!BN_sqr(r, (BIGNUM*)a, ctx)) return 0; - return BN_mod(r, r, m, ctx); -} -#endif - -#if !defined(HAVE_BN_MOD_ADD) || !defined(HAVE_BN_MOD_SUB) -int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx) -{ - if (!BN_mod(r,m,d,ctx)) return 0; - if (!r->neg) return 1; - return (d->neg ? BN_sub : BN_add)(r, r, d); -} -#endif - -#if !defined(HAVE_BN_MOD_ADD) -int -BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx) -{ - if (!BN_add(r, a, b)) return 0; - return BN_nnmod(r, r, m, ctx); -} -#endif - -#if !defined(HAVE_BN_MOD_SUB) -int -BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx) -{ - if (!BN_sub(r, a, b)) return 0; - return BN_nnmod(r, r, m, ctx); -} -#endif - -#if !defined(HAVE_BN_RAND_RANGE) || !defined(HAVE_BN_PSEUDO_RAND_RANGE) -static int -bn_rand_range(int pseudo, BIGNUM *r, BIGNUM *range) -{ - int (*bn_rand)(BIGNUM *, int, int, int) = pseudo ? BN_pseudo_rand : BN_rand; - int n; - - if (range->neg || BN_is_zero(range)) return 0; - - n = BN_num_bits(range); - - if (n == 1) { - if (!BN_zero(r)) return 0; - } else if (!BN_is_bit_set(range, n - 2) && !BN_is_bit_set(range, n - 3)) { - do { - if (!bn_rand(r, n + 1, -1, 0)) return 0; - if (BN_cmp(r ,range) >= 0) { - if (!BN_sub(r, r, range)) return 0; - if (BN_cmp(r, range) >= 0) - if (!BN_sub(r, r, range)) return 0; - } - } while (BN_cmp(r, range) >= 0); - } else { - do { - if (!bn_rand(r, n, -1, 0)) return 0; - } while (BN_cmp(r, range) >= 0); - } - - return 1; -} -#endif - -#if !defined(HAVE_BN_RAND_RANGE) -int -BN_rand_range(BIGNUM *r, BIGNUM *range) +#if !defined(OPENSSL_NO_HMAC) +#if !defined(HAVE_HMAC_CTX_COPY) +void +HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in) { - return bn_rand_range(0, r, range); -} -#endif + if (!out || !in) return; + memcpy(out, in, sizeof(HMAC_CTX)); -#if !defined(HAVE_BN_PSEUDO_RAND_RANGE) -int -BN_pseudo_rand_range(BIGNUM *r, BIGNUM *range) -{ - return bn_rand_range(1, r, range); + EVP_MD_CTX_copy(&out->md_ctx, &in->md_ctx); + EVP_MD_CTX_copy(&out->i_ctx, &in->i_ctx); + EVP_MD_CTX_copy(&out->o_ctx, &in->o_ctx); } -#endif - -#if !defined(HAVE_CONF_GET1_DEFAULT_CONFIG_FILE) -#define OPENSSL_CONF "openssl.cnf" -char * -CONF_get1_default_config_file(void) -{ - char *file; - int len; - - file = getenv("OPENSSL_CONF"); - if (file) return BUF_strdup(file); - len = strlen(X509_get_default_cert_area()); -#ifndef OPENSSL_SYS_VMS - len++; -#endif - len += strlen(OPENSSL_CONF); - file = OPENSSL_malloc(len + 1); - if (!file) return NULL; - strcpy(file,X509_get_default_cert_area()); -#ifndef OPENSSL_SYS_VMS - strcat(file,"/"); -#endif - strcat(file,OPENSSL_CONF); +#endif /* HAVE_HMAC_CTX_COPY */ +#endif /* NO_HMAC */ - return file; -} -#endif +/* added in 1.0.2 */ +#if !defined(OPENSSL_NO_EC) +#if !defined(HAVE_EC_CURVE_NIST2NID) +static struct { + const char *name; + int nid; +} nist_curves[] = { + {"B-163", NID_sect163r2}, + {"B-233", NID_sect233r1}, + {"B-283", NID_sect283r1}, + {"B-409", NID_sect409r1}, + {"B-571", NID_sect571r1}, + {"K-163", NID_sect163k1}, + {"K-233", NID_sect233k1}, + {"K-283", NID_sect283k1}, + {"K-409", NID_sect409k1}, + {"K-571", NID_sect571k1}, + {"P-192", NID_X9_62_prime192v1}, + {"P-224", NID_secp224r1}, + {"P-256", NID_X9_62_prime256v1}, + {"P-384", NID_secp384r1}, + {"P-521", NID_secp521r1} +}; -#if !defined(HAVE_PEM_DEF_CALLBACK) -#define OSSL_PASS_MIN_LENGTH 4 int -PEM_def_callback(char *buf, int num, int w, void *key) +EC_curve_nist2nid(const char *name) { - int i,j; - const char *prompt; - - if (key) { - i = strlen(key); - i = (i > num) ? num : i; - memcpy(buf, key, i); - return i; - } - - prompt = EVP_get_pw_prompt(); - if (prompt == NULL) prompt = "Enter PEM pass phrase:"; - for (;;) { - i = EVP_read_pw_string(buf, num, prompt, w); - if (i != 0) { - memset(buf, 0, (unsigned int)num); - return(-1); - } - j = strlen(buf); - if (j < OSSL_PASS_MIN_LENGTH) { - fprintf(stderr, - "phrase is too short, needs to be at least %d chars\n", - OSSL_PASS_MIN_LENGTH); - } - else break; + size_t i; + for (i = 0; i < (sizeof(nist_curves) / sizeof(nist_curves[0])); i++) { + if (!strcmp(nist_curves[i].name, name)) + return nist_curves[i].nid; } - return j; + return NID_undef; } #endif - -#if !defined(HAVE_ASN1_PUT_EOC) -int -ASN1_put_eoc(unsigned char **pp) -{ - unsigned char *p = *pp; - *p++ = 0; - *p++ = 0; - *pp = p; - return 2; -} #endif diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 2dc49d3f..b8f9d3d1 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -10,187 +10,39 @@ #if !defined(_OSSL_OPENSSL_MISSING_H_) #define _OSSL_OPENSSL_MISSING_H_ -#if defined(__cplusplus) -extern "C" { -#endif - -#ifndef TYPEDEF_D2I_OF -typedef char *d2i_of_void(); -#endif -#ifndef TYPEDEF_I2D_OF -typedef int i2d_of_void(); -#endif - -/* - * These functions are not included in headers of OPENSSL <= 0.9.6b - */ - -#if !defined(PEM_read_bio_DSAPublicKey) -# define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ - (d2i_of_void *)d2i_DSAPublicKey,PEM_STRING_DSA_PUBLIC,(bp),(void **)(x),(cb),(u)) -#endif - -#if !defined(PEM_write_bio_DSAPublicKey) -# define PEM_write_bio_DSAPublicKey(bp,x) \ - PEM_ASN1_write_bio((i2d_of_void *)i2d_DSAPublicKey,\ - PEM_STRING_DSA_PUBLIC,\ - (bp),(char *)(x), NULL, NULL, 0, NULL, NULL) -#endif - -#if !defined(DSAPrivateKey_dup) -# define DSAPrivateKey_dup(dsa) (DSA *)ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey, \ - (d2i_of_void *)d2i_DSAPrivateKey,(char *)(dsa)) -#endif - -#if !defined(DSAPublicKey_dup) -# define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup((i2d_of_void *)i2d_DSAPublicKey, \ - (d2i_of_void *)d2i_DSAPublicKey,(char *)(dsa)) -#endif - -#if !defined(X509_REVOKED_dup) -# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((i2d_of_void *)i2d_X509_REVOKED, \ - (d2i_of_void *)d2i_X509_REVOKED, (char *)(rev)) -#endif - -#if !defined(PKCS7_SIGNER_INFO_dup) -# define PKCS7_SIGNER_INFO_dup(si) (PKCS7_SIGNER_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_SIGNER_INFO, \ - (d2i_of_void *)d2i_PKCS7_SIGNER_INFO, (char *)(si)) -#endif - -#if !defined(PKCS7_RECIP_INFO_dup) -# define PKCS7_RECIP_INFO_dup(ri) (PKCS7_RECIP_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO, \ - (d2i_of_void *)d2i_PKCS7_RECIP_INFO, (char *)(ri)) -#endif - -#if !defined(HAVE_HMAC_CTX_INIT) -void HMAC_CTX_init(HMAC_CTX *ctx); +/* added in 1.0.0 */ +#if !defined(HAVE_EVP_CIPHER_CTX_COPY) +int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in); #endif #if !defined(HAVE_HMAC_CTX_COPY) void HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in); #endif -#if !defined(HAVE_HMAC_CTX_CLEANUP) -void HMAC_CTX_cleanup(HMAC_CTX *ctx); -#endif - -#if !defined(HAVE_EVP_MD_CTX_CREATE) -EVP_MD_CTX *EVP_MD_CTX_create(void); -#endif - -#if !defined(HAVE_EVP_MD_CTX_INIT) -void EVP_MD_CTX_init(EVP_MD_CTX *ctx); -#endif - -#if !defined(HAVE_EVP_MD_CTX_CLEANUP) -int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx); -#endif - -#if !defined(HAVE_EVP_MD_CTX_DESTROY) -void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); -#endif - -#if !defined(HAVE_EVP_CIPHER_CTX_COPY) -int EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, EVP_CIPHER_CTX *in); -#endif - -#if !defined(HAVE_EVP_DIGESTINIT_EX) -# define EVP_DigestInit_ex(ctx, md, engine) EVP_DigestInit((ctx), (md)) -#endif -#if !defined(HAVE_EVP_DIGESTFINAL_EX) -# define EVP_DigestFinal_ex(ctx, buf, len) EVP_DigestFinal((ctx), (buf), (len)) -#endif - -#if !defined(HAVE_EVP_CIPHERINIT_EX) -# define EVP_CipherInit_ex(ctx, type, impl, key, iv, enc) EVP_CipherInit((ctx), (type), (key), (iv), (enc)) -#endif -#if !defined(HAVE_EVP_CIPHERFINAL_EX) -# define EVP_CipherFinal_ex(ctx, outm, outl) EVP_CipherFinal((ctx), (outm), (outl)) -#endif - -#if !defined(EVP_CIPHER_name) -# define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e)) -#endif - -#if !defined(EVP_MD_name) -# define EVP_MD_name(e) OBJ_nid2sn(EVP_MD_type(e)) -#endif - -#if !defined(HAVE_EVP_HMAC_INIT_EX) -# define HMAC_Init_ex(ctx, key, len, digest, engine) HMAC_Init((ctx), (key), (len), (digest)) -#endif - -#if !defined(PKCS7_is_detached) -# define PKCS7_is_detached(p7) (PKCS7_type_is_signed(p7) && PKCS7_get_detached(p7)) +/* added in 1.0.2 */ +#if !defined(OPENSSL_NO_EC) +#if !defined(HAVE_EC_CURVE_NIST2NID) +int EC_curve_nist2nid(const char *); #endif - -#if !defined(PKCS7_type_is_encrypted) -# define PKCS7_type_is_encrypted(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_encrypted) #endif -#if !defined(HAVE_OPENSSL_CLEANSE) -#define OPENSSL_cleanse(p, l) memset((p), 0, (l)) +#if !defined(HAVE_X509_REVOKED_DUP) +# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((i2d_of_void *)i2d_X509_REVOKED, \ + (d2i_of_void *)d2i_X509_REVOKED, (char *)(rev)) #endif +/* added in 1.1.0 */ #if !defined(HAVE_X509_STORE_GET_EX_DATA) -void *X509_STORE_get_ex_data(X509_STORE *str, int idx); +# define X509_STORE_get_ex_data(x, idx) \ + CRYPTO_get_ex_data(&(x)->ex_data, (idx)) #endif #if !defined(HAVE_X509_STORE_SET_EX_DATA) -int X509_STORE_set_ex_data(X509_STORE *str, int idx, void *data); -#endif - -#if !defined(HAVE_X509_CRL_SET_VERSION) -int X509_CRL_set_version(X509_CRL *x, long version); -#endif - -#if !defined(HAVE_X509_CRL_SET_ISSUER_NAME) -int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name); -#endif - -#if !defined(HAVE_X509_CRL_SORT) -int X509_CRL_sort(X509_CRL *c); -#endif - -#if !defined(HAVE_X509_CRL_ADD0_REVOKED) -int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev); -#endif - -#if !defined(HAVE_BN_MOD_SQR) -int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); -#endif - -#if !defined(HAVE_BN_MOD_ADD) -int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); -#endif - -#if !defined(HAVE_BN_MOD_SUB) -int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); -#endif - -#if !defined(HAVE_BN_RAND_RANGE) -int BN_rand_range(BIGNUM *r, BIGNUM *range); -#endif - -#if !defined(HAVE_BN_PSEUDO_RAND_RANGE) -int BN_pseudo_rand_range(BIGNUM *r, BIGNUM *range); -#endif - -#if !defined(HAVE_CONF_GET1_DEFAULT_CONFIG_FILE) -char *CONF_get1_default_config_file(void); -#endif - -#if !defined(HAVE_PEM_DEF_CALLBACK) -int PEM_def_callback(char *buf, int num, int w, void *key); +# define X509_STORE_set_ex_data(x, idx, data) \ + CRYPTO_set_ex_data(&(x)->ex_data, (idx), (data)) +# define X509_STORE_get_ex_new_index(l, p, newf, dupf, freef) \ + CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE, (l), (p), \ + (newf), (dupf), (freef)) #endif -#if !defined(HAVE_ASN1_PUT_EOC) -int ASN1_put_eoc(unsigned char **pp); -#endif - -#if defined(__cplusplus) -} -#endif - - #endif /* _OSSL_OPENSSL_MISSING_H_ */ diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index d03dfa7a..3654b1e8 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -147,6 +147,31 @@ ossl_buf2str(char *buf, int len) /* * our default PEM callback */ + +/* + * OpenSSL requires passwords for PEM-encoded files to be at least four + * characters long. See crypto/pem/pem_lib.c (as of 1.0.2h) + */ +#define OSSL_MIN_PWD_LEN 4 + +VALUE +ossl_pem_passwd_value(VALUE pass) +{ + if (NIL_P(pass)) + return Qnil; + + StringValue(pass); + + if (RSTRING_LEN(pass) < OSSL_MIN_PWD_LEN) + ossl_raise(eOSSLError, "password must be at least %d bytes", OSSL_MIN_PWD_LEN); + /* PEM_BUFSIZE is currently used as the second argument of pem_password_cb, + * that is +max_len+ of ossl_pem_passwd_cb() */ + if (RSTRING_LEN(pass) > PEM_BUFSIZE) + ossl_raise(eOSSLError, "password must be shorter than %d bytes", PEM_BUFSIZE); + + return pass; +} + static VALUE ossl_pem_passwd_cb0(VALUE flag) { @@ -159,13 +184,29 @@ ossl_pem_passwd_cb0(VALUE flag) } int -ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd) +ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_) { - int len, status = 0; - VALUE rflag, pass; + int len, status; + VALUE rflag, pass = (VALUE)pwd_; + + if (RTEST(pass)) { + /* PEM_def_callback(buf, max_len, flag, StringValueCStr(pass)) does not + * work because it does not allow NUL characters and truncates to 1024 + * bytes silently if the input is over 1024 bytes */ + if (RB_TYPE_P(pass, T_STRING)) { + len = RSTRING_LENINT(pass); + if (len >= OSSL_MIN_PWD_LEN && len <= max_len) { + memcpy(buf, RSTRING_PTR(pass), len); + return len; + } + } + OSSL_Debug("passed data is not valid String???"); + return -1; + } - if (pwd || !rb_block_given_p()) - return PEM_def_callback(buf, max_len, flag, pwd); + if (!rb_block_given_p()) { + return PEM_def_callback(buf, max_len, flag, NULL); + } while (1) { /* @@ -181,12 +222,12 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd) return -1; } len = RSTRING_LENINT(pass); - if (len < 4) { /* 4 is OpenSSL hardcoded limit */ - rb_warning("password must be longer than 4 bytes"); + if (len < OSSL_MIN_PWD_LEN) { + rb_warning("password must be at least %d bytes", OSSL_MIN_PWD_LEN); continue; } if (len > max_len) { - rb_warning("password must be shorter then %d bytes", max_len-1); + rb_warning("password must be shorter than %d bytes", max_len); continue; } memcpy(buf, RSTRING_PTR(pass), len); @@ -198,7 +239,8 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd) /* * Verify callback */ -int ossl_verify_cb_idx; +int ossl_store_ctx_ex_verify_cb_idx; +int ossl_store_ex_verify_cb_idx; VALUE ossl_call_verify_cb_proc(struct ossl_verify_cb_args *args) @@ -214,10 +256,10 @@ ossl_verify_cb(int ok, X509_STORE_CTX *ctx) struct ossl_verify_cb_args args; int state = 0; - proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_verify_cb_idx); - if ((void*)proc == 0) - proc = (VALUE)X509_STORE_get_ex_data(ctx->ctx, ossl_verify_cb_idx); - if ((void*)proc == 0) + proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_store_ctx_ex_verify_cb_idx); + if (!proc) + proc = (VALUE)X509_STORE_get_ex_data(ctx->ctx, ossl_store_ex_verify_cb_idx); + if (!proc) return ok; if (!NIL_P(proc)) { ret = Qfalse; @@ -297,11 +339,7 @@ ossl_make_error(VALUE exc, const char *fmt, va_list args) const char *msg; long e; -#ifdef HAVE_ERR_PEEK_LAST_ERROR e = ERR_peek_last_error(); -#else - e = ERR_peek_error(); -#endif if (fmt) { str = rb_vsprintf(fmt, args); } @@ -318,12 +356,7 @@ ossl_make_error(VALUE exc, const char *fmt, va_list args) rb_str_cat2(str, msg ? msg : "(null)"); } } - if (dOSSL == Qtrue){ /* show all errors on the stack */ - while ((e = ERR_get_error()) != 0){ - rb_warn("error on stack: %s", ERR_error_string(e, NULL)); - } - } - ERR_clear_error(); + ossl_clear_error(); if (NIL_P(str)) str = rb_str_new(0, 0); return rb_exc_new3(exc, str); @@ -351,6 +384,18 @@ ossl_exc_new(VALUE exc, const char *fmt, ...) return err; } +void +ossl_clear_error(void) +{ + if (dOSSL == Qtrue) { + long e; + while ((e = ERR_get_error())) { + rb_warn("error on stack: %s", ERR_error_string(e, NULL)); + } + } + ERR_clear_error(); +} + /* * call-seq: * OpenSSL.errors -> [String...] @@ -446,7 +491,7 @@ static VALUE ossl_fips_mode_set(VALUE self, VALUE enabled) { -#ifdef HAVE_OPENSSL_FIPS +#ifdef OPENSSL_FIPS if (RTEST(enabled)) { int mode = FIPS_mode(); if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */ @@ -1018,8 +1063,8 @@ static void Init_ossl_locks(void) * * require 'socket' * - * tcp_client = TCPSocket.new 'localhost', 5000 - * ssl_client = OpenSSL::SSL::SSLSocket.new client_socket, context + * tcp_socket = TCPSocket.new 'localhost', 5000 + * ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context * ssl_client.connect * * ssl_client.puts "hello server!" @@ -1038,8 +1083,8 @@ static void Init_ossl_locks(void) * * require 'socket' * - * tcp_client = TCPSocket.new 'localhost', 5000 - * ssl_client = OpenSSL::SSL::SSLSocket.new client_socket, context + * tcp_socket = TCPSocket.new 'localhost', 5000 + * ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context * ssl_client.connect * * ssl_client.puts "hello server!" @@ -1113,7 +1158,7 @@ Init_openssl(void) /* * Boolean indicating whether OpenSSL is FIPS-enabled or not */ -#ifdef HAVE_OPENSSL_FIPS +#ifdef OPENSSL_FIPS rb_define_const(mOSSL, "OPENSSL_FIPS", Qtrue); #else rb_define_const(mOSSL, "OPENSSL_FIPS", Qfalse); @@ -1128,12 +1173,6 @@ Init_openssl(void) rb_global_variable(&eOSSLError); /* - * Verify callback Proc index for ext-data - */ - if ((ossl_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"ossl_verify_cb_idx", 0, 0, 0)) < 0) - ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); - - /* * Init debug core */ dOSSL = Qfalse; @@ -1144,6 +1183,14 @@ Init_openssl(void) rb_define_module_function(mOSSL, "errors", ossl_get_errors, 0); /* + * Verify callback Proc index for ext-data + */ + if ((ossl_store_ctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"ossl_store_ctx_ex_verify_cb_idx", 0, 0, 0)) < 0) + ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); + if ((ossl_store_ex_verify_cb_idx = X509_STORE_get_ex_new_index(0, (void *)"ossl_store_ex_verify_cb_idx", 0, 0, 0)) < 0) + ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index"); + + /* * Get ID of to_der */ ossl_s_to_der = rb_intern("to_der"); diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 3be01b0c..2317fa8b 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -12,10 +12,6 @@ #include RUBY_EXTCONF_H -#if defined(__cplusplus) -extern "C" { -#endif - #if 0 mOSSL = rb_define_module("OpenSSL"); mX509 = rb_define_module_under(mOSSL, "X509"); @@ -31,11 +27,6 @@ extern "C" { #include <ruby/io.h> #include <ruby/thread.h> -/* - * Check the OpenSSL version - * The only supported are: - * OpenSSL >= 0.9.7 - */ #include <openssl/opensslv.h> #ifdef HAVE_ASSERT_H @@ -46,7 +37,6 @@ extern "C" { #if defined(_WIN32) && !defined(LIBRESSL_VERSION_NUMBER) # include <openssl/e_os2.h> -# define OSSL_NO_CONF_API 1 # if !defined(OPENSSL_SYS_WIN32) # define OPENSSL_SYS_WIN32 1 # endif @@ -54,7 +44,7 @@ extern "C" { #endif #include <errno.h> #include <openssl/err.h> -#include <openssl/asn1_mac.h> +#include <openssl/asn1.h> #include <openssl/x509v3.h> #include <openssl/ssl.h> #include <openssl/pkcs12.h> @@ -66,22 +56,13 @@ extern "C" { #if !defined(_WIN32) # include <openssl/crypto.h> #endif -#undef X509_NAME -#undef PKCS7_SIGNER_INFO -#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_EVP_CIPHER_CTX_ENGINE) -# define OSSL_ENGINE_ENABLED +#if !defined(OPENSSL_NO_ENGINE) # include <openssl/engine.h> #endif -#if defined(HAVE_OPENSSL_OCSP_H) -# define OSSL_OCSP_ENABLED +#if !defined(OPENSSL_NO_OCSP) # include <openssl/ocsp.h> #endif -/* OpenSSL requires passwords for PEM-encoded files to be at least four - * characters long - */ -#define OSSL_MIN_PWD_LEN 4 - /* * Common Module */ @@ -116,13 +97,6 @@ extern VALUE eOSSLError; } while (0) /* - * Compatibility - */ -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -#define STACK _STACK -#endif - -/* * String to HEXString conversion */ int string2hex(const unsigned char *, int, char **, int *); @@ -146,16 +120,24 @@ do{\ }while(0) /* - * our default PEM callback + * Our default PEM callback */ +/* Convert the argument to String and validate the length. Note this may raise. */ +VALUE ossl_pem_passwd_value(VALUE); +/* Can be casted to pem_password_cb. If a password (String) is passed as the + * "arbitrary data" (typically the last parameter of PEM_{read,write}_ + * functions), uses the value. If not, but a block is given, yields to it. + * If not either, fallbacks to PEM_def_callback() which reads from stdin. */ int ossl_pem_passwd_cb(char *, int, int, void *); /* * Clear BIO* with this in PEM/DER fallback scenarios to avoid decoding * errors piling up in OpenSSL::Errors */ -#define OSSL_BIO_reset(bio) (void)BIO_reset((bio)); \ - ERR_clear_error(); +#define OSSL_BIO_reset(bio) do { \ + (void)BIO_reset((bio)); \ + ossl_clear_error(); \ +} while (0) /* * ERRor messages @@ -163,11 +145,14 @@ int ossl_pem_passwd_cb(char *, int, int, void *); #define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error()) NORETURN(void ossl_raise(VALUE, const char *, ...)); VALUE ossl_exc_new(VALUE, const char *, ...); +/* Clear OpenSSL error queue. If dOSSL is set, rb_warn() them. */ +void ossl_clear_error(void); /* * Verify callback */ -extern int ossl_verify_cb_idx; +extern int ossl_store_ctx_ex_verify_cb_idx; +extern int ossl_store_ex_verify_cb_idx; struct ossl_verify_cb_args { VALUE proc; @@ -241,8 +226,4 @@ void ossl_debug(const char *, ...); void Init_openssl(void); -#if defined(__cplusplus) -} -#endif - #endif /* _OSSL_H_ */ diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 89da5949..43a3d2d8 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -75,15 +75,10 @@ asn1time_to_time(ASN1_TIME *time) return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); } -/* - * This function is not exported in Ruby's *.h - */ -extern struct timeval rb_time_timeval(VALUE); - time_t time_to_time_t(VALUE time) { - return (time_t)NUM2LONG(rb_Integer(time)); + return (time_t)NUM2TIMET(rb_Integer(time)); } /* @@ -140,7 +135,7 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) bn = GetBNPtr(obj); } else { obj = rb_String(obj); - if (!BN_dec2bn(&bn, StringValuePtr(obj))) { + if (!BN_dec2bn(&bn, StringValueCStr(obj))) { ossl_raise(eOSSLError, NULL); } } @@ -212,19 +207,6 @@ static ID sUNIVERSAL, sAPPLICATION, sCONTEXT_SPECIFIC, sPRIVATE; static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINFINITE_LENGTH, sivUNUSED_BITS; /* - * We need to implement these for backward compatibility - * reasons, behavior of ASN1_put_object and ASN1_object_size - * for infinite length values is different in OpenSSL <= 0.9.7 - */ -#if OPENSSL_VERSION_NUMBER < 0x00908000L -#define ossl_asn1_object_size(cons, len, tag) (cons) == 2 ? (len) + ASN1_object_size((cons), 0, (tag)) : ASN1_object_size((cons), (len), (tag)) -#define ossl_asn1_put_object(pp, cons, len, tag, xc) (cons) == 2 ? ASN1_put_object((pp), (cons), 0, (tag), (xc)) : ASN1_put_object((pp), (cons), (len), (tag), (xc)) -#else -#define ossl_asn1_object_size(cons, len, tag) ASN1_object_size((cons), (len), (tag)) -#define ossl_asn1_put_object(pp, cons, len, tag, xc) ASN1_put_object((pp), (cons), (len), (tag), (xc)) -#endif - -/* * Ruby to ASN1 converters */ static ASN1_BOOLEAN @@ -233,11 +215,7 @@ obj_to_asn1bool(VALUE obj) if (NIL_P(obj)) ossl_raise(rb_eTypeError, "Can't convert nil into Boolean"); -#if OPENSSL_VERSION_NUMBER < 0x00907000L - return RTEST(obj) ? 0xff : 0x100; -#else return RTEST(obj) ? 0xff : 0x0; -#endif } static ASN1_INTEGER* @@ -293,10 +271,10 @@ obj_to_asn1obj(VALUE obj) { ASN1_OBJECT *a1obj; - StringValue(obj); + StringValueCStr(obj); a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 0); if(!a1obj) a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 1); - if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID"); + if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID %"PRIsVALUE, obj); return a1obj; } @@ -778,11 +756,11 @@ ossl_asn1data_to_der(VALUE self) if (inf_length == Qtrue) { is_cons = 2; } - if((length = ossl_asn1_object_size(is_cons, RSTRING_LENINT(value), tag)) <= 0) + if((length = ASN1_object_size(is_cons, RSTRING_LENINT(value), tag)) <= 0) ossl_raise(eASN1Error, NULL); der = rb_str_new(0, length); p = (unsigned char *)RSTRING_PTR(der); - ossl_asn1_put_object(&p, is_cons, RSTRING_LENINT(value), tag, tag_class); + ASN1_put_object(&p, is_cons, RSTRING_LENINT(value), tag, tag_class); memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value)); p += RSTRING_LEN(value); ossl_str_adjust(der, p); @@ -1184,30 +1162,6 @@ ossl_asn1eoc_initialize(VALUE self) { return self; } -static int -ossl_i2d_ASN1_TYPE(ASN1_TYPE *a, unsigned char **pp) -{ -#if OPENSSL_VERSION_NUMBER < 0x00907000L - if(!a) return 0; - if(a->type == V_ASN1_BOOLEAN) - return i2d_ASN1_BOOLEAN(a->value.boolean, pp); -#endif - return i2d_ASN1_TYPE(a, pp); -} - -static void -ossl_ASN1_TYPE_free(ASN1_TYPE *a) -{ -#if OPENSSL_VERSION_NUMBER < 0x00907000L - if(!a) return; - if(a->type == V_ASN1_BOOLEAN){ - OPENSSL_free(a); - return; - } -#endif - ASN1_TYPE_free(a); -} - /* * call-seq: * asn1.to_der => DER-encoded String @@ -1228,22 +1182,22 @@ ossl_asn1prim_to_der(VALUE self) explicit = ossl_asn1_is_explicit(self); asn1 = ossl_asn1_get_asn1type(self); - len = ossl_asn1_object_size(1, ossl_i2d_ASN1_TYPE(asn1, NULL), tn); + len = ASN1_object_size(1, i2d_ASN1_TYPE(asn1, NULL), tn); if(!(buf = OPENSSL_malloc(len))){ - ossl_ASN1_TYPE_free(asn1); + ASN1_TYPE_free(asn1); ossl_raise(eASN1Error, "cannot alloc buffer"); } p = buf; if (tc == V_ASN1_UNIVERSAL) { - ossl_i2d_ASN1_TYPE(asn1, &p); + i2d_ASN1_TYPE(asn1, &p); } else if (explicit) { - ossl_asn1_put_object(&p, 1, ossl_i2d_ASN1_TYPE(asn1, NULL), tn, tc); - ossl_i2d_ASN1_TYPE(asn1, &p); + ASN1_put_object(&p, 1, i2d_ASN1_TYPE(asn1, NULL), tn, tc); + i2d_ASN1_TYPE(asn1, &p); } else { - ossl_i2d_ASN1_TYPE(asn1, &p); + i2d_ASN1_TYPE(asn1, &p); *buf = tc | tn | (*buf & V_ASN1_CONSTRUCTED); } - ossl_ASN1_TYPE_free(asn1); + ASN1_TYPE_free(asn1); reallen = p - buf; assert(reallen <= len); str = ossl_buf2str((char *)buf, rb_long2int(reallen)); /* buf will be free in ossl_buf2str */ @@ -1309,19 +1263,19 @@ ossl_asn1cons_to_der(VALUE self) explicit = ossl_asn1_is_explicit(self); value = join_der(ossl_asn1_get_value(self)); - seq_len = ossl_asn1_object_size(constructed, RSTRING_LENINT(value), tag); - length = ossl_asn1_object_size(constructed, seq_len, tn); + seq_len = ASN1_object_size(constructed, RSTRING_LENINT(value), tag); + length = ASN1_object_size(constructed, seq_len, tn); str = rb_str_new(0, length); p = (unsigned char *)RSTRING_PTR(str); if(tc == V_ASN1_UNIVERSAL) - ossl_asn1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); + ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); else{ if(explicit){ - ossl_asn1_put_object(&p, constructed, seq_len, tn, tc); - ossl_asn1_put_object(&p, constructed, RSTRING_LENINT(value), tag, V_ASN1_UNIVERSAL); + ASN1_put_object(&p, constructed, seq_len, tn, tc); + ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tag, V_ASN1_UNIVERSAL); } else{ - ossl_asn1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); + ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); } } memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value)); @@ -1374,9 +1328,9 @@ ossl_asn1cons_each(VALUE self) static VALUE ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln) { - StringValue(oid); - StringValue(sn); - StringValue(ln); + StringValueCStr(oid); + StringValueCStr(sn); + StringValueCStr(ln); if(!OBJ_create(RSTRING_PTR(oid), RSTRING_PTR(sn), RSTRING_PTR(ln))) ossl_raise(eASN1Error, NULL); @@ -1399,7 +1353,7 @@ ossl_asn1obj_get_sn(VALUE self) int nid; val = ossl_asn1_get_value(self); - if ((nid = OBJ_txt2nid(StringValuePtr(val))) != NID_undef) + if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) ret = rb_str_new2(OBJ_nid2sn(nid)); return ret; @@ -1420,7 +1374,7 @@ ossl_asn1obj_get_ln(VALUE self) int nid; val = ossl_asn1_get_value(self); - if ((nid = OBJ_txt2nid(StringValuePtr(val))) != NID_undef) + if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) ret = rb_str_new2(OBJ_nid2ln(nid)); return ret; diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index fba86cdd..5b4207ba 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -82,8 +82,8 @@ ossl_bn_new(const BIGNUM *bn) return obj; } -BIGNUM * -GetBNPtr(VALUE obj) +static BIGNUM * +try_convert_to_bnptr(VALUE obj) { BIGNUM *bn = NULL; VALUE newobj; @@ -95,19 +95,25 @@ GetBNPtr(VALUE obj) case T_BIGNUM: obj = rb_String(obj); newobj = NewBN(cBN); /* GC bug */ - if (!BN_dec2bn(&bn, StringValuePtr(obj))) { + if (!BN_dec2bn(&bn, StringValueCStr(obj))) { ossl_raise(eBNError, NULL); } SetBN(newobj, bn); /* Handle potencial mem leaks */ break; - case T_NIL: - break; - default: - ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); } return bn; } +BIGNUM * +GetBNPtr(VALUE obj) +{ + BIGNUM *bn = try_convert_to_bnptr(obj); + if (!bn) + ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); + + return bn; +} + /* * Private */ @@ -203,26 +209,25 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self) return self; } - StringValue(str); GetBN(self, bn); switch (base) { case 0: - if (!BN_mpi2bn((unsigned char *)RSTRING_PTR(str), RSTRING_LENINT(str), bn)) { + if (!BN_mpi2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) { ossl_raise(eBNError, NULL); } break; case 2: - if (!BN_bin2bn((unsigned char *)RSTRING_PTR(str), RSTRING_LENINT(str), bn)) { + if (!BN_bin2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) { ossl_raise(eBNError, NULL); } break; case 10: - if (!BN_dec2bn(&bn, RSTRING_PTR(str))) { + if (!BN_dec2bn(&bn, StringValueCStr(str))) { ossl_raise(eBNError, NULL); } break; case 16: - if (!BN_hex2bn(&bn, RSTRING_PTR(str))) { + if (!BN_hex2bn(&bn, StringValueCStr(str))) { ossl_raise(eBNError, NULL); } break; @@ -841,21 +846,78 @@ BIGNUM_CMP(ucmp) /* * call-seq: - * big.eql?(obj) => true or false + * bn == obj => true or false * - * Returns <code>true</code> only if <i>obj</i> is a - * <code>Bignum</code> with the same value as <i>big</i>. Contrast this + * Returns +true+ only if +obj+ has the same value as +bn+. Contrast this + * with OpenSSL::BN#eql?, which requires obj to be OpenSSL::BN. */ static VALUE -ossl_bn_eql(VALUE self, VALUE other) +ossl_bn_eq(VALUE self, VALUE other) { - if (ossl_bn_cmp(self, other) == INT2FIX(0)) { + BIGNUM *bn1, *bn2; + + GetBN(self, bn1); + /* BNPtr may raise, so we can't use here */ + bn2 = try_convert_to_bnptr(other); + + if (bn2 && !BN_cmp(bn1, bn2)) { return Qtrue; } return Qfalse; } /* + * call-seq: + * bn.eql?(obj) => true or false + * + * Returns <code>true</code> only if <i>obj</i> is a + * <code>OpenSSL::BN</code> with the same value as <i>big</i>. Contrast this + * with OpenSSL::BN#==, which performs type conversions. + */ +static VALUE +ossl_bn_eql(VALUE self, VALUE other) +{ + BIGNUM *bn1, *bn2; + + if (!rb_obj_is_kind_of(other, cBN)) + return Qfalse; + GetBN(self, bn1); + GetBN(other, bn2); + + return BN_cmp(bn1, bn2) ? Qfalse : Qtrue; +} + +/* + * call-seq: + * bn.hash => Integer + * + * Returns a hash code for this object. + * + * See also Object#hash. + */ +static VALUE +ossl_bn_hash(VALUE self) +{ + BIGNUM *bn; + VALUE hash; + unsigned char *buf; + int len; + + GetBN(self, bn); + len = BN_num_bytes(bn); + buf = xmalloc(len); + if (BN_bn2bin(bn, buf) != len) { + xfree(buf); + ossl_raise(eBNError, NULL); + } + + hash = INT2FIX(rb_memhash(buf, len)); + xfree(buf); + + return hash; +} + +/* * call-seq: * bn.prime? => true | false * bn.prime?(checks) => true | false @@ -982,8 +1044,9 @@ Init_ossl_bn(void) rb_define_alias(cBN, "<=>", "cmp"); rb_define_method(cBN, "ucmp", ossl_bn_ucmp, 1); rb_define_method(cBN, "eql?", ossl_bn_eql, 1); - rb_define_alias(cBN, "==", "eql?"); - rb_define_alias(cBN, "===", "eql?"); + rb_define_method(cBN, "hash", ossl_bn_hash, 0); + rb_define_method(cBN, "==", ossl_bn_eq, 1); + rb_define_alias(cBN, "===", "=="); rb_define_method(cBN, "zero?", ossl_bn_is_zero, 0); rb_define_method(cBN, "one?", ossl_bn_is_one, 0); /* is_word */ diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 09b021d9..d56b3411 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -116,7 +116,7 @@ ossl_cipher_initialize(VALUE self, VALUE str) char *name; unsigned char key[EVP_MAX_KEY_LENGTH]; - name = StringValuePtr(str); + name = StringValueCStr(str); GetCipherInit(self, ctx); if (ctx) { ossl_raise(rb_eRuntimeError, "Cipher already inititalized!"); @@ -124,7 +124,7 @@ ossl_cipher_initialize(VALUE self, VALUE str) AllocCipher(self, ctx); EVP_CIPHER_CTX_init(ctx); if (!(cipher = EVP_get_cipherbyname(name))) { - ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%s)", name); + ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str); } /* * The EVP which has EVP_CIPH_RAND_KEY flag (such as DES3) allows @@ -158,16 +158,13 @@ ossl_cipher_copy(VALUE self, VALUE other) return self; } -#ifdef HAVE_OBJ_NAME_DO_ALL_SORTED static void* add_cipher_name_to_ary(const OBJ_NAME *name, VALUE ary) { rb_ary_push(ary, rb_str_new2(name->name)); return NULL; } -#endif -#ifdef HAVE_OBJ_NAME_DO_ALL_SORTED /* * call-seq: * OpenSSL::Cipher.ciphers -> array[string...] @@ -186,9 +183,6 @@ ossl_s_ciphers(VALUE self) return ary; } -#else -#define ossl_s_ciphers rb_f_notimplement -#endif /* * call-seq: @@ -480,15 +474,17 @@ static VALUE ossl_cipher_set_key(VALUE self, VALUE key) { EVP_CIPHER_CTX *ctx; + int key_len; StringValue(key); GetCipher(self, ctx); - if (RSTRING_LEN(key) < EVP_CIPHER_CTX_key_length(ctx)) - ossl_raise(eCipherError, "key length too short"); + key_len = EVP_CIPHER_CTX_key_length(ctx); + if (RSTRING_LEN(key) != key_len) + ossl_raise(rb_eArgError, "key must be %d bytes", key_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1) - ossl_raise(eCipherError, NULL); + ossl_raise(eCipherError, NULL); return key; } @@ -512,12 +508,14 @@ static VALUE ossl_cipher_set_iv(VALUE self, VALUE iv) { EVP_CIPHER_CTX *ctx; + int iv_len; StringValue(iv); GetCipher(self, ctx); - if (RSTRING_LEN(iv) < EVP_CIPHER_CTX_iv_length(ctx)) - ossl_raise(eCipherError, "iv length too short"); + iv_len = EVP_CIPHER_CTX_iv_length(ctx); + if (RSTRING_LEN(iv) != iv_len) + ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1) ossl_raise(eCipherError, NULL); @@ -719,7 +717,6 @@ ossl_cipher_set_key_length(VALUE self, VALUE key_length) return key_length; } -#if defined(HAVE_EVP_CIPHER_CTX_SET_PADDING) /* * call-seq: * cipher.padding = integer -> integer @@ -741,9 +738,6 @@ ossl_cipher_set_padding(VALUE self, VALUE padding) ossl_raise(eCipherError, NULL); return padding; } -#else -#define ossl_cipher_set_padding rb_f_notimplement -#endif #define CIPHER_0ARG_INT(func) \ static VALUE \ diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index 44968dd9..d137dc57 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -61,7 +61,7 @@ GetDigestPtr(VALUE obj) ASN1_OBJECT_free(oid); } if(!md) - ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%s).", name); + ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%"PRIsVALUE").", obj); } else { EVP_MD_CTX *ctx; diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index 890ec724..f84b1cff 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -9,7 +9,7 @@ */ #include "ossl.h" -#if defined(OSSL_ENGINE_ENABLED) +#if !defined(OPENSSL_NO_ENGINE) #define NewEngine(klass) \ TypedData_Wrap_Struct((klass), &ossl_engine_type, 0) @@ -96,7 +96,7 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) ENGINE_load_builtin_engines(); return Qtrue; } - StringValue(name); + StringValueCStr(name); #ifndef OPENSSL_NO_STATIC_ENGINE #if HAVE_ENGINE_LOAD_DYNAMIC OSSL_ENGINE_LOAD_IF_MATCH(dynamic); @@ -148,7 +148,7 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto); #endif OSSL_ENGINE_LOAD_IF_MATCH(openssl); - rb_warning("no such builtin loader for `%s'", RSTRING_PTR(name)); + rb_warning("no such builtin loader for `%"PRIsVALUE"'", name); return Qnil; #endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */ } @@ -165,9 +165,7 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) static VALUE ossl_engine_s_cleanup(VALUE self) { -#if defined(HAVE_ENGINE_CLEANUP) ENGINE_cleanup(); -#endif return Qnil; } @@ -213,7 +211,7 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) ENGINE *e; VALUE obj; - StringValue(id); + StringValueCStr(id); ossl_engine_s_load(1, &id, klass); obj = NewEngine(klass); if(!(e = ENGINE_by_id(RSTRING_PTR(id)))) @@ -224,7 +222,7 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) ossl_raise(eEngineError, NULL); ENGINE_ctrl(e, ENGINE_CTRL_SET_PASSWORD_CALLBACK, 0, NULL, (void(*)(void))ossl_pem_passwd_cb); - ERR_clear_error(); + ossl_clear_error(); return obj; } @@ -296,7 +294,6 @@ ossl_engine_finish(VALUE self) return Qnil; } -#if defined(HAVE_ENGINE_GET_CIPHER) /* Document-method: OpenSSL::Engine#cipher * * call-seq: @@ -318,12 +315,10 @@ ossl_engine_get_cipher(VALUE self, VALUE name) { ENGINE *e; const EVP_CIPHER *ciph, *tmp; - char *s; int nid; - s = StringValuePtr(name); - tmp = EVP_get_cipherbyname(s); - if(!tmp) ossl_raise(eEngineError, "no such cipher `%s'", s); + tmp = EVP_get_cipherbyname(StringValueCStr(name)); + if(!tmp) ossl_raise(eEngineError, "no such cipher `%"PRIsVALUE"'", name); nid = EVP_CIPHER_nid(tmp); GetEngine(self, e); ciph = ENGINE_get_cipher(e, nid); @@ -331,11 +326,7 @@ ossl_engine_get_cipher(VALUE self, VALUE name) return ossl_cipher_new(ciph); } -#else -#define ossl_engine_get_cipher rb_f_notimplement -#endif -#if defined(HAVE_ENGINE_GET_DIGEST) /* Document-method: OpenSSL::Engine#digest * * call-seq: @@ -357,12 +348,10 @@ ossl_engine_get_digest(VALUE self, VALUE name) { ENGINE *e; const EVP_MD *md, *tmp; - char *s; int nid; - s = StringValuePtr(name); - tmp = EVP_get_digestbyname(s); - if(!tmp) ossl_raise(eEngineError, "no such digest `%s'", s); + tmp = EVP_get_digestbyname(StringValueCStr(name)); + if(!tmp) ossl_raise(eEngineError, "no such digest `%"PRIsVALUE"'", name); nid = EVP_MD_nid(tmp); GetEngine(self, e); md = ENGINE_get_digest(e, nid); @@ -370,9 +359,6 @@ ossl_engine_get_digest(VALUE self, VALUE name) return ossl_digest_new(md); } -#else -#define ossl_engine_get_digest rb_f_notimplement -#endif /* Document-method: OpenSSL::Engine#load_private_key * @@ -393,14 +379,10 @@ ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self) char *sid, *sdata; rb_scan_args(argc, argv, "02", &id, &data); - sid = NIL_P(id) ? NULL : StringValuePtr(id); - sdata = NIL_P(data) ? NULL : StringValuePtr(data); + sid = NIL_P(id) ? NULL : StringValueCStr(id); + sdata = NIL_P(data) ? NULL : StringValueCStr(data); GetEngine(self, e); -#if OPENSSL_VERSION_NUMBER < 0x00907000L - pkey = ENGINE_load_private_key(e, sid, sdata); -#else pkey = ENGINE_load_private_key(e, sid, NULL, sdata); -#endif if (!pkey) ossl_raise(eEngineError, NULL); obj = ossl_pkey_new(pkey); OSSL_PKEY_SET_PRIVATE(obj); @@ -427,14 +409,10 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) char *sid, *sdata; rb_scan_args(argc, argv, "02", &id, &data); - sid = NIL_P(id) ? NULL : StringValuePtr(id); - sdata = NIL_P(data) ? NULL : StringValuePtr(data); + sid = NIL_P(id) ? NULL : StringValueCStr(id); + sdata = NIL_P(data) ? NULL : StringValueCStr(data); GetEngine(self, e); -#if OPENSSL_VERSION_NUMBER < 0x00907000L - pkey = ENGINE_load_public_key(e, sid, sdata); -#else pkey = ENGINE_load_public_key(e, sid, NULL, sdata); -#endif if (!pkey) ossl_raise(eEngineError, NULL); return ossl_pkey_new(pkey); @@ -487,10 +465,8 @@ ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self) GetEngine(self, e); rb_scan_args(argc, argv, "11", &cmd, &val); - StringValue(cmd); - if (!NIL_P(val)) StringValue(val); - ret = ENGINE_ctrl_cmd_string(e, RSTRING_PTR(cmd), - NIL_P(val) ? NULL : RSTRING_PTR(val), 0); + ret = ENGINE_ctrl_cmd_string(e, StringValueCStr(cmd), + NIL_P(val) ? NULL : StringValueCStr(val), 0); if (!ret) ossl_raise(eEngineError, NULL); return self; @@ -585,12 +561,8 @@ Init_ossl_engine(void) #ifdef ENGINE_METHOD_BN_MOD_EXP_CRT DefEngineConst(METHOD_BN_MOD_EXP_CRT); #endif -#ifdef ENGINE_METHOD_CIPHERS DefEngineConst(METHOD_CIPHERS); -#endif -#ifdef ENGINE_METHOD_DIGESTS DefEngineConst(METHOD_DIGESTS); -#endif DefEngineConst(METHOD_ALL); DefEngineConst(METHOD_NONE); } diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index 35c2e3e5..e93b1b0a 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -86,15 +86,15 @@ ossl_spki_initialize(int argc, VALUE *argv, VALUE self) return self; } StringValue(buffer); - if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), -1))) { + if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), RSTRING_LENINT(buffer)))) { + ossl_clear_error(); p = (unsigned char *)RSTRING_PTR(buffer); if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) { ossl_raise(eSPKIError, NULL); } } NETSCAPE_SPKI_free(DATA_PTR(self)); - DATA_PTR(self) = spki; - ERR_clear_error(); + SetSPKI(self, spki); return self; } diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index 02b67429..9f44bc20 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -10,7 +10,7 @@ */ #include "ossl.h" -#if defined(OSSL_OCSP_ENABLED) +#if !defined(OPENSSL_NO_OCSP) #define NewOCSPReq(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_request_type, 0) @@ -1250,7 +1250,7 @@ Init_ossl_ocsp(void) rb_define_const(mOCSP, "V_RESPID_KEY", INT2NUM(V_OCSP_RESPID_KEY)); } -#else /* ! OSSL_OCSP_ENABLED */ +#else void Init_ossl_ocsp(void) { diff --git a/ext/openssl/ossl_ocsp.h b/ext/openssl/ossl_ocsp.h index c5064fbc..21e2c99a 100644 --- a/ext/openssl/ossl_ocsp.h +++ b/ext/openssl/ossl_ocsp.h @@ -11,7 +11,7 @@ #if !defined(_OSSL_OCSP_H_) #define _OSSL_OCSP_H_ -#if defined(OSSL_OCSP_ENABLED) +#if !defined(OPENSSL_NO_OCSP) extern VALUE mOCSP; extern VALUE cOPCSReq; extern VALUE cOPCSRes; diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c index e5052d47..5f849723 100644 --- a/ext/openssl/ossl_pkcs12.c +++ b/ext/openssl/ossl_pkcs12.c @@ -100,19 +100,19 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) PKCS12 *p12; rb_scan_args(argc, argv, "46", &pass, &name, &pkey, &cert, &ca, &key_nid, &cert_nid, &key_iter, &mac_iter, &keytype); - passphrase = NIL_P(pass) ? NULL : StringValuePtr(pass); - friendlyname = NIL_P(name) ? NULL : StringValuePtr(name); + passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass); + friendlyname = NIL_P(name) ? NULL : StringValueCStr(name); key = GetPKeyPtr(pkey); x509 = GetX509CertPtr(cert); x509s = NIL_P(ca) ? NULL : ossl_x509_ary2sk(ca); /* TODO: make a VALUE to nid function */ if (!NIL_P(key_nid)) { - if ((nkey = OBJ_txt2nid(StringValuePtr(key_nid))) == NID_undef) - ossl_raise(rb_eArgError, "Unknown PBE algorithm %s", StringValuePtr(key_nid)); + if ((nkey = OBJ_txt2nid(StringValueCStr(key_nid))) == NID_undef) + ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, key_nid); } if (!NIL_P(cert_nid)) { - if ((ncert = OBJ_txt2nid(StringValuePtr(cert_nid))) == NID_undef) - ossl_raise(rb_eArgError, "Unknown PBE algorithm %s", StringValuePtr(cert_nid)); + if ((ncert = OBJ_txt2nid(StringValueCStr(cert_nid))) == NID_undef) + ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, cert_nid); } if (!NIL_P(key_iter)) kiter = NUM2INT(key_iter); @@ -158,15 +158,19 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self) PKCS12 *pkcs = DATA_PTR(self); if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) return self; - passphrase = NIL_P(pass) ? NULL : StringValuePtr(pass); + passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass); in = ossl_obj2bio(arg); d2i_PKCS12_bio(in, &pkcs); DATA_PTR(self) = pkcs; BIO_free(in); pkey = cert = ca = Qnil; + /* OpenSSL's bug; PKCS12_parse() puts errors even if it succeeds. + * Fixed in OpenSSL 1.0.0t, 1.0.1p, 1.0.2d */ + ERR_set_mark(); if(!PKCS12_parse(pkcs, passphrase, &key, &x509, &x509s)) ossl_raise(ePKCS12Error, "PKCS12_parse"); + ERR_pop_to_mark(); pkey = rb_protect((VALUE(*)_((VALUE)))ossl_pkey_new, (VALUE)key, &st); /* NO DUP */ if(st) goto err; diff --git a/ext/openssl/ossl_pkcs5.c b/ext/openssl/ossl_pkcs5.c index 73d989e1..56ed65fb 100644 --- a/ext/openssl/ossl_pkcs5.c +++ b/ext/openssl/ossl_pkcs5.c @@ -48,7 +48,6 @@ ossl_pkcs5_pbkdf2_hmac(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE key #endif -#ifdef HAVE_PKCS5_PBKDF2_HMAC_SHA1 /* * call-seq: * PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, keylen) => string @@ -81,9 +80,6 @@ ossl_pkcs5_pbkdf2_hmac_sha1(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALU return str; } -#else -#define ossl_pkcs5_pbkdf2_hmac_sha1 rb_f_notimplement -#endif void Init_ossl_pkcs5(void) diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 9ca3abd7..5b7c07de 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -127,6 +127,22 @@ static const rb_data_type_t ossl_pkcs7_recip_info_type = { * Public * (MADE PRIVATE UNTIL SOMEBODY WILL NEED THEM) */ +static PKCS7_SIGNER_INFO * +ossl_PKCS7_SIGNER_INFO_dup(const PKCS7_SIGNER_INFO *si) +{ + return (PKCS7_SIGNER_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_SIGNER_INFO, + (d2i_of_void *)d2i_PKCS7_SIGNER_INFO, + (char *)si); +} + +static PKCS7_RECIP_INFO * +ossl_PKCS7_RECIP_INFO_dup(const PKCS7_RECIP_INFO *si) +{ + return (PKCS7_RECIP_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO, + (d2i_of_void *)d2i_PKCS7_RECIP_INFO, + (char *)si); +} + static VALUE ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) { @@ -134,7 +150,7 @@ ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) VALUE obj; obj = NewPKCS7si(cPKCS7Signer); - pkcs7 = p7si ? PKCS7_SIGNER_INFO_dup(p7si) : PKCS7_SIGNER_INFO_new(); + pkcs7 = p7si ? ossl_PKCS7_SIGNER_INFO_dup(p7si) : PKCS7_SIGNER_INFO_new(); if (!pkcs7) ossl_raise(ePKCS7Error, NULL); SetPKCS7si(obj, pkcs7); @@ -147,7 +163,7 @@ DupPKCS7SignerPtr(VALUE obj) PKCS7_SIGNER_INFO *p7si, *pkcs7; SafeGetPKCS7si(obj, p7si); - if (!(pkcs7 = PKCS7_SIGNER_INFO_dup(p7si))) { + if (!(pkcs7 = ossl_PKCS7_SIGNER_INFO_dup(p7si))) { ossl_raise(ePKCS7Error, NULL); } @@ -161,7 +177,7 @@ ossl_pkcs7ri_new(PKCS7_RECIP_INFO *p7ri) VALUE obj; obj = NewPKCS7ri(cPKCS7Recipient); - pkcs7 = p7ri ? PKCS7_RECIP_INFO_dup(p7ri) : PKCS7_RECIP_INFO_new(); + pkcs7 = p7ri ? ossl_PKCS7_RECIP_INFO_dup(p7ri) : PKCS7_RECIP_INFO_new(); if (!pkcs7) ossl_raise(ePKCS7Error, NULL); SetPKCS7ri(obj, pkcs7); @@ -174,7 +190,7 @@ DupPKCS7RecipientPtr(VALUE obj) PKCS7_RECIP_INFO *p7ri, *pkcs7; SafeGetPKCS7ri(obj, p7ri); - if (!(pkcs7 = PKCS7_RECIP_INFO_dup(p7ri))) { + if (!(pkcs7 = ossl_PKCS7_RECIP_INFO_dup(p7ri))) { ossl_raise(ePKCS7Error, NULL); } @@ -429,12 +445,13 @@ ossl_pkcs7_sym2typeid(VALUE sym) { "digest", NID_pkcs7_digest }, }; - if (RB_TYPE_P(sym, T_SYMBOL)) sym = rb_sym2str(sym); + if (SYMBOL_P(sym)) sym = rb_sym2str(sym); else StringValue(sym); RSTRING_GETMEM(sym, s, l); + for(i = 0; ; i++){ if(i == numberof(p7_type_tab)) - ossl_raise(ePKCS7Error, "unknown type \"%s\"", s); + ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym); if(strlen(p7_type_tab[i].name) != l) continue; if(strcmp(p7_type_tab[i].name, s) == 0){ ret = p7_type_tab[i].nid; @@ -779,7 +796,7 @@ ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self) if (ok < 0) ossl_raise(ePKCS7Error, NULL); msg = ERR_reason_error_string(ERR_get_error()); ossl_pkcs7_set_err_string(self, msg ? rb_str_new2(msg) : Qnil); - ERR_clear_error(); + ossl_clear_error(); data = ossl_membio2str(out); ossl_pkcs7_set_data(self, data); sk_X509_pop_free(x509s, X509_free); diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index e758c9e3..e4ae549f 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -20,20 +20,6 @@ ID id_private_q; /* * callback for generating keys */ -void -ossl_generate_cb(int p, int n, void *arg) -{ - VALUE ary; - - ary = rb_ary_new2(2); - rb_ary_store(ary, 0, INT2NUM(p)); - rb_ary_store(ary, 1, INT2NUM(n)); - - rb_yield(ary); -} - -#if HAVE_BN_GENCB -/* OpenSSL 2nd version of GN generation callback */ int ossl_generate_cb_2(int p, int n, BN_GENCB *cb) { @@ -66,7 +52,6 @@ ossl_generate_cb_stop(void *ptr) struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr; arg->stop = 1; } -#endif static void ossl_evp_pkey_free(void *ptr) @@ -121,8 +106,8 @@ ossl_pkey_new_from_file(VALUE filename) FILE *fp; EVP_PKEY *pkey; - SafeStringValue(filename); - if (!(fp = fopen(RSTRING_PTR(filename), "r"))) { + rb_check_safe_obj(filename); + if (!(fp = fopen(StringValueCStr(filename), "r"))) { ossl_raise(ePKeyError, "%s", strerror(errno)); } rb_fd_fix_cloexec(fileno(fp)); @@ -152,27 +137,21 @@ ossl_pkey_new_from_file(VALUE filename) 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); } } } @@ -374,7 +353,7 @@ Init_ossl_pkey(void) * 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 diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 7288d5af..b806d63e 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -40,9 +40,6 @@ extern const rb_data_type_t ossl_evp_pkey_type; GetPKey((obj), (pkey)); \ } while (0) -void ossl_generate_cb(int, int, void *); -#define HAVE_BN_GENCB defined(HAVE_RSA_GENERATE_KEY_EX) || defined(HAVE_DH_GENERATE_PARAMETERS_EX) || defined(HAVE_DSA_GENERATE_PARAMETERS_EX) -#if HAVE_BN_GENCB struct ossl_generate_cb_arg { int yield; int stop; @@ -50,7 +47,6 @@ struct ossl_generate_cb_arg { }; int ossl_generate_cb_2(int p, int n, BN_GENCB *cb); void ossl_generate_cb_stop(void *ptr); -#endif VALUE ossl_pkey_new(EVP_PKEY *); VALUE ossl_pkey_new_from_file(VALUE); diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 2f79bfb2..ac951a6e 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -20,7 +20,7 @@ #define DH_HAS_PRIVATE(dh) ((dh)->priv_key) -#ifdef OSSL_ENGINE_ENABLED +#if !defined(OPENSSL_NO_ENGINE) # define DH_PRIVATE(dh) (DH_HAS_PRIVATE(dh) || (dh)->engine) #else # define DH_PRIVATE(dh) DH_HAS_PRIVATE(dh) @@ -82,7 +82,6 @@ ossl_dh_new(EVP_PKEY *pkey) /* * Private */ -#if defined(HAVE_DH_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB struct dh_blocking_gen_arg { DH *dh; int size; @@ -98,12 +97,10 @@ dh_blocking_gen(void *arg) gen->result = DH_generate_parameters_ex(gen->dh, gen->size, gen->gen, gen->cb); return 0; } -#endif static DH * dh_generate(int size, int gen) { -#if defined(HAVE_DH_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB BN_GENCB cb; struct ossl_generate_cb_arg cb_arg; struct dh_blocking_gen_arg gen_arg; @@ -129,15 +126,13 @@ dh_generate(int size, int gen) if (!gen_arg.result) { DH_free(dh); - if (cb_arg.state) rb_jump_tag(cb_arg.state); + if (cb_arg.state) { + /* Clear OpenSSL error queue before re-raising. */ + ossl_clear_error(); + rb_jump_tag(cb_arg.state); + } return 0; } -#else - DH *dh; - - dh = DH_generate_parameters(size, gen, rb_block_given_p() ? ossl_generate_cb : NULL, NULL); - if (!dh) return 0; -#endif if (!DH_generate_key(dh)) { DH_free(dh); @@ -506,6 +501,8 @@ ossl_dh_compute_key(VALUE self, VALUE pub) GetPKeyDH(self, pkey); dh = pkey->pkey.dh; + if (!dh->p) + ossl_raise(eDHError, "incomplete DH"); pub_key = GetBNPtr(pub); len = DH_size(dh); str = rb_str_new(0, len); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 04900cc6..704fad6d 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -76,7 +76,6 @@ ossl_dsa_new(EVP_PKEY *pkey) /* * Private */ -#if defined(HAVE_DSA_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB struct dsa_blocking_gen_arg { DSA *dsa; int size; @@ -95,12 +94,10 @@ dsa_blocking_gen(void *arg) gen->result = DSA_generate_parameters_ex(gen->dsa, gen->size, gen->seed, gen->seed_len, gen->counter, gen->h, gen->cb); return 0; } -#endif static DSA * dsa_generate(int size) { -#if defined(HAVE_DSA_GENERATE_PARAMETERS_EX) && HAVE_BN_GENCB BN_GENCB cb; struct ossl_generate_cb_arg cb_arg; struct dsa_blocking_gen_arg gen_arg; @@ -135,22 +132,16 @@ dsa_generate(int size) } if (!gen_arg.result) { DSA_free(dsa); - if (cb_arg.state) rb_jump_tag(cb_arg.state); - return 0; - } -#else - DSA *dsa; - unsigned char seed[20]; - int seed_len = 20, counter; - unsigned long h; - - if (RAND_bytes(seed, seed_len) <= 0) { + if (cb_arg.state) { + /* Clear OpenSSL error queue before re-raising. By the way, the + * documentation of DSA_generate_parameters_ex() says the error code + * can be obtained by ERR_get_error(), but the default + * implementation, dsa_builtin_paramgen() doesn't put any error... */ + ossl_clear_error(); + rb_jump_tag(cb_arg.state); + } return 0; } - dsa = DSA_generate_parameters(size, seed, seed_len, &counter, &h, - rb_block_given_p() ? ossl_generate_cb : NULL, NULL); - if(!dsa) return 0; -#endif if (!DSA_generate_key(dsa)) { DSA_free(dsa); @@ -209,7 +200,6 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) EVP_PKEY *pkey; DSA *dsa; BIO *in; - char *passwd = NULL; VALUE arg, pass; GetPKey(self, pkey); @@ -222,10 +212,10 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) } } else { - if (!NIL_P(pass)) passwd = StringValuePtr(pass); + pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(arg); - dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); + dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); if (!dsa) { OSSL_BIO_reset(in); dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL); @@ -240,11 +230,14 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) } if (!dsa) { OSSL_BIO_reset(in); +#define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ + (d2i_of_void *)d2i_DSAPublicKey, PEM_STRING_DSA_PUBLIC, (bp), (void **)(x), (cb), (u)) dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL); +#undef PEM_read_bio_DSAPublicKey } BIO_free(in); if (!dsa) { - ERR_clear_error(); + ossl_clear_error(); ossl_raise(eDSAError, "Neither PUB key nor PRIV key"); } } @@ -313,26 +306,20 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self) EVP_PKEY *pkey; BIO *out; const EVP_CIPHER *ciph = NULL; - char *passwd = NULL; VALUE cipher, pass, str; GetPKeyDSA(self, pkey); rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { ciph = GetCipherPtr(cipher); - 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"); - passwd = RSTRING_PTR(pass); - } + pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDSAError, NULL); } if (DSA_HAS_PRIVATE(pkey->pkey.dsa)) { if (!PEM_write_bio_DSAPrivateKey(out, pkey->pkey.dsa, ciph, - NULL, 0, ossl_pem_passwd_cb, passwd)){ + NULL, 0, ossl_pem_passwd_cb, (void *)pass)){ BIO_free(out); ossl_raise(eDSAError, NULL); } @@ -460,7 +447,10 @@ ossl_dsa_to_public_key(VALUE self) GetPKeyDSA(self, pkey); /* err check performed by dsa_instance */ +#define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup( \ + (i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa)) dsa = DSAPublicKey_dup(pkey->pkey.dsa); +#undef DSAPublicKey_dup obj = dsa_instance(CLASS_OF(self), dsa); if (obj == Qfalse) { DSA_free(dsa); @@ -498,10 +488,11 @@ ossl_dsa_sign(VALUE self, VALUE data) VALUE str; GetPKeyDSA(self, pkey); - StringValue(data); - if (!DSA_PRIVATE(self, pkey->pkey.dsa)) { + if (!pkey->pkey.dsa->q) + ossl_raise(eDSAError, "incomplete DSA"); + if (!DSA_PRIVATE(self, pkey->pkey.dsa)) ossl_raise(eDSAError, "Private DSA key needed!"); - } + StringValue(data); str = rb_str_new(0, ossl_dsa_buf_size(pkey)); if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *)RSTRING_PTR(str), diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index c93e3cfb..958e7105 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -149,6 +149,69 @@ VALUE ossl_ec_new(EVP_PKEY *pkey) 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; + + if (rb_obj_is_kind_of(arg, cEC_GROUP)) { + EC_GROUP *group; + + SafeRequire_EC_GROUP(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 + * + * 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() @@ -165,10 +228,8 @@ VALUE ossl_ec_new(EVP_PKEY *pkey) 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) @@ -177,58 +238,43 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) 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; + + SafeRequire_EC_KEY(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; - - 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); - - 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); - } - - BIO_free(in); - - if (ec == NULL) { - const char *name = StringValueCStr(arg); - int nid = OBJ_sn2nid(name); + BIO *in; - (void)ERR_get_error(); - if (nid == NID_undef) - ossl_raise(eECError, "unknown curve name (%s)\n", name); + pass = ossl_pem_passwd_value(pass); + in = ossl_obj2bio(arg); - if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL) - ossl_raise(eECError, "unable to create curve (%s)\n", name); + 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); - 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"); @@ -236,9 +282,6 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) rb_iv_set(self, "@group", Qnil); - if (!NIL_P(group)) - rb_funcall(self, rb_intern("group="), 1, arg); - return self; } @@ -439,32 +482,34 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE 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); - 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); - 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) @@ -473,7 +518,6 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma BIO *out; int i = -1; int private = 0; - char *password = NULL; VALUE str; Require_EC_KEY(self, ec); @@ -493,20 +537,12 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma switch(format) { case EXPORT_PEM: if (private) { - const EVP_CIPHER *cipher; + const EVP_CIPHER *cipher = NULL; 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); } - else { - cipher = NULL; - } - 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); } @@ -592,9 +628,17 @@ static VALUE ossl_ec_key_to_text(VALUE self) /* * call-seq: - * key.generate_key => self + * key.generate_key! => self + * + * Generates a new random private and public key. + * + * See also the OpenSSL documentation for EC_KEY_generate_key() * - * See 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) { @@ -808,13 +852,13 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) 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); @@ -1500,74 +1544,84 @@ static VALUE ossl_ec_point_to_bn(VALUE self) /* * call-seq: - * point.mul(bn) => point - * point.mul(bn, bn) => point - * point.mul([bn], [point]) => point - * point.mul([bn], [point], bn) => point + * 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 ommitted, 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 ommitted. +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 arg1, arg2, arg3, result; + const BIGNUM *bn_g = NULL; - Require_EC_POINT(self, point1); + Require_EC_POINT(self, point_self); 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); + result = rb_obj_alloc(cEC_POINT); + ossl_ec_point_initialize(1, &group_v, result); + Require_EC_POINT(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_obj_is_kind_of(arg1, cBN)) { + BIGNUM *bn = GetBNPtr(arg1); + if (argc >= 2) + bn_g = GetBNPtr(arg2); - 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 (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; - - 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]); - } + /* + * bignums | arg1[0] | arg1[1] | arg1[2] | ... + * points | self | arg2[0] | arg2[1] | ... + */ + long i, num; + VALUE tmp_p, tmp_b; + const EC_POINT **points; + const BIGNUM **bignums; + + if (!rb_obj_is_kind_of(arg1, rb_cArray) || + !rb_obj_is_kind_of(arg2, rb_cArray)) + ossl_raise(rb_eTypeError, "points must be 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"); + + num = RARRAY_LEN(arg1); + bignums = ALLOCV_N(const BIGNUM *, tmp_b, num); + for (i = 0; i < num; i++) + bignums[i] = GetBNPtr(RARRAY_AREF(arg1, i)); + + points = ALLOCV_N(const EC_POINT *, tmp_p, num); + points[0] = point_self; /* self */ + for (i = 0; i < num - 1; i++) + SafeRequire_EC_POINT(RARRAY_AREF(arg2, i), points[i + 1]); + + if (argc >= 3) + bn_g = GetBNPtr(arg3); + + 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) @@ -1604,12 +1658,11 @@ void Init_ossl_ec(void) ID_compressed = rb_intern("compressed"); ID_hybrid = rb_intern("hybrid"); -#ifdef OPENSSL_EC_NAMED_CURVE rb_define_const(cEC, "NAMED_CURVE", ULONG2NUM(OPENSSL_EC_NAMED_CURVE)); -#endif rb_define_singleton_method(cEC, "builtin_curves", ossl_s_builtin_curves, 0); + rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1); rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1); /* copy/dup/cmp */ @@ -1619,8 +1672,10 @@ void Init_ossl_ec(void) 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 @@ -1628,7 +1683,8 @@ void Init_ossl_ec(void) 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); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 20b993ab..ea2af251 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -77,7 +77,6 @@ ossl_rsa_new(EVP_PKEY *pkey) /* * Private */ -#if defined(HAVE_RSA_GENERATE_KEY_EX) && HAVE_BN_GENCB struct rsa_blocking_gen_arg { RSA *rsa; BIGNUM *e; @@ -93,12 +92,10 @@ rsa_blocking_gen(void *arg) gen->result = RSA_generate_key_ex(gen->rsa, gen->size, gen->e, gen->cb); return 0; } -#endif static RSA * rsa_generate(int size, unsigned long exp) { -#if defined(HAVE_RSA_GENERATE_KEY_EX) && HAVE_BN_GENCB int i; BN_GENCB cb; struct ossl_generate_cb_arg cb_arg; @@ -139,15 +136,16 @@ rsa_generate(int size, unsigned long exp) if (!gen_arg.result) { BN_free(e); RSA_free(rsa); - if (cb_arg.state) rb_jump_tag(cb_arg.state); + if (cb_arg.state) { + /* must clear OpenSSL error stack */ + ossl_clear_error(); + rb_jump_tag(cb_arg.state); + } return 0; } BN_free(e); return rsa; -#else - return RSA_generate_key(size, exp, rb_block_given_p() ? ossl_generate_cb : NULL, NULL); -#endif } /* @@ -206,7 +204,6 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) EVP_PKEY *pkey; RSA *rsa; BIO *in; - char *passwd = NULL; VALUE arg, pass; GetPKey(self, pkey); @@ -218,10 +215,10 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) if (!rsa) ossl_raise(eRSAError, NULL); } else { - if (!NIL_P(pass)) passwd = StringValuePtr(pass); + pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(arg); - rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd); + rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); if (!rsa) { OSSL_BIO_reset(in); rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL); @@ -306,7 +303,6 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self) EVP_PKEY *pkey; BIO *out; const EVP_CIPHER *ciph = NULL; - char *passwd = NULL; VALUE cipher, pass, str; GetPKeyRSA(self, pkey); @@ -315,19 +311,14 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self) if (!NIL_P(cipher)) { ciph = GetCipherPtr(cipher); - 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"); - passwd = RSTRING_PTR(pass); - } + pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eRSAError, NULL); } if (RSA_HAS_PRIVATE(pkey->pkey.rsa)) { if (!PEM_write_bio_RSAPrivateKey(out, pkey->pkey.rsa, ciph, - NULL, 0, ossl_pem_passwd_cb, passwd)) { + NULL, 0, ossl_pem_passwd_cb, (void *)pass)) { BIO_free(out); ossl_raise(eRSAError, NULL); } @@ -391,6 +382,8 @@ ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self) VALUE str, buffer, padding; GetPKeyRSA(self, pkey); + if (!pkey->pkey.rsa->n) + ossl_raise(eRSAError, "incomplete RSA"); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); @@ -420,6 +413,8 @@ ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) VALUE str, buffer, padding; GetPKeyRSA(self, pkey); + if (!pkey->pkey.rsa->n) + ossl_raise(eRSAError, "incomplete RSA"); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); @@ -449,9 +444,10 @@ ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self) VALUE str, buffer, padding; GetPKeyRSA(self, pkey); - if (!RSA_PRIVATE(self, pkey->pkey.rsa)) { - ossl_raise(eRSAError, "private key needed."); - } + if (!pkey->pkey.rsa->n) + ossl_raise(eRSAError, "incomplete RSA"); + if (!RSA_PRIVATE(self, pkey->pkey.rsa)) + ossl_raise(eRSAError, "private key needed"); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); @@ -481,9 +477,10 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) VALUE str, buffer, padding; GetPKeyRSA(self, pkey); - if (!RSA_PRIVATE(self, pkey->pkey.rsa)) { - ossl_raise(eRSAError, "private key needed."); - } + if (!pkey->pkey.rsa->n) + ossl_raise(eRSAError, "incomplete RSA"); + if (!RSA_PRIVATE(self, pkey->pkey.rsa)) + ossl_raise(eRSAError, "private key needed"); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index daf866d7..3a300d5e 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -67,9 +67,9 @@ ossl_rand_add(VALUE self, VALUE str, VALUE entropy) static VALUE ossl_rand_load_file(VALUE self, VALUE filename) { - SafeStringValue(filename); + rb_check_safe_obj(filename); - if(!RAND_load_file(RSTRING_PTR(filename), -1)) { + if(!RAND_load_file(StringValueCStr(filename), -1)) { ossl_raise(eRandomError, NULL); } return Qtrue; @@ -86,8 +86,9 @@ ossl_rand_load_file(VALUE self, VALUE filename) static VALUE ossl_rand_write_file(VALUE self, VALUE filename) { - SafeStringValue(filename); - if (RAND_write_file(RSTRING_PTR(filename)) == -1) { + rb_check_safe_obj(filename); + + if (RAND_write_file(StringValueCStr(filename)) == -1) { ossl_raise(eRandomError, NULL); } return Qtrue; @@ -114,10 +115,8 @@ ossl_rand_bytes(VALUE self, VALUE len) str = rb_str_new(0, n); ret = RAND_bytes((unsigned char *)RSTRING_PTR(str), n); - if (ret == 0){ - char buf[256]; - ERR_error_string_n(ERR_get_error(), buf, 256); - ossl_raise(eRandomError, "RAND_bytes error: %s", buf); + if (ret == 0) { + ossl_raise(eRandomError, "RAND_bytes"); } else if (ret == -1) { ossl_raise(eRandomError, "RAND_bytes is not supported"); } @@ -146,7 +145,7 @@ ossl_rand_pseudo_bytes(VALUE self, VALUE len) int n = NUM2INT(len); str = rb_str_new(0, n); - if (!RAND_pseudo_bytes((unsigned char *)RSTRING_PTR(str), n)) { + if (RAND_pseudo_bytes((unsigned char *)RSTRING_PTR(str), n) < 1) { ossl_raise(eRandomError, NULL); } @@ -163,9 +162,9 @@ ossl_rand_pseudo_bytes(VALUE self, VALUE len) static VALUE ossl_rand_egd(VALUE self, VALUE filename) { - SafeStringValue(filename); + rb_check_safe_obj(filename); - if(!RAND_egd(RSTRING_PTR(filename))) { + if (RAND_egd(StringValueCStr(filename)) == -1) { ossl_raise(eRandomError, NULL); } return Qtrue; @@ -185,9 +184,9 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) { int n = NUM2INT(len); - SafeStringValue(filename); + rb_check_safe_obj(filename); - if (!RAND_egd_bytes(RSTRING_PTR(filename), n)) { + if (RAND_egd_bytes(StringValueCStr(filename), n) == -1) { ossl_raise(eRandomError, NULL); } return Qtrue; diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 4075d6f0..0f5313bd 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -70,6 +70,10 @@ static VALUE eSSLErrorWaitWritable; #define ossl_ssl_get_x509(o) rb_iv_get((o),"@x509") #define ossl_ssl_get_key(o) rb_iv_get((o),"@key") +#define ossl_ssl_set_io(o,v) rb_iv_set((o),"@io",(v)) +#define ossl_ssl_set_ctx(o,v) rb_iv_set((o),"@context",(v)) +#define ossl_ssl_set_sync_close(o,v) rb_iv_set((o),"@sync_close",(v)) +#define ossl_ssl_set_hostname_v(o,v) rb_iv_set((o),"@hostname",(v)) #define ossl_ssl_set_x509(o,v) rb_iv_set((o),"@x509",(v)) #define ossl_ssl_set_key(o,v) rb_iv_set((o),"@key",(v)) #define ossl_ssl_set_tmp_dh(o,v) rb_iv_set((o),"@tmp_dh",(v)) @@ -90,26 +94,22 @@ static const struct { OSSL_SSL_METHOD_ENTRY(TLSv1), OSSL_SSL_METHOD_ENTRY(TLSv1_server), OSSL_SSL_METHOD_ENTRY(TLSv1_client), -#if defined(HAVE_TLSV1_2_METHOD) && defined(HAVE_TLSV1_2_SERVER_METHOD) && \ - defined(HAVE_TLSV1_2_CLIENT_METHOD) +#if defined(HAVE_TLSV1_2_METHOD) OSSL_SSL_METHOD_ENTRY(TLSv1_2), OSSL_SSL_METHOD_ENTRY(TLSv1_2_server), OSSL_SSL_METHOD_ENTRY(TLSv1_2_client), #endif -#if defined(HAVE_TLSV1_1_METHOD) && defined(HAVE_TLSV1_1_SERVER_METHOD) && \ - defined(HAVE_TLSV1_1_CLIENT_METHOD) +#if defined(HAVE_TLSV1_1_METHOD) OSSL_SSL_METHOD_ENTRY(TLSv1_1), OSSL_SSL_METHOD_ENTRY(TLSv1_1_server), OSSL_SSL_METHOD_ENTRY(TLSv1_1_client), #endif -#if defined(HAVE_SSLV2_METHOD) && defined(HAVE_SSLV2_SERVER_METHOD) && \ - defined(HAVE_SSLV2_CLIENT_METHOD) +#if defined(HAVE_SSLV2_METHOD) OSSL_SSL_METHOD_ENTRY(SSLv2), OSSL_SSL_METHOD_ENTRY(SSLv2_server), OSSL_SSL_METHOD_ENTRY(SSLv2_client), #endif -#if defined(HAVE_SSLV3_METHOD) && defined(HAVE_SSLV3_SERVER_METHOD) && \ - defined(HAVE_SSLV3_CLIENT_METHOD) +#if defined(HAVE_SSLV3_METHOD) OSSL_SSL_METHOD_ENTRY(SSLv3), OSSL_SSL_METHOD_ENTRY(SSLv3_server), OSSL_SSL_METHOD_ENTRY(SSLv3_client), @@ -145,7 +145,8 @@ static VALUE ossl_sslctx_s_alloc(VALUE klass) { SSL_CTX *ctx; - long mode = SSL_MODE_ENABLE_PARTIAL_WRITE; + long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; VALUE obj; #ifdef SSL_MODE_RELEASE_BUFFERS @@ -161,6 +162,18 @@ ossl_sslctx_s_alloc(VALUE klass) RTYPEDDATA_DATA(obj) = ctx; SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj); +#if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) + /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It + * allows to specify multiple curve names and OpenSSL will select + * automatically from them. In OpenSSL 1.0.2, the automatic selection has to + * be enabled explicitly. But OpenSSL 1.1.0 removed the knob and it is + * always enabled. To uniform the behavior, we enable the automatic + * selection also in 1.0.2. Users can still disable ECDH by removing ECDH + * cipher suites by SSLContext#ciphers=. */ + if (!SSL_CTX_set_ecdh_auto(ctx, 1)) + ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); +#endif + return obj; } @@ -307,7 +320,7 @@ ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx); - X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx, (void*)cb); + X509_STORE_CTX_set_ex_data(ctx, ossl_store_ctx_ex_verify_cb_idx, (void *)cb); return ossl_verify_cb(preverify_ok, ctx); } @@ -544,7 +557,7 @@ ssl_renegotiation_cb(const SSL *ssl) (void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj); } -#ifdef HAVE_OPENSSL_NPN_NEGOTIATED +#if defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) static VALUE ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded) { @@ -562,25 +575,12 @@ ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded) static VALUE ssl_encode_npn_protocols(VALUE protocols) { - VALUE encoded = rb_str_new2(""); + VALUE encoded = rb_str_new(NULL, 0); rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded); - StringValueCStr(encoded); return encoded; } static int -ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) -{ - VALUE sslctx_obj = (VALUE) arg; - VALUE protocols = rb_iv_get(sslctx_obj, "@_protocols"); - - *out = (const unsigned char *) RSTRING_PTR(protocols); - *outlen = RSTRING_LENINT(protocols); - - return SSL_TLSEXT_ERR_OK; -} - -static int ssl_npn_select_cb_common(VALUE cb, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { VALUE selected; @@ -609,6 +609,19 @@ ssl_npn_select_cb_common(VALUE cb, const unsigned char **out, unsigned char *out return SSL_TLSEXT_ERR_OK; } +#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB +static int +ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) +{ + VALUE sslctx_obj = (VALUE) arg; + VALUE protocols = rb_iv_get(sslctx_obj, "@_protocols"); + + *out = (const unsigned char *) RSTRING_PTR(protocols); + *outlen = RSTRING_LENINT(protocols); + + return SSL_TLSEXT_ERR_OK; +} + static int ssl_npn_select_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { @@ -619,6 +632,7 @@ ssl_npn_select_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsi return ssl_npn_select_cb_common(cb, (const unsigned char **)out, outlen, in, inlen); } +#endif #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB static int @@ -632,8 +646,7 @@ ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, c return ssl_npn_select_cb_common(cb, out, outlen, in, inlen); } #endif - -#endif +#endif /* HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB || HAVE_SSL_CTX_SET_ALPN_SELECT_CB */ /* This function may serve as the entry point to support further * callbacks. */ @@ -687,8 +700,8 @@ ossl_sslctx_set_options(VALUE self, VALUE options) * ctx.setup => nil # thereafter * * This method is called automatically when a new SSLSocket is created. - * Normally you do not need to call this method (unless you are writing an - * extension in C). + * However, it is not thread-safe and must be called before creating + * SSLSocket objects in a multi-threaded program. */ static VALUE ossl_sslctx_setup(VALUE self) @@ -710,10 +723,24 @@ ossl_sslctx_setup(VALUE self) #endif #if !defined(OPENSSL_NO_EC) - if (RTEST(ossl_sslctx_get_tmp_ecdh_cb(self))){ + /* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0, + * but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */ + if (RTEST(ossl_sslctx_get_tmp_ecdh_cb(self))) { +# if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) + rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead"); SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback); +# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) + /* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores + * tmp_ecdh_callback. So disable ecdh_auto. */ + if (!SSL_CTX_set_ecdh_auto(ctx, 0)) + ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); +# endif +# else + ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; " + "use #ecdh_curves= instead"); +# endif } -#endif +#endif /* OPENSSL_NO_EC */ val = ossl_sslctx_get_cert_store(self); if(!NIL_P(val)){ @@ -773,9 +800,9 @@ ossl_sslctx_setup(VALUE self) } val = ossl_sslctx_get_ca_file(self); - ca_file = NIL_P(val) ? NULL : StringValuePtr(val); + ca_file = NIL_P(val) ? NULL : StringValueCStr(val); val = ossl_sslctx_get_ca_path(self); - ca_path = NIL_P(val) ? NULL : StringValuePtr(val); + ca_path = NIL_P(val) ? NULL : StringValueCStr(val); if(ca_file || ca_path){ if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path)) rb_warning("can't set verify locations"); @@ -793,7 +820,7 @@ ossl_sslctx_setup(VALUE self) val = ossl_sslctx_get_verify_dep(self); if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val)); -#ifdef HAVE_OPENSSL_NPN_NEGOTIATED +#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB val = rb_iv_get(self, "@npn_protocols"); if (!NIL_P(val)) { rb_iv_set(self, "@_protocols", ssl_encode_npn_protocols(val)); @@ -810,7 +837,7 @@ ossl_sslctx_setup(VALUE self) val = rb_iv_get(self, "@alpn_protocols"); if (!NIL_P(val)) { VALUE rprotos = ssl_encode_npn_protocols(val); - SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)StringValueCStr(rprotos), RSTRING_LENINT(rprotos)); + SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), RSTRING_LENINT(rprotos)); OSSL_Debug("SSL ALPN values added"); } if (RTEST(rb_iv_get(self, "@alpn_select_cb"))) { @@ -945,13 +972,94 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v) ossl_raise(eSSLError, "SSL_CTX is not initialized."); return Qnil; } - if (!SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(str))) { + if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) { ossl_raise(eSSLError, "SSL_CTX_set_cipher_list"); } return v; } +#if !defined(OPENSSL_NO_EC) +/* + * call-seq: + * ctx.ecdh_curves = curve_list -> curve_list + * + * Sets the list of "supported elliptic curves" for this context. + * + * For a TLS client, the list is directly used in the Supported Elliptic Curves + * Extension. For a server, the list is used by OpenSSL to determine the set of + * shared curves. OpenSSL will pick the most appropriate one from it. + * + * Note that this works differently with old OpenSSL (<= 1.0.1). Only one curve + * can be set, and this has no effect for TLS clients. + * + * === Example + * ctx1 = OpenSSL::SSL::SSLContext.new + * ctx1.ecdh_curves = "X25519:P-256:P-224" + * svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1) + * Thread.new { svr.accept } + * + * ctx2 = OpenSSL::SSL::SSLContext.new + * ctx2.ecdh_curves = "P-256" + * cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2) + * cli.connect + * + * p cli.tmp_key.group.curve_name + * # => "prime256v1" (is an alias for NIST P-256) + */ +static VALUE +ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg) +{ + SSL_CTX *ctx; + + rb_check_frozen(self); + GetSSLCTX(self, ctx); + StringValueCStr(arg); + +#if defined(HAVE_SSL_CTX_SET1_CURVES_LIST) + if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg))) + ossl_raise(eSSLError, NULL); +#else + /* OpenSSL does not have SSL_CTX_set1_curves_list()... Fallback to + * SSL_CTX_set_tmp_ecdh(). So only the first curve is used. */ + { + VALUE curve, splitted; + EC_KEY *ec; + int nid; + + splitted = rb_str_split(arg, ":"); + if (!RARRAY_LEN(splitted)) + ossl_raise(eSSLError, "invalid input format"); + curve = RARRAY_AREF(splitted, 0); + StringValueCStr(curve); + + /* SSL_CTX_set1_curves_list() accepts NIST names */ + nid = EC_curve_nist2nid(RSTRING_PTR(curve)); + if (nid == NID_undef) + nid = OBJ_txt2nid(RSTRING_PTR(curve)); + if (nid == NID_undef) + ossl_raise(eSSLError, "unknown curve name"); + + ec = EC_KEY_new_by_curve_name(nid); + if (!ec) + ossl_raise(eSSLError, NULL); + EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); + SSL_CTX_set_tmp_ecdh(ctx, ec); +# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) + /* tmp_ecdh and ecdh_auto conflict. tmp_ecdh is ignored when ecdh_auto + * is enabled. So disable ecdh_auto. */ + if (!SSL_CTX_set_ecdh_auto(ctx, 0)) + ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); +# endif + } +#endif + + return arg; +} +#else +#define ossl_sslctx_set_ecdh_curves rb_f_notimplement +#endif + /* * call-seq: * ctx.session_add(session) -> true | false @@ -1142,25 +1250,29 @@ ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self) * SSLSocket class */ #ifndef OPENSSL_NO_SOCK +static inline int +ssl_started(SSL *ssl) +{ + /* the FD is set in ossl_ssl_setup(), called by #connect or #accept */ + return SSL_get_fd(ssl) >= 0; +} + static void ossl_ssl_shutdown(SSL *ssl) { - int i, rc; - - if (ssl) { - /* 4 is from SSL_smart_shutdown() of mod_ssl.c (v2.2.19) */ - /* It says max 2x pending + 2x data = 4 */ - for (i = 0; i < 4; ++i) { - /* - * Ignore the case SSL_shutdown returns -1. Empty handshake_func - * must not happen. - */ - if ((rc = SSL_shutdown(ssl)) != 0) - break; - } - SSL_clear(ssl); - ERR_clear_error(); + int i; + + /* 4 is from SSL_smart_shutdown() of mod_ssl.c (v2.2.19) */ + /* It says max 2x pending + 2x data = 4 */ + for (i = 0; i < 4; ++i) { + /* + * Ignore the case SSL_shutdown returns -1. Empty handshake_func + * must not happen. + */ + if (SSL_shutdown(ssl) != 0) + break; } + ossl_clear_error(); } static void @@ -1183,45 +1295,77 @@ ossl_ssl_s_alloc(VALUE klass) return TypedData_Wrap_Struct(klass, &ossl_ssl_type, NULL); } +/* + * call-seq: + * SSLSocket.new(io) => aSSLSocket + * SSLSocket.new(io, ctx) => aSSLSocket + * + * Creates a new SSL socket from +io+ which must be a real ruby object (not an + * IO-like object that responds to read/write). + * + * If +ctx+ is provided the SSL Sockets initial params will be taken from + * the context. + * + * The OpenSSL::Buffering module provides additional IO methods. + * + * This method will freeze the SSLContext if one is provided; + * however, session management is still allowed in the frozen SSLContext. + */ static VALUE -ossl_ssl_setup(VALUE self) +ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) { - VALUE io, v_ctx, cb; + VALUE io, v_ctx, verify_cb; + SSL *ssl; SSL_CTX *ctx; + + TypedData_Get_Struct(self, SSL, &ossl_ssl_type, ssl); + if (ssl) + ossl_raise(eSSLError, "SSL already initialized"); + + if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1) + v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); + + GetSSLCTX(v_ctx, ctx); + ossl_ssl_set_ctx(self, v_ctx); + ossl_sslctx_setup(v_ctx); + + if (rb_respond_to(io, rb_intern("nonblock="))) + rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); + ossl_ssl_set_io(self, io); + + ossl_ssl_set_sync_close(self, Qfalse); + + ssl = SSL_new(ctx); + if (!ssl) + ossl_raise(eSSLError, NULL); + RTYPEDDATA_DATA(self) = ssl; + + SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self); + SSL_set_info_callback(ssl, ssl_info_cb); + verify_cb = ossl_sslctx_get_verify_cb(v_ctx); + SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb); + + rb_call_super(0, NULL); + + return self; +} + +static VALUE +ossl_ssl_setup(VALUE self) +{ + VALUE io; SSL *ssl; rb_io_t *fptr; GetSSL(self, ssl); - if(!ssl){ -#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME - VALUE hostname = rb_iv_get(self, "@hostname"); -#endif - - v_ctx = ossl_ssl_get_ctx(self); - GetSSLCTX(v_ctx, ctx); - - ssl = SSL_new(ctx); - if (!ssl) { - ossl_raise(eSSLError, "SSL_new"); - } - DATA_PTR(self) = ssl; + if (ssl_started(ssl)) + return Qtrue; -#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME - if (!NIL_P(hostname)) { - if (SSL_set_tlsext_host_name(ssl, StringValuePtr(hostname)) != 1) - ossl_raise(eSSLError, "SSL_set_tlsext_host_name"); - } -#endif - io = ossl_ssl_get_io(self); - GetOpenFile(io, fptr); - rb_io_check_readable(fptr); - rb_io_check_writable(fptr); - SSL_set_fd(ssl, TO_SOCKET(FPTR_TO_FD(fptr))); - SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void*)self); - cb = ossl_sslctx_get_verify_cb(v_ctx); - SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void*)cb); - SSL_set_info_callback(ssl, ssl_info_cb); - } + io = ossl_ssl_get_io(self); + GetOpenFile(io, fptr); + rb_io_check_readable(fptr); + rb_io_check_writable(fptr); + SSL_set_fd(ssl, TO_SOCKET(FPTR_TO_FD(fptr))); return Qtrue; } @@ -1232,15 +1376,6 @@ ossl_ssl_setup(VALUE self) #define ssl_get_error(ssl, ret) SSL_get_error((ssl), (ret)) #endif -#define ossl_ssl_data_get_struct(v, ssl) \ -do { \ - GetSSL((v), (ssl)); \ - if (!(ssl)) { \ - rb_warning("SSL session is not started yet."); \ - return Qnil; \ - } \ -} while (0) - static void write_would_block(int nonblock) { @@ -1279,15 +1414,18 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) rb_ivar_set(self, ID_callback_state, Qnil); - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); for(;;){ ret = func(ssl); cb_state = rb_ivar_get(self, ID_callback_state); - if (!NIL_P(cb_state)) - rb_jump_tag(NUM2INT(cb_state)); + if (!NIL_P(cb_state)) { + /* must cleanup OpenSSL error stack before re-raising */ + ossl_clear_error(); + rb_jump_tag(NUM2INT(cb_state)); + } if (ret > 0) break; @@ -1436,7 +1574,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) GetSSL(self, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); - if (ssl) { + if (ssl_started(ssl)) { if(!nonblock && SSL_pending(ssl) <= 0) rb_thread_wait_fd(FPTR_TO_FD(fptr)); for (;;){ @@ -1530,7 +1668,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) GetSSL(self, ssl); GetOpenFile(ossl_ssl_get_io(self), fptr); - if (ssl) { + if (ssl_started(ssl)) { for (;;){ nwrite = SSL_write(ssl, RSTRING_PTR(str), RSTRING_LENINT(str)); switch(ssl_get_error(ssl, nwrite)){ @@ -1596,23 +1734,17 @@ ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self) * call-seq: * ssl.stop => nil * - * Stops the SSL connection and prepares it for another connection. + * Sends "close notify" to the peer and tries to shut down the SSL connection + * gracefully. */ static VALUE ossl_ssl_stop(VALUE self) { SSL *ssl; - /* ossl_ssl_data_get_struct() is not usable here because it may return - * from this function; */ - GetSSL(self, ssl); - if (ssl) { - ossl_ssl_shutdown(ssl); - SSL_free(ssl); - } - DATA_PTR(self) = NULL; + ossl_ssl_shutdown(ssl); return Qnil; } @@ -1629,7 +1761,7 @@ ossl_ssl_get_cert(VALUE self) SSL *ssl; X509 *cert = NULL; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); /* * Is this OpenSSL bug? Should add a ref? @@ -1656,7 +1788,7 @@ ossl_ssl_get_peer_cert(VALUE self) X509 *cert = NULL; VALUE obj; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */ @@ -1684,7 +1816,7 @@ ossl_ssl_get_peer_cert_chain(VALUE self) VALUE ary; int i, num; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); chain = SSL_get_peer_cert_chain(ssl); if(!chain) return Qnil; @@ -1710,7 +1842,7 @@ ossl_ssl_get_version(VALUE self) { SSL *ssl; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); return rb_str_new2(SSL_get_version(ssl)); } @@ -1727,7 +1859,7 @@ ossl_ssl_get_cipher(VALUE self) SSL *ssl; SSL_CIPHER *cipher; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); cipher = (SSL_CIPHER *)SSL_get_current_cipher(ssl); @@ -1746,7 +1878,7 @@ ossl_ssl_get_state(VALUE self) SSL *ssl; VALUE ret; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); ret = rb_str_new2(SSL_state_string(ssl)); if (ruby_verbose) { @@ -1767,7 +1899,7 @@ ossl_ssl_pending(VALUE self) { SSL *ssl; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); return INT2NUM(SSL_pending(ssl)); } @@ -1783,15 +1915,9 @@ ossl_ssl_session_reused(VALUE self) { SSL *ssl; - ossl_ssl_data_get_struct(self, ssl); - - switch(SSL_session_reused(ssl)) { - case 1: return Qtrue; - case 0: return Qfalse; - default: ossl_raise(eSSLError, "SSL_session_reused"); - } + GetSSL(self, ssl); - UNREACHABLE; + return SSL_session_reused(ssl) ? Qtrue : Qfalse; } /* @@ -1806,11 +1932,7 @@ ossl_ssl_set_session(VALUE self, VALUE arg1) SSL *ssl; SSL_SESSION *sess; -/* why is ossl_ssl_setup delayed? */ - ossl_ssl_setup(self); - - ossl_ssl_data_get_struct(self, ssl); - + GetSSL(self, ssl); SafeGetSSLSession(arg1, sess); if (SSL_set_session(ssl, sess) != 1) @@ -1821,6 +1943,35 @@ ossl_ssl_set_session(VALUE self, VALUE arg1) /* * call-seq: + * ssl.hostname = hostname -> hostname + * + * Sets the server hostname used for SNI. This needs to be set before + * SSLSocket#connect. + */ +#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME +static VALUE +ossl_ssl_set_hostname(VALUE self, VALUE arg) +{ + SSL *ssl; + char *hostname = NULL; + + GetSSL(self, ssl); + + if (!NIL_P(arg)) + hostname = StringValueCStr(arg); + + if (!SSL_set_tlsext_host_name(ssl, hostname)) + ossl_raise(eSSLError, NULL); + + /* for SSLSocket#hostname */ + ossl_ssl_set_hostname_v(self, arg); + + return arg; +} +#endif + +/* + * call-seq: * ssl.verify_result => Integer * * Returns the result of the peer certificates verification. See verify(1) @@ -1833,7 +1984,7 @@ ossl_ssl_get_verify_result(VALUE self) { SSL *ssl; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); return INT2FIX(SSL_get_verify_result(ssl)); } @@ -1855,13 +2006,13 @@ ossl_ssl_get_client_ca_list(VALUE self) SSL *ssl; STACK_OF(X509_NAME) *ca; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); ca = SSL_get_client_CA_list(ssl); return ossl_x509name_sk2ary(ca); } -# ifdef HAVE_OPENSSL_NPN_NEGOTIATED +# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB /* * call-seq: * ssl.npn_protocol => String @@ -1876,7 +2027,7 @@ ossl_ssl_npn_protocol(VALUE self) const unsigned char *out; unsigned int outlen; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); SSL_get0_next_proto_negotiated(ssl, &out, &outlen); if (!outlen) @@ -1901,7 +2052,7 @@ ossl_ssl_alpn_protocol(VALUE self) const unsigned char *out; unsigned int outlen; - ossl_ssl_data_get_struct(self, ssl); + GetSSL(self, ssl); SSL_get0_alpn_selected(ssl, &out, &outlen); if (!outlen) @@ -1910,6 +2061,26 @@ ossl_ssl_alpn_protocol(VALUE self) return rb_str_new((const char *) out, outlen); } # endif + +# ifdef HAVE_SSL_GET_SERVER_TMP_KEY +/* + * call-seq: + * ssl.tmp_key => PKey or nil + * + * Returns the ephemeral key used in case of forward secrecy cipher + */ +static VALUE +ossl_ssl_tmp_key(VALUE self) +{ + SSL *ssl; + EVP_PKEY *key; + + GetSSL(self, ssl); + if (!SSL_get_server_tmp_key(ssl, &key)) + return Qnil; + return ossl_pkey_new(key); +} +# endif /* defined(HAVE_SSL_GET_SERVER_TMP_KEY) */ #endif /* !defined(OPENSSL_NO_SOCK) */ void @@ -2055,6 +2226,7 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse); +#if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) /* * A callback invoked when ECDH parameters are required. * @@ -2062,10 +2234,11 @@ Init_ossl_ssl(void) * flag indicating the use of an export cipher and the keylength * required. * - * The callback must return an OpenSSL::PKey::EC instance of the correct - * key length. + * The callback is deprecated. This does not work with recent versions of + * OpenSSL. Use OpenSSL::SSL::SSLContext#ecdh_curves= instead. */ rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse); +#endif /* * Sets the context in which a session can be reused. This allows @@ -2132,7 +2305,7 @@ Init_ossl_ssl(void) * end */ rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse); -#ifdef HAVE_OPENSSL_NPN_NEGOTIATED +#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB /* * An Enumerable of Strings. Each String represents a protocol to be * advertised as the list of supported protocols for Next Protocol @@ -2201,6 +2374,7 @@ Init_ossl_ssl(void) rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1); rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0); rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1); + rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1); rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0); @@ -2280,9 +2454,11 @@ Init_ossl_ssl(void) cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject); #ifdef OPENSSL_NO_SOCK rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue); + rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1); #else rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse); rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc); + rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1); rb_define_method(cSSLSocket, "connect", ossl_ssl_connect, 0); rb_define_method(cSSLSocket, "connect_nonblock", ossl_ssl_connect_nonblock, -1); rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0); @@ -2304,10 +2480,17 @@ Init_ossl_ssl(void) rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1); rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0); rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0); +#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME + /* #hostname is defined in lib/openssl/ssl.rb */ + rb_define_method(cSSLSocket, "hostname=", ossl_ssl_set_hostname, 1); +#endif +# ifdef HAVE_SSL_GET_SERVER_TMP_KEY + rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0); +# endif # ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0); # endif -# ifdef HAVE_OPENSSL_NPN_NEGOTIATED +# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif #endif @@ -2327,25 +2510,17 @@ Init_ossl_ssl(void) ossl_ssl_def_const(OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG); ossl_ssl_def_const(OP_SSLREF2_REUSE_CERT_TYPE_BUG); ossl_ssl_def_const(OP_MICROSOFT_BIG_SSLV3_BUFFER); -#if defined(SSL_OP_MSIE_SSLV2_RSA_PADDING) ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING); -#endif ossl_ssl_def_const(OP_SSLEAY_080_CLIENT_DH_BUG); ossl_ssl_def_const(OP_TLS_D5_BUG); ossl_ssl_def_const(OP_TLS_BLOCK_PADDING_BUG); ossl_ssl_def_const(OP_DONT_INSERT_EMPTY_FRAGMENTS); ossl_ssl_def_const(OP_ALL); -#if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); -#endif -#if defined(SSL_OP_SINGLE_ECDH_USE) ossl_ssl_def_const(OP_SINGLE_ECDH_USE); -#endif ossl_ssl_def_const(OP_SINGLE_DH_USE); ossl_ssl_def_const(OP_EPHEMERAL_RSA); -#if defined(SSL_OP_CIPHER_SERVER_PREFERENCE) ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE); -#endif ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG); ossl_ssl_def_const(OP_NO_SSLv2); ossl_ssl_def_const(OP_NO_SSLv3); diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h index 909f6798..c1a3cd6c 100644 --- a/ext/openssl/ossl_ssl.h +++ b/ext/openssl/ossl_ssl.h @@ -12,6 +12,9 @@ #define GetSSL(obj, ssl) do { \ TypedData_Get_Struct((obj), SSL, &ossl_ssl_type, (ssl)); \ + if (!(ssl)) { \ + ossl_raise(rb_eRuntimeError, "SSL is not initialized"); \ + } \ } while (0) #define GetSSLSession(obj, sess) do { \ diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index e1bbc6fb..1b6df55c 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -46,7 +46,7 @@ static VALUE ossl_ssl_session_initialize(VALUE self, VALUE arg1) GetSSL(arg1, ssl); - if (!ssl || (ctx = SSL_get1_session(ssl)) == NULL) + if ((ctx = SSL_get1_session(ssl)) == NULL) ossl_raise(eSSLSession, "no session available"); } else { BIO *in = ossl_obj2bio(arg1); @@ -186,7 +186,6 @@ static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) return ossl_ssl_session_get_timeout(self); } -#ifdef HAVE_SSL_SESSION_GET_ID /* * call-seq: * session.id -> aString @@ -205,7 +204,6 @@ static VALUE ossl_ssl_session_get_id(VALUE self) return rb_str_new((const char *) p, i); } -#endif /* * call-seq: @@ -316,12 +314,7 @@ void Init_ossl_ssl_session(void) rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); - -#ifdef HAVE_SSL_SESSION_GET_ID rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); -#else - rb_undef_method(cSSLSession, "id"); -#endif rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c index 2fd14566..cf62b53e 100644 --- a/ext/openssl/ossl_x509.c +++ b/ext/openssl/ossl_x509.c @@ -63,12 +63,8 @@ Init_ossl_x509(void) DefX509Const(V_ERR_KEYUSAGE_NO_CERTSIGN); DefX509Const(V_ERR_APPLICATION_VERIFICATION); -#if defined(X509_V_FLAG_CRL_CHECK) DefX509Const(V_FLAG_CRL_CHECK); -#endif -#if defined(X509_V_FLAG_CRL_CHECK_ALL) DefX509Const(V_FLAG_CRL_CHECK_ALL); -#endif DefX509Const(PURPOSE_SSL_CLIENT); DefX509Const(PURPOSE_SSL_SERVER); @@ -77,21 +73,15 @@ Init_ossl_x509(void) DefX509Const(PURPOSE_SMIME_ENCRYPT); DefX509Const(PURPOSE_CRL_SIGN); DefX509Const(PURPOSE_ANY); -#if defined(X509_PURPOSE_OCSP_HELPER) DefX509Const(PURPOSE_OCSP_HELPER); -#endif DefX509Const(TRUST_COMPAT); DefX509Const(TRUST_SSL_CLIENT); DefX509Const(TRUST_SSL_SERVER); DefX509Const(TRUST_EMAIL); DefX509Const(TRUST_OBJECT_SIGN); -#if defined(X509_TRUST_OCSP_SIGN) DefX509Const(TRUST_OCSP_SIGN); -#endif -#if defined(X509_TRUST_OCSP_REQUEST) DefX509Const(TRUST_OCSP_REQUEST); -#endif DefX509Default(CERT_AREA, cert_area); DefX509Default(CERT_DIR, cert_dir); diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index d0f41c6b..7cd3fe6d 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -141,7 +141,7 @@ ossl_x509attr_set_oid(VALUE self, VALUE oid) ASN1_OBJECT *obj; char *s; - s = StringValuePtr(oid); + s = StringValueCStr(oid); obj = OBJ_txt2obj(s, 0); if(!obj) obj = OBJ_txt2obj(s, 1); if(!obj) ossl_raise(eX509AttrError, NULL); @@ -178,14 +178,6 @@ ossl_x509attr_get_oid(VALUE self) return ret; } -#if defined(HAVE_ST_X509_ATTRIBUTE_SINGLE) || defined(HAVE_ST_SINGLE) -# define OSSL_X509ATTR_IS_SINGLE(attr) ((attr)->single) -# define OSSL_X509ATTR_SET_SINGLE(attr) ((attr)->single = 1) -#else -# define OSSL_X509ATTR_IS_SINGLE(attr) (!(attr)->value.set) -# define OSSL_X509ATTR_SET_SINGLE(attr) ((attr)->value.set = 0) -#endif - /* * call-seq: * attr.value = asn1 => asn1 @@ -196,6 +188,7 @@ ossl_x509attr_set_value(VALUE self, VALUE value) X509_ATTRIBUTE *attr; ASN1_TYPE *a1type; + OSSL_Check_Kind(value, cASN1Data); if(!(a1type = ossl_asn1_get_asn1type(value))) ossl_raise(eASN1Error, "could not get ASN1_TYPE"); if(ASN1_TYPE_get(a1type) == V_ASN1_SEQUENCE){ @@ -204,10 +197,10 @@ ossl_x509attr_set_value(VALUE self, VALUE value) } GetX509Attr(self, attr); if(attr->value.set){ - if(OSSL_X509ATTR_IS_SINGLE(attr)) ASN1_TYPE_free(attr->value.single); + if(attr->single) ASN1_TYPE_free(attr->value.single); else sk_ASN1_TYPE_free(attr->value.set); } - OSSL_X509ATTR_SET_SINGLE(attr); + attr->single = 1; attr->value.single = a1type; return value; @@ -227,7 +220,7 @@ ossl_x509attr_get_value(VALUE self) GetX509Attr(self, attr); if(attr->value.ptr == NULL) return Qnil; - if(OSSL_X509ATTR_IS_SINGLE(attr)){ + if(attr->single){ length = i2d_ASN1_TYPE(attr->value.single, NULL); str = rb_str_new(0, length); p = (unsigned char *)RSTRING_PTR(str); @@ -268,7 +261,7 @@ ossl_x509attr_to_der(VALUE self) p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_ATTRIBUTE(attr, &p) <= 0) ossl_raise(eX509AttrError, NULL); - rb_str_set_len(str, p - (unsigned char*)RSTRING_PTR(str)); + ossl_str_adjust(str, p); return str; } diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index 4dafae17..34b8aae7 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -78,9 +78,9 @@ ossl_x509_new_from_file(VALUE filename) FILE *fp; VALUE obj; - SafeStringValue(filename); + rb_check_safe_obj(filename); obj = NewX509(cX509Cert); - if (!(fp = fopen(RSTRING_PTR(filename), "r"))) { + if (!(fp = fopen(StringValueCStr(filename), "r"))) { ossl_raise(eX509CertError, "%s", strerror(errno)); } rb_fd_fix_cloexec(fileno(fp)); @@ -591,18 +591,19 @@ ossl_x509_verify(VALUE self, VALUE key) { X509 *x509; EVP_PKEY *pkey; - int i; pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ GetX509(self, x509); - if ((i = X509_verify(x509, pkey)) < 0) { - ossl_raise(eX509CertError, NULL); - } - if (i > 0) { + + switch (X509_verify(x509, pkey)) { + case 1: return Qtrue; + case 0: + ossl_clear_error(); + return Qfalse; + default: + ossl_raise(eX509CertError, NULL); } - - return Qfalse; } /* diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index f64712ef..a660ccce 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -360,17 +360,17 @@ static VALUE ossl_x509crl_verify(VALUE self, VALUE key) { X509_CRL *crl; - int ret; GetX509CRL(self, crl); - if ((ret = X509_CRL_verify(crl, GetPKeyPtr(key))) < 0) { - ossl_raise(eX509CRLError, NULL); - } - if (ret == 1) { + switch (X509_CRL_verify(crl, GetPKeyPtr(key))) { + case 1: return Qtrue; + case 0: + ossl_clear_error(); + return Qfalse; + default: + ossl_raise(eX509CRLError, NULL); } - - return Qfalse; } static VALUE diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c index 70a117cc..e5817543 100644 --- a/ext/openssl/ossl_x509ext.c +++ b/ext/openssl/ossl_x509ext.c @@ -188,7 +188,6 @@ ossl_x509extfactory_set_crl(VALUE self, VALUE crl) return crl; } -#ifdef HAVE_X509V3_SET_NCONF static VALUE ossl_x509extfactory_set_config(VALUE self, VALUE config) { @@ -202,9 +201,6 @@ ossl_x509extfactory_set_config(VALUE self, VALUE config) return config; } -#else -#define ossl_x509extfactory_set_config rb_f_notimplement -#endif static VALUE ossl_x509extfactory_initialize(int argc, VALUE *argv, VALUE self) @@ -243,36 +239,29 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self) X509_EXTENSION *ext; VALUE oid, value, critical, valstr, obj; int nid; -#ifdef HAVE_X509V3_EXT_NCONF_NID VALUE rconf; CONF *conf; -#else - static LHASH *empty_lhash; -#endif rb_scan_args(argc, argv, "21", &oid, &value, &critical); - StringValue(oid); + StringValueCStr(oid); StringValue(value); if(NIL_P(critical)) critical = Qfalse; nid = OBJ_ln2nid(RSTRING_PTR(oid)); if(!nid) nid = OBJ_sn2nid(RSTRING_PTR(oid)); - if(!nid) ossl_raise(eX509ExtError, "unknown OID `%s'", RSTRING_PTR(oid)); + if(!nid) ossl_raise(eX509ExtError, "unknown OID `%"PRIsVALUE"'", oid); + valstr = rb_str_new2(RTEST(critical) ? "critical," : ""); rb_str_append(valstr, value); + StringValueCStr(valstr); + GetX509ExtFactory(self, ctx); obj = NewX509Ext(cX509Ext); -#ifdef HAVE_X509V3_EXT_NCONF_NID rconf = rb_iv_get(self, "@config"); conf = NIL_P(rconf) ? NULL : GetConfigPtr(rconf); ext = X509V3_EXT_nconf_nid(conf, ctx, nid, RSTRING_PTR(valstr)); -#else - if (!empty_lhash) empty_lhash = lh_new(NULL, NULL); - ext = X509V3_EXT_conf_nid(empty_lhash, ctx, nid, RSTRING_PTR(valstr)); -#endif if (!ext){ - ossl_raise(eX509ExtError, "%s = %s", - RSTRING_PTR(oid), RSTRING_PTR(value)); + ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr); } SetX509Ext(obj, ext); @@ -341,7 +330,7 @@ ossl_x509ext_set_oid(VALUE self, VALUE oid) ASN1_OBJECT *obj; char *s; - s = StringValuePtr(oid); + s = StringValueCStr(oid); obj = OBJ_txt2obj(s, 0); if(!obj) obj = OBJ_txt2obj(s, 1); if(!obj) ossl_raise(eX509ExtError, NULL); @@ -356,23 +345,16 @@ ossl_x509ext_set_value(VALUE self, VALUE data) { X509_EXTENSION *ext; ASN1_OCTET_STRING *asn1s; - char *s; data = ossl_to_der_if_possible(data); StringValue(data); - if(!(s = OPENSSL_malloc(RSTRING_LEN(data)))) - ossl_raise(eX509ExtError, "malloc error"); - memcpy(s, RSTRING_PTR(data), RSTRING_LEN(data)); if(!(asn1s = ASN1_OCTET_STRING_new())){ - OPENSSL_free(s); ossl_raise(eX509ExtError, NULL); } - if(!M_ASN1_OCTET_STRING_set(asn1s, s, RSTRING_LENINT(data))){ - OPENSSL_free(s); + if(!ASN1_STRING_set((ASN1_STRING *)asn1s, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data))){ ASN1_OCTET_STRING_free(asn1s); ossl_raise(eX509ExtError, NULL); } - OPENSSL_free(s); GetX509Ext(self, ext); X509_EXTENSION_set_data(ext, asn1s); @@ -424,7 +406,7 @@ ossl_x509ext_get_value(VALUE obj) if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eX509ExtError, NULL); if (!X509V3_EXT_print(out, ext, 0, 0)) - M_ASN1_OCTET_STRING_print(out, ext->value); + ASN1_STRING_print(out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); ret = ossl_membio2str(out); return ret; diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index e5ce088a..c1cdca5f 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -375,18 +375,18 @@ ossl_x509req_verify(VALUE self, VALUE key) { X509_REQ *req; EVP_PKEY *pkey; - int i; GetX509Req(self, req); pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ - if ((i = X509_REQ_verify(req, pkey)) < 0) { - ossl_raise(eX509ReqError, NULL); - } - if (i > 0) { + switch (X509_REQ_verify(req, pkey)) { + case 1: return Qtrue; + case 0: + ossl_clear_error(); + return Qfalse; + default: + ossl_raise(eX509ReqError, NULL); } - - return Qfalse; } static VALUE diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index bb6fe14d..767e30be 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -130,7 +130,7 @@ ossl_x509store_set_vfy_cb(VALUE self, VALUE cb) X509_STORE *store; GetX509Store(self, store); - X509_STORE_set_ex_data(store, ossl_verify_cb_idx, (void*)cb); + X509_STORE_set_ex_data(store, ossl_store_ex_verify_cb_idx, (void *)cb); rb_iv_set(self, "@verify_callback", cb); return cb; @@ -153,12 +153,6 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) X509_STORE_set_verify_cb_func(store, ossl_verify_cb); ossl_x509store_set_vfy_cb(self, Qnil); -#if (OPENSSL_VERSION_NUMBER < 0x00907000L) - rb_iv_set(self, "@flags", INT2FIX(0)); - rb_iv_set(self, "@purpose", INT2FIX(0)); - rb_iv_set(self, "@trust", INT2FIX(0)); -#endif - /* last verification status */ rb_iv_set(self, "@error", Qnil); rb_iv_set(self, "@error_string", Qnil); @@ -171,15 +165,11 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) static VALUE ossl_x509store_set_flags(VALUE self, VALUE flags) { -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) X509_STORE *store; long f = NUM2LONG(flags); GetX509Store(self, store); X509_STORE_set_flags(store, f); -#else - rb_iv_set(self, "@flags", flags); -#endif return flags; } @@ -187,15 +177,11 @@ ossl_x509store_set_flags(VALUE self, VALUE flags) static VALUE ossl_x509store_set_purpose(VALUE self, VALUE purpose) { -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) X509_STORE *store; int p = NUM2INT(purpose); GetX509Store(self, store); X509_STORE_set_purpose(store, p); -#else - rb_iv_set(self, "@purpose", purpose); -#endif return purpose; } @@ -203,15 +189,11 @@ ossl_x509store_set_purpose(VALUE self, VALUE purpose) static VALUE ossl_x509store_set_trust(VALUE self, VALUE trust) { -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) X509_STORE *store; int t = NUM2INT(trust); GetX509Store(self, store); X509_STORE_set_trust(store, t); -#else - rb_iv_set(self, "@trust", trust); -#endif return trust; } @@ -240,8 +222,8 @@ ossl_x509store_add_file(VALUE self, VALUE file) char *path = NULL; if(file != Qnil){ - SafeStringValue(file); - path = RSTRING_PTR(file); + rb_check_safe_obj(file); + path = StringValueCStr(file); } GetX509Store(self, store); lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); @@ -261,8 +243,8 @@ ossl_x509store_add_path(VALUE self, VALUE dir) char *path = NULL; if(dir != Qnil){ - SafeStringValue(dir); - path = RSTRING_PTR(dir); + rb_check_safe_obj(dir); + path = StringValueCStr(dir); } GetX509Store(self, store); lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); @@ -441,17 +423,10 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) SafeGetX509Store(store, x509st); if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */ if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain); -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){ sk_X509_pop_free(x509s, X509_free); ossl_raise(eX509StoreError, NULL); } -#else - X509_STORE_CTX_init(ctx, x509st, x509, x509s); - ossl_x509stctx_set_flags(self, rb_iv_get(store, "@flags")); - ossl_x509stctx_set_purpose(self, rb_iv_get(store, "@purpose")); - ossl_x509stctx_set_trust(self, rb_iv_get(store, "@trust")); -#endif if (!NIL_P(t = rb_iv_get(store, "@time"))) ossl_x509stctx_set_time(self, t); rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback")); @@ -464,14 +439,20 @@ static VALUE ossl_x509stctx_verify(VALUE self) { X509_STORE_CTX *ctx; - int result; GetX509StCtx(self, ctx); - X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx, - (void*)rb_iv_get(self, "@verify_callback")); - result = X509_verify_cert(ctx); - - return result ? Qtrue : Qfalse; + X509_STORE_CTX_set_ex_data(ctx, ossl_store_ctx_ex_verify_cb_idx, + (void *)rb_iv_get(self, "@verify_callback")); + + switch (X509_verify_cert(ctx)) { + case 1: + return Qtrue; + case 0: + ossl_clear_error(); + return Qfalse; + default: + ossl_raise(eX509CertError, NULL); + } } static VALUE @@ -556,16 +537,12 @@ ossl_x509stctx_get_curr_cert(VALUE self) static VALUE ossl_x509stctx_get_curr_crl(VALUE self) { -#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); if(!ctx->current_crl) return Qnil; return ossl_x509crl_new(ctx->current_crl); -#else - return Qnil; -#endif } static VALUE diff --git a/lib/openssl/buffering.rb b/lib/openssl/buffering.rb index d0821990..61e1f43e 100644 --- a/lib/openssl/buffering.rb +++ b/lib/openssl/buffering.rb @@ -132,7 +132,6 @@ module OpenSSL::Buffering buf.replace(ret) ret = buf end - raise EOFError if ret.empty? ret end @@ -182,7 +181,6 @@ module OpenSSL::Buffering buf.replace(ret) ret = buf end - raise EOFError if ret.empty? ret end diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb index 57519f2c..00c32753 100644 --- a/lib/openssl/ssl.rb +++ b/lib/openssl/ssl.rb @@ -56,19 +56,16 @@ module OpenSSL }.join(":"), :options => -> { opts = OpenSSL::SSL::OP_ALL - opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) + opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) - opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) - opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) + opts |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 opts }.call } DEFAULT_CERT_STORE = OpenSSL::X509::Store.new DEFAULT_CERT_STORE.set_default_paths - if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL) - DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL - end + DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL INIT_VARS = ["cert", "key", "client_ca", "ca_file", "ca_path", "timeout", "verify_mode", "verify_depth", "renegotiation_cb", @@ -250,47 +247,21 @@ module OpenSSL include Buffering include SocketForwarder - if ExtConfig::OPENSSL_NO_SOCK - def initialize(io, ctx = nil); raise NotImplementedError; end - else - if ExtConfig::HAVE_TLSEXT_HOST_NAME - attr_accessor :hostname - end - - attr_reader :io, :context - attr_accessor :sync_close - alias :to_io :io - - # call-seq: - # SSLSocket.new(io) => aSSLSocket - # SSLSocket.new(io, ctx) => aSSLSocket - # - # Creates a new SSL socket from +io+ which must be a real ruby object (not an - # IO-like object that responds to read/write). - # - # If +ctx+ is provided the SSL Sockets initial params will be taken from - # the context. - # - # The OpenSSL::Buffering module provides additional IO methods. - # - # This method will freeze the SSLContext if one is provided; - # however, session management is still allowed in the frozen SSLContext. - - def initialize(io, context = OpenSSL::SSL::SSLContext.new) - @io = io - @context = context - @sync_close = false - @hostname = nil - @io.nonblock = true if @io.respond_to?(:nonblock=) - context.setup - super() - end + if ExtConfig::HAVE_TLSEXT_HOST_NAME + attr_reader :hostname end + attr_reader :io, :context + attr_accessor :sync_close + alias :to_io :io + # call-seq: # ssl.sysclose => nil # - # Shuts down the SSL connection and prepares it for another connection. + # Sends "close notify" to the peer and tries to shut down the SSL + # connection gracefully. + # + # If sync_close is set to +true+, the underlying IO is also closed. def sysclose return if closed? stop diff --git a/test/test_asn1.rb b/test/test_asn1.rb index fd2118d8..9db9ec51 100644 --- a/test/test_asn1.rb +++ b/test/test_asn1.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -class OpenSSL::TestASN1 < Test::Unit::TestCase +class OpenSSL::TestASN1 < OpenSSL::TestCase def test_decode subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") key = OpenSSL::TestUtils::TEST_KEY_RSA1024 diff --git a/test/test_bn.rb b/test/test_bn.rb index 848d1feb..37ba5e55 100644 --- a/test/test_bn.rb +++ b/test/test_bn.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestBN < Test::Unit::TestCase +class OpenSSL::TestBN < OpenSSL::TestCase def test_new_str e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) @@ -43,10 +43,18 @@ class OpenSSL::TestBN < Test::Unit::TestCase assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1)) end - def test_cmp_nil - bn = OpenSSL::BN.new('1') - assert_equal(false, bn == nil) - assert_equal(true, bn != nil) + def test_cmp + bn1 = OpenSSL::BN.new('1') + bn2 = OpenSSL::BN.new('1') + bn3 = OpenSSL::BN.new('2') + assert_equal(false, bn1 == nil) + assert_equal(true, bn1 != nil) + assert_equal(true, bn1 == bn2) + assert_equal(false, bn1 == bn3) + assert_equal(true, bn1.eql?(bn2)) + assert_equal(false, bn1.eql?(bn3)) + assert_equal(bn1.hash, bn2.hash) + assert_not_equal(bn3.hash, bn1.hash) end end diff --git a/test/test_buffering.rb b/test/test_buffering.rb index fe07b46a..1f42cd3c 100644 --- a/test/test_buffering.rb +++ b/test/test_buffering.rb @@ -2,7 +2,7 @@ require_relative 'utils' require 'stringio' -class OpenSSL::TestBuffering < Test::Unit::TestCase +class OpenSSL::TestBuffering < OpenSSL::TestCase class IO include OpenSSL::Buffering @@ -43,19 +43,19 @@ class OpenSSL::TestBuffering < Test::Unit::TestCase def test_flush @io.write 'a' - refute @io.sync + assert_not_predicate @io, :sync assert_empty @io.string assert_equal @io, @io.flush - refute @io.sync + assert_not_predicate @io, :sync assert_equal 'a', @io.string end def test_flush_error @io.write 'a' - refute @io.sync + assert_not_predicate @io, :sync assert_empty @io.string def @io.syswrite *a @@ -66,7 +66,7 @@ class OpenSSL::TestBuffering < Test::Unit::TestCase @io.flush end - refute @io.sync, 'sync must not change' + assert_not_predicate @io, :sync, 'sync must not change' end def test_getc diff --git a/test/test_cipher.rb b/test/test_cipher.rb index 89c176f4..ec14f467 100644 --- a/test/test_cipher.rb +++ b/test/test_cipher.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestCipher < Test::Unit::TestCase +class OpenSSL::TestCipher < OpenSSL::TestCase class << self @@ -34,6 +34,7 @@ class OpenSSL::TestCipher < Test::Unit::TestCase end def teardown + super @c1 = @c2 = nil end @@ -53,8 +54,8 @@ class OpenSSL::TestCipher < Test::Unit::TestCase def test_info assert_equal("DES-EDE3-CBC", @c1.name, "name") assert_equal("DES-EDE3-CBC", @c2.name, "name") - assert_kind_of(Fixnum, @c1.key_len, "key_len") - assert_kind_of(Fixnum, @c1.iv_len, "iv_len") + assert_kind_of(Integer, @c1.key_len, "key_len") + assert_kind_of(Integer, @c1.iv_len, "iv_len") end def test_dup @@ -79,6 +80,18 @@ class OpenSSL::TestCipher < Test::Unit::TestCase assert_equal(s1, s2, "encrypt reset") end + def test_key_iv_set + # default value for DES-EDE3-CBC + assert_equal(24, @c1.key_len) + assert_equal(8, @c1.iv_len) + assert_raise(ArgumentError) { @c1.key = "\x01" * 23 } + @c1.key = "\x01" * 24 + assert_raise(ArgumentError) { @c1.key = "\x01" * 25 } + assert_raise(ArgumentError) { @c1.iv = "\x01" * 7 } + @c1.iv = "\x01" * 8 + assert_raise(ArgumentError) { @c1.iv = "\x01" * 9 } + end + def test_empty_data @c1.encrypt assert_raise(ArgumentError){ @c1.update("") } @@ -101,40 +114,38 @@ class OpenSSL::TestCipher < Test::Unit::TestCase end end if has_cipher?('aes-128-ctr') - if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00907000 - def test_ciphers - OpenSSL::Cipher.ciphers.each{|name| - next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name - begin - assert_kind_of(OpenSSL::Cipher::Cipher, OpenSSL::Cipher::Cipher.new(name)) - rescue OpenSSL::Cipher::CipherError => e - next if /wrap/ =~ name and e.message == 'wrap mode not allowed' - raise - end - } - end + def test_ciphers + OpenSSL::Cipher.ciphers.each{|name| + next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name + begin + assert_kind_of(OpenSSL::Cipher::Cipher, OpenSSL::Cipher::Cipher.new(name)) + rescue OpenSSL::Cipher::CipherError => e + next if /wrap/ =~ name and e.message == 'wrap mode not allowed' + raise + end + } + end - def test_AES - pt = File.read(__FILE__) - %w(ECB CBC CFB OFB).each{|mode| - c1 = OpenSSL::Cipher::AES256.new(mode) - c1.encrypt - c1.pkcs5_keyivgen("passwd") - ct = c1.update(pt) + c1.final - - c2 = OpenSSL::Cipher::AES256.new(mode) - c2.decrypt - c2.pkcs5_keyivgen("passwd") - assert_equal(pt, c2.update(ct) + c2.final) - } - end + def test_AES + pt = File.read(__FILE__) + %w(ECB CBC CFB OFB).each{|mode| + c1 = OpenSSL::Cipher::AES256.new(mode) + c1.encrypt + c1.pkcs5_keyivgen("passwd") + ct = c1.update(pt) + c1.final + + c2 = OpenSSL::Cipher::AES256.new(mode) + c2.decrypt + c2.pkcs5_keyivgen("passwd") + assert_equal(pt, c2.update(ct) + c2.final) + } + end - def test_AES_crush - 500.times do - assert_nothing_raised("[Bug #2768]") do - # it caused OpenSSL SEGV by uninitialized key - OpenSSL::Cipher::AES128.new("ECB").update "." * 17 - end + def test_AES_crush + 500.times do + assert_nothing_raised("[Bug #2768]") do + # it caused OpenSSL SEGV by uninitialized key + OpenSSL::Cipher::AES128.new("ECB").update "." * 17 end end end @@ -143,9 +154,9 @@ class OpenSSL::TestCipher < Test::Unit::TestCase def test_authenticated cipher = OpenSSL::Cipher.new('aes-128-gcm') - assert(cipher.authenticated?) + assert_predicate(cipher, :authenticated?) cipher = OpenSSL::Cipher.new('aes-128-cbc') - refute(cipher.authenticated?) + assert_not_predicate(cipher, :authenticated?) end def test_aes_gcm diff --git a/test/test_config.rb b/test/test_config.rb index 812b28b9..3e2e1273 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -class OpenSSL::TestConfig < Test::Unit::TestCase +class OpenSSL::TestConfig < OpenSSL::TestCase def setup file = Tempfile.open("openssl.cnf") file << <<__EOD__ @@ -18,6 +18,7 @@ __EOD__ end def teardown + super @tmpfile.close! end diff --git a/test/test_digest.rb b/test/test_digest.rb index 8b724a03..ba3e974b 100644 --- a/test/test_digest.rb +++ b/test/test_digest.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestDigest < Test::Unit::TestCase +class OpenSSL::TestDigest < OpenSSL::TestCase def setup @d1 = OpenSSL::Digest.new("MD5") @d2 = OpenSSL::Digest::MD5.new @@ -12,6 +12,7 @@ class OpenSSL::TestDigest < Test::Unit::TestCase end def teardown + super @d1 = @d2 = @md = nil end diff --git a/test/test_engine.rb b/test/test_engine.rb index 3521de63..9a0da340 100644 --- a/test/test_engine.rb +++ b/test/test_engine.rb @@ -1,9 +1,10 @@ # frozen_string_literal: false require_relative 'utils' -class OpenSSL::TestEngine < Test::Unit::TestCase +class OpenSSL::TestEngine < OpenSSL::TestCase def teardown + super OpenSSL::Engine.cleanup # [ruby-core:40669] assert_equal(0, OpenSSL::Engine.engines.size) end diff --git a/test/test_fips.rb b/test/test_fips.rb index 33769c93..534dade0 100644 --- a/test/test_fips.rb +++ b/test/test_fips.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestFIPS < Test::Unit::TestCase +class OpenSSL::TestFIPS < OpenSSL::TestCase def test_fips_mode_is_reentrant OpenSSL.fips_mode = false diff --git a/test/test_hmac.rb b/test/test_hmac.rb index 135d26f0..3c90a5de 100644 --- a/test/test_hmac.rb +++ b/test/test_hmac.rb @@ -3,7 +3,7 @@ require_relative 'utils' -class OpenSSL::TestHMAC < Test::Unit::TestCase +class OpenSSL::TestHMAC < OpenSSL::TestCase def setup @digest = OpenSSL::Digest::MD5 @key = "KEY" @@ -12,9 +12,6 @@ class OpenSSL::TestHMAC < Test::Unit::TestCase @h2 = OpenSSL::HMAC.new(@key, "MD5") end - def teardown - end - def test_hmac @h1.update(@data) @h2.update(@data) diff --git a/test/test_ns_spki.rb b/test/test_ns_spki.rb index 4f6e6f59..4740c0b2 100644 --- a/test/test_ns_spki.rb +++ b/test/test_ns_spki.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestNSSPI < Test::Unit::TestCase +class OpenSSL::TestNSSPI < OpenSSL::TestCase def setup # This request data is adopt from the specification of # "Netscape Extensions for User Key Generation". diff --git a/test/test_ocsp.rb b/test/test_ocsp.rb index 1a969fd7..d04b4216 100644 --- a/test/test_ocsp.rb +++ b/test/test_ocsp.rb @@ -3,7 +3,7 @@ require_relative "utils" if defined?(OpenSSL::TestUtils) -class OpenSSL::TestOCSP < Test::Unit::TestCase +class OpenSSL::TestOCSP < OpenSSL::TestCase def setup ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") ca_key = OpenSSL::TestUtils::TEST_KEY_RSA1024 diff --git a/test/test_pair.rb b/test/test_pair.rb index c7fc292e..c8770690 100644 --- a/test/test_pair.rb +++ b/test/test_pair.rb @@ -280,6 +280,35 @@ module OpenSSL::TestPairM } end + def test_write_nonblock_retry + ssl_pair {|s1, s2| + # fill up a socket so we hit EAGAIN + written = String.new + n = 0 + buf = 'a' * 11 + case ret = s1.write_nonblock(buf, exception: false) + when :wait_readable then break + when :wait_writable then break + when Integer + written << buf + n += ret + exp = buf.bytesize + if ret != exp + buf = buf.byteslice(ret, exp - ret) + end + end while true + assert_kind_of Symbol, ret + + # make more space for subsequent write: + readed = s2.read(n) + assert_equal written, readed + + # this fails if SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER is missing: + buf2 = Marshal.load(Marshal.dump(buf)) + assert_kind_of Integer, s1.write_nonblock(buf2, exception: false) + } + end + def tcp_pair host = "127.0.0.1" serv = TCPServer.new(host, 0) @@ -343,41 +372,73 @@ module OpenSSL::TestPairM end def test_ecdh_callback - called = false - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.ciphers = "ECDH" - ctx2.tmp_ecdh_callback = ->(*args) { - called = true - OpenSSL::PKey::EC.new "prime256v1" - } + return unless OpenSSL::SSL::SSLContext.instance_methods.include?(:tmp_ecdh_callback) + EnvUtil.suppress_warning do # tmp_ecdh_callback is deprecated (2016-05) + begin + called = false + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.ciphers = "ECDH" + ctx2.tmp_ecdh_callback = ->(*args) { + called = true + OpenSSL::PKey::EC.new "prime256v1" + } + + sock1, sock2 = tcp_pair + + s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.ciphers = "ECDH" + + s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) + th = Thread.new do + begin + rv = s1.connect_nonblock(exception: false) + case rv + when :wait_writable + IO.select(nil, [s1], nil, 5) + when :wait_readable + IO.select([s1], nil, nil, 5) + end + end until rv == s1 + end + + accepted = s2.accept + assert called, 'ecdh callback should be called' + rescue OpenSSL::SSL::SSLError => e + if e.message =~ /no cipher match/ + skip "ECDH cipher not supported." + else + raise e + end + ensure + th.join if th + s1.close if s1 + s2.close if s2 + sock1.close if sock1 + sock2.close if sock2 + end + end + end + def test_ecdh_curves sock1, sock2 = tcp_pair - s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) ctx1 = OpenSSL::SSL::SSLContext.new ctx1.ciphers = "ECDH" - + ctx1.ecdh_curves = "P-384:P-521" s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) - th = Thread.new do - begin - rv = s1.connect_nonblock(exception: false) - case rv - when :wait_writable - IO.select(nil, [s1], nil, 5) - when :wait_readable - IO.select([s1], nil, nil, 5) - end - end until rv == s1 - end - accepted = s2.accept + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.ciphers = "ECDH" + ctx2.ecdh_curves = "P-256:P-384" + s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) - assert called, 'ecdh callback should be called' - rescue OpenSSL::SSL::SSLError => e - if e.message =~ /no cipher match/ - skip "ECDH cipher not supported." - else - raise e + th = Thread.new { s1.accept } + s2.connect + + assert s2.cipher[0].start_with?("AECDH"), "AECDH should be used" + if s2.respond_to?(:tmp_key) + assert_equal "secp384r1", s2.tmp_key.group.curve_name end ensure th.join if th @@ -385,7 +446,6 @@ module OpenSSL::TestPairM s2.close if s2 sock1.close if sock1 sock2.close if sock2 - accepted.close if accepted.respond_to?(:close) end def test_connect_accept_nonblock_no_exception @@ -488,36 +548,36 @@ module OpenSSL::TestPairM end end -class OpenSSL::TestEOF1 < Test::Unit::TestCase +class OpenSSL::TestEOF1 < OpenSSL::TestCase include TestEOF include OpenSSL::SSLPair include OpenSSL::TestEOF1M end -class OpenSSL::TestEOF1LowlevelSocket < Test::Unit::TestCase +class OpenSSL::TestEOF1LowlevelSocket < OpenSSL::TestCase include TestEOF include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestEOF1M end -class OpenSSL::TestEOF2 < Test::Unit::TestCase +class OpenSSL::TestEOF2 < OpenSSL::TestCase include TestEOF include OpenSSL::SSLPair include OpenSSL::TestEOF2M end -class OpenSSL::TestEOF2LowlevelSocket < Test::Unit::TestCase +class OpenSSL::TestEOF2LowlevelSocket < OpenSSL::TestCase include TestEOF include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestEOF2M end -class OpenSSL::TestPair < Test::Unit::TestCase +class OpenSSL::TestPair < OpenSSL::TestCase include OpenSSL::SSLPair include OpenSSL::TestPairM end -class OpenSSL::TestPairLowlevelSocket < Test::Unit::TestCase +class OpenSSL::TestPairLowlevelSocket < OpenSSL::TestCase include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestPairM end diff --git a/test/test_pkcs12.rb b/test/test_pkcs12.rb index ba07d767..61fb4474 100644 --- a/test/test_pkcs12.rb +++ b/test/test_pkcs12.rb @@ -4,7 +4,7 @@ require_relative "utils" if defined?(OpenSSL::TestUtils) module OpenSSL - class TestPKCS12 < Test::Unit::TestCase + class TestPKCS12 < OpenSSL::TestCase include OpenSSL::TestUtils def setup diff --git a/test/test_pkcs5.rb b/test/test_pkcs5.rb index f38fd716..ad8132c2 100644 --- a/test/test_pkcs5.rb +++ b/test/test_pkcs5.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -class OpenSSL::TestPKCS5 < Test::Unit::TestCase +class OpenSSL::TestPKCS5 < OpenSSL::TestCase def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20 p ="password" diff --git a/test/test_pkcs7.rb b/test/test_pkcs7.rb index ce99db50..def4910c 100644 --- a/test/test_pkcs7.rb +++ b/test/test_pkcs7.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestPKCS7 < Test::Unit::TestCase +class OpenSSL::TestPKCS7 < OpenSSL::TestCase def setup @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 @@ -123,12 +123,6 @@ class OpenSSL::TestPKCS7 < Test::Unit::TestCase end def test_enveloped - if OpenSSL::OPENSSL_VERSION_NUMBER <= 0x0090704f - # PKCS7_encrypt() of OpenSSL-0.9.7d goes to SEGV. - # http://www.mail-archive.com/openssl-dev@openssl.org/msg17376.html - return - end - certs = [@ee1_cert, @ee2_cert] cipher = OpenSSL::Cipher::AES.new("128-CBC") data = "aaaaa\nbbbbb\nccccc\n" diff --git a/test/test_pkey_dh.rb b/test/test_pkey_dh.rb index a0eca53c..afd7a318 100644 --- a/test/test_pkey_dh.rb +++ b/test/test_pkey_dh.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestPKeyDH < Test::Unit::TestCase +class OpenSSL::TestPKeyDH < OpenSSL::TestCase NEW_KEYLEN = 256 diff --git a/test/test_pkey_dsa.rb b/test/test_pkey_dsa.rb index eb3e4f1c..680a1239 100644 --- a/test/test_pkey_dsa.rb +++ b/test/test_pkey_dsa.rb @@ -4,7 +4,7 @@ require 'base64' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestPKeyDSA < Test::Unit::TestCase +class OpenSSL::TestPKeyDSA < OpenSSL::TestCase def test_private key = OpenSSL::PKey::DSA.new(256) assert(key.private?) @@ -20,7 +20,6 @@ class OpenSSL::TestPKeyDSA < Test::Unit::TestCase key = OpenSSL::PKey::DSA.new 256 pem = key.public_key.to_pem OpenSSL::PKey::DSA.new pem - assert_equal([], OpenSSL.errors) end def test_new_break @@ -84,7 +83,6 @@ end assert_equal(g, key.g) assert_equal(y, key.pub_key) assert_equal(nil, key.priv_key) - assert_equal([], OpenSSL.errors) end def test_read_DSAPublicKey_pem @@ -109,7 +107,6 @@ fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ== assert_equal(g, key.g) assert_equal(y, key.pub_key) assert_equal(nil, key.priv_key) - assert_equal([], OpenSSL.errors) end def test_read_DSA_PUBKEY_pem @@ -135,7 +132,6 @@ YNMbNw== assert_equal(g, key.g) assert_equal(y, key.pub_key) assert_equal(nil, key.priv_key) - assert_equal([], OpenSSL.errors) end def test_export_format_is_DSA_PUBKEY_pem @@ -165,7 +161,6 @@ YNMbNw== pub_key = OpenSSL::ASN1.decode(seq[1].value) assert_equal(OpenSSL::ASN1::INTEGER, pub_key.tag) assert_equal(key.pub_key, pub_key.value) - assert_equal([], OpenSSL.errors) end def test_read_private_key_der @@ -174,7 +169,6 @@ YNMbNw== key2 = OpenSSL::PKey.read(der) assert(key2.private?) assert_equal(der, key2.to_der) - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem @@ -183,7 +177,6 @@ YNMbNw== key2 = OpenSSL::PKey.read(pem) assert(key2.private?) assert_equal(pem, key2.to_pem) - assert_equal([], OpenSSL.errors) end def test_read_public_key_der @@ -192,7 +185,6 @@ YNMbNw== key2 = OpenSSL::PKey.read(der) assert(!key2.private?) assert_equal(der, key2.to_der) - assert_equal([], OpenSSL.errors) end def test_read_public_key_pem @@ -201,7 +193,6 @@ YNMbNw== key2 = OpenSSL::PKey.read(pem) assert(!key2.private?) assert_equal(pem, key2.to_pem) - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem_pw @@ -216,7 +207,6 @@ YNMbNw== key2 = OpenSSL::PKey.read(pem, 'secret') assert(key2.private?) #omit pem equality check, will be different due to cipher iv - assert_equal([], OpenSSL.errors) end def test_export_password_length @@ -228,6 +218,18 @@ YNMbNw== assert(pem) end + def test_export_password_funny + key = OpenSSL::TestUtils::TEST_KEY_DSA256 + pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), "pass\0wd") + assert_raise(ArgumentError) do + OpenSSL::PKey.read(pem, "pass") + end + key2 = OpenSSL::PKey.read(pem, "pass\0wd") + assert(key2.private?) + key3 = OpenSSL::PKey::DSA.new(pem, "pass\0wd") + assert(key3.private?) + end + private def check_sign_verify(digest) diff --git a/test/test_pkey_ec.rb b/test/test_pkey_ec.rb index fe128fd4..4161e9b0 100644 --- a/test/test_pkey_ec.rb +++ b/test/test_pkey_ec.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC) -class OpenSSL::TestEC < Test::Unit::TestCase +class OpenSSL::TestEC < OpenSSL::TestCase def setup @data1 = 'foo' @data2 = 'bar' * 1000 # data too long for DSA sig @@ -16,7 +16,7 @@ class OpenSSL::TestEC < Test::Unit::TestCase group = OpenSSL::PKey::EC::Group.new(curve) key = OpenSSL::PKey::EC.new(group) - key.generate_key + key.generate_key! @groups << group @keys << key @@ -38,11 +38,32 @@ class OpenSSL::TestEC < Test::Unit::TestCase end end + def test_generate + assert_raise(OpenSSL::PKey::ECError) { OpenSSL::PKey::EC.generate("non-existent") } + g = OpenSSL::PKey::EC::Group.new("prime256v1") + ec = OpenSSL::PKey::EC.generate(g) + assert_equal(true, ec.private?) + ec = OpenSSL::PKey::EC.generate("prime256v1") + assert_equal(true, ec.private?) + end + def test_check_key for key in @keys - assert_equal(key.check_key, true) - assert_equal(key.private_key?, true) - assert_equal(key.public_key?, true) + assert_equal(true, key.check_key) + assert_equal(true, key.private?) + assert_equal(true, key.public?) + key2 = OpenSSL::PKey::EC.new(key.group) + assert_equal(false, key2.private?) + assert_equal(false, key2.public?) + key2.public_key = key.public_key + assert_equal(false, key2.private?) + assert_equal(true, key2.public?) + key2.private_key = key.private_key + assert_equal(true, key2.private?) + assert_equal(true, key2.public?) + assert_equal(true, key2.check_key) + key2.private_key += 1 + assert_raise(OpenSSL::PKey::ECError) { key2.check_key } end end @@ -115,7 +136,7 @@ class OpenSSL::TestEC < Test::Unit::TestCase def test_dh_compute_key for key in @keys k = OpenSSL::PKey::EC.new(key.group) - k.generate_key + k.generate_key! puba = key.public_key pubb = k.public_key @@ -131,7 +152,6 @@ class OpenSSL::TestEC < Test::Unit::TestCase ec2 = OpenSSL::PKey.read(der) assert(ec2.private_key?) assert_equal(der, ec2.to_der) - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem @@ -140,7 +160,6 @@ class OpenSSL::TestEC < Test::Unit::TestCase ec2 = OpenSSL::PKey.read(pem) assert(ec2.private_key?) assert_equal(pem, ec2.to_pem) - assert_equal([], OpenSSL.errors) end def test_read_public_key_der @@ -151,7 +170,6 @@ class OpenSSL::TestEC < Test::Unit::TestCase ec3 = OpenSSL::PKey.read(der) assert(!ec3.private_key?) assert_equal(der, ec3.to_der) - assert_equal([], OpenSSL.errors) end def test_read_public_key_pem @@ -162,7 +180,6 @@ class OpenSSL::TestEC < Test::Unit::TestCase ec3 = OpenSSL::PKey.read(pem) assert(!ec3.private_key?) assert_equal(pem, ec3.to_pem) - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem_pw @@ -177,7 +194,6 @@ class OpenSSL::TestEC < Test::Unit::TestCase ec2 = OpenSSL::PKey.read(pem, 'secret') assert(ec2.private_key?) #omit pem equality check, will be different due to cipher iv - assert_equal([], OpenSSL.errors) end def test_export_password_length @@ -189,20 +205,57 @@ class OpenSSL::TestEC < Test::Unit::TestCase assert(pem) end + def test_export_password_funny + key = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), "pass\0wd") + assert_raise(ArgumentError) do + OpenSSL::PKey.read(pem, "pass") + end + key2 = OpenSSL::PKey.read(pem, "pass\0wd") + assert(key2.private_key?) + key3 = OpenSSL::PKey::EC.new(pem, "pass\0wd") + assert(key3.private_key?) + end + def test_ec_point_mul - ec = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 - p1 = ec.public_key - bn1 = OpenSSL::BN.new('10') - bn2 = OpenSSL::BN.new('20') - - p2 = p1.mul(bn1) - assert(p1.group == p2.group) - p2 = p1.mul(bn1, bn2) - assert(p1.group == p2.group) - p2 = p1.mul([bn1, bn2], [p1]) - assert(p1.group == p2.group) - p2 = p1.mul([bn1, bn2], [p1], bn2) - assert(p1.group == p2.group) + begin + # y^2 = x^3 + 2x + 2 over F_17 + # generator is (5, 1) + group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2) + gen = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new("040501", 16)) + group.set_generator(gen, 0, 0) + + # 3 * (6, 3) = (16, 13) + point_a = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new("040603", 16)) + result_a1 = point_a.mul(3.to_bn) + assert_equal("04100D", result_a1.to_bn.to_s(16)) + # 3 * (6, 3) + 3 * (5, 1) = (7, 6) + result_a2 = point_a.mul(3.to_bn, 3.to_bn) + assert_equal("040706", result_a2.to_bn.to_s(16)) + # 3 * point_a = 3 * (6, 3) = (16, 13) + result_b1 = point_a.mul([3.to_bn], []) + assert_equal("04100D", result_b1.to_bn.to_s(16)) + # 3 * point_a + 2 * point_a = 3 * (6, 3) + 2 * (6, 3) = (7, 11) + result_b1 = point_a.mul([3.to_bn, 2.to_bn], [point_a]) + assert_equal("04070B", result_b1.to_bn.to_s(16)) + # 3 * point_a + 5 * point_a.group.generator = 3 * (6, 3) + 5 * (5, 1) = (13, 10) + result_b1 = point_a.mul([3.to_bn], [], 5) + assert_equal("040D0A", result_b1.to_bn.to_s(16)) + rescue OpenSSL::PKey::EC::Group::Error + # CentOS patches OpenSSL to reject curves defined over Fp where p < 256 bits + raise if $!.message !~ /unsupported field/ + end + + p256_key = OpenSSL::TestUtils::TEST_KEY_EC_P256V1 + p256_g = p256_key.group + assert_equal(p256_key.public_key, p256_g.generator.mul(p256_key.private_key)) + + # invalid argument + point = p256_key.public_key + assert_raise(TypeError) { point.mul(nil) } + assert_raise(ArgumentError) { point.mul([1.to_bn], [point]) } + assert_raise(TypeError) { point.mul([1.to_bn], nil) } + assert_raise(TypeError) { point.mul([nil], []) } end # test Group: asn1_flag, point_conversion diff --git a/test/test_pkey_rsa.rb b/test/test_pkey_rsa.rb index 165b1ec9..c3532f63 100644 --- a/test/test_pkey_rsa.rb +++ b/test/test_pkey_rsa.rb @@ -4,7 +4,7 @@ require 'base64' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestPKeyRSA < Test::Unit::TestCase +class OpenSSL::TestPKeyRSA < OpenSSL::TestCase def test_padding key = OpenSSL::PKey::RSA.new(512, 3) @@ -180,7 +180,6 @@ AudJR1JobbIbDJrQu6AXnWh5k/YtAgMBAAE= assert_equal(nil, key.d) assert_equal(nil, key.p) assert_equal(nil, key.q) - assert_equal([], OpenSSL.errors) end def test_read_RSA_PUBKEY_pem @@ -201,7 +200,6 @@ AwEAAQ== assert_equal(nil, key.d) assert_equal(nil, key.p) assert_equal(nil, key.q) - assert_equal([], OpenSSL.errors) end def test_export_format_is_RSA_PUBKEY @@ -223,7 +221,6 @@ AwEAAQ== key = OpenSSL::PKey.read(der) assert(key.private?) assert_equal(der, key.to_der) - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem @@ -231,7 +228,6 @@ AwEAAQ== key = OpenSSL::PKey.read(pem) assert(key.private?) assert_equal(pem, key.to_pem) - assert_equal([], OpenSSL.errors) end def test_read_public_key_der @@ -239,7 +235,6 @@ AwEAAQ== key = OpenSSL::PKey.read(der) assert(!key.private?) assert_equal(der, key.to_der) - assert_equal([], OpenSSL.errors) end def test_read_public_key_pem @@ -247,7 +242,6 @@ AwEAAQ== key = OpenSSL::PKey.read(pem) assert(!key.private?) assert_equal(pem, key.to_pem) - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem_pw @@ -261,7 +255,6 @@ AwEAAQ== key = OpenSSL::PKey.read(pem, 'secret') assert(key.private?) #omit pem equality check, will be different due to cipher iv - assert_equal([], OpenSSL.errors) end def test_read_private_key_pem_pw_exception @@ -272,7 +265,6 @@ AwEAAQ== raise RuntimeError end end - assert_equal([], OpenSSL.errors) end def test_export_password_length @@ -284,6 +276,24 @@ AwEAAQ== assert(pem) end + def test_export_password_funny + key = OpenSSL::TestUtils::TEST_KEY_RSA1024 + # this assertion may fail in the future because of OpenSSL change. + # the current upper limit is 1024 + assert_raise(OpenSSL::OpenSSLError) do + key.export(OpenSSL::Cipher.new('AES-128-CBC'), 'secr' * 1024) + end + # password containing NUL byte + pem = key.export(OpenSSL::Cipher.new('AES-128-CBC'), "pass\0wd") + assert_raise(ArgumentError) do + OpenSSL::PKey.read(pem, "pass") + end + key2 = OpenSSL::PKey.read(pem, "pass\0wd") + assert(key2.private?) + key3 = OpenSSL::PKey::RSA.new(pem, "pass\0wd") + assert(key3.private?) + end + private def check_PUBKEY(asn1, key) @@ -306,7 +316,6 @@ AwEAAQ== assert_equal(key.n, pub_key.value[0].value) assert_equal(OpenSSL::ASN1::INTEGER, pub_key.value[1].tag) assert_equal(key.e, pub_key.value[1].value) - assert_equal([], OpenSSL.errors) end end diff --git a/test/test_random.rb b/test/test_random.rb index 8c69d543..ca8427f7 100644 --- a/test/test_random.rb +++ b/test/test_random.rb @@ -1,10 +1,7 @@ # frozen_string_literal: false -begin - require "openssl" -rescue LoadError -end +require_relative "utils" -class OpenSSL::TestRandom < Test::Unit::TestCase +class OpenSSL::TestRandom < OpenSSL::TestCase def test_random_bytes assert_equal("", OpenSSL::Random.random_bytes(0)) assert_equal(12, OpenSSL::Random.random_bytes(12).bytesize) @@ -14,4 +11,4 @@ class OpenSSL::TestRandom < Test::Unit::TestCase assert_equal("", OpenSSL::Random.pseudo_bytes(0)) assert_equal(12, OpenSSL::Random.pseudo_bytes(12).bytesize) end -end if defined?(OpenSSL::Random) +end if defined?(OpenSSL::TestCase) diff --git a/test/test_ssl.rb b/test/test_ssl.rb index f5ca3e9f..164a6aac 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -752,7 +752,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase ctx3 = OpenSSL::SSL::SSLContext.new ctx3.ciphers = "DH" - refute_predicate ctx3, :frozen? + assert_not_predicate ctx3, :frozen? ctx2 = OpenSSL::SSL::SSLContext.new ctx2.ciphers = "DH" @@ -869,10 +869,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => ctx_proc, :server_proc => server_proc) do |server, port| 2.times do |i| ctx = OpenSSL::SSL::SSLContext.new - if defined?(OpenSSL::SSL::OP_NO_TICKET) - # disable RFC4507 support - ctx.options = OpenSSL::SSL::OP_NO_TICKET - end + # disable RFC4507 support + ctx.options = OpenSSL::SSL::OP_NO_TICKET server_connect(port, ctx) { |ssl| ssl.hostname = (i & 1 == 0) ? 'foo.example.com' : 'bar.example.com' str = "x" * 100 + "\n" @@ -1064,7 +1062,9 @@ if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10002000 end end -if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 +if OpenSSL::OPENSSL_VERSION_NUMBER > 0x10001000 && + OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) + # NPN may be disabled by OpenSSL configure option def test_npn_protocol_selection_ary advertised = ["http/1.1", "spdy/2"] @@ -1167,6 +1167,57 @@ end } end + def test_close_and_socket_close_while_connecting + # test it doesn't cause a segmentation fault + ctx = OpenSSL::SSL::SSLContext.new + ctx.ciphers = "aNULL" + + sock1, sock2 = socketpair + ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx) + ssl2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx) + + t = Thread.new { ssl1.connect } + ssl2.accept + + ssl1.close + sock1.close + t.value rescue nil + ensure + ssl1.close if ssl1 + ssl2.close if ssl2 + sock1.close if sock1 + sock2.close if sock2 + end + + def test_get_ephemeral_key + return unless OpenSSL::SSL::SSLSocket.method_defined?(:tmp_key) + pkey = OpenSSL::PKey + ciphers = { + 'ECDHE-RSA-AES128-SHA' => (pkey::EC if defined?(pkey::EC)), + 'DHE-RSA-AES128-SHA' => (pkey::DH if defined?(pkey::DH)), + 'AES128-SHA' => nil + } + conf_proc = Proc.new { |ctx| ctx.ciphers = 'ALL' } + start_server(OpenSSL::SSL::VERIFY_NONE, true, :ctx_proc => conf_proc) do |server, port| + ciphers.each do |cipher, ephemeral| + ctx = OpenSSL::SSL::SSLContext.new + begin + ctx.ciphers = cipher + rescue OpenSSL::SSL::SSLError => e + next if /no cipher match/ =~ e.message + raise + end + server_connect(port, ctx) do |ssl| + if ephemeral + assert_instance_of(ephemeral, ssl.tmp_key) + else + assert_nil(ssl.tmp_key) + end + end + end + end + end + private def start_server_version(version, ctx_proc=nil, server_proc=nil, &blk) diff --git a/test/test_ssl_session.rb b/test/test_ssl_session.rb index 3294aa7f..cdcab02f 100644 --- a/test/test_ssl_session.rb +++ b/test/test_ssl_session.rb @@ -55,16 +55,13 @@ tddwpBAEDjcwMzA5NTYzMTU1MzAwpQMCARM= session = ssl.session assert(session == OpenSSL::SSL::Session.new(session.to_pem)) assert(session == OpenSSL::SSL::Session.new(ssl)) - assert_equal(300, session.timeout) session.timeout = 5 assert_equal(5, session.timeout) assert_not_nil(session.time) # SSL_SESSION_time keeps long value so we can't keep nsec fragment. session.time = t1 = Time.now.to_i assert_equal(Time.at(t1), session.time) - if session.respond_to?(:id) - assert_not_nil(session.id) - end + assert_not_nil(session.id) pem = session.to_pem assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem) assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem) @@ -171,10 +168,7 @@ __EOS__ session = ssl.session if last_session assert(ssl.session_reused?) - - if session.respond_to?(:id) - assert_equal(session.id, last_session.id) - end + assert_equal(session.id, last_session.id) assert_equal(session.to_pem, last_session.to_pem) assert_equal(session.to_der, last_session.to_der) # Older version of OpenSSL may not be consistent. Look up which versions later. @@ -247,10 +241,8 @@ __EOS__ 10.times do |i| sock = TCPSocket.new("127.0.0.1", port) ctx = OpenSSL::SSL::SSLContext.new - if defined?(OpenSSL::SSL::OP_NO_TICKET) - # disable RFC4507 support - ctx.options = OpenSSL::SSL::OP_NO_TICKET - end + # disable RFC4507 support + ctx.options = OpenSSL::SSL::OP_NO_TICKET ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.sync_close = true ssl.session = first_session if first_session diff --git a/test/test_x509attr.rb b/test/test_x509attr.rb new file mode 100644 index 00000000..1ba8a875 --- /dev/null +++ b/test/test_x509attr.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: false +require_relative "utils" + +if defined?(OpenSSL::TestUtils) + +class OpenSSL::TestX509Attribute < OpenSSL::TestCase + def test_new + ef = OpenSSL::X509::ExtensionFactory.new + val = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([ + ef.create_extension("keyUsage", "keyCertSign", true) + ])]) + attr = OpenSSL::X509::Attribute.new("extReq", val) + assert_equal("extReq", attr.oid) + assert_equal(val.to_der, attr.value.to_der) + end + + def test_from_der + # oid: challengePassword, values: Set[UTF8String<"abc123">] + test_der = "\x30\x15\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x07\x31\x08" \ + "\x0c\x06\x61\x62\x63\x31\x32\x33".b + attr = OpenSSL::X509::Attribute.new(test_der) + assert_equal(test_der, attr.to_der) + assert_equal("challengePassword", attr.oid) + assert_equal("abc123", attr.value.value[0].value) + end + + def test_to_der + ef = OpenSSL::X509::ExtensionFactory.new + val = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([ + ef.create_extension("keyUsage", "keyCertSign", true) + ])]) + attr = OpenSSL::X509::Attribute.new("extReq", val) + expected = OpenSSL::ASN1::Sequence.new([ + OpenSSL::ASN1::ObjectId.new("extReq"), + val + ]) + assert_equal(expected.to_der, attr.to_der) + end + + def test_invalid_value + # should not change the original value + test_der = "\x30\x15\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x07\x31\x08" \ + "\x0c\x06\x61\x62\x63\x31\x32\x33".b + attr = OpenSSL::X509::Attribute.new(test_der) + assert_raise(TypeError) { + attr.value = "1234" + } + assert_equal(test_der, attr.to_der) + assert_raise(OpenSSL::X509::AttributeError) { + attr.oid = "abc123" + } + assert_equal(test_der, attr.to_der) + end +end + +end diff --git a/test/test_x509cert.rb b/test/test_x509cert.rb index 72cb9e60..ae7a0f08 100644 --- a/test/test_x509cert.rb +++ b/test/test_x509cert.rb @@ -3,7 +3,7 @@ require_relative "utils" if defined?(OpenSSL::TestUtils) -class OpenSSL::TestX509Certificate < Test::Unit::TestCase +class OpenSSL::TestX509Certificate < OpenSSL::TestCase def setup @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 @@ -14,9 +14,6 @@ class OpenSSL::TestX509Certificate < Test::Unit::TestCase @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") end - def teardown - end - def issue_cert(*args) OpenSSL::TestUtils.issue_cert(*args) end diff --git a/test/test_x509crl.rb b/test/test_x509crl.rb index 7994ddea..fd66c975 100644 --- a/test/test_x509crl.rb +++ b/test/test_x509crl.rb @@ -3,7 +3,7 @@ require_relative "utils" if defined?(OpenSSL::TestUtils) -class OpenSSL::TestX509CRL < Test::Unit::TestCase +class OpenSSL::TestX509CRL < OpenSSL::TestCase def setup @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 @@ -14,9 +14,6 @@ class OpenSSL::TestX509CRL < Test::Unit::TestCase @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") end - def teardown - end - def issue_crl(*args) OpenSSL::TestUtils.issue_crl(*args) end diff --git a/test/test_x509ext.rb b/test/test_x509ext.rb index e6d49bb6..99e2eda5 100644 --- a/test/test_x509ext.rb +++ b/test/test_x509ext.rb @@ -3,7 +3,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestX509Extension < Test::Unit::TestCase +class OpenSSL::TestX509Extension < OpenSSL::TestCase def setup @basic_constraints_value = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Boolean(true), # CA @@ -16,9 +16,6 @@ class OpenSSL::TestX509Extension < Test::Unit::TestCase ]) end - def teardown - end - def test_new ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der) assert_equal("basicConstraints", ext.oid) diff --git a/test/test_x509name.rb b/test/test_x509name.rb index 56e79879..d26174ef 100644 --- a/test/test_x509name.rb +++ b/test/test_x509name.rb @@ -4,7 +4,7 @@ require_relative 'utils' if defined?(OpenSSL::TestUtils) -class OpenSSL::TestX509Name < Test::Unit::TestCase +class OpenSSL::TestX509Name < OpenSSL::TestCase OpenSSL::ASN1::ObjectId.register( "1.2.840.113549.1.9.1", "emailAddress", "emailAddress") OpenSSL::ASN1::ObjectId.register( @@ -15,9 +15,6 @@ class OpenSSL::TestX509Name < Test::Unit::TestCase @obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE) end - def teardown - end - def test_s_new dn = [ ["C", "JP"], ["O", "example"], ["CN", "www.example.jp"] ] name = OpenSSL::X509::Name.new(dn) diff --git a/test/test_x509req.rb b/test/test_x509req.rb index ee2347b5..c473b47a 100644 --- a/test/test_x509req.rb +++ b/test/test_x509req.rb @@ -3,7 +3,7 @@ require_relative "utils" if defined?(OpenSSL::TestUtils) -class OpenSSL::TestX509Request < Test::Unit::TestCase +class OpenSSL::TestX509Request < OpenSSL::TestCase def setup @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 diff --git a/test/test_x509store.rb b/test/test_x509store.rb index 9964cc8f..8dee1679 100644 --- a/test/test_x509store.rb +++ b/test/test_x509store.rb @@ -3,7 +3,7 @@ require_relative "utils" if defined?(OpenSSL::TestUtils) -class OpenSSL::TestX509Store < Test::Unit::TestCase +class OpenSSL::TestX509Store < OpenSSL::TestCase def setup @rsa1024 = OpenSSL::TestUtils::TEST_KEY_RSA1024 @rsa2048 = OpenSSL::TestUtils::TEST_KEY_RSA2048 @@ -15,9 +15,6 @@ class OpenSSL::TestX509Store < Test::Unit::TestCase @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") end - def teardown - end - def test_nosegv_on_cleanup cert = OpenSSL::X509::Certificate.new store = OpenSSL::X509::Store.new @@ -151,8 +148,6 @@ class OpenSSL::TestX509Store < Test::Unit::TestCase assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert))) assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) - return unless defined?(OpenSSL::X509::V_FLAG_CRL_CHECK) - store = OpenSSL::X509::Store.new store.purpose = OpenSSL::X509::PURPOSE_ANY store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK diff --git a/test/utils.rb b/test/utils.rb index 2e9b7395..b6cf6377 100644 --- a/test/utils.rb +++ b/test/utils.rb @@ -182,7 +182,14 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC end end - class OpenSSL::SSLTestCase < Test::Unit::TestCase + class OpenSSL::TestCase < Test::Unit::TestCase + def teardown + # OpenSSL error stack must be empty + assert_equal([], OpenSSL.errors) + end + end + + class OpenSSL::SSLTestCase < OpenSSL::TestCase RUBY = EnvUtil.rubybin ITERATIONS = ($0 == __FILE__) ? 100 : 10 @@ -207,9 +214,6 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC @server = nil end - def teardown - end - def issue_cert(*arg) OpenSSL::TestUtils.issue_cert(*arg) end @@ -278,6 +282,7 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC ctx.cert = @svr_cert ctx.key = @svr_key ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 } + ctx.ecdh_curves = "P-256" ctx.verify_mode = verify_mode ctx_proc.call(ctx) if ctx_proc |