diff options
94 files changed, 3796 insertions, 2535 deletions
diff --git a/.travis.yml b/.travis.yml index 1342da6d..aec0e443 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,12 +18,9 @@ matrix: fast_finish: true include: - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=openssl-1.0.2 - - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=openssl-1.0.0 - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=openssl-1.0.1 - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=openssl-1.0.2 - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=openssl-1.1.0 - - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.3 - - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.4 - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.5 - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=libressl-2.6 - language: ruby @@ -1,3 +1,34 @@ +Version 2.1.0.beta1 +=================== + +Notable changes +--------------- + +* Support for OpenSSL versions before 1.0.1 is removed. + [[GitHub #86]](https://github.com/ruby/openssl/pull/86) +* OpenSSL::BN#negative?, #+@, and #-@ are added. +* OpenSSL::SSL::SSLSocket#connect raises a more informative exception when + certificate verification fails. + [[GitHub #99]](https://github.com/ruby/openssl/pull/99) +* OpenSSL::KDF module is newly added. Support for scrypt is added. + [[GitHub #109]](https://github.com/ruby/openssl/pull/109) +* OpenSSL.fips_mode is added. We have had the setter, but not the getter. + [[GitHub #125]](https://github.com/ruby/openssl/pull/125) +* OpenSSL::OCSP::Request#signed? is added. +* OpenSSL::ASN1 handles the indefinite length form better. OpenSSL::ASN1.decode + no longer wrongly treats the end-of-contents octets as part of the content. + OpenSSL::ASN1::ASN1Data#infinite_length is renamed to #indefinite_length. + [[GitHub #98]](https://github.com/ruby/openssl/pull/98) +* OpenSSL::X509::Name#add_entry now accepts two additional keyword arguments + 'loc' and 'set'. + [[GitHub #94]](https://github.com/ruby/openssl/issues/94) +* OpenSSL::SSL::SSLContext#min_version= and #max_version= are added. + [[GitHub #142]](https://github.com/ruby/openssl/pull/142) +* OpenSSL::X509::Name#to_utf8 is added. + [[GitHub #26]](https://github.com/ruby/openssl/issues/26) + [[GitHub #143]](https://github.com/ruby/openssl/pull/143) + + Version 2.0.6 ============= @@ -27,7 +27,7 @@ Alternatively, you can install the gem with `bundler`: # Gemfile gem 'openssl' # or specify git master -gem 'openssl', github: 'ruby/openssl' +gem 'openssl', git: 'https://github.com/ruby/openssl' ``` After doing `bundle install`, you should have the gem installed in your bundle. @@ -20,7 +20,7 @@ RDoc::Task.new do |rdoc| rdoc.rdoc_files.include("*.md", "lib/**/*.rb", "ext/**/*.c") end -task :test => :debug +task :test => [:compile, :debug] task :debug do ruby "-I./lib -ropenssl -ve'puts OpenSSL::OPENSSL_VERSION, OpenSSL::OPENSSL_LIBRARY_VERSION'" end @@ -77,3 +77,5 @@ namespace :sync do puts "Don't forget to update ext/openssl/depend" end end + +task :default => :test diff --git a/ext/openssl/deprecation.rb b/ext/openssl/deprecation.rb index 0c3ab628..1d51d065 100644 --- a/ext/openssl/deprecation.rb +++ b/ext/openssl/deprecation.rb @@ -3,9 +3,6 @@ module OpenSSL def self.deprecated_warning_flag unless flag = (@deprecated_warning_flag ||= nil) if try_compile("", flag = "-Werror=deprecated-declarations") - if /darwin/ =~ RUBY_PLATFORM and with_config("broken-apple-openssl") - flag = "-Wno-deprecated-declarations" - end $warnflags << " #{flag}" else flag = "" diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 75da65cd..5212903b 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -91,30 +91,19 @@ unless result unless find_openssl_library Logging::message "=== Checking for required stuff failed. ===\n" Logging::message "Makefile wasn't created. Fix the errors above.\n" - exit 1 + raise "OpenSSL library could not be found. You might want to use " \ + "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \ + "is installed." end end -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 - -if /darwin/ =~ RUBY_PLATFORM and !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')" +unless checking_for("OpenSSL version is 1.0.1 or later") { + try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") } + raise "OpenSSL >= 1.0.1 or LibreSSL is required" end Logging::message "=== Checking for OpenSSL features... ===\n" # compile options - -# SSLv2 and SSLv3 may be removed in future versions of OpenSSL, and even macros -# like OPENSSL_NO_SSL2 may not be defined. -have_func("SSLv2_method") -have_func("SSLv3_method") -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} @@ -122,30 +111,6 @@ engines.each { |name| OpenSSL.check_func_or_macro("ENGINE_load_#{name}", "openssl/engine.h") } -# added in 0.9.8X -have_func("EVP_CIPHER_CTX_new") -have_func("EVP_CIPHER_CTX_free") -OpenSSL.check_func_or_macro("SSL_CTX_clear_options", "openssl/ssl.h") - -# added in 1.0.0 -have_func("ASN1_TIME_adj") -have_func("EVP_CIPHER_CTX_copy") -have_func("EVP_PKEY_base_id") -have_func("HMAC_CTX_copy") -have_func("PKCS5_PBKDF2_HMAC") -have_func("X509_NAME_hash_old") -have_func("X509_STORE_CTX_get0_current_crl") -have_func("X509_STORE_set_verify_cb") -have_func("i2d_ASN1_SET_ANY") -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_func("EVP_PKEY_get0") - -# 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") @@ -189,6 +154,7 @@ OpenSSL.check_func_or_macro("SSL_CTX_set_min_proto_version", "openssl/ssl.h") have_func("SSL_CTX_get_security_level") have_func("X509_get0_notBefore") have_func("SSL_SESSION_get_protocol_version") +have_func("EVP_PBE_scrypt") Logging::message "=== Checking done. ===\n" diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c index 94ce85af..b36ef028 100644 --- a/ext/openssl/openssl_missing.c +++ b/ext/openssl/openssl_missing.c @@ -20,73 +20,6 @@ #include "openssl_missing.h" -/* added in 0.9.8X */ -#if !defined(HAVE_EVP_CIPHER_CTX_NEW) -EVP_CIPHER_CTX * -ossl_EVP_CIPHER_CTX_new(void) -{ - EVP_CIPHER_CTX *ctx = OPENSSL_malloc(sizeof(EVP_CIPHER_CTX)); - if (!ctx) - return NULL; - EVP_CIPHER_CTX_init(ctx); - return ctx; -} -#endif - -#if !defined(HAVE_EVP_CIPHER_CTX_FREE) -void -ossl_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx) -{ - if (ctx) { - EVP_CIPHER_CTX_cleanup(ctx); - OPENSSL_free(ctx); - } -} -#endif - -/* added in 1.0.0 */ -#if !defined(HAVE_EVP_CIPHER_CTX_COPY) -/* - * this function does not exist in OpenSSL yet... or ever?. - * a future version may break this function. - * tested on 0.9.7d. - */ -int -ossl_EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) -{ - memcpy(out, in, sizeof(EVP_CIPHER_CTX)); - -#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); - memcpy(out->cipher_data, in->cipher_data, in->cipher->ctx_size); - } -#endif - - return 1; -} -#endif - -#if !defined(OPENSSL_NO_HMAC) -#if !defined(HAVE_HMAC_CTX_COPY) -int -ossl_HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in) -{ - if (!out || !in) - return 0; - - 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); - - return 1; -} -#endif /* HAVE_HMAC_CTX_COPY */ -#endif /* NO_HMAC */ - /* added in 1.0.2 */ #if !defined(OPENSSL_NO_EC) #if !defined(HAVE_EC_CURVE_NIST2NID) diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 3d11aec2..debd25ad 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -12,53 +12,6 @@ #include "ruby/config.h" -/* added in 0.9.8X */ -#if !defined(HAVE_EVP_CIPHER_CTX_NEW) -EVP_CIPHER_CTX *ossl_EVP_CIPHER_CTX_new(void); -# define EVP_CIPHER_CTX_new ossl_EVP_CIPHER_CTX_new -#endif - -#if !defined(HAVE_EVP_CIPHER_CTX_FREE) -void ossl_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); -# define EVP_CIPHER_CTX_free ossl_EVP_CIPHER_CTX_free -#endif - -#if !defined(HAVE_SSL_CTX_CLEAR_OPTIONS) -# define SSL_CTX_clear_options(ctx, op) ((ctx)->options &= ~(op)) -#endif - -/* added in 1.0.0 */ -#if !defined(HAVE_EVP_PKEY_BASE_ID) -# define EVP_PKEY_base_id(pkey) EVP_PKEY_type((pkey)->type) -#endif - -#if !defined(HAVE_EVP_CIPHER_CTX_COPY) -int ossl_EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *, const EVP_CIPHER_CTX *); -# define EVP_CIPHER_CTX_copy ossl_EVP_CIPHER_CTX_copy -#endif - -#if !defined(HAVE_HMAC_CTX_COPY) -int ossl_HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in); -# define HMAC_CTX_copy ossl_HMAC_CTX_copy -#endif - -#if !defined(HAVE_X509_STORE_CTX_GET0_CURRENT_CRL) -# define X509_STORE_CTX_get0_current_crl(x) ((x)->current_crl) -#endif - -#if !defined(HAVE_X509_STORE_SET_VERIFY_CB) -# define X509_STORE_set_verify_cb X509_STORE_set_verify_cb_func -#endif - -#if !defined(HAVE_I2D_ASN1_SET_ANY) -# define i2d_ASN1_SET_ANY(sk, x) i2d_ASN1_SET_OF_ASN1_TYPE((sk), (x), \ - i2d_ASN1_TYPE, V_ASN1_SET, V_ASN1_UNIVERSAL, 0) -#endif - -#if !defined(HAVE_EVP_PKEY_GET0) -# define EVP_PKEY_get0(pk) (pk->pkey.ptr) -#endif - /* added in 1.0.2 */ #if !defined(OPENSSL_NO_EC) #if !defined(HAVE_EC_CURVE_NIST2NID) @@ -245,7 +198,7 @@ IMPL_PKEY_GETTER(EC_KEY, ec) #undef IMPL_KEY_ACCESSOR3 #endif /* HAVE_OPAQUE_OPENSSL */ -#if defined(HAVE_AUTHENTICATED_ENCRYPTION) && !defined(EVP_CTRL_AEAD_GET_TAG) +#if !defined(EVP_CTRL_AEAD_GET_TAG) # define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG # define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG # define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN @@ -256,6 +209,10 @@ IMPL_PKEY_GETTER(EC_KEY, ec) # define X509_get0_notAfter(x) X509_get_notAfter(x) # define X509_CRL_get0_lastUpdate(x) X509_CRL_get_lastUpdate(x) # define X509_CRL_get0_nextUpdate(x) X509_CRL_get_nextUpdate(x) +# define X509_set1_notBefore(x, t) X509_set_notBefore(x, t) +# define X509_set1_notAfter(x, t) X509_set_notAfter(x, t) +# define X509_CRL_set1_lastUpdate(x, t) X509_CRL_set_lastUpdate(x, t) +# define X509_CRL_set1_nextUpdate(x, t) X509_CRL_set_nextUpdate(x, t) #endif #if !defined(HAVE_SSL_SESSION_GET_PROTOCOL_VERSION) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 88bb8f21..245385e7 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -92,22 +92,40 @@ OSSL_IMPL_SK2ARY(x509crl, X509_CRL) OSSL_IMPL_SK2ARY(x509name, X509_NAME) static VALUE -ossl_str_new(int size) +ossl_str_new_i(VALUE size) { - return rb_str_new(0, size); + return rb_str_new(NULL, (long)size); +} + +VALUE +ossl_str_new(const char *ptr, long len, int *pstate) +{ + VALUE str; + int state; + + str = rb_protect(ossl_str_new_i, len, &state); + if (pstate) + *pstate = state; + if (state) { + if (!pstate) + rb_set_errinfo(Qnil); + return Qnil; + } + if (ptr) + memcpy(RSTRING_PTR(str), ptr, len); + return str; } VALUE ossl_buf2str(char *buf, int len) { VALUE str; - int status = 0; + int state; - str = rb_protect((VALUE (*)(VALUE))ossl_str_new, len, &status); - if(!NIL_P(str)) memcpy(RSTRING_PTR(str), buf, len); + str = ossl_str_new(buf, len, &state); OPENSSL_free(buf); - if(status) rb_jump_tag(status); - + if (state) + rb_jump_tag(state); return str; } @@ -220,7 +238,7 @@ VALUE eOSSLError; /* * Convert to DER string */ -ID ossl_s_to_der; +static ID ossl_s_to_der; VALUE ossl_to_der(VALUE obj) @@ -248,18 +266,15 @@ static VALUE ossl_make_error(VALUE exc, const char *fmt, va_list args) { VALUE str = Qnil; - const char *msg; - long e; + unsigned long e; - e = ERR_peek_last_error(); if (fmt) { str = rb_vsprintf(fmt, args); } + e = ERR_peek_last_error(); if (e) { - if (dOSSL == Qtrue) /* FULL INFO */ - msg = ERR_error_string(e, NULL); - else - msg = ERR_reason_error_string(e); + const char *msg = ERR_reason_error_string(e); + if (NIL_P(str)) { if (msg) str = rb_str_new_cstr(msg); } @@ -267,8 +282,8 @@ ossl_make_error(VALUE exc, const char *fmt, va_list args) if (RSTRING_LEN(str)) rb_str_cat2(str, ": "); rb_str_cat2(str, msg ? msg : "(null)"); } + ossl_clear_error(); } - ossl_clear_error(); if (NIL_P(str)) str = rb_str_new(0, 0); return rb_exc_new3(exc, str); @@ -319,7 +334,8 @@ ossl_clear_error(void) * * See any remaining errors held in queue. * - * Any errors you see here are probably due to a bug in ruby's OpenSSL implementation. + * Any errors you see here are probably due to a bug in Ruby's OpenSSL + * implementation. */ VALUE ossl_get_errors(void) @@ -382,6 +398,23 @@ ossl_debug_set(VALUE self, VALUE val) } /* + * call-seq + * OpenSSL.fips_mode -> true | false + */ +static VALUE +ossl_fips_mode_get(VALUE self) +{ + +#ifdef OPENSSL_FIPS + VALUE enabled; + enabled = FIPS_mode() ? Qtrue : Qfalse; + return enabled; +#else + return Qfalse; +#endif +} + +/* * call-seq: * OpenSSL.fips_mode = boolean -> boolean * @@ -443,7 +476,7 @@ mem_check_start(VALUE self) * Prints detected memory leaks to standard error. This cleans the global state * up thus you cannot use any methods of the library after calling this. * - * Returns true if leaks detected, false otherwise. + * Returns +true+ if leaks detected, +false+ otherwise. * * This is available only when built with a capable OpenSSL and --enable-debug * configure option. @@ -540,19 +573,11 @@ ossl_dyn_destroy_callback(struct CRYPTO_dynlock_value *l, const char *file, int OPENSSL_free(l); } -#ifdef HAVE_CRYPTO_THREADID_PTR static void ossl_threadid_func(CRYPTO_THREADID *id) { /* register native thread id */ CRYPTO_THREADID_set_pointer(id, (void *)rb_nativethread_self()); } -#else -static unsigned long ossl_thread_id(void) -{ - /* before OpenSSL 1.0, this is 'unsigned long' */ - return (unsigned long)rb_nativethread_self(); -} -#endif static struct CRYPTO_dynlock_value *ossl_locks; @@ -571,11 +596,7 @@ static void Init_ossl_locks(void) for (i = 0; i < num_locks; i++) ossl_lock_init(&ossl_locks[i]); -#ifdef HAVE_CRYPTO_THREADID_PTR CRYPTO_THREADID_set_callback(ossl_threadid_func); -#else - CRYPTO_set_id_callback(ossl_thread_id); -#endif CRYPTO_set_locking_callback(ossl_lock_callback); CRYPTO_set_dynlock_create_callback(ossl_dyn_create_callback); CRYPTO_set_dynlock_lock_callback(ossl_dyn_lock_callback); @@ -585,7 +606,7 @@ static void Init_ossl_locks(void) /* * OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the - * OpenSSL[http://www.openssl.org/] library. + * OpenSSL[https://www.openssl.org/] library. * * = Examples * @@ -1088,25 +1109,14 @@ Init_openssl(void) /* * Init all digests, ciphers */ - /* CRYPTO_malloc_init(); */ - /* ENGINE_load_builtin_engines(); */ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 + if (!OPENSSL_init_ssl(0, NULL)) + rb_raise(rb_eRuntimeError, "OPENSSL_init_ssl"); +#else OpenSSL_add_ssl_algorithms(); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); SSL_load_error_strings(); - - /* - * FIXME: - * On unload do: - */ -#if 0 - CONF_modules_unload(1); - destroy_ui_method(); - EVP_cleanup(); - ENGINE_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); - ERR_free_strings(); #endif /* @@ -1128,7 +1138,11 @@ Init_openssl(void) /* * Version of OpenSSL the ruby OpenSSL extension is running with */ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 + rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); +#else rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); +#endif /* * Version number of OpenSSL the ruby OpenSSL extension was built with @@ -1137,7 +1151,7 @@ Init_openssl(void) rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER)); /* - * Boolean indicating whether OpenSSL is FIPS-enabled or not + * Boolean indicating whether OpenSSL is FIPS-capable or not */ rb_define_const(mOSSL, "OPENSSL_FIPS", #ifdef OPENSSL_FIPS @@ -1147,6 +1161,7 @@ Init_openssl(void) #endif ); + rb_define_module_function(mOSSL, "fips_mode", ossl_fips_mode_get, 0); rb_define_module_function(mOSSL, "fips_mode=", ossl_fips_mode_set, 1); /* @@ -1186,7 +1201,6 @@ Init_openssl(void) Init_ossl_ns_spki(); Init_ossl_pkcs12(); Init_ossl_pkcs7(); - Init_ossl_pkcs5(); Init_ossl_pkey(); Init_ossl_rand(); Init_ossl_ssl(); @@ -1194,6 +1208,7 @@ Init_openssl(void) Init_ossl_ocsp(); Init_ossl_engine(); Init_ossl_asn1(); + Init_ossl_kdf(); #if defined(OSSL_DEBUG) /* diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 78eddd09..5a15839c 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -35,6 +35,11 @@ #if !defined(OPENSSL_NO_OCSP) # include <openssl/ocsp.h> #endif +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/evp.h> +#include <openssl/dh.h> /* * Common Module @@ -56,29 +61,29 @@ extern VALUE eOSSLError; }\ } while (0) -#define OSSL_Check_Instance(obj, klass) do {\ - if (!rb_obj_is_instance_of((obj), (klass))) {\ - ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected instance of %"PRIsVALUE")",\ - rb_obj_class(obj), (klass));\ - }\ -} while (0) - -#define OSSL_Check_Same_Class(obj1, obj2) do {\ - if (!rb_obj_is_instance_of((obj1), rb_obj_class(obj2))) {\ - ossl_raise(rb_eTypeError, "wrong argument type");\ - }\ -} while (0) +/* + * Type conversions + */ +#if !defined(NUM2UINT64T) /* in case Ruby starts to provide */ +# if SIZEOF_LONG == 8 +# define NUM2UINT64T(x) ((uint64_t)NUM2ULONG(x)) +# elif defined(HAVE_LONG_LONG) && SIZEOF_LONG_LONG == 8 +# define NUM2UINT64T(x) ((uint64_t)NUM2ULL(x)) +# else +# error "unknown platform; no 64-bit width integer" +# endif +#endif /* * Data Conversion */ -STACK_OF(X509) *ossl_x509_ary2sk0(VALUE); STACK_OF(X509) *ossl_x509_ary2sk(VALUE); STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*); VALUE ossl_x509_sk2ary(const STACK_OF(X509) *certs); VALUE ossl_x509crl_sk2ary(const STACK_OF(X509_CRL) *crl); VALUE ossl_x509name_sk2ary(const STACK_OF(X509_NAME) *names); VALUE ossl_buf2str(char *buf, int len); +VALUE ossl_str_new(const char *, long, int *); #define ossl_str_adjust(str, p) \ do{\ long len = RSTRING_LEN(str);\ @@ -115,7 +120,6 @@ int ossl_pem_passwd_cb(char *, int, int, void *); /* * ERRor messages */ -#define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error()) NORETURN(void ossl_raise(VALUE, const char *, ...)); /* Clear OpenSSL error queue. If dOSSL is set, rb_warn() them. */ void ossl_clear_error(void); @@ -123,7 +127,6 @@ void ossl_clear_error(void); /* * String to DER String */ -extern ID ossl_s_to_der; VALUE ossl_to_der(VALUE); VALUE ossl_to_der_if_possible(VALUE); @@ -141,20 +144,9 @@ extern VALUE dOSSL; } \ } while (0) -#define OSSL_Warning(fmt, ...) do { \ - OSSL_Debug((fmt), ##__VA_ARGS__); \ - rb_warning((fmt), ##__VA_ARGS__); \ -} while (0) - -#define OSSL_Warn(fmt, ...) do { \ - OSSL_Debug((fmt), ##__VA_ARGS__); \ - rb_warn((fmt), ##__VA_ARGS__); \ -} while (0) #else void ossl_debug(const char *, ...); #define OSSL_Debug ossl_debug -#define OSSL_Warning rb_warning -#define OSSL_Warn rb_warn #endif /* @@ -173,13 +165,13 @@ void ossl_debug(const char *, ...); #include "ossl_ocsp.h" #include "ossl_pkcs12.h" #include "ossl_pkcs7.h" -#include "ossl_pkcs5.h" #include "ossl_pkey.h" #include "ossl_rand.h" #include "ossl_ssl.h" #include "ossl_version.h" #include "ossl_x509.h" #include "ossl_engine.h" +#include "ossl_kdf.h" void Init_openssl(void); diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 1d3ee4ac..efa6b787 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -9,11 +9,9 @@ */ #include "ossl.h" -static VALUE join_der(VALUE enumerable); static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, int yield, long *num_read); static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); -static VALUE ossl_asn1eoc_initialize(VALUE self); /* * DATE conversion @@ -25,7 +23,6 @@ asn1time_to_time(const ASN1_TIME *time) VALUE argv[6]; int count; - if (!time || !time->data) return Qnil; memset(&tm, 0, sizeof(struct tm)); switch (time->type) { @@ -72,7 +69,6 @@ asn1time_to_time(const ASN1_TIME *time) return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); } -#if defined(HAVE_ASN1_TIME_ADJ) void ossl_time_split(VALUE time, time_t *sec, int *days) { @@ -88,13 +84,6 @@ ossl_time_split(VALUE time, time_t *sec, int *days) *sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400))); } } -#else -time_t -time_to_time_t(VALUE time) -{ - return (time_t)NUM2TIMET(rb_Integer(time)); -} -#endif /* * STRING conversion @@ -155,13 +144,13 @@ num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) #define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) #define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) #define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) -#define ossl_asn1_get_infinite_length(o) rb_attr_get((o),sivINFINITE_LENGTH) +#define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH) #define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) #define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) #define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) #define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) -#define ossl_asn1_set_infinite_length(o,v) rb_ivar_set((o),sivINFINITE_LENGTH,(v)) +#define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v)) VALUE mASN1; VALUE eASN1Error; @@ -187,7 +176,7 @@ VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ static VALUE sym_IMPLICIT, sym_EXPLICIT; static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; -static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINFINITE_LENGTH, sivUNUSED_BITS; +static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS; static ID id_each; /* @@ -213,13 +202,15 @@ obj_to_asn1bstr(VALUE obj, long unused_bits) { ASN1_BIT_STRING *bstr; - if(unused_bits < 0) unused_bits = 0; + if (unused_bits < 0 || unused_bits > 7) + ossl_raise(eASN1Error, "unused_bits for a bitstring value must be in "\ + "the range 0 to 7"); StringValue(obj); if(!(bstr = ASN1_BIT_STRING_new())) ossl_raise(eASN1Error, NULL); ASN1_BIT_STRING_set(bstr, (unsigned char *)RSTRING_PTR(obj), RSTRING_LENINT(obj)); bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */ - bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT|(unused_bits&0x07); + bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits; return bstr; } @@ -269,15 +260,10 @@ obj_to_asn1utime(VALUE time) time_t sec; ASN1_UTCTIME *t; -#if defined(HAVE_ASN1_TIME_ADJ) int off_days; ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_UTCTIME_adj(NULL, sec, off_days, 0))) -#else - sec = time_to_time_t(time); - if (!(t = ASN1_UTCTIME_set(NULL, sec))) -#endif ossl_raise(eASN1Error, NULL); return t; @@ -289,15 +275,10 @@ obj_to_asn1gtime(VALUE time) time_t sec; ASN1_GENERALIZEDTIME *t; -#if defined(HAVE_ASN1_TIME_ADJ) int off_days; ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_GENERALIZEDTIME_adj(NULL, sec, off_days, 0))) -#else - sec = time_to_time_t(time); - if (!(t = ASN1_GENERALIZEDTIME_set(NULL, sec))) -#endif ossl_raise(eASN1Error, NULL); return t; @@ -517,7 +498,7 @@ ossl_asn1_get_asn1type(VALUE obj) VALUE value, rflag; void *ptr; void (*free_func)(); - int tag, flag; + int tag; tag = ossl_asn1_default_tag(obj); value = ossl_asn1_get_value(obj); @@ -533,8 +514,7 @@ ossl_asn1_get_asn1type(VALUE obj) break; case V_ASN1_BIT_STRING: rflag = rb_attr_get(obj, sivUNUSED_BITS); - flag = NIL_P(rflag) ? -1 : NUM2INT(rflag); - ptr = obj_to_asn1bstr(value, flag); + ptr = obj_to_asn1bstr(value, NUM2INT(rflag)); free_func = ASN1_BIT_STRING_free; break; case V_ASN1_NULL: @@ -598,8 +578,8 @@ ossl_asn1_default_tag(VALUE obj) return NUM2INT(tag); tmp_class = rb_class_superclass(tmp_class); } - ossl_raise(eASN1Error, "universal tag for %"PRIsVALUE" not found", - rb_obj_class(obj)); + + return -1; } static int @@ -615,20 +595,6 @@ ossl_asn1_tag(VALUE obj) } static int -ossl_asn1_is_explicit(VALUE obj) -{ - VALUE s; - - s = ossl_asn1_get_tagging(obj); - if (NIL_P(s) || s == sym_IMPLICIT) - return 0; - else if (s == sym_EXPLICIT) - return 1; - else - ossl_raise(eASN1Error, "invalid tag default"); -} - -static int ossl_asn1_tag_class(VALUE obj) { VALUE s; @@ -663,12 +629,12 @@ ossl_asn1_class2sym(int tc) * call-seq: * OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data * - * +value+: Please have a look at Constructive and Primitive to see how Ruby + * _value_: Please have a look at Constructive and Primitive to see how Ruby * types are mapped to ASN.1 types and vice versa. * - * +tag+: A +Number+ indicating the tag number. + * _tag_: An Integer indicating the tag number. * - * +tag_class+: A +Symbol+ indicating the tag class. Please cf. ASN1 for + * _tag_class_: A Symbol indicating the tag class. Please cf. ASN1 for * possible values. * * == Example @@ -680,73 +646,85 @@ ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) { if(!SYMBOL_P(tag_class)) ossl_raise(eASN1Error, "invalid tag class"); - if (tag_class == sym_UNIVERSAL && NUM2INT(tag) > 31) - ossl_raise(eASN1Error, "tag number for Universal too large"); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); ossl_asn1_set_tag_class(self, tag_class); - ossl_asn1_set_infinite_length(self, Qfalse); + ossl_asn1_set_indefinite_length(self, Qfalse); return self; } static VALUE -join_der_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, str)) +to_der_internal(VALUE self, int constructed, int indef_len, VALUE body) { - i = ossl_to_der_if_possible(i); - StringValue(i); - rb_str_append(str, i); - return Qnil; -} + int encoding = constructed ? indef_len ? 2 : 1 : 0; + int tag_class = ossl_asn1_tag_class(self); + int tag_number = ossl_asn1_tag(self); + int default_tag_number = ossl_asn1_default_tag(self); + int body_length, total_length; + VALUE str; + unsigned char *p; -static VALUE -join_der(VALUE enumerable) -{ - VALUE str = rb_str_new(0, 0); - rb_block_call(enumerable, id_each, 0, 0, join_der_i, str); + body_length = RSTRING_LENINT(body); + if (ossl_asn1_get_tagging(self) == sym_EXPLICIT) { + int inner_length, e_encoding = indef_len ? 2 : 1; + + if (default_tag_number == -1) + ossl_raise(eASN1Error, "explicit tagging of unknown tag"); + + inner_length = ASN1_object_size(encoding, body_length, default_tag_number); + total_length = ASN1_object_size(e_encoding, inner_length, tag_number); + str = rb_str_new(NULL, total_length); + p = (unsigned char *)RSTRING_PTR(str); + /* Put explicit tag */ + ASN1_put_object(&p, e_encoding, inner_length, tag_number, tag_class); + /* Append inner object */ + ASN1_put_object(&p, encoding, body_length, default_tag_number, V_ASN1_UNIVERSAL); + memcpy(p, RSTRING_PTR(body), body_length); + p += body_length; + if (indef_len) { + ASN1_put_eoc(&p); /* For inner object */ + ASN1_put_eoc(&p); /* For wrapper object */ + } + } + else { + total_length = ASN1_object_size(encoding, body_length, tag_number); + str = rb_str_new(NULL, total_length); + p = (unsigned char *)RSTRING_PTR(str); + ASN1_put_object(&p, encoding, body_length, tag_number, tag_class); + memcpy(p, RSTRING_PTR(body), body_length); + p += body_length; + if (indef_len) + ASN1_put_eoc(&p); + } + assert(p - (unsigned char *)RSTRING_PTR(str) == total_length); return str; } +static VALUE ossl_asn1prim_to_der(VALUE); +static VALUE ossl_asn1cons_to_der(VALUE); /* * call-seq: * asn1.to_der => DER-encoded String * * Encodes this ASN1Data into a DER-encoded String value. The result is - * DER-encoded except for the possibility of infinite length encodings. - * Infinite length encodings are not allowed in strict DER, so strictly - * speaking the result of such an encoding would be a BER-encoding. + * DER-encoded except for the possibility of indefinite length forms. + * Indefinite length forms are not allowed in strict DER, so strictly speaking + * the result of such an encoding would be a BER-encoding. */ static VALUE ossl_asn1data_to_der(VALUE self) { - VALUE value, der, inf_length; - int tag, tag_class, is_cons = 0; - long length; - unsigned char *p; - - value = ossl_asn1_get_value(self); - if(rb_obj_is_kind_of(value, rb_cArray)){ - is_cons = 1; - value = join_der(value); - } - StringValue(value); + VALUE value = ossl_asn1_get_value(self); - tag = ossl_asn1_tag(self); - tag_class = ossl_asn1_tag_class(self); - inf_length = ossl_asn1_get_infinite_length(self); - if (inf_length == Qtrue) { - is_cons = 2; + if (rb_obj_is_kind_of(value, rb_cArray)) + return ossl_asn1cons_to_der(self); + else { + if (RTEST(ossl_asn1_get_indefinite_length(self))) + ossl_raise(eASN1Error, "indefinite length form cannot be used " \ + "with primitive encoding"); + return ossl_asn1prim_to_der(self); } - 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); - 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); - - return der; } static VALUE @@ -829,46 +807,33 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, int tag, VALUE tc, long *num_read) { VALUE value, asn1data, ary; - int infinite; + int indefinite; long available_len, off = *offset; - infinite = (j == 0x21); + indefinite = (j == 0x21); ary = rb_ary_new(); - available_len = infinite ? max_len : length; + available_len = indefinite ? max_len : length; while (available_len > 0) { long inner_read = 0; value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); *num_read += inner_read; available_len -= inner_read; - rb_ary_push(ary, value); - if (infinite && - NUM2INT(ossl_asn1_get_tag(value)) == V_ASN1_EOC && + if (indefinite && + ossl_asn1_tag(value) == V_ASN1_EOC && ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) { break; } + rb_ary_push(ary, value); } if (tc == sym_UNIVERSAL) { VALUE args[4]; - int not_sequence_or_set; - - not_sequence_or_set = tag != V_ASN1_SEQUENCE && tag != V_ASN1_SET; - - if (not_sequence_or_set) { - if (infinite) { - asn1data = rb_obj_alloc(cASN1Constructive); - } - else { - ossl_raise(eASN1Error, "invalid non-infinite tag"); - return Qnil; - } - } - else { - VALUE klass = *ossl_asn1_info[tag].klass; - asn1data = rb_obj_alloc(klass); - } + if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET) + asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass); + else + asn1data = rb_obj_alloc(cASN1Constructive); args[0] = ary; args[1] = INT2NUM(tag); args[2] = Qnil; @@ -880,10 +845,10 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); } - if (infinite) - ossl_asn1_set_infinite_length(asn1data, Qtrue); + if (indefinite) + ossl_asn1_set_indefinite_length(asn1data, Qtrue); else - ossl_asn1_set_infinite_length(asn1data, Qfalse); + ossl_asn1_set_indefinite_length(asn1data, Qfalse); *offset = off; return asn1data; @@ -936,7 +901,8 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, inner_read += hlen; } else { - if ((j & 0x01) && (len == 0)) ossl_raise(eASN1Error, "Infinite length for primitive value"); + if ((j & 0x01) && (len == 0)) + ossl_raise(eASN1Error, "indefinite length for primitive value"); asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read); off += hlen + len; } @@ -968,13 +934,13 @@ int_ossl_decode_sanity_check(long len, long read, long offset) * * If a block is given, it prints out each of the elements encountered. * Block parameters are (in that order): - * * depth: The recursion depth, plus one with each constructed value being encountered (Number) - * * offset: Current byte offset (Number) - * * header length: Combined length in bytes of the Tag and Length headers. (Number) - * * length: The overall remaining length of the entire data (Number) + * * depth: The recursion depth, plus one with each constructed value being encountered (Integer) + * * offset: Current byte offset (Integer) + * * header length: Combined length in bytes of the Tag and Length headers. (Integer) + * * length: The overall remaining length of the entire data (Integer) * * constructed: Whether this value is constructed or not (Boolean) * * tag_class: Current tag class (Symbol) - * * tag: The current tag (Number) + * * tag: The current tag number (Integer) * * == Example * der = File.binread('asn1data.der') @@ -1004,9 +970,9 @@ ossl_asn1_traverse(VALUE self, VALUE obj) * call-seq: * OpenSSL::ASN1.decode(der) -> ASN1Data * - * Decodes a BER- or DER-encoded value and creates an ASN1Data instance. +der+ - * may be a +String+ or any object that features a +#to_der+ method transforming - * it into a BER-/DER-encoded +String+. + * Decodes a BER- or DER-encoded value and creates an ASN1Data instance. _der_ + * may be a String or any object that features a +.to_der+ method transforming + * it into a BER-/DER-encoded String+ * * == Example * der = File.binread('asn1data') @@ -1034,9 +1000,9 @@ ossl_asn1_decode(VALUE self, VALUE obj) * call-seq: * OpenSSL::ASN1.decode_all(der) -> Array of ASN1Data * - * Similar to +decode+ with the difference that +decode+ expects one - * distinct value represented in +der+. +decode_all+ on the contrary - * decodes a sequence of sequential BER/DER values lined up in +der+ + * Similar to #decode with the difference that #decode expects one + * distinct value represented in _der_. #decode_all on the contrary + * decodes a sequence of sequential BER/DER values lined up in _der_ * and returns them as an array. * * == Example @@ -1071,19 +1037,19 @@ ossl_asn1_decode_all(VALUE self, VALUE obj) /* * call-seq: - * OpenSSL::ASN1::Primitive.new( value [, tag, tagging, tag_class ]) => Primitive + * OpenSSL::ASN1::Primitive.new(value [, tag, tagging, tag_class ]) => Primitive * - * +value+: is mandatory. + * _value_: is mandatory. * - * +tag+: optional, may be specified for tagged values. If no +tag+ is + * _tag_: optional, may be specified for tagged values. If no _tag_ is * specified, the UNIVERSAL tag corresponding to the Primitive sub-class * is used by default. * - * +tagging+: may be used as an encoding hint to encode a value either + * _tagging_: may be used as an encoding hint to encode a value either * explicitly or implicitly, see ASN1 for possible values. * - * +tag_class+: if +tag+ and +tagging+ are +nil+ then this is set to - * +:UNIVERSAL+ by default. If either +tag+ or +tagging+ are set then + * _tag_class_: if _tag_ and _tagging_ are +nil+ then this is set to + * +:UNIVERSAL+ by default. If either _tag_ or _tagging_ are set then * +:CONTEXT_SPECIFIC+ is used as the default. For possible values please * cf. ASN1. * @@ -1096,9 +1062,12 @@ static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) { VALUE value, tag, tagging, tag_class; + int default_tag; rb_scan_args(argc, argv, "13", &value, &tag, &tagging, &tag_class); - if(argc > 1){ + default_tag = ossl_asn1_default_tag(self); + + if (default_tag == -1 || argc > 1) { if(NIL_P(tag)) ossl_raise(eASN1Error, "must specify tag number"); if(!NIL_P(tagging) && !SYMBOL_P(tagging)) @@ -1111,11 +1080,9 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) } if(!SYMBOL_P(tag_class)) ossl_raise(eASN1Error, "invalid tag class"); - if (tagging == sym_IMPLICIT && NUM2INT(tag) > 31) - ossl_raise(eASN1Error, "tag number for Universal too large"); } else{ - tag = INT2NUM(ossl_asn1_default_tag(self)); + tag = INT2NUM(default_tag); tagging = Qnil; tag_class = sym_UNIVERSAL; } @@ -1123,7 +1090,9 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) ossl_asn1_set_value(self, value); ossl_asn1_set_tagging(self, tagging); ossl_asn1_set_tag_class(self, tag_class); - ossl_asn1_set_infinite_length(self, Qfalse); + ossl_asn1_set_indefinite_length(self, Qfalse); + if (default_tag == V_ASN1_BIT_STRING) + rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0)); return self; } @@ -1131,7 +1100,7 @@ ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) static VALUE ossl_asn1eoc_initialize(VALUE self) { VALUE tag, tagging, tag_class, value; - tag = INT2NUM(ossl_asn1_default_tag(self)); + tag = INT2FIX(0); tagging = Qnil; tag_class = sym_UNIVERSAL; value = rb_str_new("", 0); @@ -1139,51 +1108,58 @@ ossl_asn1eoc_initialize(VALUE self) { ossl_asn1_set_value(self, value); ossl_asn1_set_tagging(self, tagging); ossl_asn1_set_tag_class(self, tag_class); - ossl_asn1_set_infinite_length(self, Qfalse); + ossl_asn1_set_indefinite_length(self, Qfalse); return self; } +static VALUE +ossl_asn1eoc_to_der(VALUE self) +{ + return rb_str_new("\0\0", 2); +} + /* * call-seq: * asn1.to_der => DER-encoded String * - * See ASN1Data#to_der for details. * + * See ASN1Data#to_der for details. */ static VALUE ossl_asn1prim_to_der(VALUE self) { ASN1_TYPE *asn1; - int tn, tc, explicit; - long len, reallen; - unsigned char *buf, *p; + long alllen, bodylen; + unsigned char *p0, *p1; + int j, tag, tc, state; VALUE str; - tn = NUM2INT(ossl_asn1_get_tag(self)); - tc = ossl_asn1_tag_class(self); - explicit = ossl_asn1_is_explicit(self); - asn1 = ossl_asn1_get_asn1type(self); + if (ossl_asn1_default_tag(self) == -1) { + str = ossl_asn1_get_value(self); + return to_der_internal(self, 0, 0, StringValue(str)); + } - len = ASN1_object_size(1, i2d_ASN1_TYPE(asn1, NULL), tn); - if(!(buf = OPENSSL_malloc(len))){ + asn1 = ossl_asn1_get_asn1type(self); + alllen = i2d_ASN1_TYPE(asn1, NULL); + if (alllen < 0) { ASN1_TYPE_free(asn1); - ossl_raise(eASN1Error, "cannot alloc buffer"); + ossl_raise(eASN1Error, "i2d_ASN1_TYPE"); } - p = buf; - if (tc == V_ASN1_UNIVERSAL) { - i2d_ASN1_TYPE(asn1, &p); - } else if (explicit) { - ASN1_put_object(&p, 1, i2d_ASN1_TYPE(asn1, NULL), tn, tc); - i2d_ASN1_TYPE(asn1, &p); - } else { - i2d_ASN1_TYPE(asn1, &p); - *buf = tc | tn | (*buf & V_ASN1_CONSTRUCTED); + str = ossl_str_new(NULL, alllen, &state); + if (state) { + ASN1_TYPE_free(asn1); + rb_jump_tag(state); } + p0 = p1 = (unsigned char *)RSTRING_PTR(str); + i2d_ASN1_TYPE(asn1, &p0); 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 */ + assert(p0 - p1 == alllen); - return str; + /* Strip header since to_der_internal() wants only the payload */ + j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen); + if (j & 0x80) + ossl_raise(eASN1Error, "ASN1_get_object"); /* should not happen */ + + return to_der_internal(self, 0, 0, rb_str_drop_bytes(str, alllen - bodylen)); } /* @@ -1195,92 +1171,41 @@ ossl_asn1prim_to_der(VALUE self) static VALUE ossl_asn1cons_to_der(VALUE self) { - int tag, tn, tc, explicit, constructed = 1; - int found_prim = 0, seq_len; - long length; - unsigned char *p; - VALUE value, str, inf_length; - - tn = NUM2INT(ossl_asn1_get_tag(self)); - tc = ossl_asn1_tag_class(self); - inf_length = ossl_asn1_get_infinite_length(self); - if (inf_length == Qtrue) { - VALUE ary, example; - constructed = 2; - if (rb_obj_class(self) == cASN1Sequence || - rb_obj_class(self) == cASN1Set) { - tag = ossl_asn1_default_tag(self); - } - else { /* must be a constructive encoding of a primitive value */ - ary = ossl_asn1_get_value(self); - if (!rb_obj_is_kind_of(ary, rb_cArray)) - ossl_raise(eASN1Error, "Constructive value must be an Array"); - /* Recursively descend until a primitive value is found. - The overall value of the entire constructed encoding - is of the type of the first primitive encoding to be - found. */ - while (!found_prim){ - example = rb_ary_entry(ary, 0); - if (rb_obj_is_kind_of(example, cASN1Primitive)){ - found_prim = 1; - } - else { - /* example is another ASN1Constructive */ - if (!rb_obj_is_kind_of(example, cASN1Constructive)){ - ossl_raise(eASN1Error, "invalid constructed encoding"); - return Qnil; /* dummy */ - } - ary = ossl_asn1_get_value(example); - } - } - tag = ossl_asn1_default_tag(example); - } - } - else { - if (rb_obj_class(self) == cASN1Constructive) - ossl_raise(eASN1Error, "Constructive shall only be used with infinite length"); - tag = ossl_asn1_default_tag(self); - } - explicit = ossl_asn1_is_explicit(self); - value = join_der(ossl_asn1_get_value(self)); - - 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) - ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); - else{ - if(explicit){ - ASN1_put_object(&p, constructed, seq_len, tn, tc); - ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tag, V_ASN1_UNIVERSAL); - } - else{ - ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); + VALUE ary, str; + long i; + int indef_len; + + indef_len = RTEST(ossl_asn1_get_indefinite_length(self)); + ary = rb_convert_type(ossl_asn1_get_value(self), T_ARRAY, "Array", "to_a"); + str = rb_str_new(NULL, 0); + for (i = 0; i < RARRAY_LEN(ary); i++) { + VALUE item = RARRAY_AREF(ary, i); + + if (indef_len && rb_obj_is_kind_of(item, cASN1EndOfContent)) { + if (i != RARRAY_LEN(ary) - 1) + ossl_raise(eASN1Error, "illegal EOC octets in value"); + + /* + * EOC is not really part of the content, but we required to add one + * at the end in the past. + */ + break; } - } - memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value)); - p += RSTRING_LEN(value); - /* In this case we need an additional EOC (one for the explicit part and - * one for the Constructive itself. The EOC for the Constructive is - * supplied by the user, but that for the "explicit wrapper" must be - * added here. - */ - if (explicit && inf_length == Qtrue) { - ASN1_put_eoc(&p); + item = ossl_to_der_if_possible(item); + StringValue(item); + rb_str_append(str, item); } - ossl_str_adjust(str, p); - return str; + return to_der_internal(self, 1, indef_len, str); } /* * call-seq: * asn1_ary.each { |asn1| block } => asn1_ary * - * Calls <i>block</i> once for each element in +self+, passing that element - * as parameter +asn1+. If no block is given, an enumerator is returned + * Calls the given block once for each element in self, passing that element + * as parameter _asn1_. If no block is given, an enumerator is returned * instead. * * == Example @@ -1300,8 +1225,8 @@ ossl_asn1cons_each(VALUE self) * call-seq: * OpenSSL::ASN1::ObjectId.register(object_id, short_name, long_name) * - * This adds a new ObjectId to the internal tables. Where +object_id+ is the - * numerical form, +short_name+ is the short name, and +long_name+ is the long + * This adds a new ObjectId to the internal tables. Where _object_id_ is the + * numerical form, _short_name_ is the short name, and _long_name_ is the long * name. * * Returns +true+ if successful. Raises an OpenSSL::ASN1::ASN1Error if it fails. @@ -1320,14 +1245,13 @@ ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln) return Qtrue; } -/* Document-method: OpenSSL::ASN1::ObjectId#sn +/* + * call-seq: + * oid.sn -> string + * oid.short_name -> string * * The short name of the ObjectId, as defined in <openssl/objects.h>. */ -/* Document-method: OpenSSL::ASN1::ObjectId#short_name - * - * +short_name+ is an alias to +sn+ - */ static VALUE ossl_asn1obj_get_sn(VALUE self) { @@ -1341,14 +1265,13 @@ ossl_asn1obj_get_sn(VALUE self) return ret; } -/* Document-method: OpenSSL::ASN1::ObjectId#ln +/* + * call-seq: + * oid.ln -> string + * oid.long_name -> string * * The long name of the ObjectId, as defined in <openssl/objects.h>. */ -/* Document-method: OpenSSL::ASN1::ObjectId#long_name - * - * +long_name+ is an alias to +ln+ - */ static VALUE ossl_asn1obj_get_ln(VALUE self) { @@ -1362,23 +1285,48 @@ ossl_asn1obj_get_ln(VALUE self) return ret; } -/* Document-method: OpenSSL::ASN1::ObjectId#oid +static VALUE +asn1obj_get_oid_i(VALUE vobj) +{ + ASN1_OBJECT *a1obj = (void *)vobj; + VALUE str; + int len; + + str = rb_usascii_str_new(NULL, 127); + len = OBJ_obj2txt(RSTRING_PTR(str), RSTRING_LENINT(str), a1obj, 1); + if (len <= 0 || len == INT_MAX) + ossl_raise(eASN1Error, "OBJ_obj2txt"); + if (len > RSTRING_LEN(str)) { + /* +1 is for the \0 terminator added by OBJ_obj2txt() */ + rb_str_resize(str, len + 1); + len = OBJ_obj2txt(RSTRING_PTR(str), len + 1, a1obj, 1); + if (len <= 0) + ossl_raise(eASN1Error, "OBJ_obj2txt"); + } + rb_str_set_len(str, len); + return str; +} + +/* + * call-seq: + * oid.oid -> string * - * The object identifier as a +String+, e.g. "1.2.3.4.5" + * Returns a String representing the Object Identifier in the dot notation, + * e.g. "1.2.3.4.5" */ static VALUE ossl_asn1obj_get_oid(VALUE self) { - VALUE val; + VALUE str; ASN1_OBJECT *a1obj; - char buf[128]; + int state; - val = ossl_asn1_get_value(self); - a1obj = obj_to_asn1obj(val); - OBJ_obj2txt(buf, sizeof(buf), a1obj, 1); + a1obj = obj_to_asn1obj(ossl_asn1_get_value(self)); + str = rb_protect(asn1obj_get_oid_i, (VALUE)a1obj, &state); ASN1_OBJECT_free(a1obj); - - return rb_str_new2(buf); + if (state) + rb_jump_tag(state); + return str; } #define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \ @@ -1431,7 +1379,7 @@ Init_ossl_asn1(void) sivTAG = rb_intern("@tag"); sivTAGGING = rb_intern("@tagging"); sivTAG_CLASS = rb_intern("@tag_class"); - sivINFINITE_LENGTH = rb_intern("@infinite_length"); + sivINDEFINITE_LENGTH = rb_intern("@indefinite_length"); sivUNUSED_BITS = rb_intern("@unused_bits"); /* @@ -1457,24 +1405,21 @@ Init_ossl_asn1(void) * == ASN.1 class hierarchy * * The base class representing ASN.1 structures is ASN1Data. ASN1Data offers - * attributes to read and set the +tag+, the +tag_class+ and finally the - * +value+ of a particular ASN.1 item. Upon parsing, any tagged values + * attributes to read and set the _tag_, the _tag_class_ and finally the + * _value_ of a particular ASN.1 item. Upon parsing, any tagged values * (implicit or explicit) will be represented by ASN1Data instances because * their "real type" can only be determined using out-of-band information * from the ASN.1 type declaration. Since this information is normally * known when encoding a type, all sub-classes of ASN1Data offer an - * additional attribute +tagging+ that allows to encode a value implicitly + * additional attribute _tagging_ that allows to encode a value implicitly * (+:IMPLICIT+) or explicitly (+:EXPLICIT+). * * === Constructive * * Constructive is, as its name implies, the base class for all * constructed encodings, i.e. those that consist of several values, - * opposed to "primitive" encodings with just one single value. - * Primitive values that are encoded with "infinite length" are typically - * constructed (their values come in multiple chunks) and are therefore - * represented by instances of Constructive. The value of an Constructive - * is always an Array. + * opposed to "primitive" encodings with just one single value. The value of + * an Constructive is always an Array. * * ==== ASN1::Set and ASN1::Sequence * @@ -1491,18 +1436,18 @@ Init_ossl_asn1(void) * Please cf. Primitive documentation for details on sub-classes and * their respective mappings of ASN.1 data types to Ruby objects. * - * == Possible values for +tagging+ + * == Possible values for _tagging_ * * When constructing an ASN1Data object the ASN.1 type definition may * require certain elements to be either implicitly or explicitly tagged. - * This can be achieved by setting the +tagging+ attribute manually for + * This can be achieved by setting the _tagging_ attribute manually for * sub-classes of ASN1Data. Use the symbol +:IMPLICIT+ for implicit * tagging and +:EXPLICIT+ if the element requires explicit tagging. * - * == Possible values for +tag_class+ + * == Possible values for _tag_class_ * * It is possible to create arbitrary ASN1Data objects that also support - * a PRIVATE or APPLICATION tag class. Possible values for the +tag_class+ + * a PRIVATE or APPLICATION tag class. Possible values for the _tag_class_ * attribute are: * * +:UNIVERSAL+ (the default for untagged values) * * +:CONTEXT_SPECIFIC+ (the default for tagged values) @@ -1604,9 +1549,9 @@ Init_ossl_asn1(void) * * An implicitly 1-tagged INTEGER value will be parsed as an * ASN1Data with - * * +tag+ equal to 1 - * * +tag_class+ equal to +:CONTEXT_SPECIFIC+ - * * +value+ equal to a +String+ that carries the raw encoding + * * _tag_ equal to 1 + * * _tag_class_ equal to +:CONTEXT_SPECIFIC+ + * * _value_ equal to a String that carries the raw encoding * of the INTEGER. * This implies that a subsequent decoding step is required to * completely decode implicitly tagged values. @@ -1615,9 +1560,9 @@ Init_ossl_asn1(void) * * An explicitly 1-tagged INTEGER value will be parsed as an * ASN1Data with - * * +tag+ equal to 1 - * * +tag_class+ equal to +:CONTEXT_SPECIFIC+ - * * +value+ equal to an +Array+ with one single element, an + * * _tag_ equal to 1 + * * _tag_class_ equal to +:CONTEXT_SPECIFIC+ + * * _value_ equal to an Array with one single element, an * instance of OpenSSL::ASN1::Integer, i.e. the inner element * is the non-tagged primitive value, and the tagging is represented * in the outer ASN1Data @@ -1628,13 +1573,13 @@ Init_ossl_asn1(void) * der = seq.to_der * asn1 = OpenSSL::ASN1.decode(der) * # pp asn1 => #<OpenSSL::ASN1::Sequence:0x87326e0 - * # @infinite_length=false, + * # @indefinite_length=false, * # @tag=16, * # @tag_class=:UNIVERSAL, * # @tagging=nil, * # @value= * # [#<OpenSSL::ASN1::ASN1Data:0x87326f4 - * # @infinite_length=false, + * # @indefinite_length=false, * # @tag=0, * # @tag_class=:CONTEXT_SPECIFIC, * # @value="\x01">]> @@ -1651,18 +1596,18 @@ Init_ossl_asn1(void) * der = seq.to_der * asn1 = OpenSSL::ASN1.decode(der) * # pp asn1 => #<OpenSSL::ASN1::Sequence:0x87326e0 - * # @infinite_length=false, + * # @indefinite_length=false, * # @tag=16, * # @tag_class=:UNIVERSAL, * # @tagging=nil, * # @value= * # [#<OpenSSL::ASN1::ASN1Data:0x87326f4 - * # @infinite_length=false, + * # @indefinite_length=false, * # @tag=0, * # @tag_class=:CONTEXT_SPECIFIC, * # @value= * # [#<OpenSSL::ASN1::Integer:0x85bf308 - * # @infinite_length=false, + * # @indefinite_length=false, * # @tag=2, * # @tag_class=:UNIVERSAL * # @tagging=nil, @@ -1678,73 +1623,75 @@ Init_ossl_asn1(void) */ rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0); /* - * A +Number+ representing the tag number of this ASN1Data. Never +nil+. + * An Integer representing the tag number of this ASN1Data. Never +nil+. */ rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0); /* - * A +Symbol+ representing the tag class of this ASN1Data. Never +nil+. + * A Symbol representing the tag class of this ASN1Data. Never +nil+. * See ASN1Data for possible values. */ rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0); /* - * Never +nil+. A +Boolean+ indicating whether the encoding was infinite - * length (in the case of parsing) or whether an infinite length encoding - * shall be used (in the encoding case). - * In DER, every value has a finite length associated with it. But in - * scenarios where large amounts of data need to be transferred it - * might be desirable to have some kind of streaming support available. + * Never +nil+. A boolean value indicating whether the encoding uses + * indefinite length (in the case of parsing) or whether an indefinite + * length form shall be used (in the encoding case). + * In DER, every value uses definite length form. But in scenarios where + * large amounts of data need to be transferred it might be desirable to + * have some kind of streaming support available. * For example, huge OCTET STRINGs are preferably sent in smaller-sized * chunks, each at a time. * This is possible in BER by setting the length bytes of an encoding * to zero and by this indicating that the following value will be - * sent in chunks. Infinite length encodings are always constructed. + * sent in chunks. Indefinite length encodings are always constructed. * The end of such a stream of chunks is indicated by sending a EOC - * (End of Content) tag. SETs and SEQUENCEs may use an infinite length + * (End of Content) tag. SETs and SEQUENCEs may use an indefinite length * encoding, but also primitive types such as e.g. OCTET STRINGS or * BIT STRINGS may leverage this functionality (cf. ITU-T X.690). */ - rb_attr(cASN1Data, rb_intern("infinite_length"), 1, 1, 0); + rb_attr(cASN1Data, rb_intern("indefinite_length"), 1, 1, 0); + rb_define_alias(cASN1Data, "infinite_length", "indefinite_length"); + rb_define_alias(cASN1Data, "infinite_length=", "indefinite_length="); rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3); rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0); /* Document-class: OpenSSL::ASN1::Primitive * * The parent class for all primitive encodings. Attributes are the same as - * for ASN1Data, with the addition of +tagging+. - * Primitive values can never be infinite length encodings, thus it is not - * possible to set the +infinite_length+ attribute for Primitive and its - * sub-classes. + * for ASN1Data, with the addition of _tagging_. + * Primitive values can never be encoded with indefinite length form, thus + * it is not possible to set the _indefinite_length_ attribute for Primitive + * and its sub-classes. * * == Primitive sub-classes and their mapping to Ruby classes - * * OpenSSL::ASN1::EndOfContent <=> +value+ is always +nil+ - * * OpenSSL::ASN1::Boolean <=> +value+ is a +Boolean+ - * * OpenSSL::ASN1::Integer <=> +value+ is a +Number+ - * * OpenSSL::ASN1::BitString <=> +value+ is a +String+ - * * OpenSSL::ASN1::OctetString <=> +value+ is a +String+ - * * OpenSSL::ASN1::Null <=> +value+ is always +nil+ - * * OpenSSL::ASN1::Object <=> +value+ is a +String+ - * * OpenSSL::ASN1::Enumerated <=> +value+ is a +Number+ - * * OpenSSL::ASN1::UTF8String <=> +value+ is a +String+ - * * OpenSSL::ASN1::NumericString <=> +value+ is a +String+ - * * OpenSSL::ASN1::PrintableString <=> +value+ is a +String+ - * * OpenSSL::ASN1::T61String <=> +value+ is a +String+ - * * OpenSSL::ASN1::VideotexString <=> +value+ is a +String+ - * * OpenSSL::ASN1::IA5String <=> +value+ is a +String+ - * * OpenSSL::ASN1::UTCTime <=> +value+ is a +Time+ - * * OpenSSL::ASN1::GeneralizedTime <=> +value+ is a +Time+ - * * OpenSSL::ASN1::GraphicString <=> +value+ is a +String+ - * * OpenSSL::ASN1::ISO64String <=> +value+ is a +String+ - * * OpenSSL::ASN1::GeneralString <=> +value+ is a +String+ - * * OpenSSL::ASN1::UniversalString <=> +value+ is a +String+ - * * OpenSSL::ASN1::BMPString <=> +value+ is a +String+ + * * OpenSSL::ASN1::EndOfContent <=> _value_ is always +nil+ + * * OpenSSL::ASN1::Boolean <=> _value_ is +true+ or +false+ + * * OpenSSL::ASN1::Integer <=> _value_ is an Integer + * * OpenSSL::ASN1::BitString <=> _value_ is a String + * * OpenSSL::ASN1::OctetString <=> _value_ is a String + * * OpenSSL::ASN1::Null <=> _value_ is always +nil+ + * * OpenSSL::ASN1::Object <=> _value_ is a String + * * OpenSSL::ASN1::Enumerated <=> _value_ is an Integer + * * OpenSSL::ASN1::UTF8String <=> _value_ is a String + * * OpenSSL::ASN1::NumericString <=> _value_ is a String + * * OpenSSL::ASN1::PrintableString <=> _value_ is a String + * * OpenSSL::ASN1::T61String <=> _value_ is a String + * * OpenSSL::ASN1::VideotexString <=> _value_ is a String + * * OpenSSL::ASN1::IA5String <=> _value_ is a String + * * OpenSSL::ASN1::UTCTime <=> _value_ is a Time + * * OpenSSL::ASN1::GeneralizedTime <=> _value_ is a Time + * * OpenSSL::ASN1::GraphicString <=> _value_ is a String + * * OpenSSL::ASN1::ISO64String <=> _value_ is a String + * * OpenSSL::ASN1::GeneralString <=> _value_ is a String + * * OpenSSL::ASN1::UniversalString <=> _value_ is a String + * * OpenSSL::ASN1::BMPString <=> _value_ is a String * * == OpenSSL::ASN1::BitString * * === Additional attributes - * +unused_bits+: if the underlying BIT STRING's - * length is a multiple of 8 then +unused_bits+ is 0. Otherwise - * +unused_bits+ indicates the number of bits that are to be ignored in - * the final octet of the +BitString+'s +value+. + * _unused_bits_: if the underlying BIT STRING's + * length is a multiple of 8 then _unused_bits_ is 0. Otherwise + * _unused_bits_ indicates the number of bits that are to be ignored in + * the final octet of the BitString's _value_. * * == OpenSSL::ASN1::ObjectId * @@ -1753,15 +1700,15 @@ Init_ossl_asn1(void) * parsed ASN1 encodings. * * === Additional attributes - * * +sn+: the short name as defined in <openssl/objects.h>. - * * +ln+: the long name as defined in <openssl/objects.h>. - * * +oid+: the object identifier as a +String+, e.g. "1.2.3.4.5" - * * +short_name+: alias for +sn+. - * * +long_name+: alias for +ln+. + * * _sn_: the short name as defined in <openssl/objects.h>. + * * _ln_: the long name as defined in <openssl/objects.h>. + * * _oid_: the object identifier as a String, e.g. "1.2.3.4.5" + * * _short_name_: alias for _sn_. + * * _long_name_: alias for _ln_. * * == Examples * With the Exception of OpenSSL::ASN1::EndOfContent, each Primitive class - * constructor takes at least one parameter, the +value+. + * constructor takes at least one parameter, the _value_. * * === Creating EndOfContent * eoc = OpenSSL::ASN1::EndOfContent.new @@ -1775,19 +1722,20 @@ Init_ossl_asn1(void) /* * May be used as a hint for encoding a value either implicitly or * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. - * +tagging+ is not set when a ASN.1 structure is parsed using + * _tagging_ is not set when a ASN.1 structure is parsed using * OpenSSL::ASN1.decode. */ rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue); + rb_undef_method(cASN1Primitive, "indefinite_length="); rb_undef_method(cASN1Primitive, "infinite_length="); rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1); rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0); /* Document-class: OpenSSL::ASN1::Constructive * - * The parent class for all constructed encodings. The +value+ attribute - * of a Constructive is always an +Array+. Attributes are the same as - * for ASN1Data, with the addition of +tagging+. + * The parent class for all constructed encodings. The _value_ attribute + * of a Constructive is always an Array. Attributes are the same as + * for ASN1Data, with the addition of _tagging_. * * == SET and SEQUENCE * @@ -1809,48 +1757,13 @@ Init_ossl_asn1(void) * int = OpenSSL::ASN1::Integer.new(1) * str = OpenSSL::ASN1::PrintableString.new('abc') * set = OpenSSL::ASN1::Set.new( [ int, str ] ) - * - * == Infinite length primitive values - * - * The only case where Constructive is used directly is for infinite - * length encodings of primitive values. These encodings are always - * constructed, with the contents of the +value+ +Array+ being either - * UNIVERSAL non-infinite length partial encodings of the actual value - * or again constructive encodings with infinite length (i.e. infinite - * length primitive encodings may be constructed recursively with another - * infinite length value within an already infinite length value). Each - * partial encoding must be of the same UNIVERSAL type as the overall - * encoding. The value of the overall encoding consists of the - * concatenation of each partial encoding taken in sequence. The +value+ - * array of the outer infinite length value must end with a - * OpenSSL::ASN1::EndOfContent instance. - * - * Please note that it is not possible to encode Constructive without - * the +infinite_length+ attribute being set to +true+, use - * OpenSSL::ASN1::Sequence or OpenSSL::ASN1::Set in these cases instead. - * - * === Example - Infinite length OCTET STRING - * partial1 = OpenSSL::ASN1::OctetString.new("\x01") - * partial2 = OpenSSL::ASN1::OctetString.new("\x02") - * inf_octets = OpenSSL::ASN1::Constructive.new( [ partial1, - * partial2, - * OpenSSL::ASN1::EndOfContent.new ], - * OpenSSL::ASN1::OCTET_STRING, - * nil, - * :UNIVERSAL ) - * # The real value of inf_octets is "\x01\x02", i.e. the concatenation - * # of partial1 and partial2 - * inf_octets.infinite_length = true - * der = inf_octets.to_der - * asn1 = OpenSSL::ASN1.decode(der) - * puts asn1.infinite_length # => true */ cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data); rb_include_module(cASN1Constructive, rb_mEnumerable); /* * May be used as a hint for encoding a value either implicitly or * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. - * +tagging+ is not set when a ASN.1 structure is parsed using + * _tagging_ is not set when a ASN.1 structure is parsed using * OpenSSL::ASN1.decode. */ rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue); @@ -1907,6 +1820,7 @@ do{\ rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0); rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0); + rb_define_method(cASN1EndOfContent, "to_der", ossl_asn1eoc_to_der, 0); class_tag_map = rb_hash_new(); rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC)); diff --git a/ext/openssl/ossl_asn1.h b/ext/openssl/ossl_asn1.h index d6a170c8..939a96ce 100644 --- a/ext/openssl/ossl_asn1.h +++ b/ext/openssl/ossl_asn1.h @@ -14,15 +14,11 @@ * ASN1_DATE conversions */ VALUE asn1time_to_time(const ASN1_TIME *); -#if defined(HAVE_ASN1_TIME_ADJ) /* Splits VALUE to seconds and offset days. VALUE is typically a Time or an * Integer. This is used when updating ASN1_*TIME with ASN1_TIME_adj() or * X509_time_adj_ex(). We can't use ASN1_TIME_set() and X509_time_adj() because * they have the Year 2038 issue on sizeof(time_t) == 4 environment */ void ossl_time_split(VALUE, time_t *, int *); -#else -time_t time_to_time_t(VALUE); -#endif /* * ASN1_STRING conversions diff --git a/ext/openssl/ossl_bio.c b/ext/openssl/ossl_bio.c index 8fa0b696..42833d90 100644 --- a/ext/openssl/ossl_bio.c +++ b/ext/openssl/ossl_bio.c @@ -26,32 +26,17 @@ ossl_obj2bio(volatile VALUE *pobj) } VALUE -ossl_membio2str0(BIO *bio) +ossl_membio2str(BIO *bio) { VALUE ret; + int state; BUF_MEM *buf; BIO_get_mem_ptr(bio, &buf); - ret = rb_str_new(buf->data, buf->length); - - return ret; -} - -VALUE -ossl_protect_membio2str(BIO *bio, int *status) -{ - return rb_protect((VALUE (*)(VALUE))ossl_membio2str0, (VALUE)bio, status); -} - -VALUE -ossl_membio2str(BIO *bio) -{ - VALUE ret; - int status = 0; - - ret = ossl_protect_membio2str(bio, &status); + ret = ossl_str_new(buf->data, buf->length, &state); BIO_free(bio); - if(status) rb_jump_tag(status); + if (state) + rb_jump_tag(state); return ret; } diff --git a/ext/openssl/ossl_bio.h b/ext/openssl/ossl_bio.h index 2c3d952b..da68c5e5 100644 --- a/ext/openssl/ossl_bio.h +++ b/ext/openssl/ossl_bio.h @@ -11,8 +11,6 @@ #define _OSSL_BIO_H_ BIO *ossl_obj2bio(volatile VALUE *); -VALUE ossl_membio2str0(BIO*); VALUE ossl_membio2str(BIO*); -VALUE ossl_protect_membio2str(BIO*,int*); #endif diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index 29dc1a21..4666ce6c 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -26,11 +26,6 @@ } \ } while (0) -#define SafeGetBN(obj, bn) do { \ - OSSL_Check_Kind((obj), cBN); \ - GetBN((obj), (bn)); \ -} while (0) - static void ossl_bn_free(void *ptr) { @@ -176,8 +171,7 @@ ossl_bn_alloc(VALUE klass) return obj; } -/* Document-method: OpenSSL::BN.new - * +/* * call-seq: * OpenSSL::BN.new => aBN * OpenSSL::BN.new(bn) => aBN @@ -185,7 +179,7 @@ ossl_bn_alloc(VALUE klass) * OpenSSL::BN.new(string) => aBN * OpenSSL::BN.new(string, 0 | 2 | 10 | 16) => aBN * - * Construct a new OpenSSL BigNum object. + * Construct a new OpenSSL BIGNUM object. */ static VALUE ossl_bn_initialize(int argc, VALUE *argv, VALUE self) @@ -250,7 +244,7 @@ ossl_bn_initialize(int argc, VALUE *argv, VALUE self) * bn.to_s(base) => string * * === Parameters - * * +base+ - integer + * * _base_ - Integer * Valid values: * * 0 - MPI * * 2 - binary @@ -377,6 +371,21 @@ BIGNUM_BOOL1(is_one) */ BIGNUM_BOOL1(is_odd) +/* + * call-seq: + * bn.negative? => true | false + */ +static VALUE +ossl_bn_is_negative(VALUE self) +{ + BIGNUM *bn; + + GetBN(self, bn); + if (BN_is_zero(bn)) + return Qfalse; + return BN_is_negative(bn) ? Qtrue : Qfalse; +} + #define BIGNUM_1c(func) \ static VALUE \ ossl_bn_##func(VALUE self) \ @@ -498,7 +507,6 @@ BIGNUM_2c(mod_sqr) BIGNUM_2c(mod_inverse) /* - * Document-method: OpenSSL::BN#/ * call-seq: * bn1 / bn2 => [result, remainder] * @@ -614,12 +622,11 @@ BIGNUM_BIT(clear_bit) */ BIGNUM_BIT(mask_bits) -/* Document-method: OpenSSL::BN#bit_set? +/* * call-seq: * bn.bit_set?(bit) => true | false * - * Returns boolean of whether +bit+ is set. - * Bitwise operations for openssl BIGNUMs. + * Tests bit _bit_ in _bn_ and returns +true+ if set, +false+ if not set. */ static VALUE ossl_bn_is_bit_set(VALUE self, VALUE bit) @@ -774,15 +781,15 @@ BIGNUM_RAND_RANGE(pseudo_rand) * call-seq: * BN.generate_prime(bits, [, safe [, add [, rem]]]) => bn * - * Generates a random prime number of bit length +bits+. If +safe+ is true, - * generates a safe prime. If +add+ is specified, generates a prime that + * Generates a random prime number of bit length _bits_. If _safe_ is set to + * +true+, generates a safe prime. If _add_ is specified, generates a prime that * fulfills condition <tt>p % add = rem</tt>. * * === Parameters - * * +bits+ - integer - * * +safe+ - boolean - * * +add+ - BN - * * +rem+ - BN + * * _bits_ - integer + * * _safe_ - boolean + * * _add_ - BN + * * _rem_ - BN */ static VALUE ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass) @@ -856,6 +863,37 @@ ossl_bn_copy(VALUE self, VALUE other) return self; } +/* + * call-seq: + * +bn -> aBN + */ +static VALUE +ossl_bn_uplus(VALUE self) +{ + return self; +} + +/* + * call-seq: + * -bn -> aBN + */ +static VALUE +ossl_bn_uminus(VALUE self) +{ + VALUE obj; + BIGNUM *bn1, *bn2; + + GetBN(self, bn1); + obj = NewBN(cBN); + bn2 = BN_dup(bn1); + if (!bn2) + ossl_raise(eBNError, "BN_dup"); + SetBN(obj, bn2); + BN_set_negative(bn2, !BN_is_negative(bn2)); + + return obj; +} + #define BIGNUM_CMP(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE other) \ @@ -888,7 +926,7 @@ BIGNUM_CMP(ucmp) * call-seq: * bn == obj => true or false * - * Returns +true+ only if +obj+ has the same value as +bn+. 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 @@ -913,7 +951,7 @@ ossl_bn_eq(VALUE self, VALUE other) * 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 + * <code>OpenSSL::BN</code> with the same value as <i>bn</i>. Contrast this * with OpenSSL::BN#==, which performs type conversions. */ static VALUE @@ -941,20 +979,20 @@ static VALUE ossl_bn_hash(VALUE self) { BIGNUM *bn; - VALUE hash; + VALUE tmp, hash; unsigned char *buf; int len; GetBN(self, bn); len = BN_num_bytes(bn); - buf = xmalloc(len); + buf = ALLOCV(tmp, len); if (BN_bn2bin(bn, buf) != len) { - xfree(buf); - ossl_raise(eBNError, NULL); + ALLOCV_END(tmp); + ossl_raise(eBNError, "BN_bn2bin"); } hash = ST2FIX(rb_memhash(buf, len)); - xfree(buf); + ALLOCV_END(tmp); return hash; } @@ -964,12 +1002,12 @@ ossl_bn_hash(VALUE self) * bn.prime? => true | false * bn.prime?(checks) => true | false * - * Performs a Miller-Rabin probabilistic primality test with +checks+ - * iterations. If +nchecks+ is not specified, a number of iterations is used + * Performs a Miller-Rabin probabilistic primality test with _checks_ + * iterations. If _checks_ is not specified, a number of iterations is used * that yields a false positive rate of at most 2^-80 for random input. * * === Parameters - * * +checks+ - integer + * * _checks_ - integer */ static VALUE ossl_bn_is_prime(int argc, VALUE *argv, VALUE self) @@ -1004,8 +1042,8 @@ ossl_bn_is_prime(int argc, VALUE *argv, VALUE self) * first attempts trial divisions with some small primes. * * === Parameters - * * +checks+ - integer - * * +trial_div+ - boolean + * * _checks_ - integer + * * _trial_div_ - boolean */ static VALUE ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self) @@ -1059,7 +1097,7 @@ Init_ossl_bn(void) rb_define_alloc_func(cBN, ossl_bn_alloc); rb_define_method(cBN, "initialize", ossl_bn_initialize, -1); - rb_define_copy_func(cBN, ossl_bn_copy); + rb_define_method(cBN, "initialize_copy", ossl_bn_copy, 1); rb_define_method(cBN, "copy", ossl_bn_copy, 1); /* swap (=coerce?) */ @@ -1068,6 +1106,9 @@ Init_ossl_bn(void) rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0); /* num_bits_word */ + rb_define_method(cBN, "+@", ossl_bn_uplus, 0); + rb_define_method(cBN, "-@", ossl_bn_uminus, 0); + rb_define_method(cBN, "+", ossl_bn_add, 1); rb_define_method(cBN, "-", ossl_bn_sub, 1); rb_define_method(cBN, "*", ossl_bn_mul, 1); @@ -1101,6 +1142,7 @@ Init_ossl_bn(void) rb_define_method(cBN, "one?", ossl_bn_is_one, 0); /* is_word */ rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0); + rb_define_method(cBN, "negative?", ossl_bn_is_negative, 0); /* zero * one diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 740f04b2..3038a766 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -26,10 +26,6 @@ ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \ } \ } while (0) -#define SafeGetCipher(obj, ctx) do { \ - OSSL_Check_Kind((obj), cCipher); \ - GetCipher((obj), (ctx)); \ -} while (0) /* * Classes @@ -53,7 +49,7 @@ static const rb_data_type_t ossl_cipher_type = { * PUBLIC */ const EVP_CIPHER * -GetCipherPtr(VALUE obj) +ossl_evp_get_cipherbyname(VALUE obj) { if (rb_obj_is_kind_of(obj, cCipher)) { EVP_CIPHER_CTX *ctx; @@ -108,7 +104,7 @@ ossl_cipher_alloc(VALUE klass) * call-seq: * Cipher.new(string) -> cipher * - * The string must contain a valid cipher name like "AES-128-CBC" or "3DES". + * The string must be a valid cipher name like "AES-128-CBC" or "3DES". * * A list of cipher names is available by calling OpenSSL::Cipher.ciphers. */ @@ -146,7 +142,7 @@ ossl_cipher_copy(VALUE self, VALUE other) if (!ctx1) { AllocCipher(self, ctx1); } - SafeGetCipher(other, ctx2); + GetCipher(other, ctx2); if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1) ossl_raise(eCipherError, NULL); @@ -296,9 +292,9 @@ ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) * OpenSSL::PKCS5 instead. * * === Parameters - * * +salt+ must be an 8 byte string if provided. - * * +iterations+ is an integer with a default of 2048. - * * +digest+ is a Digest object that defaults to 'MD5' + * * _salt_ must be an 8 byte string if provided. + * * _iterations_ is an integer with a default of 2048. + * * _digest_ is a Digest object that defaults to 'MD5' * * A minimum of 1000 iterations is recommended. * @@ -321,7 +317,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) salt = (unsigned char *)RSTRING_PTR(vsalt); } iter = NIL_P(viter) ? 2048 : NUM2INT(viter); - digest = NIL_P(vdigest) ? EVP_md5() : GetDigestPtr(vdigest); + digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest); GetCipher(self, ctx); EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt, (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv); @@ -365,12 +361,12 @@ ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_p * cipher.update(data [, buffer]) -> string or buffer * * Encrypts data in a streaming fashion. Hand consecutive blocks of data - * to the +update+ method in order to encrypt it. Returns the encrypted + * to the #update method in order to encrypt it. Returns the encrypted * data chunk. When done, the output of Cipher#final should be additionally * added to the result. * - * If +buffer+ is given, the encryption/decryption result will be written to - * it. +buffer+ will be resized automatically. + * If _buffer_ is given, the encryption/decryption result will be written to + * it. _buffer_ will be resized automatically. */ static VALUE ossl_cipher_update(int argc, VALUE *argv, VALUE self) @@ -512,10 +508,8 @@ ossl_cipher_set_iv(VALUE self, VALUE iv) StringValue(iv); GetCipher(self, ctx); -#if defined(HAVE_AUTHENTICATED_ENCRYPTION) - if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); -#endif if (!iv_len) iv_len = EVP_CIPHER_CTX_iv_length(ctx); if (RSTRING_LEN(iv) != iv_len) @@ -541,14 +535,9 @@ ossl_cipher_is_authenticated(VALUE self) GetCipher(self, ctx); -#if defined(HAVE_AUTHENTICATED_ENCRYPTION) - return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; -#else - return Qfalse; -#endif + return (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; } -#ifdef HAVE_AUTHENTICATED_ENCRYPTION /* * call-seq: * cipher.auth_data = string -> string @@ -596,8 +585,8 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) * Gets the authentication tag generated by Authenticated Encryption Cipher * modes (GCM for example). This tag may be stored along with the ciphertext, * then set on the decryption cipher to authenticate the contents of the - * ciphertext against changes. If the optional integer parameter +tag_len+ is - * given, the returned tag will be +tag_len+ bytes long. If the parameter is + * ciphertext against changes. If the optional integer parameter _tag_len_ is + * given, the returned tag will be _tag_len_ bytes long. If the parameter is * omitted, the default length of 16 bytes or the length previously set by * #auth_tag_len= will be used. For maximum security, the longest possible * should be chosen. @@ -619,7 +608,7 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); ret = rb_str_new(NULL, tag_len); @@ -654,7 +643,7 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) tag_len = RSTRING_LENINT(vtag); GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) @@ -681,7 +670,7 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL)) @@ -708,7 +697,7 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "cipher does not support AEAD"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL)) @@ -722,13 +711,6 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) return iv_length; } -#else -#define ossl_cipher_set_auth_data rb_f_notimplement -#define ossl_cipher_get_auth_tag rb_f_notimplement -#define ossl_cipher_set_auth_tag rb_f_notimplement -#define ossl_cipher_set_auth_tag_len rb_f_notimplement -#define ossl_cipher_set_iv_length rb_f_notimplement -#endif /* * call-seq: @@ -806,10 +788,8 @@ ossl_cipher_iv_length(VALUE self) int len = 0; GetCipher(self, ctx); -#if defined(HAVE_AUTHENTICATED_ENCRYPTION) - if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); -#endif if (!len) len = EVP_CIPHER_CTX_iv_length(ctx); @@ -1020,9 +1000,9 @@ Init_ossl_cipher(void) * encryption and later decryption, the OpenSSL library still requires a * value to be set - "" may be used in case none is available. * - * An example using the GCM (Galois/Counter Mode). You have 16 bytes +key+, - * 12 bytes (96 bits) +nonce+ and the associated data +auth_data+. Be sure - * not to reuse the +key+ and +nonce+ pair. Reusing an nonce ruins the + * An example using the GCM (Galois/Counter Mode). You have 16 bytes _key_, + * 12 bytes (96 bits) _nonce_ and the associated data _auth_data_. Be sure + * not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the * security guarantees of GCM mode. * * cipher = OpenSSL::Cipher::AES.new(128, :GCM).encrypt @@ -1033,8 +1013,8 @@ Init_ossl_cipher(void) * encrypted = cipher.update(data) + cipher.final * tag = cipher.auth_tag # produces 16 bytes tag by default * - * Now you are the receiver. You know the +key+ and have received +nonce+, - * +auth_data+, +encrypted+ and +tag+ through an untrusted network. Note + * Now you are the receiver. You know the _key_ and have received _nonce_, + * _auth_data_, _encrypted_ and _tag_ through an untrusted network. Note * that GCM accepts an arbitrary length tag between 1 and 16 bytes. You may * additionally need to check that the received tag has the correct length, * or you allow attackers to forge a valid single byte tag for the tampered @@ -1055,7 +1035,7 @@ Init_ossl_cipher(void) eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError); rb_define_alloc_func(cCipher, ossl_cipher_alloc); - rb_define_copy_func(cCipher, ossl_cipher_copy); + rb_define_method(cCipher, "initialize_copy", ossl_cipher_copy, 1); rb_define_module_function(cCipher, "ciphers", ossl_s_ciphers, 0); rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1); rb_define_method(cCipher, "reset", ossl_cipher_reset, 0); diff --git a/ext/openssl/ossl_cipher.h b/ext/openssl/ossl_cipher.h index c444089f..2392d41c 100644 --- a/ext/openssl/ossl_cipher.h +++ b/ext/openssl/ossl_cipher.h @@ -13,7 +13,7 @@ extern VALUE cCipher; extern VALUE eCipherError; -const EVP_CIPHER *GetCipherPtr(VALUE); +const EVP_CIPHER *ossl_evp_get_cipherbyname(VALUE); VALUE ossl_cipher_new(const EVP_CIPHER *); void Init_ossl_cipher(void); diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index fdafda00..112ce336 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -15,10 +15,6 @@ ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ } \ } while (0) -#define SafeGetDigest(obj, ctx) do { \ - OSSL_Check_Kind((obj), cDigest); \ - GetDigest((obj), (ctx)); \ -} while (0) /* * Classes @@ -46,7 +42,7 @@ static const rb_data_type_t ossl_digest_type = { * Public */ const EVP_MD * -GetDigestPtr(VALUE obj) +ossl_evp_get_digestbyname(VALUE obj) { const EVP_MD *md; ASN1_OBJECT *oid = NULL; @@ -65,7 +61,7 @@ GetDigestPtr(VALUE obj) } else { EVP_MD_CTX *ctx; - SafeGetDigest(obj, ctx); + GetDigest(obj, ctx); md = EVP_MD_CTX_md(ctx); } @@ -106,15 +102,15 @@ VALUE ossl_digest_update(VALUE, VALUE); * call-seq: * Digest.new(string [, data]) -> Digest * - * Creates a Digest instance based on +string+, which is either the ln + * Creates a Digest instance based on _string_, which is either the ln * (long name) or sn (short name) of a supported digest algorithm. * - * If +data+ (a +String+) is given, it is used as the initial input to the + * If _data_ (a String) is given, it is used as the initial input to the * Digest instance, i.e. * * digest = OpenSSL::Digest.new('sha256', 'digestdata') * - * is equal to + * is equivalent to * * digest = OpenSSL::Digest.new('sha256') * digest.update('digestdata') @@ -127,7 +123,7 @@ ossl_digest_initialize(int argc, VALUE *argv, VALUE self) VALUE type, data; rb_scan_args(argc, argv, "11", &type, &data); - md = GetDigestPtr(type); + md = ossl_evp_get_digestbyname(type); if (!NIL_P(data)) StringValue(data); TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx); @@ -158,7 +154,7 @@ ossl_digest_copy(VALUE self, VALUE other) if (!ctx1) ossl_raise(eDigestError, "EVP_MD_CTX_new"); } - SafeGetDigest(other, ctx2); + GetDigest(other, ctx2); if (!EVP_MD_CTX_copy(ctx1, ctx2)) { ossl_raise(eDigestError, NULL); @@ -448,7 +444,7 @@ Init_ossl_digest(void) rb_define_alloc_func(cDigest, ossl_digest_alloc); rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1); - rb_define_copy_func(cDigest, ossl_digest_copy); + rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1); rb_define_method(cDigest, "reset", ossl_digest_reset, 0); rb_define_method(cDigest, "update", ossl_digest_update, 1); rb_define_alias(cDigest, "<<", "update"); diff --git a/ext/openssl/ossl_digest.h b/ext/openssl/ossl_digest.h index 512f7d3a..50bf5666 100644 --- a/ext/openssl/ossl_digest.h +++ b/ext/openssl/ossl_digest.h @@ -13,7 +13,7 @@ extern VALUE cDigest; extern VALUE eDigestError; -const EVP_MD *GetDigestPtr(VALUE); +const EVP_MD *ossl_evp_get_digestbyname(VALUE); VALUE ossl_digest_new(const EVP_MD *); void Init_ossl_digest(void); diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index e840bfd9..5ca0d4ca 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -25,10 +25,6 @@ ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ } \ } while (0) -#define SafeGetEngine(obj, engine) do { \ - OSSL_Check_Kind((obj), cEngine); \ - GetPKCS7((obj), (engine)); \ -} while (0) /* * Classes @@ -50,13 +46,25 @@ VALUE eEngineError; /* * Private */ -#define OSSL_ENGINE_LOAD_IF_MATCH(x) \ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 +#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ +do{\ + if(!strcmp(#engine_name, RSTRING_PTR(name))){\ + if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\ + return Qtrue;\ + else\ + ossl_raise(eEngineError, "OPENSSL_init_crypto"); \ + }\ +}while(0) +#else +#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ do{\ - if(!strcmp(#x, RSTRING_PTR(name))){\ - ENGINE_load_##x();\ + if(!strcmp(#engine_name, RSTRING_PTR(name))){\ + ENGINE_load_##engine_name();\ return Qtrue;\ }\ }while(0) +#endif static void ossl_engine_free(void *engine) @@ -72,14 +80,13 @@ static const rb_data_type_t ossl_engine_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; -/* Document-method: OpenSSL::Engine.load - * +/* * call-seq: - * load(enginename = nil) + * OpenSSL::Engine.load(name = nil) * - * This method loads engines. If +name+ is nil, then all builtin engines are - * loaded. Otherwise, the given +name+, as a string, is loaded if available to - * your runtime, and returns true. If +name+ is not found, then nil is + * This method loads engines. If _name_ is nil, then all builtin engines are + * loaded. Otherwise, the given _name_, as a String, is loaded if available to + * your runtime, and returns true. If _name_ is not found, then nil is * returned. * */ @@ -99,63 +106,63 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) StringValueCStr(name); #ifndef OPENSSL_NO_STATIC_ENGINE #if HAVE_ENGINE_LOAD_DYNAMIC - OSSL_ENGINE_LOAD_IF_MATCH(dynamic); + OSSL_ENGINE_LOAD_IF_MATCH(dynamic, DYNAMIC); #endif #if HAVE_ENGINE_LOAD_4758CCA - OSSL_ENGINE_LOAD_IF_MATCH(4758cca); + OSSL_ENGINE_LOAD_IF_MATCH(4758cca, 4758CCA); #endif #if HAVE_ENGINE_LOAD_AEP - OSSL_ENGINE_LOAD_IF_MATCH(aep); + OSSL_ENGINE_LOAD_IF_MATCH(aep, AEP); #endif #if HAVE_ENGINE_LOAD_ATALLA - OSSL_ENGINE_LOAD_IF_MATCH(atalla); + OSSL_ENGINE_LOAD_IF_MATCH(atalla, ATALLA); #endif #if HAVE_ENGINE_LOAD_CHIL - OSSL_ENGINE_LOAD_IF_MATCH(chil); + OSSL_ENGINE_LOAD_IF_MATCH(chil, CHIL); #endif #if HAVE_ENGINE_LOAD_CSWIFT - OSSL_ENGINE_LOAD_IF_MATCH(cswift); + OSSL_ENGINE_LOAD_IF_MATCH(cswift, CSWIFT); #endif #if HAVE_ENGINE_LOAD_NURON - OSSL_ENGINE_LOAD_IF_MATCH(nuron); + OSSL_ENGINE_LOAD_IF_MATCH(nuron, NURON); #endif #if HAVE_ENGINE_LOAD_SUREWARE - OSSL_ENGINE_LOAD_IF_MATCH(sureware); + OSSL_ENGINE_LOAD_IF_MATCH(sureware, SUREWARE); #endif #if HAVE_ENGINE_LOAD_UBSEC - OSSL_ENGINE_LOAD_IF_MATCH(ubsec); + OSSL_ENGINE_LOAD_IF_MATCH(ubsec, UBSEC); #endif #if HAVE_ENGINE_LOAD_PADLOCK - OSSL_ENGINE_LOAD_IF_MATCH(padlock); + OSSL_ENGINE_LOAD_IF_MATCH(padlock, PADLOCK); #endif #if HAVE_ENGINE_LOAD_CAPI - OSSL_ENGINE_LOAD_IF_MATCH(capi); + OSSL_ENGINE_LOAD_IF_MATCH(capi, CAPI); #endif #if HAVE_ENGINE_LOAD_GMP - OSSL_ENGINE_LOAD_IF_MATCH(gmp); + OSSL_ENGINE_LOAD_IF_MATCH(gmp, GMP); #endif #if HAVE_ENGINE_LOAD_GOST - OSSL_ENGINE_LOAD_IF_MATCH(gost); + OSSL_ENGINE_LOAD_IF_MATCH(gost, GOST); #endif #if HAVE_ENGINE_LOAD_CRYPTODEV - OSSL_ENGINE_LOAD_IF_MATCH(cryptodev); + OSSL_ENGINE_LOAD_IF_MATCH(cryptodev, CRYPTODEV); #endif #if HAVE_ENGINE_LOAD_AESNI - OSSL_ENGINE_LOAD_IF_MATCH(aesni); + OSSL_ENGINE_LOAD_IF_MATCH(aesni, AESNI); #endif #endif #ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO - OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto); + OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto, OPENBSD_DEV_CRYPTO); #endif - OSSL_ENGINE_LOAD_IF_MATCH(openssl); + OSSL_ENGINE_LOAD_IF_MATCH(openssl, OPENSSL); rb_warning("no such builtin loader for `%"PRIsVALUE"'", name); return Qnil; #endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */ } -/* Document-method: OpenSSL::Engine.cleanup +/* * call-seq: - * OpenSSL::Engine.cleanup + * OpenSSL::Engine.cleanup * * It is only necessary to run cleanup when engines are loaded via * OpenSSL::Engine.load. However, running cleanup before exit is recommended. @@ -165,11 +172,15 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) static VALUE ossl_engine_s_cleanup(VALUE self) { +#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000 ENGINE_cleanup(); +#endif return Qnil; } -/* Document-method: OpenSSL::Engine.engines +/* + * call-seq: + * OpenSSL::Engine.engines -> [engine, ...] * * Returns an array of currently loaded engines. */ @@ -193,17 +204,16 @@ ossl_engine_s_engines(VALUE klass) return ary; } -/* Document-method: OpenSSL::Engine.by_id - * +/* * call-seq: - * by_id(name) -> engine + * OpenSSL::Engine.by_id(name) -> engine * - * Fetch the engine as specified by the +id+ String + * Fetches the engine as specified by the _id_ String. * * OpenSSL::Engine.by_id("openssl") * => #<OpenSSL::Engine id="openssl" name="Software engine support"> * - * See OpenSSL::Engine.engines for the currently loaded engines + * See OpenSSL::Engine.engines for the currently loaded engines. */ static VALUE ossl_engine_s_by_id(VALUE klass, VALUE id) @@ -227,9 +237,11 @@ ossl_engine_s_by_id(VALUE klass, VALUE id) return obj; } -/* Document-method: OpenSSL::Engine#id +/* + * call-seq: + * engine.id -> string * - * Get the id for this engine + * Gets the id for this engine. * * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#<OpenSSL::Engine#>, ...] @@ -244,9 +256,11 @@ ossl_engine_get_id(VALUE self) return rb_str_new2(ENGINE_get_id(e)); } -/* Document-method: OpenSSL::Engine#name +/* + * call-seq: + * engine.name -> string * - * Get the descriptive name for this engine + * Get the descriptive name for this engine. * * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#<OpenSSL::Engine#>, ...] @@ -262,7 +276,9 @@ ossl_engine_get_name(VALUE self) return rb_str_new2(ENGINE_get_name(e)); } -/* Document-method: OpenSSL::Engine#finish +/* + * call-seq: + * engine.finish -> nil * * Releases all internal structural references for this engine. * @@ -279,13 +295,12 @@ ossl_engine_finish(VALUE self) return Qnil; } -/* Document-method: OpenSSL::Engine#cipher - * +/* * call-seq: * engine.cipher(name) -> OpenSSL::Cipher * - * This returns an OpenSSL::Cipher by +name+, if it is available in this - * engine. + * Returns a new instance of OpenSSL::Cipher by _name_, if it is available in + * this engine. * * An EngineError will be raised if the cipher is unavailable. * @@ -312,12 +327,11 @@ ossl_engine_get_cipher(VALUE self, VALUE name) return ossl_cipher_new(ciph); } -/* Document-method: OpenSSL::Engine#digest - * +/* * call-seq: * engine.digest(name) -> OpenSSL::Digest * - * This returns an OpenSSL::Digest by +name+. + * Returns a new instance of OpenSSL::Digest by _name_. * * Will raise an EngineError if the digest is unavailable. * @@ -345,12 +359,11 @@ ossl_engine_get_digest(VALUE self, VALUE name) return ossl_digest_new(md); } -/* Document-method: OpenSSL::Engine#load_private_key - * +/* * call-seq: * engine.load_private_key(id = nil, data = nil) -> OpenSSL::PKey * - * Loads the given private key by +id+ and +data+. + * Loads the given private key identified by _id_ and _data_. * * An EngineError is raised of the OpenSSL::PKey is unavailable. * @@ -375,12 +388,11 @@ ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self) return obj; } -/* Document-method: OpenSSL::Engine#load_public_key - * +/* * call-seq: * engine.load_public_key(id = nil, data = nil) -> OpenSSL::PKey * - * Loads the given private key by +id+ and +data+. + * Loads the given public key identified by _id_ and _data_. * * An EngineError is raised of the OpenSSL::PKey is unavailable. * @@ -403,16 +415,15 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) return ossl_pkey_new(pkey); } -/* Document-method: OpenSSL::Engine#set_default - * +/* * call-seq: * engine.set_default(flag) * - * Set the defaults for this engine with the given +flag+. + * Set the defaults for this engine with the given _flag_. * * These flags are used to control combinations of algorithm methods. * - * +flag+ can be one of the following, other flags are available depending on + * _flag_ can be one of the following, other flags are available depending on * your OS. * * [All flags] 0xFFFF @@ -432,14 +443,13 @@ ossl_engine_set_default(VALUE self, VALUE flag) return Qtrue; } -/* Document-method: OpenSSL::Engine#ctrl_cmd - * +/* * call-seq: * engine.ctrl_cmd(command, value = nil) -> engine * - * Send the given +command+ to this engine. + * Sends the given _command_ to this engine. * - * Raises an EngineError if the +command+ fails. + * Raises an EngineError if the command fails. */ static VALUE ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self) @@ -469,7 +479,9 @@ ossl_engine_cmd_flag_to_name(int flag) } } -/* Document-method: OpenSSL::Engine#cmds +/* + * call-seq: + * engine.cmds -> [["name", "description", "flags"], ...] * * Returns an array of command definitions for the current engine */ @@ -495,9 +507,11 @@ ossl_engine_get_cmds(VALUE self) return ary; } -/* Document-method: OpenSSL::Engine#inspect +/* + * call-seq: + * engine.inspect -> string * - * Pretty print this engine + * Pretty prints this engine. */ static VALUE ossl_engine_inspect(VALUE self) diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index 270979ed..564dcab5 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -19,10 +19,6 @@ ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ } \ } while (0) -#define SafeGetHMAC(obj, ctx) do { \ - OSSL_Check_Kind((obj), cHMAC); \ - GetHMAC((obj), (ctx)); \ -} while (0) /* * Classes @@ -110,7 +106,7 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) StringValue(key); GetHMAC(self, ctx); HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key), - GetDigestPtr(digest), NULL); + ossl_evp_get_digestbyname(digest), NULL); return self; } @@ -124,7 +120,7 @@ ossl_hmac_copy(VALUE self, VALUE other) if (self == other) return self; GetHMAC(self, ctx1); - SafeGetHMAC(other, ctx2); + GetHMAC(other, ctx2); if (!HMAC_CTX_copy(ctx1, ctx2)) ossl_raise(eHMACError, "HMAC_CTX_copy"); @@ -135,7 +131,7 @@ ossl_hmac_copy(VALUE self, VALUE other) * call-seq: * hmac.update(string) -> self * - * Returns +self+ updated with the message to be authenticated. + * Returns _hmac_ updated with the message to be authenticated. * Can be called repeatedly with chunks of the message. * * === Example @@ -234,7 +230,7 @@ ossl_hmac_hexdigest(VALUE self) * call-seq: * hmac.reset -> self * - * Returns +self+ as it was when it was first initialized, with all processed + * Returns _hmac_ as it was when it was first initialized, with all processed * data cleared from it. * * === Example @@ -264,16 +260,16 @@ ossl_hmac_reset(VALUE self) * call-seq: * HMAC.digest(digest, key, data) -> aString * - * Returns the authentication code as a binary string. The +digest+ parameter - * must be an instance of OpenSSL::Digest. + * Returns the authentication code as a binary string. The _digest_ parameter + * specifies the digest algorithm to use. This may be a String representing + * the algorithm name or an instance of OpenSSL::Digest. * * === Example * * key = 'key' * data = 'The quick brown fox jumps over the lazy dog' - * digest = OpenSSL::Digest.new('sha1') * - * hmac = OpenSSL::HMAC.digest(digest, key, data) + * hmac = OpenSSL::HMAC.digest('sha1', key, data) * #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9" * */ @@ -285,8 +281,9 @@ ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data) StringValue(key); StringValue(data); - buf = HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key), - (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), NULL, &buf_len); + buf = HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key), + RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data), + RSTRING_LEN(data), NULL, &buf_len); return rb_str_new((const char *)buf, buf_len); } @@ -295,16 +292,16 @@ ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data) * call-seq: * HMAC.hexdigest(digest, key, data) -> aString * - * Returns the authentication code as a hex-encoded string. The +digest+ - * parameter must be an instance of OpenSSL::Digest. + * Returns the authentication code as a hex-encoded string. The _digest_ + * parameter specifies the digest algorithm to use. This may be a String + * representing the algorithm name or an instance of OpenSSL::Digest. * * === Example * * key = 'key' * data = 'The quick brown fox jumps over the lazy dog' - * digest = OpenSSL::Digest.new('sha1') * - * hmac = OpenSSL::HMAC.hexdigest(digest, key, data) + * hmac = OpenSSL::HMAC.hexdigest('sha1', key, data) * #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9" * */ @@ -318,9 +315,9 @@ ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data) StringValue(key); StringValue(data); - if (!HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key), - (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), - buf, &buf_len)) + if (!HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key), + RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data), + RSTRING_LEN(data), buf, &buf_len)) ossl_raise(eHMACError, "HMAC"); ret = rb_str_new(NULL, buf_len * 2); @@ -377,7 +374,7 @@ Init_ossl_hmac(void) rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3); rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2); - rb_define_copy_func(cHMAC, ossl_hmac_copy); + rb_define_method(cHMAC, "initialize_copy", ossl_hmac_copy, 1); rb_define_method(cHMAC, "reset", ossl_hmac_reset, 0); rb_define_method(cHMAC, "update", ossl_hmac_update, 1); diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c new file mode 100644 index 00000000..ee124718 --- /dev/null +++ b/ext/openssl/ossl_kdf.c @@ -0,0 +1,319 @@ +/* + * Ruby/OpenSSL Project + * Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors + */ +#include "ossl.h" +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) +# include <openssl/kdf.h> +#endif + +static VALUE mKDF, eKDF; + +/* + * call-seq: + * KDF.pbkdf2_hmac(pass, salt:, iterations:, length:, hash:) -> aString + * + * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in combination + * with HMAC. Takes _pass_, _salt_ and _iterations_, and then derives a key + * of _length_ bytes. + * + * For more information about PBKDF2, see RFC 2898 Section 5.2 + * (https://tools.ietf.org/html/rfc2898#section-5.2). + * + * === Parameters + * pass :: The passphrase. + * salt :: The salt. Salts prevent attacks based on dictionaries of common + * passwords and attacks based on rainbow tables. It is a public + * value that can be safely stored along with the password (e.g. + * if the derived value is used for password storage). + * iterations :: The iteration count. This provides the ability to tune the + * algorithm. It is better to use the highest count possible for + * the maximum resistance to brute-force attacks. + * length :: The desired length of the derived key in octets. + * hash :: The hash algorithm used with HMAC for the PRF. May be a String + * representing the algorithm name, or an instance of + * OpenSSL::Digest. + */ +static VALUE +kdf_pbkdf2_hmac(int argc, VALUE *argv, VALUE self) +{ + VALUE pass, salt, opts, kwargs[4], str; + static ID kwargs_ids[4]; + int iters, len; + const EVP_MD *md; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("iterations"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); + } + rb_scan_args(argc, argv, "1:", &pass, &opts); + rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); + + StringValue(pass); + salt = StringValue(kwargs[0]); + iters = NUM2INT(kwargs[1]); + len = NUM2INT(kwargs[2]); + md = ossl_evp_get_digestbyname(kwargs[3]); + + str = rb_str_new(0, len); + if (!PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass), + (unsigned char *)RSTRING_PTR(salt), + RSTRING_LENINT(salt), iters, md, len, + (unsigned char *)RSTRING_PTR(str))) + ossl_raise(eKDF, "PKCS5_PBKDF2_HMAC"); + + return str; +} + +#if defined(HAVE_EVP_PBE_SCRYPT) +/* + * call-seq: + * KDF.scrypt(pass, salt:, N:, r:, p:, length:) -> aString + * + * Derives a key from _pass_ using given parameters with the scrypt + * password-based key derivation function. The result can be used for password + * storage. + * + * scrypt is designed to be memory-hard and more secure against brute-force + * attacks using custom hardwares than alternative KDFs such as PBKDF2 or + * bcrypt. + * + * The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914 + * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states + * that using values r=8 and p=1 appears to yield good results. + * + * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information. + * + * === Parameters + * pass :: Passphrase. + * salt :: Salt. + * N :: CPU/memory cost parameter. This must be a power of 2. + * r :: Block size parameter. + * p :: Parallelization parameter. + * length :: Length in octets of the derived key. + * + * === Example + * pass = "password" + * salt = SecureRandom.random_bytes(16) + * dk = OpenSSL::KDF.scrypt(pass, salt: salt, N: 2**14, r: 8, p: 1, length: 32) + * p dk #=> "\xDA\xE4\xE2...\x7F\xA1\x01T" + */ +static VALUE +kdf_scrypt(int argc, VALUE *argv, VALUE self) +{ + VALUE pass, salt, opts, kwargs[5], str; + static ID kwargs_ids[5]; + size_t len; + uint64_t N, r, p, maxmem; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("N"); + kwargs_ids[2] = rb_intern_const("r"); + kwargs_ids[3] = rb_intern_const("p"); + kwargs_ids[4] = rb_intern_const("length"); + } + rb_scan_args(argc, argv, "1:", &pass, &opts); + rb_get_kwargs(opts, kwargs_ids, 5, 0, kwargs); + + StringValue(pass); + salt = StringValue(kwargs[0]); + N = NUM2UINT64T(kwargs[1]); + r = NUM2UINT64T(kwargs[2]); + p = NUM2UINT64T(kwargs[3]); + len = NUM2LONG(kwargs[4]); + /* + * OpenSSL uses 32MB by default (if zero is specified), which is too small. + * Let's not limit memory consumption but just let malloc() fail inside + * OpenSSL. The amount is controllable by other parameters. + */ + maxmem = SIZE_MAX; + + str = rb_str_new(0, len); + if (!EVP_PBE_scrypt(RSTRING_PTR(pass), RSTRING_LEN(pass), + (unsigned char *)RSTRING_PTR(salt), RSTRING_LEN(salt), + N, r, p, maxmem, (unsigned char *)RSTRING_PTR(str), len)) + ossl_raise(eKDF, "EVP_PBE_scrypt"); + + return str; +} +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) +/* + * call-seq: + * KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String + * + * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in + * {RFC 5869}[https://tools.ietf.org/html/rfc5869]. + * + * New in OpenSSL 1.1.0. + * + * === Parameters + * _ikm_:: + * The input keying material. + * _salt_:: + * The salt. + * _info_:: + * The context and application specific information. + * _length_:: + * The output length in octets. Must be <= <tt>255 * HashLen</tt>, where + * HashLen is the length of the hash function output in octets. + * _hash_:: + * The hash function. + */ +static VALUE +kdf_hkdf(int argc, VALUE *argv, VALUE self) +{ + VALUE ikm, salt, info, opts, kwargs[4], str; + static ID kwargs_ids[4]; + int saltlen, ikmlen, infolen; + size_t len; + const EVP_MD *md; + EVP_PKEY_CTX *pctx; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("info"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); + } + rb_scan_args(argc, argv, "1:", &ikm, &opts); + rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); + + StringValue(ikm); + ikmlen = RSTRING_LENINT(ikm); + salt = StringValue(kwargs[0]); + saltlen = RSTRING_LENINT(salt); + info = StringValue(kwargs[1]); + infolen = RSTRING_LENINT(info); + len = (size_t)NUM2LONG(kwargs[2]); + if (len > LONG_MAX) + rb_raise(rb_eArgError, "length must be non-negative"); + md = ossl_evp_get_digestbyname(kwargs[3]); + + str = rb_str_new(NULL, (long)len); + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!pctx) + ossl_raise(eKDF, "EVP_PKEY_CTX_new_id"); + if (EVP_PKEY_derive_init(pctx) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive_init"); + } + if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md"); + } + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt), + saltlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt"); + } + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm), + ikmlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key"); + } + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info), + infolen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info"); + } + if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive"); + } + rb_str_set_len(str, (long)len); + EVP_PKEY_CTX_free(pctx); + + return str; +} +#endif + +void +Init_ossl_kdf(void) +{ +#if 0 + mOSSL = rb_define_module("OpenSSL"); + eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); +#endif + + /* + * Document-module: OpenSSL::KDF + * + * Provides functionality of various KDFs (key derivation function). + * + * KDF is typically used for securely deriving arbitrary length symmetric + * keys to be used with an OpenSSL::Cipher from passwords. Another use case + * is for storing passwords: Due to the ability to tweak the effort of + * computation by increasing the iteration count, computation can be slowed + * down artificially in order to render possible attacks infeasible. + * + * Currently, OpenSSL::KDF provides implementations for the following KDF: + * + * * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in + * combination with HMAC + * * scrypt + * * HKDF + * + * == Examples + * === Generating a 128 bit key for a Cipher (e.g. AES) + * pass = "secret" + * salt = OpenSSL::Random.random_bytes(16) + * iter = 20_000 + * key_len = 16 + * key = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter, + * length: key_len, hash: "sha1") + * + * === Storing Passwords + * pass = "secret" + * # store this with the generated value + * salt = OpenSSL::Random.random_bytes(16) + * iter = 20_000 + * hash = OpenSSL::Digest::SHA256.new + * len = hash.digest_length + * # the final value to be stored + * value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter, + * length: len, hash: hash) + * + * == Important Note on Checking Passwords + * When comparing passwords provided by the user with previously stored + * values, a common mistake made is comparing the two values using "==". + * Typically, "==" short-circuits on evaluation, and is therefore + * vulnerable to timing attacks. The proper way is to use a method that + * always takes the same amount of time when comparing two values, thus + * not leaking any information to potential attackers. To compare two + * values, the following could be used: + * + * def eql_time_cmp(a, b) + * unless a.length == b.length + * return false + * end + * cmp = b.bytes + * result = 0 + * a.bytes.each_with_index {|c,i| + * result |= c ^ cmp[i] + * } + * result == 0 + * end + * + * Please note that the premature return in case of differing lengths + * typically does not leak valuable information - when using PBKDF2, the + * length of the values to be compared is of fixed size. + */ + mKDF = rb_define_module_under(mOSSL, "KDF"); + /* + * Generic exception class raised if an error occurs in OpenSSL::KDF module. + */ + eKDF = rb_define_class_under(mKDF, "KDFError", eOSSLError); + + rb_define_module_function(mKDF, "pbkdf2_hmac", kdf_pbkdf2_hmac, -1); +#if defined(HAVE_EVP_PBE_SCRYPT) + rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1); +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) + rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1); +#endif +} diff --git a/ext/openssl/ossl_kdf.h b/ext/openssl/ossl_kdf.h new file mode 100644 index 00000000..b6503f8d --- /dev/null +++ b/ext/openssl/ossl_kdf.h @@ -0,0 +1,6 @@ +#if !defined(OSSL_KDF_H) +#define OSSL_KDF_H + +void Init_ossl_kdf(void); + +#endif diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index 5ac8ef55..6f61e61b 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -73,7 +73,7 @@ ossl_spki_alloc(VALUE klass) * SPKI.new([request]) => spki * * === Parameters - * * +request+ - optional raw request, either in PEM or DER format. + * * _request_ - optional raw request, either in PEM or DER format. */ static VALUE ossl_spki_initialize(int argc, VALUE *argv, VALUE self) @@ -198,7 +198,7 @@ ossl_spki_get_public_key(VALUE self) * spki.public_key = pub => pkey * * === Parameters - * * +pub+ - the public key to be set for this instance + * * _pub_ - the public key to be set for this instance * * Sets the public key to be associated with the SPKI, an instance of * OpenSSL::PKey. This should be the public key corresponding to the @@ -244,7 +244,7 @@ ossl_spki_get_challenge(VALUE self) * spki.challenge = str => string * * === Parameters - * * +str+ - the challenge string to be set for this instance + * * _str_ - the challenge string to be set for this instance * * Sets the challenge to be associated with the SPKI. May be used by the * server, e.g. to prevent replay. @@ -269,8 +269,8 @@ ossl_spki_set_challenge(VALUE self, VALUE str) * spki.sign(key, digest) => spki * * === Parameters - * * +key+ - the private key to be used for signing this instance - * * +digest+ - the digest to be used for signing this instance + * * _key_ - the private key to be used for signing this instance + * * _digest_ - the digest to be used for signing this instance * * To sign an SPKI, the private key corresponding to the public key set * for this instance should be used, in addition to a digest algorithm in @@ -285,7 +285,7 @@ ossl_spki_sign(VALUE self, VALUE key, VALUE digest) const EVP_MD *md; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); GetSPKI(self, spki); if (!NETSCAPE_SPKI_sign(spki, pkey, md)) { ossl_raise(eSPKIError, NULL); @@ -299,7 +299,7 @@ ossl_spki_sign(VALUE self, VALUE key, VALUE digest) * spki.verify(key) => boolean * * === Parameters - * * +key+ - the public key to be used for verifying the SPKI signature + * * _key_ - the public key to be used for verifying the SPKI signature * * Returns +true+ if the signature is valid, +false+ otherwise. To verify an * SPKI, the public key contained within the SPKI should be used. diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index a8b3503d..c0237791 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -22,10 +22,6 @@ TypedData_Get_Struct((obj), OCSP_REQUEST, &ossl_ocsp_request_type, (req)); \ if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ } while (0) -#define SafeGetOCSPReq(obj, req) do { \ - OSSL_Check_Kind((obj), cOCSPReq); \ - GetOCSPReq((obj), (req)); \ -} while (0) #define NewOCSPRes(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_response_type, 0) @@ -37,10 +33,6 @@ TypedData_Get_Struct((obj), OCSP_RESPONSE, &ossl_ocsp_response_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ } while (0) -#define SafeGetOCSPRes(obj, res) do { \ - OSSL_Check_Kind((obj), cOCSPRes); \ - GetOCSPRes((obj), (res)); \ -} while (0) #define NewOCSPBasicRes(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_basicresp_type, 0) @@ -52,10 +44,6 @@ TypedData_Get_Struct((obj), OCSP_BASICRESP, &ossl_ocsp_basicresp_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ } while (0) -#define SafeGetOCSPBasicRes(obj, res) do { \ - OSSL_Check_Kind((obj), cOCSPBasicRes); \ - GetOCSPBasicRes((obj), (res)); \ -} while (0) #define NewOCSPSingleRes(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_singleresp_type, 0) @@ -67,10 +55,6 @@ TypedData_Get_Struct((obj), OCSP_SINGLERESP, &ossl_ocsp_singleresp_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "SingleResponse wasn't initialized!"); \ } while (0) -#define SafeGetOCSPSingleRes(obj, res) do { \ - OSSL_Check_Kind((obj), cOCSPSingleRes); \ - GetOCSPSingleRes((obj), (res)); \ -} while (0) #define NewOCSPCertId(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_certid_type, 0) @@ -82,10 +66,6 @@ TypedData_Get_Struct((obj), OCSP_CERTID, &ossl_ocsp_certid_type, (cid)); \ if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ } while (0) -#define SafeGetOCSPCertId(obj, cid) do { \ - OSSL_Check_Kind((obj), cOCSPCertId); \ - GetOCSPCertId((obj), (cid)); \ -} while (0) VALUE mOCSP; VALUE eOCSPError; @@ -200,7 +180,7 @@ ossl_ocspreq_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetOCSPReq(self, req_old); - SafeGetOCSPReq(other, req); + GetOCSPReq(other, req); req_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_REQUEST), req); if (!req_new) @@ -218,7 +198,7 @@ ossl_ocspreq_initialize_copy(VALUE self, VALUE other) * OpenSSL::OCSP::Request.new(request_der) -> request * * Creates a new OpenSSL::OCSP::Request. The request may be created empty or - * from a +request_der+ string. + * from a _request_der_ string. */ static VALUE @@ -248,7 +228,7 @@ ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) * call-seq: * request.add_nonce(nonce = nil) -> request * - * Adds a +nonce+ to the OCSP request. If no nonce is given a random one will + * Adds a _nonce_ to the OCSP request. If no nonce is given a random one will * be generated. * * The nonce is used to prevent replay attacks but some servers do not support @@ -281,7 +261,7 @@ ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) * call-seq: * request.check_nonce(response) -> result * - * Checks the nonce validity for this request and +response+. + * Checks the nonce validity for this request and _response_. * * The return value is one of the following: * @@ -291,7 +271,7 @@ ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) * 2 :: nonces both absent. * 3 :: nonce present in response only. * - * For most responses, clients can check +result+ > 0. If a responder doesn't + * For most responses, clients can check _result_ > 0. If a responder doesn't * handle nonces <code>result.nonzero?</code> may be necessary. A result of * <code>0</code> is always an error. */ @@ -304,7 +284,7 @@ ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) int res; GetOCSPReq(self, req); - SafeGetOCSPBasicRes(basic_resp, bs); + GetOCSPBasicRes(basic_resp, bs); res = OCSP_check_nonce(req, bs); return INT2NUM(res); @@ -314,7 +294,7 @@ ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) * call-seq: * request.add_certid(certificate_id) -> request * - * Adds +certificate_id+ to the request. + * Adds _certificate_id_ to the request. */ static VALUE @@ -371,17 +351,17 @@ ossl_ocspreq_get_certid(VALUE self) * call-seq: * request.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self * - * Signs this OCSP request using +cert+, +key+ and optional +digest+. If - * +digest+ is not specified, SHA-1 is used. +certs+ is an optional Array of + * Signs this OCSP request using _cert_, _key_ and optional _digest_. If + * _digest_ is not specified, SHA-1 is used. _certs_ is an optional Array of * additional certificates which are included in the request in addition to - * the signer certificate. Note that if +certs+ is nil or not given, flag + * the signer certificate. Note that if _certs_ is +nil+ or not given, flag * OpenSSL::OCSP::NOCERTS is enabled. Pass an empty array to include only the * signer certificate. * - * +flags+ can be a bitwise OR of the following constants: + * _flags_ is a bitwise OR of the following constants: * * OpenSSL::OCSP::NOCERTS:: - * Don't include any certificates in the request. +certs+ will be ignored. + * Don't include any certificates in the request. _certs_ will be ignored. */ static VALUE ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) @@ -404,7 +384,7 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) if (NIL_P(digest)) md = EVP_sha1(); else - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); if (NIL_P(certs)) flg |= OCSP_NOCERTS; else @@ -421,9 +401,12 @@ ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) * call-seq: * request.verify(certificates, store, flags = 0) -> true or false * - * Verifies this request using the given +certificates+ and +store+. - * +certificates+ is an array of OpenSSL::X509::Certificate, +store+ is an + * Verifies this request using the given _certificates_ and _store_. + * _certificates_ is an array of OpenSSL::X509::Certificate, _store_ is an * OpenSSL::X509::Store. + * + * Note that +false+ is returned if the request does not have a signature. + * Use #signed? to check whether the request is signed or not. */ static VALUE @@ -473,13 +456,29 @@ ossl_ocspreq_to_der(VALUE self) } /* + * call-seq: + * request.signed? -> true or false + * + * Returns +true+ if the request is signed, +false+ otherwise. Note that the + * validity of the signature is *not* checked. Use #verify to verify that. + */ +static VALUE +ossl_ocspreq_signed_p(VALUE self) +{ + OCSP_REQUEST *req; + + GetOCSPReq(self, req); + return OCSP_request_is_signed(req) ? Qtrue : Qfalse; +} + +/* * OCSP::Response */ /* call-seq: * OpenSSL::OCSP::Response.create(status, basic_response = nil) -> response * - * Creates an OpenSSL::OCSP::Response from +status+ and +basic_response+. + * Creates an OpenSSL::OCSP::Response from _status_ and _basic_response_. */ static VALUE @@ -521,7 +520,7 @@ ossl_ocspres_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetOCSPRes(self, res_old); - SafeGetOCSPRes(other, res); + GetOCSPRes(other, res); res_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_RESPONSE), res); if (!res_new) @@ -539,7 +538,7 @@ ossl_ocspres_initialize_copy(VALUE self, VALUE other) * OpenSSL::OCSP::Response.new(response_der) -> response * * Creates a new OpenSSL::OCSP::Response. The response may be created empty or - * from a +response_der+ string. + * from a _response_der_ string. */ static VALUE @@ -677,7 +676,7 @@ ossl_ocspbres_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetOCSPBasicRes(self, bs_old); - SafeGetOCSPBasicRes(other, bs); + GetOCSPBasicRes(other, bs); bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs); if (!bs_new) @@ -693,7 +692,7 @@ ossl_ocspbres_initialize_copy(VALUE self, VALUE other) * call-seq: * OpenSSL::OCSP::BasicResponse.new(der_string = nil) -> basic_response * - * Creates a new BasicResponse. If +der_string+ is given, decodes +der_string+ + * Creates a new BasicResponse. If _der_string_ is given, decodes _der_string_ * as DER. */ @@ -724,7 +723,7 @@ ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) * call-seq: * basic_response.copy_nonce(request) -> Integer * - * Copies the nonce from +request+ into this response. Returns 1 on success + * Copies the nonce from _request_ into this response. Returns 1 on success * and 0 on failure. */ @@ -736,7 +735,7 @@ ossl_ocspbres_copy_nonce(VALUE self, VALUE request) int ret; GetOCSPBasicRes(self, bs); - SafeGetOCSPReq(request, req); + GetOCSPReq(request, req); ret = OCSP_copy_nonce(bs, req); return INT2NUM(ret); @@ -746,7 +745,7 @@ ossl_ocspbres_copy_nonce(VALUE self, VALUE request) * call-seq: * basic_response.add_nonce(nonce = nil) * - * Adds +nonce+ to this response. If no nonce was provided a random nonce + * Adds _nonce_ to this response. If no nonce was provided a random nonce * will be added. */ @@ -792,26 +791,26 @@ add_status_convert_time(VALUE obj) * call-seq: * basic_response.add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) -> basic_response * - * Adds a certificate status for +certificate_id+. +status+ is the status, and + * Adds a certificate status for _certificate_id_. _status_ is the status, and * must be one of these: * * - OpenSSL::OCSP::V_CERTSTATUS_GOOD * - OpenSSL::OCSP::V_CERTSTATUS_REVOKED * - OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN * - * +reason+ and +revocation_time+ can be given only when +status+ is - * OpenSSL::OCSP::V_CERTSTATUS_REVOKED. +reason+ describes the reason for the + * _reason_ and _revocation_time_ can be given only when _status_ is + * OpenSSL::OCSP::V_CERTSTATUS_REVOKED. _reason_ describes the reason for the * revocation, and must be one of OpenSSL::OCSP::REVOKED_STATUS_* constants. - * +revocation_time+ is the time when the certificate is revoked. + * _revocation_time_ is the time when the certificate is revoked. * - * +this_update+ and +next_update+ indicate the time at which ths status is + * _this_update_ and _next_update_ indicate the time at which ths status is * verified to be correct and the time at or before which newer information - * will be available, respectively. +next_update+ is optional. + * will be available, respectively. _next_update_ is optional. * - * +extensions+ is an Array of OpenSSL::X509::Extension to be included in the + * _extensions_ is an Array of OpenSSL::X509::Extension to be included in the * SingleResponse. This is also optional. * - * Note that the times, +revocation_time+, +this_update+ and +next_update+ + * Note that the times, _revocation_time_, _this_update_ and _next_update_ * can be specified in either of Integer or Time object. If they are Integer, it * is treated as the relative seconds from the current time. */ @@ -829,7 +828,7 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, VALUE tmp; GetOCSPBasicRes(self, bs); - SafeGetOCSPCertId(cid, id); + GetOCSPCertId(cid, id); st = NUM2INT(status); if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */ ext = rb_check_array_type(ext); @@ -888,7 +887,7 @@ ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, * Returns an Array of statuses for this response. Each status contains a * CertificateId, the status (0 for good, 1 for revoked, 2 for unknown), the * reason for the status, the revocation time, the time of this update, the time - * for the next update and a list of OpenSSL::X509::Extensions. + * for the next update and a list of OpenSSL::X509::Extension. * * This should be superseded by BasicResponse#responses and #find_response that * return SingleResponse. @@ -977,7 +976,7 @@ ossl_ocspbres_get_responses(VALUE self) * call-seq: * basic_response.find_response(certificate_id) -> SingleResponse | nil * - * Returns a SingleResponse whose CertId matches with +certificate_id+, or nil + * Returns a SingleResponse whose CertId matches with _certificate_id_, or +nil+ * if this BasicResponse does not contain it. */ static VALUE @@ -988,7 +987,7 @@ ossl_ocspbres_find_response(VALUE self, VALUE target) OCSP_CERTID *id; int n; - SafeGetOCSPCertId(target, id); + GetOCSPCertId(target, id); GetOCSPBasicRes(self, bs); if ((n = OCSP_resp_find(bs, id, -1)) == -1) @@ -1006,10 +1005,10 @@ ossl_ocspbres_find_response(VALUE self, VALUE target) * call-seq: * basic_response.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self * - * Signs this OCSP response using the +cert+, +key+ and optional +digest+. This + * Signs this OCSP response using the _cert_, _key_ and optional _digest_. This * behaves in the similar way as OpenSSL::OCSP::Request#sign. * - * +flags+ can include: + * _flags_ can include: * OpenSSL::OCSP::NOCERTS:: don't include certificates * OpenSSL::OCSP::NOTIME:: don't set producedAt * OpenSSL::OCSP::RESPID_KEY:: use signer's public key hash as responderID @@ -1036,7 +1035,7 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) if (NIL_P(digest)) md = EVP_sha1(); else - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); if (NIL_P(certs)) flg |= OCSP_NOCERTS; else @@ -1053,8 +1052,8 @@ ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) * call-seq: * basic_response.verify(certificates, store, flags = 0) -> true or false * - * Verifies the signature of the response using the given +certificates+ and - * +store+. This works in the similar way as OpenSSL::OCSP::Request#verify. + * Verifies the signature of the response using the given _certificates_ and + * _store_. This works in the similar way as OpenSSL::OCSP::Request#verify. */ static VALUE ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) @@ -1184,7 +1183,7 @@ ossl_ocspsres_alloc(VALUE klass) * call-seq: * OpenSSL::OCSP::SingleResponse.new(der_string) -> SingleResponse * - * Creates a new SingleResponse from +der_string+. + * Creates a new SingleResponse from _der_string_. */ static VALUE ossl_ocspsres_initialize(VALUE self, VALUE arg) @@ -1213,7 +1212,7 @@ ossl_ocspsres_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetOCSPSingleRes(self, sres_old); - SafeGetOCSPSingleRes(other, sres); + GetOCSPSingleRes(other, sres); sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); if (!sres_new) @@ -1235,10 +1234,10 @@ ossl_ocspsres_initialize_copy(VALUE self, VALUE other) * * It is possible that the OCSP request takes a few seconds or the time is not * accurate. To avoid rejecting a valid response, this method allows the times - * to be within +nsec+ of the current time. + * to be within _nsec_ seconds of the current time. * * Some responders don't set the nextUpdate field. This may cause a very old - * response to be considered valid. The +maxsec+ parameter can be used to limit + * response to be considered valid. The _maxsec_ parameter can be used to limit * the age of responses. */ static VALUE @@ -1329,8 +1328,10 @@ ossl_ocspsres_get_this_update(VALUE self) status = OCSP_single_get0_status(sres, NULL, NULL, &time, NULL); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); + if (!time) + return Qnil; - return asn1time_to_time(time); /* will handle NULL */ + return asn1time_to_time(time); } /* @@ -1348,6 +1349,8 @@ ossl_ocspsres_get_next_update(VALUE self) status = OCSP_single_get0_status(sres, NULL, NULL, NULL, &time); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); + if (!time) + return Qnil; return asn1time_to_time(time); } @@ -1369,6 +1372,8 @@ ossl_ocspsres_get_revocation_time(VALUE self) ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) ossl_raise(eOCSPError, "certificate is not revoked"); + if (!time) + return Qnil; return asn1time_to_time(time); } @@ -1468,7 +1473,7 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetOCSPCertId(self, cid_old); - SafeGetOCSPCertId(other, cid); + GetOCSPCertId(other, cid); cid_new = OCSP_CERTID_dup(cid); if (!cid_new) @@ -1485,14 +1490,13 @@ ossl_ocspcid_initialize_copy(VALUE self, VALUE other) * OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id * OpenSSL::OCSP::CertificateId.new(der_string) -> certificate_id * - * Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and - * +issuer+ X509 certificates. The +digest+ is used to compute the - * certificate ID and must be an OpenSSL::Digest instance. + * Creates a new OpenSSL::OCSP::CertificateId for the given _subject_ and + * _issuer_ X509 certificates. The _digest_ is a digest algorithm that is used + * to compute the hash values. This defaults to SHA-1. * * If only one argument is given, decodes it as DER representation of a * certificate ID. */ - static VALUE ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) { @@ -1517,7 +1521,7 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ - md = !NIL_P(digest) ? GetDigestPtr(digest) : NULL; + md = !NIL_P(digest) ? ossl_evp_get_digestbyname(digest) : NULL; newid = OCSP_cert_to_id(md, x509s, x509i); if (!newid) @@ -1534,7 +1538,7 @@ ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) * call-seq: * certificate_id.cmp(other) -> true or false * - * Compares this certificate id with +other+ and returns true if they are the + * Compares this certificate id with _other_ and returns +true+ if they are the * same. */ static VALUE @@ -1544,7 +1548,7 @@ ossl_ocspcid_cmp(VALUE self, VALUE other) int result; GetOCSPCertId(self, id); - SafeGetOCSPCertId(other, id2); + GetOCSPCertId(other, id2); result = OCSP_id_cmp(id, id2); return (result == 0) ? Qtrue : Qfalse; @@ -1554,7 +1558,7 @@ ossl_ocspcid_cmp(VALUE self, VALUE other) * call-seq: * certificate_id.cmp_issuer(other) -> true or false * - * Compares this certificate id's issuer with +other+ and returns true if + * Compares this certificate id's issuer with _other_ and returns +true+ if * they are the same. */ @@ -1565,7 +1569,7 @@ ossl_ocspcid_cmp_issuer(VALUE self, VALUE other) int result; GetOCSPCertId(self, id); - SafeGetOCSPCertId(other, id2); + GetOCSPCertId(other, id2); result = OCSP_id_issuer_cmp(id, id2); return (result == 0) ? Qtrue : Qfalse; @@ -1824,12 +1828,13 @@ Init_ossl_ocsp(void) cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject); rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc); - rb_define_copy_func(cOCSPReq, ossl_ocspreq_initialize_copy); + rb_define_method(cOCSPReq, "initialize_copy", ossl_ocspreq_initialize_copy, 1); rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1); rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1); rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1); rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1); rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0); + rb_define_method(cOCSPReq, "signed?", ossl_ocspreq_signed_p, 0); rb_define_method(cOCSPReq, "sign", ossl_ocspreq_sign, -1); rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1); rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0); @@ -1842,7 +1847,7 @@ Init_ossl_ocsp(void) cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject); rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2); rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc); - rb_define_copy_func(cOCSPRes, ossl_ocspres_initialize_copy); + rb_define_method(cOCSPRes, "initialize_copy", ossl_ocspres_initialize_copy, 1); rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1); rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0); rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0); @@ -1857,7 +1862,7 @@ Init_ossl_ocsp(void) cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject); rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc); - rb_define_copy_func(cOCSPBasicRes, ossl_ocspbres_initialize_copy); + rb_define_method(cOCSPBasicRes, "initialize_copy", ossl_ocspbres_initialize_copy, 1); rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1); rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1); rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1); @@ -1876,7 +1881,7 @@ Init_ossl_ocsp(void) */ cOCSPSingleRes = rb_define_class_under(mOCSP, "SingleResponse", rb_cObject); rb_define_alloc_func(cOCSPSingleRes, ossl_ocspsres_alloc); - rb_define_copy_func(cOCSPSingleRes, ossl_ocspsres_initialize_copy); + rb_define_method(cOCSPSingleRes, "initialize_copy", ossl_ocspsres_initialize_copy, 1); rb_define_method(cOCSPSingleRes, "initialize", ossl_ocspsres_initialize, 1); rb_define_method(cOCSPSingleRes, "check_validity", ossl_ocspsres_check_validity, -1); rb_define_method(cOCSPSingleRes, "certid", ossl_ocspsres_get_certid, 0); @@ -1895,7 +1900,7 @@ Init_ossl_ocsp(void) cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject); rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc); - rb_define_copy_func(cOCSPCertId, ossl_ocspcid_initialize_copy); + rb_define_method(cOCSPCertId, "initialize_copy", ossl_ocspcid_initialize_copy, 1); rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1); rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1); rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1); diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c index 8502a6de..ddb7d939 100644 --- a/ext/openssl/ossl_pkcs12.c +++ b/ext/openssl/ossl_pkcs12.c @@ -17,11 +17,6 @@ if(!(p12)) ossl_raise(rb_eRuntimeError, "PKCS12 wasn't initialized."); \ } while (0) -#define SafeGetPKCS12(obj, p12) do { \ - OSSL_Check_Kind((obj), cPKCS12); \ - GetPKCS12((obj), (p12)); \ -} while (0) - #define ossl_pkcs12_set_key(o,v) rb_iv_set((o), "@key", (v)) #define ossl_pkcs12_set_cert(o,v) rb_iv_set((o), "@certificate", (v)) #define ossl_pkcs12_set_ca_certs(o,v) rb_iv_set((o), "@ca_certs", (v)) @@ -72,7 +67,7 @@ ossl_pkcs12_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetPKCS12(self, p12_old); - SafeGetPKCS12(other, p12); + GetPKCS12(other, p12); p12_new = ASN1_dup((i2d_of_void *)i2d_PKCS12, (d2i_of_void *)d2i_PKCS12, (char *)p12); if (!p12_new) @@ -89,20 +84,20 @@ ossl_pkcs12_initialize_copy(VALUE self, VALUE other) * PKCS12.create(pass, name, key, cert [, ca, [, key_pbe [, cert_pbe [, key_iter [, mac_iter [, keytype]]]]]]) * * === Parameters - * * +pass+ - string - * * +name+ - A string describing the key. - * * +key+ - Any PKey. - * * +cert+ - A X509::Certificate. + * * _pass_ - string + * * _name_ - A string describing the key. + * * _key_ - Any PKey. + * * _cert_ - A X509::Certificate. * * The public_key portion of the certificate must contain a valid public key. * * The not_before and not_after fields must be filled in. - * * +ca+ - An optional array of X509::Certificate's. - * * +key_pbe+ - string - * * +cert_pbe+ - string - * * +key_iter+ - integer - * * +mac_iter+ - integer - * * +keytype+ - An integer representing an MSIE specific extension. + * * _ca_ - An optional array of X509::Certificate's. + * * _key_pbe_ - string + * * _cert_pbe_ - string + * * _key_iter_ - integer + * * _mac_iter_ - integer + * * _keytype_ - An integer representing an MSIE specific extension. * - * Any optional arguments may be supplied as nil to preserve the OpenSSL defaults. + * Any optional arguments may be supplied as +nil+ to preserve the OpenSSL defaults. * * See the OpenSSL documentation for PKCS12_create(). */ @@ -161,8 +156,8 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) * PKCS12.new(str, pass) -> pkcs12 * * === Parameters - * * +str+ - Must be a DER encoded PKCS12 string. - * * +pass+ - string + * * _str_ - Must be a DER encoded PKCS12 string. + * * _pass_ - string */ static VALUE ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self) @@ -252,7 +247,7 @@ Init_ossl_pkcs12(void) rb_define_singleton_method(cPKCS12, "create", ossl_pkcs12_s_create, -1); rb_define_alloc_func(cPKCS12, ossl_pkcs12_s_allocate); - rb_define_copy_func(cPKCS12, ossl_pkcs12_initialize_copy); + rb_define_method(cPKCS12, "initialize_copy", ossl_pkcs12_initialize_copy, 1); rb_attr(cPKCS12, rb_intern("key"), 1, 0, Qfalse); rb_attr(cPKCS12, rb_intern("certificate"), 1, 0, Qfalse); rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse); diff --git a/ext/openssl/ossl_pkcs5.c b/ext/openssl/ossl_pkcs5.c deleted file mode 100644 index 47c5bfa3..00000000 --- a/ext/openssl/ossl_pkcs5.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2007 Technorama Ltd. <oss-ruby@technorama.net> - */ -#include "ossl.h" - -VALUE mPKCS5; -VALUE ePKCS5; - -#ifdef HAVE_PKCS5_PBKDF2_HMAC -/* - * call-seq: - * PKCS5.pbkdf2_hmac(pass, salt, iter, keylen, digest) => string - * - * === Parameters - * * +pass+ - string - * * +salt+ - string - should be at least 8 bytes long. - * * +iter+ - integer - should be greater than 1000. 20000 is better. - * * +keylen+ - integer - * * +digest+ - a string or OpenSSL::Digest object. - * - * Available in OpenSSL >= 1.0.0. - * - * Digests other than SHA1 may not be supported by other cryptography libraries. - */ -static VALUE -ossl_pkcs5_pbkdf2_hmac(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen, VALUE digest) -{ - VALUE str; - const EVP_MD *md; - int len = NUM2INT(keylen); - - StringValue(pass); - StringValue(salt); - md = GetDigestPtr(digest); - - str = rb_str_new(0, len); - - if (PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass), - (unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), - NUM2INT(iter), md, len, - (unsigned char *)RSTRING_PTR(str)) != 1) - ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC"); - - return str; -} -#else -#define ossl_pkcs5_pbkdf2_hmac rb_f_notimplement -#endif - - -/* - * call-seq: - * PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, keylen) => string - * - * === Parameters - * * +pass+ - string - * * +salt+ - string - should be at least 8 bytes long. - * * +iter+ - integer - should be greater than 1000. 20000 is better. - * * +keylen+ - integer - * - * This method is available in almost any version of OpenSSL. - * - * Conforms to RFC 2898. - */ -static VALUE -ossl_pkcs5_pbkdf2_hmac_sha1(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen) -{ - VALUE str; - int len = NUM2INT(keylen); - - StringValue(pass); - StringValue(salt); - - str = rb_str_new(0, len); - - if (PKCS5_PBKDF2_HMAC_SHA1(RSTRING_PTR(pass), RSTRING_LENINT(pass), - (const unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), NUM2INT(iter), - len, (unsigned char *)RSTRING_PTR(str)) != 1) - ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC_SHA1"); - - return str; -} - -void -Init_ossl_pkcs5(void) -{ -#if 0 - mOSSL = rb_define_module("OpenSSL"); - eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); -#endif - - /* Document-class: OpenSSL::PKCS5 - * - * Provides password-based encryption functionality based on PKCS#5. - * Typically used for securely deriving arbitrary length symmetric keys - * to be used with an OpenSSL::Cipher from passwords. Another use case - * is for storing passwords: Due to the ability to tweak the effort of - * computation by increasing the iteration count, computation can be - * slowed down artificially in order to render possible attacks infeasible. - * - * PKCS5 offers support for PBKDF2 with an OpenSSL::Digest::SHA1-based - * HMAC, or an arbitrary Digest if the underlying version of OpenSSL - * already supports it (>= 1.0.0). - * - * === Parameters - * ==== Password - * Typically an arbitrary String that represents the password to be used - * for deriving a key. - * ==== Salt - * Prevents attacks based on dictionaries of common passwords. It is a - * public value that can be safely stored along with the password (e.g. - * if PBKDF2 is used for password storage). For maximum security, a fresh, - * random salt should be generated for each stored password. According - * to PKCS#5, a salt should be at least 8 bytes long. - * ==== Iteration Count - * Allows to tweak the length that the actual computation will take. The - * larger the iteration count, the longer it will take. - * ==== Key Length - * Specifies the length in bytes of the output that will be generated. - * Typically, the key length should be larger than or equal to the output - * length of the underlying digest function, otherwise an attacker could - * simply try to brute-force the key. According to PKCS#5, security is - * limited by the output length of the underlying digest function, i.e. - * security is not improved if a key length strictly larger than the - * digest output length is chosen. Therefore, when using PKCS5 for - * password storage, it suffices to store values equal to the digest - * output length, nothing is gained by storing larger values. - * - * == Examples - * === Generating a 128 bit key for a Cipher (e.g. AES) - * pass = "secret" - * salt = OpenSSL::Random.random_bytes(16) - * iter = 20000 - * key_len = 16 - * key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len) - * - * === Storing Passwords - * pass = "secret" - * salt = OpenSSL::Random.random_bytes(16) #store this with the generated value - * iter = 20000 - * digest = OpenSSL::Digest::SHA256.new - * len = digest.digest_length - * #the final value to be stored - * value = OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter, len, digest) - * - * === Important Note on Checking Passwords - * When comparing passwords provided by the user with previously stored - * values, a common mistake made is comparing the two values using "==". - * Typically, "==" short-circuits on evaluation, and is therefore - * vulnerable to timing attacks. The proper way is to use a method that - * always takes the same amount of time when comparing two values, thus - * not leaking any information to potential attackers. To compare two - * values, the following could be used: - * def eql_time_cmp(a, b) - * unless a.length == b.length - * return false - * end - * cmp = b.bytes.to_a - * result = 0 - * a.bytes.each_with_index {|c,i| - * result |= c ^ cmp[i] - * } - * result == 0 - * end - * Please note that the premature return in case of differing lengths - * typically does not leak valuable information - when using PKCS#5, the - * length of the values to be compared is of fixed size. - */ - - mPKCS5 = rb_define_module_under(mOSSL, "PKCS5"); - /* Document-class: OpenSSL::PKCS5::PKCS5Error - * - * Generic Exception class that is raised if an error occurs during a - * computation. - */ - ePKCS5 = rb_define_class_under(mPKCS5, "PKCS5Error", eOSSLError); - - rb_define_module_function(mPKCS5, "pbkdf2_hmac", ossl_pkcs5_pbkdf2_hmac, 5); - rb_define_module_function(mPKCS5, "pbkdf2_hmac_sha1", ossl_pkcs5_pbkdf2_hmac_sha1, 4); -} diff --git a/ext/openssl/ossl_pkcs5.h b/ext/openssl/ossl_pkcs5.h deleted file mode 100644 index a3b132bc..00000000 --- a/ext/openssl/ossl_pkcs5.h +++ /dev/null @@ -1,6 +0,0 @@ -#if !defined(_OSSL_PKCS5_H_) -#define _OSSL_PKCS5_H_ - -void Init_ossl_pkcs5(void); - -#endif /* _OSSL_PKCS5_H_ */ diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 40cc5f23..6395fa6f 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ } \ } while (0) -#define SafeGetPKCS7(obj, pkcs7) do { \ - OSSL_Check_Kind((obj), cPKCS7); \ - GetPKCS7((obj), (pkcs7)); \ -} while (0) #define NewPKCS7si(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs7_signer_info_type, 0) @@ -42,10 +38,6 @@ ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ } while (0) -#define SafeGetPKCS7si(obj, p7si) do { \ - OSSL_Check_Kind((obj), cPKCS7Signer); \ - GetPKCS7si((obj), (p7si)); \ -} while (0) #define NewPKCS7ri(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs7_recip_info_type, 0) @@ -61,10 +53,6 @@ ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ } while (0) -#define SafeGetPKCS7ri(obj, p7ri) do { \ - OSSL_Check_Kind((obj), cPKCS7Recipient); \ - GetPKCS7ri((obj), (p7ri)); \ -} while (0) #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) @@ -162,7 +150,7 @@ DupPKCS7SignerPtr(VALUE obj) { PKCS7_SIGNER_INFO *p7si, *pkcs7; - SafeGetPKCS7si(obj, p7si); + GetPKCS7si(obj, p7si); if (!(pkcs7 = ossl_PKCS7_SIGNER_INFO_dup(p7si))) { ossl_raise(ePKCS7Error, NULL); } @@ -189,7 +177,7 @@ DupPKCS7RecipientPtr(VALUE obj) { PKCS7_RECIP_INFO *p7ri, *pkcs7; - SafeGetPKCS7ri(obj, p7ri); + GetPKCS7ri(obj, p7ri); if (!(pkcs7 = ossl_PKCS7_RECIP_INFO_dup(p7ri))) { ossl_raise(ePKCS7Error, NULL); } @@ -238,7 +226,7 @@ ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass) rb_scan_args(argc, argv, "12", &pkcs7, &data, &flags); flg = NIL_P(flags) ? 0 : NUM2INT(flags); if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7); - SafeGetPKCS7(pkcs7, p7); + GetPKCS7(pkcs7, p7); if(!NIL_P(data) && PKCS7_is_detached(p7)) flg |= PKCS7_DETACHED; in = NIL_P(data) ? NULL : ossl_obj2bio(&data); @@ -331,7 +319,7 @@ ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass) #endif } - else ciph = GetCipherPtr(cipher); /* NO NEED TO DUP */ + else ciph = ossl_evp_get_cipherbyname(cipher); flg = NIL_P(flags) ? 0 : NUM2INT(flags); ret = NewPKCS7(cPKCS7); in = ossl_obj2bio(&data); @@ -414,7 +402,7 @@ ossl_pkcs7_copy(VALUE self, VALUE other) if (self == other) return self; GetPKCS7(self, a); - SafeGetPKCS7(other, b); + GetPKCS7(other, b); pkcs7 = PKCS7_dup(b); if (!pkcs7) { @@ -537,7 +525,7 @@ ossl_pkcs7_set_cipher(VALUE self, VALUE cipher) PKCS7 *pkcs7; GetPKCS7(self, pkcs7); - if (!PKCS7_set_cipher(pkcs7, GetCipherPtr(cipher))) { + if (!PKCS7_set_cipher(pkcs7, ossl_evp_get_cipherbyname(cipher))) { ossl_raise(ePKCS7Error, NULL); } @@ -933,7 +921,7 @@ ossl_pkcs7si_initialize(VALUE self, VALUE cert, VALUE key, VALUE digest) pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); GetPKCS7si(self, p7si); if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, (EVP_MD*)md))) { ossl_raise(ePKCS7Error, NULL); @@ -1068,7 +1056,7 @@ Init_ossl_pkcs7(void) rb_attr(cPKCS7, rb_intern("data"), 1, 0, Qfalse); rb_attr(cPKCS7, rb_intern("error_string"), 1, 1, Qfalse); rb_define_alloc_func(cPKCS7, ossl_pkcs7_alloc); - rb_define_copy_func(cPKCS7, ossl_pkcs7_copy); + rb_define_method(cPKCS7, "initialize_copy", ossl_pkcs7_copy, 1); rb_define_method(cPKCS7, "initialize", ossl_pkcs7_initialize, -1); rb_define_method(cPKCS7, "type=", ossl_pkcs7_set_type, 1); rb_define_method(cPKCS7, "type", ossl_pkcs7_get_type, 0); diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index aad3e2e4..2b96ece5 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -92,7 +92,7 @@ pkey_new0(EVP_PKEY *pkey) case EVP_PKEY_DH: return ossl_dh_new(pkey); #endif -#if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) +#if !defined(OPENSSL_NO_EC) case EVP_PKEY_EC: return ossl_ec_new(pkey); #endif @@ -123,15 +123,15 @@ ossl_pkey_new(EVP_PKEY *pkey) * OpenSSL::PKey.read(string [, pwd ]) -> PKey * OpenSSL::PKey.read(io [, pwd ]) -> PKey * - * Reads a DER or PEM encoded string from +string+ or +io+ and returns an + * Reads a DER or PEM encoded string from _string_ or _io_ and returns an * instance of the appropriate PKey class. * * === Parameters - * * +string+ is a DER- or PEM-encoded string containing an arbitrary private + * * _string+ is a DER- or PEM-encoded string containing an arbitrary private * or public key. - * * +io+ is an instance of +IO+ containing a DER- or PEM-encoded + * * _io_ is an instance of IO containing a DER- or PEM-encoded * arbitrary private or public key. - * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted + * * _pwd_ is an optional password in case _string_ or _io_ is an encrypted * PEM resource. */ static VALUE @@ -208,7 +208,7 @@ GetPKeyPtr(VALUE obj) { EVP_PKEY *pkey; - SafeGetPKey(obj, pkey); + GetPKey(obj, pkey); return pkey; } @@ -221,7 +221,7 @@ GetPrivPKeyPtr(VALUE obj) if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) { ossl_raise(rb_eArgError, "Private key is needed."); } - SafeGetPKey(obj, pkey); + GetPKey(obj, pkey); return pkey; } @@ -231,7 +231,7 @@ DupPKeyPtr(VALUE obj) { EVP_PKEY *pkey; - SafeGetPKey(obj, pkey); + GetPKey(obj, pkey); EVP_PKEY_up_ref(pkey); return pkey; @@ -260,7 +260,7 @@ ossl_pkey_alloc(VALUE klass) * PKeyClass.new -> self * * Because PKey is an abstract class, actually calling this method explicitly - * will raise a +NotImplementedError+. + * will raise a NotImplementedError. */ static VALUE ossl_pkey_initialize(VALUE self) @@ -275,10 +275,10 @@ ossl_pkey_initialize(VALUE self) * call-seq: * pkey.sign(digest, data) -> String * - * To sign the +String+ +data+, +digest+, an instance of OpenSSL::Digest, must - * be provided. The return value is again a +String+ containing the signature. + * To sign the String _data_, _digest_, an instance of OpenSSL::Digest, must + * be provided. The return value is again a String containing the signature. * A PKeyError is raised should errors occur. - * Any previous state of the +Digest+ instance is irrelevant to the signature + * Any previous state of the Digest instance is irrelevant to the signature * outcome, the digest instance is reset to its initial state during the * operation. * @@ -299,7 +299,7 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) int result; pkey = GetPrivPKeyPtr(self); - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); StringValue(data); str = rb_str_new(0, EVP_PKEY_size(pkey)); @@ -327,12 +327,12 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) * call-seq: * pkey.verify(digest, signature, data) -> String * - * To verify the +String+ +signature+, +digest+, an instance of + * To verify the String _signature_, _digest_, an instance of * OpenSSL::Digest, must be provided to re-compute the message digest of the - * original +data+, also a +String+. The return value is +true+ if the + * original _data_, also a String. The return value is +true+ if the * signature is valid, +false+ otherwise. A PKeyError is raised should errors * occur. - * Any previous state of the +Digest+ instance is irrelevant to the validation + * Any previous state of the Digest instance is irrelevant to the validation * outcome, the digest instance is reset to its initial state during the * operation. * @@ -354,7 +354,7 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) GetPKey(self, pkey); ossl_pkey_check_public_key(pkey); - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); StringValue(sig); siglen = RSTRING_LENINT(sig); StringValue(data); diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index a0b49744..2b17bf53 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -34,10 +34,6 @@ extern const rb_data_type_t ossl_evp_pkey_type; rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ } \ } while (0) -#define SafeGetPKey(obj, pkey) do { \ - OSSL_Check_Kind((obj), cPKey); \ - GetPKey((obj), (pkey)); \ -} while (0) struct ossl_generate_cb_arg { int yield; diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index 92832710..31f3b8e7 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -150,8 +150,8 @@ dh_generate(int size, int gen) * components alike. * * === Parameters - * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure. - * * +generator+ is a small number > 1, typically 2 or 5. + * * _size_ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure. + * * _generator_ is a small number > 1, typically 2 or 5. * */ static VALUE @@ -181,15 +181,15 @@ ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass) * DH.new(size [, generator]) -> dh * * Either generates a DH instance from scratch or by reading already existing - * DH parameters from +string+. Note that when reading a DH instance from + * DH parameters from _string_. Note that when reading a DH instance from * data that was encoded from a DH instance by using DH#to_pem or DH#to_der * the result will *not* contain a public/private key pair yet. This needs to * be generated using DH#generate_key! first. * * === Parameters - * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure. - * * +generator+ is a small number > 1, typically 2 or 5. - * * +string+ contains the DER or PEM encoded key. + * * _size_ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure. + * * _generator_ is a small number > 1, typically 2 or 5. + * * _string_ contains the DER or PEM encoded key. * * === Examples * DH.new # -> dh @@ -436,7 +436,7 @@ ossl_dh_to_text(VALUE self) * dh.public_key -> aDH * * Returns a new DH instance that carries just the public information, i.e. - * the prime +p+ and the generator +g+, but no public/private key yet. Such + * the prime _p_ and the generator _g_, but no public/private key yet. Such * a pair may be generated using DH#generate_key!. The "public key" needed * for a key exchange with DH#compute_key is considered as per-session * information and may be retrieved with DH#pub_key once a key pair has @@ -526,7 +526,7 @@ ossl_dh_generate_key(VALUE self) * See DH_compute_key() for further information. * * === Parameters - * * +pub_bn+ is a OpenSSL::BN, *not* the DH instance returned by + * * _pub_bn_ is a OpenSSL::BN, *not* the DH instance returned by * DH#public_key as that contains the DH parameters only. */ static VALUE @@ -557,7 +557,7 @@ ossl_dh_compute_key(VALUE self, VALUE pub) * call-seq: * dh.set_pqg(p, q, g) -> self * - * Sets +p+, +q+, +g+ for the DH instance. + * Sets _p_, _q_, _g_ to the DH instance. */ OSSL_PKEY_BN_DEF3(dh, DH, pqg, p, q, g) /* @@ -565,7 +565,7 @@ OSSL_PKEY_BN_DEF3(dh, DH, pqg, p, q, g) * call-seq: * dh.set_key(pub_key, priv_key) -> self * - * Sets +pub_key+ and +priv_key+ for the DH instance. +priv_key+ may be nil. + * Sets _pub_key_ and _priv_key_ for the DH instance. _priv_key_ may be +nil+. */ OSSL_PKEY_BN_DEF2(dh, DH, key, pub_key, priv_key) @@ -618,7 +618,7 @@ Init_ossl_dh(void) cDH = rb_define_class_under(mPKey, "DH", cPKey); rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1); rb_define_method(cDH, "initialize", ossl_dh_initialize, -1); - rb_define_copy_func(cDH, ossl_dh_initialize_copy); + rb_define_method(cDH, "initialize_copy", ossl_dh_initialize_copy, 1); rb_define_method(cDH, "public?", ossl_dh_is_public, 0); rb_define_method(cDH, "private?", ossl_dh_is_private, 0); rb_define_method(cDH, "to_text", ossl_dh_to_text, 0); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index cd4313b1..56cc9dd4 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -172,7 +172,7 @@ dsa_generate(int size) * from scratch. * * === Parameters - * * +size+ is an integer representing the desired key size. + * * _size_ is an integer representing the desired key size. * */ static VALUE @@ -195,12 +195,12 @@ ossl_dsa_s_generate(VALUE klass, VALUE size) * DSA.new(size) -> dsa * DSA.new(string [, pass]) -> dsa * - * Creates a new DSA instance by reading an existing key from +string+. + * Creates a new DSA instance by reading an existing key from _string_. * * === Parameters - * * +size+ is an integer representing the desired key size. - * * +string+ contains a DER or PEM encoded key. - * * +pass+ is a string that contains an optional password. + * * _size_ is an integer representing the desired key size. + * * _string_ contains a DER or PEM encoded key. + * * _pass_ is a string that contains an optional password. * * === Examples * DSA.new -> dsa @@ -329,8 +329,8 @@ ossl_dsa_is_private(VALUE self) * Encodes this DSA to its PEM encoding. * * === Parameters - * * +cipher+ is an OpenSSL::Cipher. - * * +password+ is a string containing your password. + * * _cipher_ is an OpenSSL::Cipher. + * * _password_ is a string containing your password. * * === Examples * DSA.to_pem -> aString @@ -348,7 +348,7 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self) GetDSA(self, dsa); rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { - ciph = GetCipherPtr(cipher); + ciph = ossl_evp_get_cipherbyname(cipher); pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { @@ -503,12 +503,12 @@ ossl_dsa_to_public_key(VALUE self) * call-seq: * dsa.syssign(string) -> aString * - * Computes and returns the DSA signature of +string+, where +string+ is + * Computes and returns the DSA signature of _string_, where _string_ is * expected to be an already-computed message digest of the original input * data. The signature is issued using the private key of this DSA instance. * * === Parameters - * * +string+ is a message digest of the original input data to be signed + * * _string_ is a message digest of the original input data to be signed. * * === Example * dsa = OpenSSL::PKey::DSA.new(2048) @@ -549,11 +549,11 @@ ossl_dsa_sign(VALUE self, VALUE data) * dsa.sysverify(digest, sig) -> true | false * * Verifies whether the signature is valid given the message digest input. It - * does so by validating +sig+ using the public key of this DSA instance. + * does so by validating _sig_ using the public key of this DSA instance. * * === Parameters - * * +digest+ is a message digest of the original input data to be signed - * * +sig+ is a DSA signature value + * * _digest_ is a message digest of the original input data to be signed + * * _sig_ is a DSA signature value * * === Example * dsa = OpenSSL::PKey::DSA.new(2048) @@ -590,7 +590,7 @@ ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig) * call-seq: * dsa.set_pqg(p, q, g) -> self * - * Sets +p+, +q+, +g+ for the DSA instance. + * Sets _p_, _q_, _g_ to the DSA instance. */ OSSL_PKEY_BN_DEF3(dsa, DSA, pqg, p, q, g) /* @@ -598,7 +598,7 @@ OSSL_PKEY_BN_DEF3(dsa, DSA, pqg, p, q, g) * call-seq: * dsa.set_key(pub_key, priv_key) -> self * - * Sets +pub_key+ and +priv_key+ for the DSA instance. +priv_key+ may be nil. + * Sets _pub_key_ and _priv_key_ for the DSA instance. _priv_key_ may be +nil+. */ OSSL_PKEY_BN_DEF2(dsa, DSA, key, pub_key, priv_key) @@ -627,18 +627,12 @@ Init_ossl_dsa(void) * DSA, the Digital Signature Algorithm, is specified in NIST's * FIPS 186-3. It is an asymmetric public key algorithm that may be used * similar to e.g. RSA. - * Please note that for OpenSSL versions prior to 1.0.0 the digest - * algorithms OpenSSL::Digest::DSS (equivalent to SHA) or - * OpenSSL::Digest::DSS1 (equivalent to SHA-1) must be used for issuing - * signatures with a DSA key using OpenSSL::PKey#sign. - * Starting with OpenSSL 1.0.0, digest algorithms are no longer restricted, - * any Digest may be used for signing. */ cDSA = rb_define_class_under(mPKey, "DSA", cPKey); rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1); rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1); - rb_define_copy_func(cDSA, ossl_dsa_initialize_copy); + rb_define_method(cDSA, "initialize_copy", ossl_dsa_initialize_copy, 1); rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index 5262d3b2..9c406931 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -4,7 +4,7 @@ #include "ossl.h" -#if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) +#if !defined(OPENSSL_NO_EC) #define EXPORT_PEM 0 #define EXPORT_DER 1 @@ -23,33 +23,21 @@ static const rb_data_type_t ossl_ec_point_type; GetPKeyEC(obj, _pkey); \ (key) = EVP_PKEY_get0_EC_KEY(_pkey); \ } while (0) -#define SafeGetEC(obj, key) do { \ - OSSL_Check_Kind(obj, cEC); \ - GetEC(obj, key); \ -} while (0) #define GetECGroup(obj, group) do { \ TypedData_Get_Struct(obj, EC_GROUP, &ossl_ec_group_type, group); \ if ((group) == NULL) \ ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ } while (0) -#define SafeGetECGroup(obj, group) do { \ - OSSL_Check_Kind((obj), cEC_GROUP); \ - GetECGroup(obj, group); \ -} while (0) #define GetECPoint(obj, point) do { \ TypedData_Get_Struct(obj, EC_POINT, &ossl_ec_point_type, point); \ if ((point) == NULL) \ ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ } while (0) -#define SafeGetECPoint(obj, point) do { \ - OSSL_Check_Kind((obj), cEC_POINT); \ - GetECPoint(obj, point); \ -} while(0) #define GetECPointGroup(obj, group) do { \ VALUE _group = rb_attr_get(obj, id_i_group); \ - SafeGetECGroup(_group, group); \ + GetECGroup(_group, group); \ } while (0) VALUE cEC; @@ -128,7 +116,7 @@ ec_key_new_from_group(VALUE arg) if (rb_obj_is_kind_of(arg, cEC_GROUP)) { EC_GROUP *group; - SafeGetECGroup(arg, group); + GetECGroup(arg, group); if (!(ec = EC_KEY_new())) ossl_raise(eECError, NULL); @@ -208,7 +196,7 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) } else if (rb_obj_is_kind_of(arg, cEC)) { EC_KEY *other_ec = NULL; - SafeGetEC(arg, other_ec); + GetEC(arg, other_ec); if (!(ec = EC_KEY_dup(other_ec))) ossl_raise(eECError, NULL); } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) { @@ -257,7 +245,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other) GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eECError, "EC already initialized"); - SafeGetEC(other, ec); + GetEC(other, ec); ec_new = EC_KEY_dup(ec); if (!ec_new) @@ -275,7 +263,7 @@ ossl_ec_key_initialize_copy(VALUE self, VALUE other) * key.group => group * * Returns the EC::Group that the key is associated with. Modifying the returned - * group does not affect +key+. + * group does not affect _key_. */ static VALUE ossl_ec_key_get_group(VALUE self) @@ -296,7 +284,7 @@ ossl_ec_key_get_group(VALUE self) * key.group = group * * Sets the EC::Group for the key. The group structure is internally copied so - * modification to +group+ after assigning to a key has no effect on the key. + * modification to _group_ after assigning to a key has no effect on the key. */ static VALUE ossl_ec_key_set_group(VALUE self, VALUE group_v) @@ -305,7 +293,7 @@ ossl_ec_key_set_group(VALUE self, VALUE group_v) EC_GROUP *group; GetEC(self, ec); - SafeGetECGroup(group_v, group); + GetECGroup(group_v, group); if (EC_KEY_set_group(ec, group) != 1) ossl_raise(eECError, "EC_KEY_set_group"); @@ -390,7 +378,7 @@ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) GetEC(self, ec); if (!NIL_P(public_key)) - SafeGetECPoint(public_key, point); + GetECPoint(public_key, point); switch (EC_KEY_set_public_key(ec, point)) { case 1: @@ -458,7 +446,7 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma private = 1; if (!NIL_P(ciph)) { - cipher = GetCipherPtr(ciph); + cipher = ossl_evp_get_cipherbyname(ciph); pass = ossl_pem_passwd_value(pass); } @@ -502,8 +490,8 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma * key.export([cipher, pass_phrase]) => String * key.to_pem([cipher, pass_phrase]) => String * - * Outputs the EC key in PEM encoding. If +cipher+ and +pass_phrase+ are given - * they will be used to encrypt the key. +cipher+ must be an OpenSSL::Cipher + * Outputs the EC key in PEM encoding. If _cipher_ and _pass_phrase_ are given + * they will be used to encrypt the key. _cipher_ must be an OpenSSL::Cipher * instance. Note that encryption will only be effective for a private key, * public keys will always be encoded in plain text. */ @@ -608,7 +596,7 @@ static VALUE ossl_ec_key_dh_compute_key(VALUE self, VALUE pubkey) VALUE str; GetEC(self, ec); - SafeGetECPoint(pubkey, point); + GetECPoint(pubkey, point); /* BUG: need a way to figure out the maximum string size */ buf_len = 1024; @@ -724,7 +712,7 @@ ec_group_new(const EC_GROUP *group) * * Creates a new EC::Group object. * - * +ec_method+ is a symbol that represents an EC_METHOD. Currently the following + * _ec_method_ is a symbol that represents an EC_METHOD. Currently the following * are supported: * * * :GFp_simple @@ -771,7 +759,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { const EC_GROUP *arg1_group; - SafeGetECGroup(arg1, arg1_group); + GetECGroup(arg1, arg1_group); if ((group = EC_GROUP_dup(arg1_group)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_dup"); } else { @@ -847,7 +835,7 @@ ossl_ec_group_initialize_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group_new); if (group_new) ossl_raise(eEC_GROUP, "EC::Group already initialized"); - SafeGetECGroup(other, group); + GetECGroup(other, group); group_new = EC_GROUP_dup(group); if (!group_new) @@ -862,15 +850,15 @@ ossl_ec_group_initialize_copy(VALUE self, VALUE other) * group1.eql?(group2) => true | false * group1 == group2 => true | false * - * Returns true if the two groups use the same curve and have the same - * parameters, false otherwise. + * Returns +true+ if the two groups use the same curve and have the same + * parameters, +false+ otherwise. */ static VALUE ossl_ec_group_eql(VALUE a, VALUE b) { EC_GROUP *group1 = NULL, *group2 = NULL; GetECGroup(a, group1); - SafeGetECGroup(b, group2); + GetECGroup(b, group2); if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1) return Qfalse; @@ -903,8 +891,8 @@ static VALUE ossl_ec_group_get_generator(VALUE self) * call-seq: * group.set_generator(generator, order, cofactor) => self * - * Sets the curve parameters. +generator+ must be an instance of EC::Point that - * is on the curve. +order+ and +cofactor+ are integers. + * Sets the curve parameters. _generator_ must be an instance of EC::Point that + * is on the curve. _order_ and _cofactor_ are integers. * * See the OpenSSL documentation for EC_GROUP_set_generator() */ @@ -915,7 +903,7 @@ static VALUE ossl_ec_group_set_generator(VALUE self, VALUE generator, VALUE orde const BIGNUM *o, *co; GetECGroup(self, group); - SafeGetECPoint(generator, point); + GetECPoint(generator, point); o = GetBNPtr(order); co = GetBNPtr(cofactor); @@ -1127,14 +1115,14 @@ parse_point_conversion_form_symbol(VALUE sym) * * Sets the form how EC::Point data is encoded as ASN.1 as defined in X9.62. * - * +format+ can be one of these: + * _format_ can be one of these: * - * :compressed:: + * +:compressed+:: * Encoded as z||x, where z is an octet indicating which solution of the * equation y is. z will be 0x02 or 0x03. - * :uncompressed:: + * +:uncompressed+:: * Encoded as z||x||y, where z is an octet 0x04. - * :hybrid:: + * +:hybrid+:: * Encodes as z||x||y, where z is an octet indicating which solution of the * equation y is. z will be 0x06 or 0x07. * @@ -1356,13 +1344,13 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) const EC_POINT *arg_point; group_v = rb_attr_get(arg1, id_i_group); - SafeGetECGroup(group_v, group); - SafeGetECPoint(arg1, arg_point); + GetECGroup(group_v, group); + GetECPoint(arg1, arg_point); point = EC_POINT_dup(arg_point, group); } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { group_v = arg1; - SafeGetECGroup(group_v, group); + GetECGroup(group_v, group); point = EC_POINT_new(group); } else { @@ -1374,7 +1362,7 @@ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) if (!rb_obj_is_kind_of(arg1, cEC_GROUP)) ossl_raise(rb_eArgError, "1st argument must be OpenSSL::PKey::EC::Group"); group_v = arg1; - SafeGetECGroup(group_v, group); + GetECGroup(group_v, group); if (rb_obj_is_kind_of(arg2, cBN)) { const BIGNUM *bn = GetBNPtr(arg2); @@ -1418,10 +1406,10 @@ ossl_ec_point_initialize_copy(VALUE self, VALUE other) TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point_new); if (point_new) ossl_raise(eEC_POINT, "EC::Point already initialized"); - SafeGetECPoint(other, point); + GetECPoint(other, point); group_v = rb_obj_dup(rb_attr_get(other, id_i_group)); - SafeGetECGroup(group_v, group); + GetECGroup(group_v, group); point_new = EC_POINT_dup(point, group); if (!point_new) @@ -1448,8 +1436,8 @@ static VALUE ossl_ec_point_eql(VALUE a, VALUE b) return Qfalse; GetECPoint(a, point1); - SafeGetECPoint(b, point2); - SafeGetECGroup(group_v1, group); + GetECPoint(b, point2); + GetECGroup(group_v1, group); if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1) return Qfalse; @@ -1558,7 +1546,7 @@ static VALUE ossl_ec_point_set_to_infinity(VALUE self) * point.to_bn(conversion_form = nil) => OpenSSL::BN * * Convert the EC point into an octet string and store in an OpenSSL::BN. If - * +conversion_form+ is given, the point data is converted using the specified + * _conversion_form_ is given, the point data is converted using the specified * form. If not given, the default form set in the EC::Group object is used. * * See also EC::Point#point_conversion_form=. @@ -1597,12 +1585,12 @@ ossl_ec_point_to_bn(int argc, VALUE *argv, VALUE self) * Performs elliptic curve point multiplication. * * The first form calculates <tt>bn1 * point + bn2 * G</tt>, where +G+ is the - * generator of the group of +point+. +bn2+ may be omitted, and in that case, + * generator of the group of _point_. _bn2_ may be omitted, and in that case, * the result is just <tt>bn1 * point</tt>. * * The second form calculates <tt>bns[0] * point + bns[1] * points[0] + ... - * + bns[-1] * points[-1] + bn2 * G</tt>. +bn2+ may be omitted. +bns+ must be - * an array of OpenSSL::BN. +points+ must be an array of + * + bns[-1] * points[-1] + bn2 * G</tt>. _bn2_ may be omitted. _bns_ must be + * an array of OpenSSL::BN. _points_ must be an array of * OpenSSL::PKey::EC::Point. Please note that <tt>points[0]</tt> is not * multiplied by <tt>bns[0]</tt>, but <tt>bns[1]</tt>. */ @@ -1615,7 +1603,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) const BIGNUM *bn_g = NULL; GetECPoint(self, point_self); - SafeGetECGroup(group_v, group); + GetECGroup(group_v, group); result = rb_obj_alloc(cEC_POINT); ossl_ec_point_initialize(1, &group_v, result); @@ -1656,7 +1644,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) points = ALLOCV_N(const EC_POINT *, tmp_p, num); points[0] = point_self; /* self */ for (i = 0; i < num - 1; i++) - SafeGetECPoint(RARRAY_AREF(arg2, i), points[i + 1]); + GetECPoint(RARRAY_AREF(arg2, i), points[i + 1]); if (!NIL_P(arg3)) bn_g = GetBNPtr(arg3); @@ -1726,7 +1714,7 @@ void Init_ossl_ec(void) rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1); rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1); - rb_define_copy_func(cEC, ossl_ec_key_initialize_copy); + rb_define_method(cEC, "initialize_copy", ossl_ec_key_initialize_copy, 1); /* copy/dup/cmp */ rb_define_method(cEC, "group", ossl_ec_key_get_group, 0); @@ -1763,7 +1751,7 @@ void Init_ossl_ec(void) rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); rb_define_method(cEC_GROUP, "initialize", ossl_ec_group_initialize, -1); - rb_define_copy_func(cEC_GROUP, ossl_ec_group_initialize_copy); + rb_define_method(cEC_GROUP, "initialize_copy", ossl_ec_group_initialize_copy, 1); rb_define_method(cEC_GROUP, "eql?", ossl_ec_group_eql, 1); rb_define_alias(cEC_GROUP, "==", "eql?"); /* copy/dup/cmp */ @@ -1799,7 +1787,7 @@ void Init_ossl_ec(void) rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc); rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1); - rb_define_copy_func(cEC_POINT, ossl_ec_point_initialize_copy); + rb_define_method(cEC_POINT, "initialize_copy", ossl_ec_point_initialize_copy, 1); rb_attr(cEC_POINT, rb_intern("group"), 1, 0, 0); rb_define_method(cEC_POINT, "eql?", ossl_ec_point_eql, 1); rb_define_alias(cEC_POINT, "==", "eql?"); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 1fcdd52c..4800fb27 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -172,8 +172,8 @@ rsa_generate(int size, unsigned long exp) * RSA.generate(size) => RSA instance * RSA.generate(size, exponent) => RSA instance * - * Generates an RSA keypair. +size+ is an integer representing the desired key - * size. Keys smaller than 1024 should be considered insecure. +exponent+ is + * Generates an RSA keypair. _size_ is an integer representing the desired key + * size. Keys smaller than 1024 should be considered insecure. _exponent_ is * an odd number normally 3, 17, or 65537. */ static VALUE @@ -203,12 +203,12 @@ ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass) * RSA.new(encoded_key) => RSA instance * RSA.new(encoded_key, pass_phrase) => RSA instance * - * Generates or loads an RSA keypair. If an integer +key_size+ is given it + * Generates or loads an RSA keypair. If an integer _key_size_ is given it * represents the desired key size. Keys less than 1024 bits should be * considered insecure. * - * A key can instead be loaded from an +encoded_key+ which must be PEM or DER - * encoded. A +pass_phrase+ can be used to decrypt the key. If none is given + * A key can instead be loaded from an _encoded_key_ which must be PEM or DER + * encoded. A _pass_phrase_ can be used to decrypt the key. If none is given * OpenSSL will prompt for the pass phrase. * * = Examples @@ -295,7 +295,7 @@ ossl_rsa_initialize_copy(VALUE self, VALUE other) * call-seq: * rsa.public? => true * - * The return value is always true since every private key is also a public + * The return value is always +true+ since every private key is also a public * key. */ static VALUE @@ -333,8 +333,8 @@ ossl_rsa_is_private(VALUE self) * rsa.to_pem([cipher, pass_phrase]) => PEM-format String * rsa.to_s([cipher, pass_phrase]) => PEM-format String * - * Outputs this keypair in PEM encoding. If +cipher+ and +pass_phrase+ are - * given they will be used to encrypt the key. +cipher+ must be an + * Outputs this keypair in PEM encoding. If _cipher_ and _pass_phrase_ are + * given they will be used to encrypt the key. _cipher_ must be an * OpenSSL::Cipher instance. */ static VALUE @@ -350,7 +350,7 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { - ciph = GetCipherPtr(cipher); + ciph = ossl_evp_get_cipherbyname(cipher); pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { @@ -409,7 +409,7 @@ ossl_rsa_to_der(VALUE self) * rsa.public_encrypt(string) => String * rsa.public_encrypt(string, padding) => String * - * Encrypt +string+ with the public key. +padding+ defaults to PKCS1_PADDING. + * Encrypt _string_ with the public key. _padding_ defaults to PKCS1_PADDING. * The encrypted string output can be decrypted using #private_decrypt. */ static VALUE @@ -441,8 +441,8 @@ ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self) * rsa.public_decrypt(string) => String * rsa.public_decrypt(string, padding) => String * - * Decrypt +string+, which has been encrypted with the private key, with the - * public key. +padding+ defaults to PKCS1_PADDING. + * Decrypt _string_, which has been encrypted with the private key, with the + * public key. _padding_ defaults to PKCS1_PADDING. */ static VALUE ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) @@ -473,7 +473,7 @@ ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) * rsa.private_encrypt(string) => String * rsa.private_encrypt(string, padding) => String * - * Encrypt +string+ with the private key. +padding+ defaults to PKCS1_PADDING. + * Encrypt _string_ with the private key. _padding_ defaults to PKCS1_PADDING. * The encrypted string output can be decrypted using #public_decrypt. */ static VALUE @@ -507,8 +507,8 @@ ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self) * rsa.private_decrypt(string) => String * rsa.private_decrypt(string, padding) => String * - * Decrypt +string+, which has been encrypted with the public key, with the - * private key. +padding+ defaults to PKCS1_PADDING. + * Decrypt _string_, which has been encrypted with the public key, with the + * private key. _padding_ defaults to PKCS1_PADDING. */ static VALUE ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) @@ -538,6 +538,196 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) /* * call-seq: + * rsa.sign_pss(digest, data, salt_length:, mgf1_hash:) -> String + * + * Signs _data_ using the Probabilistic Signature Scheme (RSA-PSS) and returns + * the calculated signature. + * + * RSAError will be raised if an error occurs. + * + * See #verify_pss for the verification operation. + * + * === Parameters + * _digest_:: + * A String containing the message digest algorithm name. + * _data_:: + * A String. The data to be signed. + * _salt_length_:: + * The length in octets of the salt. Two special values are reserved: + * +:digest+ means the digest length, and +:max+ means the maximum possible + * length for the combination of the private key and the selected message + * digest algorithm. + * _mgf1_hash_:: + * The hash algorithm used in MGF1 (the currently supported mask generation + * function (MGF)). + * + * === Example + * data = "Sign me!" + * pkey = OpenSSL::PKey::RSA.new(2048) + * signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256") + * pub_key = pkey.public_key + * puts pub_key.verify_pss("SHA256", signature, data, + * salt_length: :auto, mgf1_hash: "SHA256") # => true + */ +static VALUE +ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) +{ + VALUE digest, data, options, kwargs[2], signature; + static ID kwargs_ids[2]; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pkey_ctx; + const EVP_MD *md, *mgf1md; + EVP_MD_CTX *md_ctx; + size_t buf_len; + int salt_len; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); + } + rb_scan_args(argc, argv, "2:", &digest, &data, &options); + rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); + if (kwargs[0] == ID2SYM(rb_intern("max"))) + salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ + else if (kwargs[0] == ID2SYM(rb_intern("digest"))) + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + else + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + + pkey = GetPrivPKeyPtr(self); + buf_len = EVP_PKEY_size(pkey); + md = ossl_evp_get_digestbyname(digest); + StringValue(data); + signature = rb_str_new(NULL, (long)buf_len); + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) + goto err; + + if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) + goto err; + + if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + goto err; + + if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1) + goto err; + + rb_str_set_len(signature, (long)buf_len); + + EVP_MD_CTX_free(md_ctx); + return signature; + + err: + EVP_MD_CTX_free(md_ctx); + ossl_raise(eRSAError, NULL); +} + +/* + * call-seq: + * rsa.verify_pss(digest, signature, data, salt_length:, mgf1_hash:) -> true | false + * + * Verifies _data_ using the Probabilistic Signature Scheme (RSA-PSS). + * + * The return value is +true+ if the signature is valid, +false+ otherwise. + * RSAError will be raised if an error occurs. + * + * See #sign_pss for the signing operation and an example code. + * + * === Parameters + * _digest_:: + * A String containing the message digest algorithm name. + * _data_:: + * A String. The data to be signed. + * _salt_length_:: + * The length in octets of the salt. Two special values are reserved: + * +:digest+ means the digest length, and +:auto+ means automatically + * determining the length based on the signature. + * _mgf1_hash_:: + * The hash algorithm used in MGF1. + */ +static VALUE +ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) +{ + VALUE digest, signature, data, options, kwargs[2]; + static ID kwargs_ids[2]; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pkey_ctx; + const EVP_MD *md, *mgf1md; + EVP_MD_CTX *md_ctx; + int result, salt_len; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); + } + rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options); + rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); + if (kwargs[0] == ID2SYM(rb_intern("auto"))) + salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ + else if (kwargs[0] == ID2SYM(rb_intern("digest"))) + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + else + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + + GetPKey(self, pkey); + md = ossl_evp_get_digestbyname(digest); + StringValue(signature); + StringValue(data); + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) + goto err; + + if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) + goto err; + + if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + goto err; + + result = EVP_DigestVerifyFinal(md_ctx, + (unsigned char *)RSTRING_PTR(signature), + RSTRING_LEN(signature)); + + switch (result) { + case 0: + ossl_clear_error(); + EVP_MD_CTX_free(md_ctx); + return Qfalse; + case 1: + EVP_MD_CTX_free(md_ctx); + return Qtrue; + default: + goto err; + } + + err: + EVP_MD_CTX_free(md_ctx); + ossl_raise(eRSAError, NULL); +} + +/* + * call-seq: * rsa.params => hash * * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! @@ -659,7 +849,7 @@ ossl_rsa_blinding_off(VALUE self) * call-seq: * rsa.set_key(n, e, d) -> self * - * Sets +n+, +e+, +d+ for the RSA instance. + * Sets _n_, _e_, _d_ for the RSA instance. */ OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d) /* @@ -667,7 +857,7 @@ OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d) * call-seq: * rsa.set_factors(p, q) -> self * - * Sets +p+, +q+ for the RSA instance. + * Sets _p_, _q_ for the RSA instance. */ OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q) /* @@ -675,7 +865,7 @@ OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q) * call-seq: * rsa.set_crt_params(dmp1, dmq1, iqmp) -> self * - * Sets +dmp1+, +dmq1+, +iqmp+ for the RSA instance. They are calculated by + * Sets _dmp1_, _dmq1_, _iqmp_ for the RSA instance. They are calculated by * <tt>d mod (p - 1)</tt>, <tt>d mod (q - 1)</tt> and <tt>q^(-1) mod p</tt> * respectively. */ @@ -717,7 +907,7 @@ Init_ossl_rsa(void) rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1); rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1); - rb_define_copy_func(cRSA, ossl_rsa_initialize_copy); + rb_define_method(cRSA, "initialize_copy", ossl_rsa_initialize_copy, 1); rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); @@ -731,6 +921,8 @@ Init_ossl_rsa(void) rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1); rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1); rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1); + rb_define_method(cRSA, "sign_pss", ossl_rsa_sign_pss, -1); + rb_define_method(cRSA, "verify_pss", ossl_rsa_verify_pss, -1); DEF_OSSL_PKEY_BN(cRSA, rsa, n); DEF_OSSL_PKEY_BN(cRSA, rsa, e); diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c index 688c525a..c9585706 100644 --- a/ext/openssl/ossl_rand.c +++ b/ext/openssl/ossl_rand.c @@ -16,7 +16,7 @@ VALUE eRandomError; * call-seq: * seed(str) -> str * - * ::seed is equivalent to ::add where +entropy+ is length of +str+. + * ::seed is equivalent to ::add where _entropy_ is length of _str_. */ static VALUE ossl_rand_seed(VALUE self, VALUE str) @@ -31,15 +31,15 @@ ossl_rand_seed(VALUE self, VALUE str) * call-seq: * add(str, entropy) -> self * - * Mixes the bytes from +str+ into the Pseudo Random Number Generator(PRNG) + * Mixes the bytes from _str_ into the Pseudo Random Number Generator(PRNG) * state. * - * Thus, if the data from +str+ are unpredictable to an adversary, this + * Thus, if the data from _str_ are unpredictable to an adversary, this * increases the uncertainty about the state and makes the PRNG output less * predictable. * - * The +entropy+ argument is (the lower bound of) an estimate of how much - * randomness is contained in +str+, measured in bytes. + * The _entropy_ argument is (the lower bound of) an estimate of how much + * randomness is contained in _str_, measured in bytes. * * === Example * @@ -62,7 +62,7 @@ ossl_rand_add(VALUE self, VALUE str, VALUE entropy) * call-seq: * load_random_file(filename) -> true * - * Reads bytes from +filename+ and adds them to the PRNG. + * Reads bytes from _filename_ and adds them to the PRNG. */ static VALUE ossl_rand_load_file(VALUE self, VALUE filename) @@ -79,7 +79,7 @@ ossl_rand_load_file(VALUE self, VALUE filename) * call-seq: * write_random_file(filename) -> true * - * Writes a number of random generated bytes (currently 1024) to +filename+ + * Writes a number of random generated bytes (currently 1024) to _filename_ * which can be used to initialize the PRNG by calling ::load_random_file in a * later session. */ @@ -98,7 +98,7 @@ ossl_rand_write_file(VALUE self, VALUE filename) * call-seq: * random_bytes(length) -> string * - * Generates +string+ with +length+ number of cryptographically strong + * Generates a String with _length_ number of cryptographically strong * pseudo-random bytes. * * === Example @@ -129,7 +129,7 @@ ossl_rand_bytes(VALUE self, VALUE len) * call-seq: * pseudo_bytes(length) -> string * - * Generates +string+ with +length+ number of pseudo-random bytes. + * Generates a String with _length_ number of pseudo-random bytes. * * Pseudo-random byte sequences generated by ::pseudo_bytes will be unique if * they are of sufficient length, but are not necessarily unpredictable. @@ -176,9 +176,9 @@ ossl_rand_egd(VALUE self, VALUE filename) * call-seq: * egd_bytes(filename, length) -> true * - * Queries the entropy gathering daemon EGD on socket path given by +filename+. + * Queries the entropy gathering daemon EGD on socket path given by _filename_. * - * Fetches +length+ number of bytes and uses ::add to seed the OpenSSL built-in + * Fetches _length_ number of bytes and uses ::add to seed the OpenSSL built-in * PRNG. */ static VALUE @@ -199,7 +199,7 @@ ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) * call-seq: * status? => true | false * - * Return true if the PRNG has been seeded with enough data, false otherwise. + * Return +true+ if the PRNG has been seeded with enough data, +false+ otherwise. */ static VALUE ossl_rand_status(VALUE self) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 736f4924..8cdb9737 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -32,7 +32,7 @@ VALUE cSSLSocket; static VALUE eSSLErrorWaitReadable; static VALUE eSSLErrorWaitWritable; -static ID ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback, +static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback, id_npn_protocols_encoded; static VALUE sym_exception, sym_wait_readable, sym_wait_writable; @@ -46,54 +46,19 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, id_i_verify_hostname; static ID id_i_io, id_i_context, id_i_hostname; -/* - * SSLContext class - */ -static const struct { - const char *name; - SSL_METHOD *(*func)(void); /* FIXME: constify when dropping 0.9.8 */ - int version; -} ossl_ssl_method_tab[] = { -#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) -#define OSSL_SSL_METHOD_ENTRY(name, version) \ - { #name, (SSL_METHOD *(*)(void))TLS_method, version }, \ - { #name"_server", (SSL_METHOD *(*)(void))TLS_server_method, version }, \ - { #name"_client", (SSL_METHOD *(*)(void))TLS_client_method, version } -#else -#define OSSL_SSL_METHOD_ENTRY(name, version) \ - { #name, (SSL_METHOD *(*)(void))name##_method, version }, \ - { #name"_server", (SSL_METHOD *(*)(void))name##_server_method, version }, \ - { #name"_client", (SSL_METHOD *(*)(void))name##_client_method, version } -#endif -#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL2_METHOD) && defined(HAVE_SSLV2_METHOD) - OSSL_SSL_METHOD_ENTRY(SSLv2, SSL2_VERSION), -#endif -#if !defined(OPENSSL_NO_SSL3) && !defined(OPENSSL_NO_SSL3_METHOD) && defined(HAVE_SSLV3_METHOD) - OSSL_SSL_METHOD_ENTRY(SSLv3, SSL3_VERSION), -#endif -#if !defined(OPENSSL_NO_TLS1) && !defined(OPENSSL_NO_TLS1_METHOD) - OSSL_SSL_METHOD_ENTRY(TLSv1, TLS1_VERSION), -#endif -#if !defined(OPENSSL_NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1_METHOD) && defined(HAVE_TLSV1_1_METHOD) - OSSL_SSL_METHOD_ENTRY(TLSv1_1, TLS1_1_VERSION), -#endif -#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2_METHOD) && defined(HAVE_TLSV1_2_METHOD) - OSSL_SSL_METHOD_ENTRY(TLSv1_2, TLS1_2_VERSION), -#endif - OSSL_SSL_METHOD_ENTRY(SSLv23, 0), -#undef OSSL_SSL_METHOD_ENTRY -}; - static int ossl_ssl_ex_vcb_idx; -static int ossl_ssl_ex_store_p; static int ossl_ssl_ex_ptr_idx; +static int ossl_sslctx_ex_ptr_idx; +#if !defined(HAVE_X509_STORE_UP_REF) +static int ossl_sslctx_ex_store_p; +#endif static void ossl_sslctx_free(void *ptr) { SSL_CTX *ctx = ptr; #if !defined(HAVE_X509_STORE_UP_REF) - if(ctx && SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_store_p)== (void*)1) + if (ctx && SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_store_p)) ctx->cert_store = NULL; #endif SSL_CTX_free(ctx); @@ -111,22 +76,24 @@ static VALUE ossl_sslctx_s_alloc(VALUE klass) { SSL_CTX *ctx; - long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; + long mode = 0 | + SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS; VALUE obj; -#ifdef SSL_MODE_RELEASE_BUFFERS - mode |= SSL_MODE_RELEASE_BUFFERS; -#endif - obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0); +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) + ctx = SSL_CTX_new(TLS_method()); +#else ctx = SSL_CTX_new(SSLv23_method()); +#endif if (!ctx) { ossl_raise(eSSLError, "SSL_CTX_new"); } SSL_CTX_set_mode(ctx, mode); RTYPEDDATA_DATA(obj) = ctx; - SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj); + SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_ptr_idx, (void *)obj); #if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_ECDH_AUTO) /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It @@ -143,49 +110,89 @@ ossl_sslctx_s_alloc(VALUE klass) return obj; } +static int +parse_proto_version(VALUE str) +{ + int i; + static const struct { + const char *name; + int version; + } map[] = { + { "SSL2", SSL2_VERSION }, + { "SSL3", SSL3_VERSION }, + { "TLS1", TLS1_VERSION }, + { "TLS1_1", TLS1_1_VERSION }, + { "TLS1_2", TLS1_2_VERSION }, +#ifdef TLS1_3_VERSION + { "TLS1_3", TLS1_3_VERSION }, +#endif + }; + + if (NIL_P(str)) + return 0; + if (RB_INTEGER_TYPE_P(str)) + return NUM2INT(str); + + if (SYMBOL_P(str)) + str = rb_sym2str(str); + StringValue(str); + for (i = 0; i < numberof(map); i++) + if (!strncmp(map[i].name, RSTRING_PTR(str), RSTRING_LEN(str))) + return map[i].version; + rb_raise(rb_eArgError, "unrecognized version %+"PRIsVALUE, str); +} + /* * call-seq: - * ctx.ssl_version = :TLSv1 - * ctx.ssl_version = "SSLv23_client" - * - * Sets the SSL/TLS protocol version for the context. This forces connections to - * use only the specified protocol version. + * ctx.set_minmax_proto_version(min, max) -> nil * - * You can get a list of valid versions with OpenSSL::SSL::SSLContext::METHODS + * Sets the minimum and maximum supported protocol versions. See #min_version= + * and #max_version=. */ static VALUE -ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method) +ossl_sslctx_set_minmax_proto_version(VALUE self, VALUE min_v, VALUE max_v) { SSL_CTX *ctx; - const char *s; - VALUE m = ssl_method; - int i; + int min, max; GetSSLCTX(self, ctx); - if (RB_TYPE_P(ssl_method, T_SYMBOL)) - m = rb_sym2str(ssl_method); - s = StringValueCStr(m); - for (i = 0; i < numberof(ossl_ssl_method_tab); i++) { - if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) { -#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) - int version = ossl_ssl_method_tab[i].version; -#endif - SSL_METHOD *method = ossl_ssl_method_tab[i].func(); - - if (SSL_CTX_set_ssl_version(ctx, method) != 1) - ossl_raise(eSSLError, "SSL_CTX_set_ssl_version"); + min = parse_proto_version(min_v); + max = parse_proto_version(max_v); + +#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION + if (!SSL_CTX_set_min_proto_version(ctx, min)) + ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version"); + if (!SSL_CTX_set_max_proto_version(ctx, max)) + ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version"); +#else + { + unsigned long sum = 0, opts = 0; + int i; + static const struct { + int ver; + unsigned long opts; + } options_map[] = { + { SSL2_VERSION, SSL_OP_NO_SSLv2 }, + { SSL3_VERSION, SSL_OP_NO_SSLv3 }, + { TLS1_VERSION, SSL_OP_NO_TLSv1 }, + { TLS1_1_VERSION, SSL_OP_NO_TLSv1_1 }, + { TLS1_2_VERSION, SSL_OP_NO_TLSv1_2 }, +# if defined(TLS1_3_VERSION) + { TLS1_3_VERSION, SSL_OP_NO_TLSv1_3 }, +# endif + }; -#if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) - if (!SSL_CTX_set_min_proto_version(ctx, version)) - ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version"); - if (!SSL_CTX_set_max_proto_version(ctx, version)) - ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version"); -#endif - return ssl_method; - } + for (i = 0; i < numberof(options_map); i++) { + sum |= options_map[i].opts; + if (min && min > options_map[i].ver || max && max < options_map[i].ver) + opts |= options_map[i].opts; + } + SSL_CTX_clear_options(ctx, sum); + SSL_CTX_set_options(ctx, opts); } +#endif - ossl_raise(rb_eArgError, "unknown SSL method `%"PRIsVALUE"'.", m); + return Qnil; } static VALUE @@ -198,7 +205,7 @@ ossl_call_client_cert_cb(VALUE obj) if (NIL_P(cb)) return Qnil; - ary = rb_funcall(cb, rb_intern("call"), 1, obj); + ary = rb_funcallv(cb, id_call, 1, &obj); Check_Type(ary, T_ARRAY); GetX509CertPtr(cert = rb_ary_entry(ary, 0)); GetPrivPKeyPtr(key = rb_ary_entry(ary, 1)); @@ -241,8 +248,8 @@ ossl_call_tmp_dh_callback(struct tmp_dh_callback_args *args) cb = rb_funcall(args->ssl_obj, args->id, 0); if (NIL_P(cb)) return NULL; - dh = rb_funcall(cb, rb_intern("call"), 3, - args->ssl_obj, INT2NUM(args->is_export), INT2NUM(args->keylength)); + dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export), + INT2NUM(args->keylength)); pkey = GetPKeyPtr(dh); if (EVP_PKEY_base_id(pkey) != args->type) return NULL; @@ -367,12 +374,12 @@ ossl_call_session_get_cb(VALUE ary) cb = rb_funcall(ssl_obj, rb_intern("session_get_cb"), 0); if (NIL_P(cb)) return Qnil; - return rb_funcall(cb, rb_intern("call"), 1, ary); + return rb_funcallv(cb, id_call, 1, &ary); } /* this method is currently only called for servers (in OpenSSL <= 0.9.8e) */ static SSL_SESSION * -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy) #else ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy) @@ -380,13 +387,10 @@ ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy) { VALUE ary, ssl_obj, ret_obj; SSL_SESSION *sess; - void *ptr; int state = 0; OSSL_Debug("SSL SESSION get callback entered"); - if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) - return NULL; - ssl_obj = (VALUE)ptr; + ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); ary = rb_ary_new2(2); rb_ary_push(ary, ssl_obj); rb_ary_push(ary, rb_str_new((const char *)buf, len)); @@ -399,7 +403,7 @@ ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy) if (!rb_obj_is_instance_of(ret_obj, cSSLSession)) return NULL; - SafeGetSSLSession(ret_obj, sess); + GetSSLSession(ret_obj, sess); *copy = 1; return sess; @@ -416,7 +420,7 @@ ossl_call_session_new_cb(VALUE ary) cb = rb_funcall(ssl_obj, rb_intern("session_new_cb"), 0); if (NIL_P(cb)) return Qnil; - return rb_funcall(cb, rb_intern("call"), 1, ary); + return rb_funcallv(cb, id_call, 1, &ary); } /* return 1 normal. return 0 removes the session */ @@ -424,14 +428,11 @@ static int ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess) { VALUE ary, ssl_obj, sess_obj; - void *ptr; int state = 0; OSSL_Debug("SSL SESSION new callback entered"); - if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) - return 1; - ssl_obj = (VALUE)ptr; + ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); sess_obj = rb_obj_alloc(cSSLSession); SSL_SESSION_up_ref(sess); DATA_PTR(sess_obj) = sess; @@ -466,14 +467,13 @@ ossl_call_session_remove_cb(VALUE ary) cb = rb_attr_get(sslctx_obj, id_i_session_remove_cb); if (NIL_P(cb)) return Qnil; - return rb_funcall(cb, rb_intern("call"), 1, ary); + return rb_funcallv(cb, id_call, 1, &ary); } static void ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) { VALUE ary, sslctx_obj, sess_obj; - void *ptr; int state = 0; /* @@ -485,9 +485,7 @@ ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) OSSL_Debug("SSL SESSION remove callback entered"); - if ((ptr = SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_ptr_idx)) == NULL) - return; - sslctx_obj = (VALUE)ptr; + sslctx_obj = (VALUE)SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_ptr_idx); sess_obj = rb_obj_alloc(cSSLSession); SSL_SESSION_up_ref(sess); DATA_PTR(sess_obj) = sess; @@ -523,7 +521,6 @@ ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) static VALUE ossl_sslctx_setup(VALUE self); -#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME static VALUE ossl_call_servername_cb(VALUE ary) { @@ -536,7 +533,7 @@ ossl_call_servername_cb(VALUE ary) cb = rb_attr_get(sslctx_obj, id_i_servername_cb); if (NIL_P(cb)) return Qnil; - ret_obj = rb_funcall(cb, rb_intern("call"), 1, ary); + ret_obj = rb_funcallv(cb, id_call, 1, &ary); if (rb_obj_is_kind_of(ret_obj, cSSLContext)) { SSL *ssl; SSL_CTX *ctx2; @@ -558,16 +555,13 @@ static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { VALUE ary, ssl_obj; - void *ptr; int state = 0; const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!servername) return SSL_TLSEXT_ERR_OK; - if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) - return SSL_TLSEXT_ERR_ALERT_FATAL; - ssl_obj = (VALUE)ptr; + ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); ary = rb_ary_new2(2); rb_ary_push(ary, ssl_obj); rb_ary_push(ary, rb_str_new2(servername)); @@ -580,26 +574,21 @@ ssl_servername_cb(SSL *ssl, int *ad, void *arg) return SSL_TLSEXT_ERR_OK; } -#endif static void ssl_renegotiation_cb(const SSL *ssl) { VALUE ssl_obj, sslctx_obj, cb; - void *ptr; - - if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) - ossl_raise(eSSLError, "SSL object could not be retrieved"); - ssl_obj = (VALUE)ptr; + ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); sslctx_obj = rb_attr_get(ssl_obj, id_i_context); cb = rb_attr_get(sslctx_obj, id_i_renegotiation_cb); if (NIL_P(cb)) return; - (void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj); + rb_funcallv(cb, id_call, 1, &ssl_obj); } -#if defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || \ +#if !defined(OPENSSL_NO_NEXTPROTONEG) || \ defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) static VALUE ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded) @@ -646,7 +635,7 @@ npn_select_cb_common_i(VALUE tmp) in += l; } - selected = rb_funcall(args->cb, rb_intern("call"), 1, protocols); + selected = rb_funcallv(args->cb, id_call, 1, &protocols); StringValue(selected); len = RSTRING_LEN(selected); if (len < 1 || len >= 256) { @@ -684,7 +673,7 @@ ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, } #endif -#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB +#ifndef OPENSSL_NO_NEXTPROTONEG static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) @@ -744,7 +733,11 @@ ossl_sslctx_get_options(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); - return LONG2NUM(SSL_CTX_get_options(ctx)); + /* + * Do explicit cast because SSL_CTX_get_options() returned (signed) long in + * OpenSSL before 1.1.0. + */ + return ULONG2NUM((unsigned long)SSL_CTX_get_options(ctx)); } /* @@ -763,7 +756,7 @@ ossl_sslctx_set_options(VALUE self, VALUE options) if (NIL_P(options)) { SSL_CTX_set_options(ctx, SSL_OP_ALL); } else { - SSL_CTX_set_options(ctx, NUM2LONG(options)); + SSL_CTX_set_options(ctx, NUM2ULONG(options)); } return self; @@ -827,7 +820,7 @@ ossl_sslctx_setup(VALUE self) * X509_STORE_free() doesn't care it. * So we won't increment it but mark it by ex_data. */ - SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void *)1); + SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_store_p, ctx); #else /* Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2) */ X509_STORE_up_ref(store); #endif @@ -898,7 +891,7 @@ ossl_sslctx_setup(VALUE self) val = rb_attr_get(self, id_i_verify_depth); if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val)); -#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB +#ifndef OPENSSL_NO_NEXTPROTONEG val = rb_attr_get(self, id_i_npn_protocols); if (!NIL_P(val)) { VALUE encoded = ssl_encode_npn_protocols(val); @@ -953,13 +946,11 @@ ossl_sslctx_setup(VALUE self) OSSL_Debug("SSL SESSION remove callback added"); } -#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME val = rb_attr_get(self, id_i_servername_cb); if (!NIL_P(val)) { SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); OSSL_Debug("SSL TLSEXT servername callback added"); } -#endif return Qtrue; } @@ -996,12 +987,7 @@ ossl_sslctx_get_ciphers(VALUE self) int i, num; GetSSLCTX(self, ctx); - if(!ctx){ - rb_warning("SSL_CTX is not initialized."); - return Qnil; - } ciphers = SSL_CTX_get_ciphers(ctx); - if (!ciphers) return rb_ary_new(); @@ -1207,11 +1193,139 @@ ossl_sslctx_set_security_level(VALUE self, VALUE value) return value; } +#ifdef SSL_MODE_SEND_FALLBACK_SCSV +/* + * call-seq: + * ctx.enable_fallback_scsv() => nil + * + * Activate TLS_FALLBACK_SCSV for this context. + * See RFC 7507. + */ +static VALUE +ossl_sslctx_enable_fallback_scsv(VALUE self) +{ + SSL_CTX *ctx; + + GetSSLCTX(self, ctx); + SSL_CTX_set_mode(ctx, SSL_MODE_SEND_FALLBACK_SCSV); + + return Qnil; +} +#endif + +/* + * call-seq: + * ctx.add_certificate(certiticate, pkey [, extra_certs]) -> self + * + * Adds a certificate to the context. _pkey_ must be a corresponding private + * key with _certificate_. + * + * Multiple certificates with different public key type can be added by + * repeated calls of this method, and OpenSSL will choose the most appropriate + * certificate during the handshake. + * + * #cert=, #key=, and #extra_chain_cert= are old accessor methods for setting + * certificate and internally call this method. + * + * === Parameters + * _certificate_:: + * A certificate. An instance of OpenSSL::X509::Certificate. + * _pkey_:: + * The private key for _certificate_. An instance of OpenSSL::PKey::PKey. + * _extra_certs_:: + * Optional. An array of OpenSSL::X509::Certificate. When sending a + * certificate chain, the certificates specified by this are sent following + * _certificate_, in the order in the array. + * + * === Example + * rsa_cert = OpenSSL::X509::Certificate.new(...) + * rsa_pkey = OpenSSL::PKey.read(...) + * ca_intermediate_cert = OpenSSL::X509::Certificate.new(...) + * ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert]) + * + * ecdsa_cert = ... + * ecdsa_pkey = ... + * another_ca_cert = ... + * ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert]) + * + * === Note + * OpenSSL before the version 1.0.2 could handle only one extra chain across + * all key types. Calling this method discards the chain set previously. + */ +static VALUE +ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self) +{ + VALUE cert, key, extra_chain_ary; + SSL_CTX *ctx; + X509 *x509; + STACK_OF(X509) *extra_chain = NULL; + EVP_PKEY *pkey, *pub_pkey; + + GetSSLCTX(self, ctx); + rb_scan_args(argc, argv, "21", &cert, &key, &extra_chain_ary); + rb_check_frozen(self); + x509 = GetX509CertPtr(cert); + pkey = GetPrivPKeyPtr(key); + + /* + * The reference counter is bumped, and decremented immediately. + * X509_get0_pubkey() is only available in OpenSSL >= 1.1.0. + */ + pub_pkey = X509_get_pubkey(x509); + EVP_PKEY_free(pub_pkey); + if (!pub_pkey) + rb_raise(rb_eArgError, "certificate does not contain public key"); + if (EVP_PKEY_cmp(pub_pkey, pkey) != 1) + rb_raise(rb_eArgError, "public key mismatch"); + + if (argc >= 3) + extra_chain = ossl_x509_ary2sk(extra_chain_ary); + + if (!SSL_CTX_use_certificate(ctx, x509)) { + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_use_certificate"); + } + if (!SSL_CTX_use_PrivateKey(ctx, pkey)) { + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); + } + + if (extra_chain) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER) + if (!SSL_CTX_set0_chain(ctx, extra_chain)) { + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_set0_chain"); + } +#else + STACK_OF(X509) *orig_extra_chain; + X509 *x509_tmp; + + /* First, clear the existing chain */ + SSL_CTX_get_extra_chain_certs(ctx, &orig_extra_chain); + if (orig_extra_chain && sk_X509_num(orig_extra_chain)) { + rb_warning("SSL_CTX_set0_chain() is not available; " \ + "clearing previously set certificate chain"); + SSL_CTX_clear_extra_chain_certs(ctx); + } + while ((x509_tmp = sk_X509_shift(extra_chain))) { + /* Transfers ownership */ + if (!SSL_CTX_add_extra_chain_cert(ctx, x509_tmp)) { + X509_free(x509_tmp); + sk_X509_pop_free(extra_chain, X509_free); + ossl_raise(eSSLError, "SSL_CTX_add_extra_chain_cert"); + } + } + sk_X509_free(extra_chain); +#endif + } + return self; +} + /* * call-seq: * ctx.session_add(session) -> true | false * - * Adds +session+ to the session cache. + * Adds _session_ to the session cache. */ static VALUE ossl_sslctx_session_add(VALUE self, VALUE arg) @@ -1220,7 +1334,7 @@ ossl_sslctx_session_add(VALUE self, VALUE arg) SSL_SESSION *sess; GetSSLCTX(self, ctx); - SafeGetSSLSession(arg, sess); + GetSSLSession(arg, sess); return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse; } @@ -1229,7 +1343,7 @@ ossl_sslctx_session_add(VALUE self, VALUE arg) * call-seq: * ctx.session_remove(session) -> true | false * - * Removes +session+ from the session cache. + * Removes _session_ from the session cache. */ static VALUE ossl_sslctx_session_remove(VALUE self, VALUE arg) @@ -1238,7 +1352,7 @@ ossl_sslctx_session_remove(VALUE self, VALUE arg) SSL_SESSION *sess; GetSSLCTX(self, ctx); - SafeGetSSLSession(arg, sess); + GetSSLSession(arg, sess); return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse; } @@ -1365,9 +1479,9 @@ ossl_sslctx_get_session_cache_stats(VALUE self) /* * call-seq: - * ctx.flush_sessions(time | nil) -> self + * ctx.flush_sessions(time) -> self * - * Removes sessions in the internal cache that have expired at +time+. + * Removes sessions in the internal cache that have expired at _time_. */ static VALUE ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self) @@ -1429,10 +1543,10 @@ ossl_ssl_s_alloc(VALUE klass) * SSLSocket.new(io) => aSSLSocket * SSLSocket.new(io, ctx) => aSSLSocket * - * Creates a new SSL socket from +io+ which must be a real IO object (not an + * Creates a new SSL socket from _io_ which must be a real IO object (not an * IO-like object that responds to read/write). * - * If +ctx+ is provided the SSL Sockets initial params will be taken from + * If _ctx_ is provided the SSL Sockets initial params will be taken from * the context. * * The OpenSSL::Buffering module provides additional IO methods. @@ -1492,7 +1606,7 @@ ossl_ssl_setup(VALUE self) GetOpenFile(io, fptr); rb_io_check_readable(fptr); rb_io_check_writable(fptr); - if (!SSL_set_fd(ssl, TO_SOCKET(FPTR_TO_FD(fptr)))) + if (!SSL_set_fd(ssl, TO_SOCKET(fptr->fd))) ossl_raise(eSSLError, "SSL_set_fd"); return Qtrue; @@ -1535,6 +1649,9 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) int ret, ret2; VALUE cb_state; int nonblock = opts != Qfalse; +#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED) + unsigned long err; +#endif rb_ivar_set(self, ID_callback_state, Qnil); @@ -1558,16 +1675,33 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); - rb_io_wait_writable(FPTR_TO_FD(fptr)); + rb_io_wait_writable(fptr->fd); continue; case SSL_ERROR_WANT_READ: if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); - rb_io_wait_readable(FPTR_TO_FD(fptr)); + rb_io_wait_readable(fptr->fd); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(funcname); ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); +#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED) + case SSL_ERROR_SSL: + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_SSL && + ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) { + const char *err_msg = ERR_reason_error_string(err), + *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl)); + if (!err_msg) + err_msg = "(null)"; + if (!verify_msg) + verify_msg = "(null)"; + ossl_clear_error(); /* let ossl_raise() not append message */ + ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s: %s (%s)", + funcname, ret2, errno, SSL_state_string_long(ssl), + err_msg, verify_msg); + } +#endif default: ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); } @@ -1608,10 +1742,10 @@ ossl_ssl_connect(VALUE self) * retry * end * - * By specifying `exception: false`, the options hash allows you to indicate + * By specifying a keyword argument _exception_ to +false+, you can indicate * that connect_nonblock should not raise an IO::WaitReadable or - * IO::WaitWritable exception, but return the symbol :wait_readable or - * :wait_writable instead. + * IO::WaitWritable exception, but return the symbol +:wait_readable+ or + * +:wait_writable+ instead. */ static VALUE ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self) @@ -1656,10 +1790,10 @@ ossl_ssl_accept(VALUE self) * retry * end * - * By specifying `exception: false`, the options hash allows you to indicate + * By specifying a keyword argument _exception_ to +false+, you can indicate * that accept_nonblock should not raise an IO::WaitReadable or - * IO::WaitWritable exception, but return the symbol :wait_readable or - * :wait_writable instead. + * IO::WaitWritable exception, but return the symbol +:wait_readable+ or + * +:wait_writable+ instead. */ static VALUE ossl_ssl_accept_nonblock(int argc, VALUE *argv, VALUE self) @@ -1717,12 +1851,12 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); - rb_io_wait_writable(FPTR_TO_FD(fptr)); + rb_io_wait_writable(fptr->fd); continue; case SSL_ERROR_WANT_READ: if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); - rb_io_wait_readable(FPTR_TO_FD(fptr)); + rb_io_wait_readable(fptr->fd); continue; case SSL_ERROR_SYSCALL: if (!ERR_peek_error()) { @@ -1765,7 +1899,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) * ssl.sysread(length) => string * ssl.sysread(length, buffer) => buffer * - * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ + * Reads _length_ bytes from the SSL connection. If a pre-allocated _buffer_ * is provided the data will be written into it. */ static VALUE @@ -1784,7 +1918,7 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self) * block. If "exception: false" is passed, this method returns a symbol of * :wait_readable, :wait_writable, or nil, rather than raising an exception. * - * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ + * Reads _length_ bytes from the SSL connection. If a pre-allocated _buffer_ * is provided the data will be written into it. */ static VALUE @@ -1821,12 +1955,12 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); - rb_io_wait_writable(FPTR_TO_FD(fptr)); + rb_io_wait_writable(fptr->fd); continue; case SSL_ERROR_WANT_READ: if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); - rb_io_wait_readable(FPTR_TO_FD(fptr)); + rb_io_wait_readable(fptr->fd); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(0); @@ -1854,7 +1988,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) * call-seq: * ssl.syswrite(string) => Integer * - * Writes +string+ to the SSL connection. + * Writes _string_ to the SSL connection. */ static VALUE ossl_ssl_write(VALUE self, VALUE str) @@ -1866,7 +2000,7 @@ ossl_ssl_write(VALUE self, VALUE str) * call-seq: * ssl.syswrite_nonblock(string) => Integer * - * Writes +string+ to the SSL connection in a non-blocking manner. Raises an + * Writes _string_ to the SSL connection in a non-blocking manner. Raises an * SSLError if writing would block. */ static VALUE @@ -2010,22 +2144,21 @@ ossl_ssl_get_version(VALUE self) } /* -* call-seq: -* ssl.cipher => [name, version, bits, alg_bits] -* -* The cipher being used for the current connection -*/ + * call-seq: + * ssl.cipher -> nil or [name, version, bits, alg_bits] + * + * Returns the cipher suite actually used in the current session, or nil if + * no session has been established. + */ static VALUE ossl_ssl_get_cipher(VALUE self) { SSL *ssl; - SSL_CIPHER *cipher; + const SSL_CIPHER *cipher; GetSSL(self, ssl); - - cipher = (SSL_CIPHER *)SSL_get_current_cipher(ssl); - - return ossl_ssl_cipher_to_ary(cipher); + cipher = SSL_get_current_cipher(ssl); + return cipher ? ossl_ssl_cipher_to_ary(cipher) : Qnil; } /* @@ -2071,7 +2204,7 @@ ossl_ssl_pending(VALUE self) * call-seq: * ssl.session_reused? -> true | false * - * Returns true if a reused session was negotiated during the handshake. + * Returns +true+ if a reused session was negotiated during the handshake. */ static VALUE ossl_ssl_session_reused(VALUE self) @@ -2096,7 +2229,7 @@ ossl_ssl_set_session(VALUE self, VALUE arg1) SSL_SESSION *sess; GetSSL(self, ssl); - SafeGetSSLSession(arg1, sess); + GetSSLSession(arg1, sess); if (SSL_set_session(ssl, sess) != 1) ossl_raise(eSSLError, "SSL_set_session"); @@ -2104,7 +2237,6 @@ ossl_ssl_set_session(VALUE self, VALUE arg1) return arg1; } -#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME /* * call-seq: * ssl.hostname = hostname -> hostname @@ -2131,7 +2263,6 @@ ossl_ssl_set_hostname(VALUE self, VALUE arg) return arg; } -#endif /* * call-seq: @@ -2175,7 +2306,7 @@ ossl_ssl_get_client_ca_list(VALUE self) return ossl_x509name_sk2ary(ca); } -# ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB +# ifndef OPENSSL_NO_NEXTPROTONEG /* * call-seq: * ssl.npn_protocol => String | nil @@ -2251,9 +2382,6 @@ ossl_ssl_tmp_key(VALUE self) void Init_ossl_ssl(void) { - int i; - VALUE ary; - #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); @@ -2261,11 +2389,23 @@ Init_ossl_ssl(void) rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); #endif + id_call = rb_intern("call"); ID_callback_state = rb_intern("callback_state"); - ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_vcb_idx",0,0,0); - ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_store_p",0,0,0); - ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_ptr_idx",0,0,0); + ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_vcb_idx", 0, 0, 0); + if (ossl_ssl_ex_vcb_idx < 0) + ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); + ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_ptr_idx", 0, 0, 0); + if (ossl_ssl_ex_ptr_idx < 0) + ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index"); + ossl_sslctx_ex_ptr_idx = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_ptr_idx", 0, 0, 0); + if (ossl_sslctx_ex_ptr_idx < 0) + ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); +#if !defined(HAVE_X509_STORE_UP_REF) + ossl_sslctx_ex_store_p = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_store_p", 0, 0, 0); + if (ossl_sslctx_ex_store_p < 0) + ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index"); +#endif /* Document-module: OpenSSL::SSL * @@ -2281,7 +2421,7 @@ Init_ossl_ssl(void) * This module contains configuration information about the SSL extension, * for example if socket support is enabled, or the host name TLS extension * is enabled. Constants in this module will always be defined, but contain - * `true` or `false` values depending on the configuration of your OpenSSL + * +true+ or +false+ values depending on the configuration of your OpenSSL * installation. */ mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig"); @@ -2313,11 +2453,17 @@ Init_ossl_ssl(void) /* * Context certificate + * + * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated. + * It is recommended to use #add_certificate instead. */ rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse); /* * Context private key + * + * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated. + * It is recommended to use #add_certificate instead. */ rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse); @@ -2365,12 +2511,12 @@ Init_ossl_ssl(void) * A callback for additional certificate verification. The callback is * invoked for each certificate in the chain. * - * The callback is invoked with two values. +preverify_ok+ indicates - * indicates if the verification was passed (true) or not (false). - * +store_context+ is an OpenSSL::X509::StoreContext containing the + * The callback is invoked with two values. _preverify_ok_ indicates + * indicates if the verification was passed (+true+) or not (+false+). + * _store_context_ is an OpenSSL::X509::StoreContext containing the * context used for certificate verification. * - * If the callback returns false, the chain verification is immediately + * If the callback returns +false+, the chain verification is immediately * stopped and a bad_certificate alert is then sent. */ rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse); @@ -2391,6 +2537,9 @@ Init_ossl_ssl(void) /* * An Array of extra X509 certificates to be added to the certificate * chain. + * + * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated. + * It is recommended to use #add_certificate instead. */ rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse); @@ -2437,7 +2586,7 @@ Init_ossl_ssl(void) /* * A callback invoked when a new session was negotiated. * - * The callback is invoked with an SSLSocket. If false is returned the + * The callback is invoked with an SSLSocket. If +false+ is returned the * session will be removed from the internal cache. */ rb_attr(cSSLContext, rb_intern("session_new_cb"), 1, 1, Qfalse); @@ -2453,17 +2602,7 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse); -#ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue); -#else - rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qfalse); -#endif - -#ifdef TLS_DH_anon_WITH_AES_256_GCM_SHA384 - rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qtrue); -#else - rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qfalse); -#endif /* * A callback invoked whenever a new handshake is initiated. May be used @@ -2487,7 +2626,7 @@ Init_ossl_ssl(void) * end */ rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse); -#ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB +#ifndef OPENSSL_NO_NEXTPROTONEG /* * An Enumerable of Strings. Each String represents a protocol to be * advertised as the list of supported protocols for Next Protocol @@ -2553,12 +2692,17 @@ Init_ossl_ssl(void) rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); rb_define_alias(cSSLContext, "ssl_timeout=", "timeout="); - rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1); + rb_define_private_method(cSSLContext, "set_minmax_proto_version", + ossl_sslctx_set_minmax_proto_version, 2); 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, "security_level", ossl_sslctx_get_security_level, 0); rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1); +#ifdef SSL_MODE_SEND_FALLBACK_SCSV + rb_define_method(cSSLContext, "enable_fallback_scsv", ossl_sslctx_enable_fallback_scsv, 0); +#endif + rb_define_method(cSSLContext, "add_certificate", ossl_sslctx_add_certificate, -1); rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0); rb_define_alias(cSSLContext, "freeze", "setup"); @@ -2621,14 +2765,6 @@ Init_ossl_ssl(void) rb_define_method(cSSLContext, "options", ossl_sslctx_get_options, 0); rb_define_method(cSSLContext, "options=", ossl_sslctx_set_options, 1); - ary = rb_ary_new2(numberof(ossl_ssl_method_tab)); - for (i = 0; i < numberof(ossl_ssl_method_tab); i++) { - rb_ary_push(ary, ID2SYM(rb_intern(ossl_ssl_method_tab[i].name))); - } - rb_obj_freeze(ary); - /* The list of available SSL/TLS methods */ - rb_define_const(cSSLContext, "METHODS", ary); - /* * Document-class: OpenSSL::SSL::SSLSocket */ @@ -2662,67 +2798,120 @@ 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_SSL_CTX_SET_NEXT_PROTO_SELECT_CB +# ifndef OPENSSL_NO_NEXTPROTONEG rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif #endif -#define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, LONG2NUM(SSL_##x)) + rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE)); + rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER)); + rb_define_const(mSSL, "VERIFY_FAIL_IF_NO_PEER_CERT", INT2NUM(SSL_VERIFY_FAIL_IF_NO_PEER_CERT)); + rb_define_const(mSSL, "VERIFY_CLIENT_ONCE", INT2NUM(SSL_VERIFY_CLIENT_ONCE)); - ossl_ssl_def_const(VERIFY_NONE); - ossl_ssl_def_const(VERIFY_PEER); - ossl_ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT); - ossl_ssl_def_const(VERIFY_CLIENT_ONCE); - /* Introduce constants included in OP_ALL. These constants are mostly for - * unset some bits in OP_ALL such as; - * ctx.options = OP_ALL & ~OP_DONT_INSERT_EMPTY_FRAGMENTS - */ - ossl_ssl_def_const(OP_MICROSOFT_SESS_ID_BUG); - ossl_ssl_def_const(OP_NETSCAPE_CHALLENGE_BUG); - 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); - ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING); - 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); - ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); - ossl_ssl_def_const(OP_SINGLE_ECDH_USE); - ossl_ssl_def_const(OP_SINGLE_DH_USE); - ossl_ssl_def_const(OP_EPHEMERAL_RSA); - ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE); - ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG); - ossl_ssl_def_const(OP_NO_SSLv2); - ossl_ssl_def_const(OP_NO_SSLv3); - ossl_ssl_def_const(OP_NO_TLSv1); -#if defined(SSL_OP_NO_TLSv1_1) - ossl_ssl_def_const(OP_NO_TLSv1_1); + rb_define_const(mSSL, "OP_ALL", ULONG2NUM(SSL_OP_ALL)); + rb_define_const(mSSL, "OP_LEGACY_SERVER_CONNECT", ULONG2NUM(SSL_OP_LEGACY_SERVER_CONNECT)); +#ifdef SSL_OP_TLSEXT_PADDING /* OpenSSL 1.0.1h and OpenSSL 1.0.2 */ + rb_define_const(mSSL, "OP_TLSEXT_PADDING", ULONG2NUM(SSL_OP_TLSEXT_PADDING)); +#endif +#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG /* OpenSSL 1.0.1f and OpenSSL 1.0.2 */ + rb_define_const(mSSL, "OP_SAFARI_ECDHE_ECDSA_BUG", ULONG2NUM(SSL_OP_SAFARI_ECDHE_ECDSA_BUG)); +#endif +#ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_ALLOW_NO_DHE_KEX", ULONG2NUM(SSL_OP_ALLOW_NO_DHE_KEX)); +#endif + rb_define_const(mSSL, "OP_DONT_INSERT_EMPTY_FRAGMENTS", ULONG2NUM(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)); + rb_define_const(mSSL, "OP_NO_TICKET", ULONG2NUM(SSL_OP_NO_TICKET)); + rb_define_const(mSSL, "OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)); + rb_define_const(mSSL, "OP_NO_COMPRESSION", ULONG2NUM(SSL_OP_NO_COMPRESSION)); + rb_define_const(mSSL, "OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", ULONG2NUM(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)); +#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_NO_ENCRYPT_THEN_MAC", ULONG2NUM(SSL_OP_NO_ENCRYPT_THEN_MAC)); #endif -#if defined(SSL_OP_NO_TLSv1_2) - ossl_ssl_def_const(OP_NO_TLSv1_2); + rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE)); + rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG)); +#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION)); #endif -#if defined(SSL_OP_NO_TICKET) - ossl_ssl_def_const(OP_NO_TICKET); + rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG)); + + rb_define_const(mSSL, "OP_NO_SSLv3", ULONG2NUM(SSL_OP_NO_SSLv3)); + rb_define_const(mSSL, "OP_NO_TLSv1", ULONG2NUM(SSL_OP_NO_TLSv1)); + rb_define_const(mSSL, "OP_NO_TLSv1_1", ULONG2NUM(SSL_OP_NO_TLSv1_1)); + rb_define_const(mSSL, "OP_NO_TLSv1_2", ULONG2NUM(SSL_OP_NO_TLSv1_2)); +#ifdef SSL_OP_NO_TLSv1_3 /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_NO_TLSv1_3", ULONG2NUM(SSL_OP_NO_TLSv1_3)); #endif -#if defined(SSL_OP_NO_COMPRESSION) - ossl_ssl_def_const(OP_NO_COMPRESSION); + + /* SSL_OP_* flags for DTLS */ +#if 0 + rb_define_const(mSSL, "OP_NO_QUERY_MTU", ULONG2NUM(SSL_OP_NO_QUERY_MTU)); + rb_define_const(mSSL, "OP_COOKIE_EXCHANGE", ULONG2NUM(SSL_OP_COOKIE_EXCHANGE)); + rb_define_const(mSSL, "OP_CISCO_ANYCONNECT", ULONG2NUM(SSL_OP_CISCO_ANYCONNECT)); #endif - ossl_ssl_def_const(OP_PKCS1_CHECK_1); - ossl_ssl_def_const(OP_PKCS1_CHECK_2); - ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG); - ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG); + + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_MICROSOFT_SESS_ID_BUG", ULONG2NUM(SSL_OP_MICROSOFT_SESS_ID_BUG)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_NETSCAPE_CHALLENGE_BUG", ULONG2NUM(SSL_OP_NETSCAPE_CHALLENGE_BUG)); + /* Deprecated in OpenSSL 0.9.8q and 1.0.0c. */ + rb_define_const(mSSL, "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG", ULONG2NUM(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG)); + /* Deprecated in OpenSSL 1.0.1h and 1.0.2. */ + rb_define_const(mSSL, "OP_SSLREF2_REUSE_CERT_TYPE_BUG", ULONG2NUM(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_MICROSOFT_BIG_SSLV3_BUFFER", ULONG2NUM(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)); + /* Deprecated in OpenSSL 0.9.7h and 0.9.8b. */ + rb_define_const(mSSL, "OP_MSIE_SSLV2_RSA_PADDING", ULONG2NUM(SSL_OP_MSIE_SSLV2_RSA_PADDING)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_SSLEAY_080_CLIENT_DH_BUG", ULONG2NUM(SSL_OP_SSLEAY_080_CLIENT_DH_BUG)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_TLS_D5_BUG", ULONG2NUM(SSL_OP_TLS_D5_BUG)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_TLS_BLOCK_PADDING_BUG", ULONG2NUM(SSL_OP_TLS_BLOCK_PADDING_BUG)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_SINGLE_ECDH_USE", ULONG2NUM(SSL_OP_SINGLE_ECDH_USE)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_SINGLE_DH_USE", ULONG2NUM(SSL_OP_SINGLE_DH_USE)); + /* Deprecated in OpenSSL 1.0.1k and 1.0.2. */ + rb_define_const(mSSL, "OP_EPHEMERAL_RSA", ULONG2NUM(SSL_OP_EPHEMERAL_RSA)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_NO_SSLv2", ULONG2NUM(SSL_OP_NO_SSLv2)); + /* Deprecated in OpenSSL 1.0.1. */ + rb_define_const(mSSL, "OP_PKCS1_CHECK_1", ULONG2NUM(SSL_OP_PKCS1_CHECK_1)); + /* Deprecated in OpenSSL 1.0.1. */ + rb_define_const(mSSL, "OP_PKCS1_CHECK_2", ULONG2NUM(SSL_OP_PKCS1_CHECK_2)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_NETSCAPE_CA_DN_BUG", ULONG2NUM(SSL_OP_NETSCAPE_CA_DN_BUG)); + /* Deprecated in OpenSSL 1.1.0. */ + rb_define_const(mSSL, "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", ULONG2NUM(SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG)); + + + /* + * SSL/TLS version constants. Used by SSLContext#min_version= and + * #max_version= + */ + /* SSL 2.0 */ + rb_define_const(mSSL, "SSL2_VERSION", INT2NUM(SSL2_VERSION)); + /* SSL 3.0 */ + rb_define_const(mSSL, "SSL3_VERSION", INT2NUM(SSL3_VERSION)); + /* TLS 1.0 */ + rb_define_const(mSSL, "TLS1_VERSION", INT2NUM(TLS1_VERSION)); + /* TLS 1.1 */ + rb_define_const(mSSL, "TLS1_1_VERSION", INT2NUM(TLS1_1_VERSION)); + /* TLS 1.2 */ + rb_define_const(mSSL, "TLS1_2_VERSION", INT2NUM(TLS1_2_VERSION)); +#ifdef TLS1_3_VERSION /* OpenSSL 1.1.1 */ + /* TLS 1.3 */ + rb_define_const(mSSL, "TLS1_3_VERSION", INT2NUM(TLS1_3_VERSION)); +#endif + sym_exception = ID2SYM(rb_intern("exception")); sym_wait_readable = ID2SYM(rb_intern("wait_readable")); diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h index c1a3cd6c..535c5609 100644 --- a/ext/openssl/ossl_ssl.h +++ b/ext/openssl/ossl_ssl.h @@ -24,11 +24,6 @@ } \ } while (0) -#define SafeGetSSLSession(obj, sess) do { \ - OSSL_Check_Kind((obj), cSSLSession); \ - GetSSLSession((obj), (sess)); \ -} while (0) - extern const rb_data_type_t ossl_ssl_type; extern const rb_data_type_t ossl_ssl_session_type; extern VALUE mSSL; diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c index 661f53ee..55140873 100644 --- a/ext/openssl/ossl_ssl_session.c +++ b/ext/openssl/ossl_ssl_session.c @@ -80,7 +80,7 @@ ossl_ssl_session_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); sess = RTYPEDDATA_DATA(self); /* XXX */ - SafeGetSSLSession(other, sess_other); + GetSSLSession(other, sess_other); sess_new = ASN1_dup((i2d_of_void *)i2d_SSL_SESSION, (d2i_of_void *)d2i_SSL_SESSION, (char *)sess_other); @@ -93,8 +93,8 @@ ossl_ssl_session_initialize_copy(VALUE self, VALUE other) return self; } -#if !defined(HAVE_SSL_SESSION_CMP) -int ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) +static int +ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) { unsigned int a_len; const unsigned char *a_sid = SSL_SESSION_get_id(a, &a_len); @@ -108,23 +108,21 @@ int ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) return CRYPTO_memcmp(a_sid, b_sid, a_len); } -#define SSL_SESSION_cmp(a, b) ossl_SSL_SESSION_cmp(a, b) -#endif /* * call-seq: * session1 == session2 -> boolean * - * Returns true if the two Session is the same, false if not. + * Returns +true+ if the two Session is the same, +false+ if not. */ static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2) { SSL_SESSION *ctx1, *ctx2; GetSSLSession(val1, ctx1); - SafeGetSSLSession(val2, ctx2); + GetSSLSession(val2, ctx2); - switch (SSL_SESSION_cmp(ctx1, ctx2)) { + switch (ossl_SSL_SESSION_cmp(ctx1, ctx2)) { case 0: return Qtrue; default: return Qfalse; } @@ -319,7 +317,7 @@ void Init_ossl_ssl_session(void) rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); - rb_define_copy_func(cSSLSession, ossl_ssl_session_initialize_copy); + rb_define_method(cSSLSession, "initialize_copy", ossl_ssl_session_initialize_copy, 1); rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); diff --git a/ext/openssl/ossl_version.h b/ext/openssl/ossl_version.h index 7725bc05..4167c9c8 100644 --- a/ext/openssl/ossl_version.h +++ b/ext/openssl/ossl_version.h @@ -10,6 +10,6 @@ #if !defined(_OSSL_VERSION_H_) #define _OSSL_VERSION_H_ -#define OSSL_VERSION "2.0.6" +#define OSSL_VERSION "2.1.0" #endif /* _OSSL_VERSION_H_ */ diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c index 19ec274a..8a061b06 100644 --- a/ext/openssl/ossl_x509.c +++ b/ext/openssl/ossl_x509.c @@ -20,15 +20,10 @@ ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) { time_t sec; -#if defined(HAVE_ASN1_TIME_ADJ) int off_days; ossl_time_split(time, &sec, &off_days); return X509_time_adj_ex(s, off_days, 0, &sec); -#else - sec = time_to_time_t(time); - return X509_time_adj(s, 0, &sec); -#endif } void @@ -112,21 +107,15 @@ Init_ossl_x509(void) DefX509Const(V_FLAG_INHIBIT_MAP); /* Set by Store#flags= and StoreContext#flags=. */ DefX509Const(V_FLAG_NOTIFY_POLICY); -#if defined(X509_V_FLAG_EXTENDED_CRL_SUPPORT) /* Set by Store#flags= and StoreContext#flags=. Enables some additional * features including support for indirect signed CRLs. */ DefX509Const(V_FLAG_EXTENDED_CRL_SUPPORT); -#endif -#if defined(X509_V_FLAG_USE_DELTAS) /* Set by Store#flags= and StoreContext#flags=. Uses delta CRLs. If not * specified, deltas are ignored. */ DefX509Const(V_FLAG_USE_DELTAS); -#endif -#if defined(X509_V_FLAG_CHECK_SS_SIGNATURE) /* Set by Store#flags= and StoreContext#flags=. Enables checking of the * signature of the root self-signed CA. */ DefX509Const(V_FLAG_CHECK_SS_SIGNATURE); -#endif #if defined(X509_V_FLAG_TRUSTED_FIRST) /* Set by Store#flags= and StoreContext#flags=. When constructing a * certificate chain, search the Store first for the issuer certificate. @@ -161,10 +150,8 @@ Init_ossl_x509(void) DefX509Const(PURPOSE_ANY); /* Set by Store#purpose=. OCSP helper. */ DefX509Const(PURPOSE_OCSP_HELPER); -#if defined(X509_PURPOSE_TIMESTAMP_SIGN) /* Set by Store#purpose=. Time stamps signer. */ DefX509Const(PURPOSE_TIMESTAMP_SIGN); -#endif DefX509Const(TRUST_COMPAT); DefX509Const(TRUST_SSL_CLIENT); @@ -173,9 +160,7 @@ Init_ossl_x509(void) DefX509Const(TRUST_OBJECT_SIGN); DefX509Const(TRUST_OCSP_SIGN); DefX509Const(TRUST_OCSP_REQUEST); -#if defined(X509_TRUST_TSA) DefX509Const(TRUST_TSA); -#endif DefX509Default(CERT_AREA, cert_area); DefX509Default(CERT_DIR, cert_dir); diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h index a60f7c3d..4fadfa6b 100644 --- a/ext/openssl/ossl_x509.h +++ b/ext/openssl/ossl_x509.h @@ -41,7 +41,6 @@ extern VALUE cX509Cert; extern VALUE eX509CertError; VALUE ossl_x509_new(X509 *); -VALUE ossl_x509_new_from_file(VALUE); X509 *GetX509CertPtr(VALUE); X509 *DupX509CertPtr(VALUE); void Init_ossl_x509cert(void); @@ -54,7 +53,6 @@ extern VALUE eX509CRLError; VALUE ossl_x509crl_new(X509_CRL *); X509_CRL *GetX509CRLPtr(VALUE); -X509_CRL *DupX509CRLPtr(VALUE); void Init_ossl_x509crl(void); /* @@ -84,9 +82,7 @@ void Init_ossl_x509name(void); extern VALUE cX509Req; extern VALUE eX509ReqError; -VALUE ossl_x509req_new(X509_REQ *); X509_REQ *GetX509ReqPtr(VALUE); -X509_REQ *DupX509ReqPtr(VALUE); void Init_ossl_x509req(void); /* @@ -106,11 +102,8 @@ extern VALUE cX509Store; extern VALUE cX509StoreContext; extern VALUE eX509StoreError; -VALUE ossl_x509store_new(X509_STORE *); X509_STORE *GetX509StorePtr(VALUE); -X509_STORE *DupX509StorePtr(VALUE); -X509_STORE_CTX *GetX509StCtxtPtr(VALUE); void Init_ossl_x509store(void); /* diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c index ae0b347b..60846cfe 100644 --- a/ext/openssl/ossl_x509attr.c +++ b/ext/openssl/ossl_x509attr.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509Attr(obj, attr) do { \ - OSSL_Check_Kind((obj), cX509Attr); \ - GetX509Attr((obj), (attr)); \ -} while (0) /* * Classes @@ -76,7 +72,7 @@ GetX509AttrPtr(VALUE obj) { X509_ATTRIBUTE *attr; - SafeGetX509Attr(obj, attr); + GetX509Attr(obj, attr); return attr; } @@ -134,7 +130,7 @@ ossl_x509attr_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetX509Attr(self, attr); - SafeGetX509Attr(other, attr_other); + GetX509Attr(other, attr_other); attr_new = X509_ATTRIBUTE_dup(attr_other); if (!attr_new) @@ -319,7 +315,7 @@ Init_ossl_x509attr(void) cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject); rb_define_alloc_func(cX509Attr, ossl_x509attr_alloc); rb_define_method(cX509Attr, "initialize", ossl_x509attr_initialize, -1); - rb_define_copy_func(cX509Attr, ossl_x509attr_initialize_copy); + rb_define_method(cX509Attr, "initialize_copy", ossl_x509attr_initialize_copy, 1); rb_define_method(cX509Attr, "oid=", ossl_x509attr_set_oid, 1); rb_define_method(cX509Attr, "oid", ossl_x509attr_get_oid, 0); rb_define_method(cX509Attr, "value=", ossl_x509attr_set_value, 1); diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index 91c25c4e..40542c4a 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509(obj, x509) do { \ - OSSL_Check_Kind((obj), cX509Cert); \ - GetX509((obj), (x509)); \ -} while (0) /* * Classes @@ -71,46 +67,12 @@ ossl_x509_new(X509 *x509) return obj; } -VALUE -ossl_x509_new_from_file(VALUE filename) -{ - X509 *x509; - FILE *fp; - VALUE obj; - - rb_check_safe_obj(filename); - obj = NewX509(cX509Cert); - if (!(fp = fopen(StringValueCStr(filename), "r"))) { - ossl_raise(eX509CertError, "%s", strerror(errno)); - } - rb_fd_fix_cloexec(fileno(fp)); - x509 = PEM_read_X509(fp, NULL, NULL, NULL); - /* - * prepare for DER... -#if !defined(OPENSSL_NO_FP_API) - if (!x509) { - (void)ERR_get_error(); - rewind(fp); - - x509 = d2i_X509_fp(fp, NULL); - } -#endif - */ - fclose(fp); - if (!x509) { - ossl_raise(eX509CertError, NULL); - } - SetX509(obj, x509); - - return obj; -} - X509 * GetX509CertPtr(VALUE obj) { X509 *x509; - SafeGetX509(obj, x509); + GetX509(obj, x509); return x509; } @@ -120,7 +82,7 @@ DupX509CertPtr(VALUE obj) { X509 *x509; - SafeGetX509(obj, x509); + GetX509(obj, x509); X509_up_ref(x509); @@ -184,7 +146,7 @@ ossl_x509_copy(VALUE self, VALUE other) if (self == other) return self; GetX509(self, a); - SafeGetX509(other, b); + GetX509(other, b); x509 = X509_dup(b); if (!x509) ossl_raise(eX509CertError, NULL); @@ -478,7 +440,7 @@ ossl_x509_set_not_before(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_set_notBefore(x509, asn1time)) { + if (!X509_set1_notBefore(x509, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CertError, "X509_set_notBefore"); } @@ -517,7 +479,7 @@ ossl_x509_set_not_after(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_set_notAfter(x509, asn1time)) { + if (!X509_set1_notAfter(x509, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CertError, "X509_set_notAfter"); } @@ -574,7 +536,7 @@ ossl_x509_sign(VALUE self, VALUE key, VALUE digest) const EVP_MD *md; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); GetX509(self, x509); if (!X509_sign(x509, pkey, md)) { ossl_raise(eX509CertError, NULL); @@ -587,7 +549,8 @@ ossl_x509_sign(VALUE self, VALUE key, VALUE digest) * call-seq: * cert.verify(key) => true | false * - * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' + * Verifies the signature of the certificate, with the public key _key_. _key_ + * must be an instance of OpenSSL::PKey. */ static VALUE ossl_x509_verify(VALUE self, VALUE key) @@ -611,9 +574,10 @@ ossl_x509_verify(VALUE self, VALUE key) /* * call-seq: - * cert.check_private_key(key) + * cert.check_private_key(key) -> true | false * - * Checks if 'key' is PRIV key for this cert + * Returns +true+ if _key_ is the corresponding private key to the Subject + * Public Key Information, +false+ otherwise. */ static VALUE ossl_x509_check_private_key(VALUE self, VALUE key) @@ -721,6 +685,26 @@ ossl_x509_inspect(VALUE self) } /* + * call-seq: + * cert1 == cert2 -> true | false + * + * Compares the two certificates. Note that this takes into account all fields, + * not just the issuer name and the serial number. + */ +static VALUE +ossl_x509_eq(VALUE self, VALUE other) +{ + X509 *a, *b; + + GetX509(self, a); + if (!rb_obj_is_kind_of(other, cX509Cert)) + return Qfalse; + GetX509(other, b); + + return !X509_cmp(a, b) ? Qtrue : Qfalse; +} + +/* * INIT */ void @@ -830,7 +814,7 @@ Init_ossl_x509cert(void) rb_define_alloc_func(cX509Cert, ossl_x509_alloc); rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1); - rb_define_copy_func(cX509Cert, ossl_x509_copy); + rb_define_method(cX509Cert, "initialize_copy", ossl_x509_copy, 1); rb_define_method(cX509Cert, "to_der", ossl_x509_to_der, 0); rb_define_method(cX509Cert, "to_pem", ossl_x509_to_pem, 0); @@ -858,4 +842,5 @@ Init_ossl_x509cert(void) rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1); rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1); rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0); + rb_define_method(cX509Cert, "==", ossl_x509_eq, 1); } diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index add72c6c..b0badf45 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509CRL(obj, crl) do { \ - OSSL_Check_Kind((obj), cX509CRL); \ - GetX509CRL((obj), (crl)); \ -} while (0) /* * Classes @@ -56,18 +52,7 @@ GetX509CRLPtr(VALUE obj) { X509_CRL *crl; - SafeGetX509CRL(obj, crl); - - return crl; -} - -X509_CRL * -DupX509CRLPtr(VALUE obj) -{ - X509_CRL *crl; - - SafeGetX509CRL(obj, crl); - X509_CRL_up_ref(crl); + GetX509CRL(obj, crl); return crl; } @@ -137,7 +122,7 @@ ossl_x509crl_copy(VALUE self, VALUE other) rb_check_frozen(self); if (self == other) return self; GetX509CRL(self, a); - SafeGetX509CRL(other, b); + GetX509CRL(other, b); if (!(crl = X509_CRL_dup(b))) { ossl_raise(eX509CRLError, NULL); } @@ -223,10 +208,14 @@ static VALUE ossl_x509crl_get_last_update(VALUE self) { X509_CRL *crl; + const ASN1_TIME *time; GetX509CRL(self, crl); + time = X509_CRL_get0_lastUpdate(crl); + if (!time) + return Qnil; - return asn1time_to_time(X509_CRL_get0_lastUpdate(crl)); + return asn1time_to_time(time); } static VALUE @@ -237,7 +226,7 @@ ossl_x509crl_set_last_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_CRL_set_lastUpdate(crl, asn1time)) { + if (!X509_CRL_set1_lastUpdate(crl, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); } @@ -250,10 +239,14 @@ static VALUE ossl_x509crl_get_next_update(VALUE self) { X509_CRL *crl; + const ASN1_TIME *time; GetX509CRL(self, crl); + time = X509_CRL_get0_nextUpdate(crl); + if (!time) + return Qnil; - return asn1time_to_time(X509_CRL_get0_nextUpdate(crl)); + return asn1time_to_time(time); } static VALUE @@ -264,7 +257,7 @@ ossl_x509crl_set_next_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_CRL_set_nextUpdate(crl, asn1time)) { + if (!X509_CRL_set1_nextUpdate(crl, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); } @@ -354,7 +347,7 @@ ossl_x509crl_sign(VALUE self, VALUE key, VALUE digest) GetX509CRL(self, crl); pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); if (!X509_CRL_sign(crl, pkey, md)) { ossl_raise(eX509CRLError, NULL); } @@ -523,7 +516,7 @@ Init_ossl_x509crl(void) rb_define_alloc_func(cX509CRL, ossl_x509crl_alloc); rb_define_method(cX509CRL, "initialize", ossl_x509crl_initialize, -1); - rb_define_copy_func(cX509CRL, ossl_x509crl_copy); + rb_define_method(cX509CRL, "initialize_copy", ossl_x509crl_copy, 1); rb_define_method(cX509CRL, "version", ossl_x509crl_get_version, 0); rb_define_method(cX509CRL, "version=", ossl_x509crl_set_version, 1); diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c index b92b0786..2d9a7a31 100644 --- a/ext/openssl/ossl_x509ext.c +++ b/ext/openssl/ossl_x509ext.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509Ext(obj, ext) do { \ - OSSL_Check_Kind((obj), cX509Ext); \ - GetX509Ext((obj), (ext)); \ -} while (0) #define MakeX509ExtFactory(klass, obj, ctx) do { \ (obj) = TypedData_Wrap_Struct((klass), &ossl_x509extfactory_type, 0); \ if (!((ctx) = OPENSSL_malloc(sizeof(X509V3_CTX)))) \ @@ -90,7 +86,7 @@ GetX509ExtPtr(VALUE obj) { X509_EXTENSION *ext; - SafeGetX509Ext(obj, ext); + GetX509Ext(obj, ext); return ext; } @@ -263,15 +259,15 @@ ossl_x509ext_alloc(VALUE klass) /* * call-seq: - * OpenSSL::X509::Extension.new asn1 - * OpenSSL::X509::Extension.new name, value - * OpenSSL::X509::Extension.new name, value, critical + * OpenSSL::X509::Extension.new(der) + * OpenSSL::X509::Extension.new(oid, value) + * OpenSSL::X509::Extension.new(oid, value, critical) * * Creates an X509 extension. * - * The extension may be created from +asn1+ data or from an extension +name+ - * and +value+. The +name+ may be either an OID or an extension name. If - * +critical+ is true the extension is marked critical. + * The extension may be created from _der_ data or from an extension _oid_ + * and _value_. The _oid_ may be either an OID or an extension name. If + * _critical_ is +true+ the extension is marked critical. */ static VALUE ossl_x509ext_initialize(int argc, VALUE *argv, VALUE self) @@ -305,7 +301,7 @@ ossl_x509ext_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetX509Ext(self, ext); - SafeGetX509Ext(other, ext_other); + GetX509Ext(other, ext_other); ext_new = X509_EXTENSION_dup(ext_other); if (!ext_new) @@ -469,7 +465,7 @@ Init_ossl_x509ext(void) cX509Ext = rb_define_class_under(mX509, "Extension", rb_cObject); rb_define_alloc_func(cX509Ext, ossl_x509ext_alloc); rb_define_method(cX509Ext, "initialize", ossl_x509ext_initialize, -1); - rb_define_copy_func(cX509Ext, ossl_x509ext_initialize_copy); + rb_define_method(cX509Ext, "initialize_copy", ossl_x509ext_initialize_copy, 1); rb_define_method(cX509Ext, "oid=", ossl_x509ext_set_oid, 1); rb_define_method(cX509Ext, "value=", ossl_x509ext_set_value, 1); rb_define_method(cX509Ext, "critical=", ossl_x509ext_set_critical, 1); diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c index ac98c1b9..c900bcbe 100644 --- a/ext/openssl/ossl_x509name.c +++ b/ext/openssl/ossl_x509name.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ } while (0) -#define SafeGetX509Name(obj, name) do { \ - OSSL_Check_Kind((obj), cX509Name); \ - GetX509Name((obj), (name)); \ -} while (0) #define OBJECT_TYPE_TEMPLATE \ rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE")) @@ -81,7 +77,7 @@ GetX509NamePtr(VALUE obj) { X509_NAME *name; - SafeGetX509Name(obj, name); + GetX509Name(obj, name); return name; } @@ -135,15 +131,15 @@ ossl_x509name_init_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) * * Creates a new Name. * - * A name may be created from a DER encoded string +der+, an Array - * representing a +distinguished_name+ or a +distinguished_name+ along with a - * +template+. + * A name may be created from a DER encoded string _der_, an Array + * representing a _distinguished_name_ or a _distinguished_name_ along with a + * _template_. * * name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']] * * name = OpenSSL::X509::Name.new name.to_der * - * See add_entry for a description of the +distinguished_name+ Array's + * See add_entry for a description of the _distinguished_name_ Array's * contents */ static VALUE @@ -188,7 +184,7 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetX509Name(self, name); - SafeGetX509Name(other, name_other); + GetX509Name(other, name_other); name_new = X509_NAME_dup(name_other); if (!name_new) @@ -202,9 +198,9 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other) /* * call-seq: - * name.add_entry(oid, value [, type]) => self + * name.add_entry(oid, value [, type], loc: -1, set: 0) => self * - * Adds a new entry with the given +oid+ and +value+ to this name. The +oid+ + * Adds a new entry with the given _oid_ and _value_ to this name. The _oid_ * is an object identifier defined in ASN.1. Some common OIDs are: * * C:: Country Name @@ -213,24 +209,39 @@ ossl_x509name_initialize_copy(VALUE self, VALUE other) * O:: Organization Name * OU:: Organizational Unit Name * ST:: State or Province Name + * + * The optional keyword parameters _loc_ and _set_ specify where to insert the + * new attribute. Refer to the manpage of X509_NAME_add_entry(3) for details. + * _loc_ defaults to -1 and _set_ defaults to 0. This appends a single-valued + * RDN to the end. */ static VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self) { X509_NAME *name; - VALUE oid, value, type; + VALUE oid, value, type, opts, kwargs[2]; + static ID kwargs_ids[2]; const char *oid_name; + int loc = -1, set = 0; - rb_scan_args(argc, argv, "21", &oid, &value, &type); + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("loc"); + kwargs_ids[1] = rb_intern_const("set"); + } + rb_scan_args(argc, argv, "21:", &oid, &value, &type, &opts); + rb_get_kwargs(opts, kwargs_ids, 0, 2, kwargs); oid_name = StringValueCStr(oid); StringValue(value); if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid); + if (kwargs[0] != Qundef) + loc = NUM2INT(kwargs[0]); + if (kwargs[1] != Qundef) + set = NUM2INT(kwargs[1]); GetX509Name(self, name); if (!X509_NAME_add_entry_by_txt(name, oid_name, NUM2INT(type), - (const unsigned char *)RSTRING_PTR(value), RSTRING_LENINT(value), -1, 0)) { - ossl_raise(eX509NameError, NULL); - } - + (unsigned char *)RSTRING_PTR(value), + RSTRING_LENINT(value), loc, set)) + ossl_raise(eX509NameError, "X509_NAME_add_entry_by_txt"); return self; } @@ -249,42 +260,73 @@ ossl_x509name_to_s_old(VALUE self) return str; } +static VALUE +x509name_print(VALUE self, unsigned long iflag) +{ + X509_NAME *name; + BIO *out; + + GetX509Name(self, name); + out = BIO_new(BIO_s_mem()); + if (!out) + ossl_raise(eX509NameError, NULL); + if (!X509_NAME_print_ex(out, name, 0, iflag)) { + BIO_free(out); + ossl_raise(eX509NameError, "X509_NAME_print_ex"); + } + return ossl_membio2str(out); +} + /* * call-seq: - * name.to_s => string - * name.to_s(flags) => string + * name.to_s -> string + * name.to_s(format) -> string * - * Returns this name as a Distinguished Name string. +flags+ may be one of: + * Returns a String representation of the Distinguished Name. _format_ is + * one of: * * * OpenSSL::X509::Name::COMPAT * * OpenSSL::X509::Name::RFC2253 * * OpenSSL::X509::Name::ONELINE * * OpenSSL::X509::Name::MULTILINE + * + * If _format_ is omitted, the largely broken and traditional OpenSSL format + * is used. */ static VALUE ossl_x509name_to_s(int argc, VALUE *argv, VALUE self) { - X509_NAME *name; - VALUE flag, str; - BIO *out; - unsigned long iflag; - - rb_scan_args(argc, argv, "01", &flag); - if (NIL_P(flag)) + rb_check_arity(argc, 0, 1); + /* name.to_s(nil) was allowed */ + if (!argc || NIL_P(argv[0])) return ossl_x509name_to_s_old(self); - else iflag = NUM2ULONG(flag); - if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eX509NameError, NULL); - GetX509Name(self, name); - if (!X509_NAME_print_ex(out, name, 0, iflag)){ - BIO_free(out); - ossl_raise(eX509NameError, NULL); - } - str = ossl_membio2str(out); + else + return x509name_print(self, NUM2ULONG(argv[0])); +} +/* + * call-seq: + * name.to_utf8 -> string + * + * Returns an UTF-8 representation of the distinguished name, as specified + * in {RFC 2253}[https://www.ietf.org/rfc/rfc2253.txt]. + */ +static VALUE +ossl_x509name_to_utf8(VALUE self) +{ + VALUE str = x509name_print(self, XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB); + rb_enc_associate_index(str, rb_utf8_encindex()); return str; } +/* :nodoc: */ +static VALUE +ossl_x509name_inspect(VALUE self) +{ + return rb_enc_sprintf(rb_utf8_encoding(), "#<%"PRIsVALUE" %"PRIsVALUE">", + rb_obj_class(self), ossl_x509name_to_utf8(self)); +} + /* * call-seq: * name.to_a => [[name, data, type], ...] @@ -338,18 +380,18 @@ ossl_x509name_cmp0(VALUE self, VALUE other) X509_NAME *name1, *name2; GetX509Name(self, name1); - SafeGetX509Name(other, name2); + GetX509Name(other, name2); return X509_NAME_cmp(name1, name2); } /* * call-seq: - * name.cmp other => integer - * name.<=> other => integer + * name.cmp(other) -> -1 | 0 | 1 + * name <=> other -> -1 | 0 | 1 * - * Compares this Name with +other+ and returns 0 if they are the same and -1 or - * +1 if they are greater or less than each other respectively. + * Compares this Name with _other_ and returns +0+ if they are the same and +-1+ + * or ++1+ if they are greater or less than each other respectively. */ static VALUE ossl_x509name_cmp(VALUE self, VALUE other) @@ -365,9 +407,9 @@ ossl_x509name_cmp(VALUE self, VALUE other) /* * call-seq: - * name.eql? other => boolean + * name.eql?(other) -> true | false * - * Returns true if +name+ and +other+ refer to the same hash key. + * Returns true if _name_ and _other_ refer to the same hash key. */ static VALUE ossl_x509name_eql(VALUE self, VALUE other) @@ -398,7 +440,6 @@ ossl_x509name_hash(VALUE self) return ULONG2NUM(hash); } -#ifdef HAVE_X509_NAME_HASH_OLD /* * call-seq: * name.hash_old => integer @@ -417,7 +458,6 @@ ossl_x509name_hash_old(VALUE self) return ULONG2NUM(hash); } -#endif /* * call-seq: @@ -478,17 +518,17 @@ Init_ossl_x509name(void) rb_define_alloc_func(cX509Name, ossl_x509name_alloc); rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1); - rb_define_copy_func(cX509Name, ossl_x509name_initialize_copy); + rb_define_method(cX509Name, "initialize_copy", ossl_x509name_initialize_copy, 1); rb_define_method(cX509Name, "add_entry", ossl_x509name_add_entry, -1); rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, -1); + rb_define_method(cX509Name, "to_utf8", ossl_x509name_to_utf8, 0); + rb_define_method(cX509Name, "inspect", ossl_x509name_inspect, 0); rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0); rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1); rb_define_alias(cX509Name, "<=>", "cmp"); rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1); rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0); -#ifdef HAVE_X509_NAME_HASH_OLD rb_define_method(cX509Name, "hash_old", ossl_x509name_hash_old, 0); -#endif rb_define_method(cX509Name, "to_der", ossl_x509name_to_der, 0); utf8str = INT2NUM(V_ASN1_UTF8STRING); diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index 0f7fecdc..2c20042a 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509Req(obj, req) do { \ - OSSL_Check_Kind((obj), cX509Req); \ - GetX509Req((obj), (req)); \ -} while (0) /* * Classes @@ -51,49 +47,16 @@ static const rb_data_type_t ossl_x509req_type = { /* * Public functions */ -VALUE -ossl_x509req_new(X509_REQ *req) -{ - X509_REQ *new; - VALUE obj; - - obj = NewX509Req(cX509Req); - if (!req) { - new = X509_REQ_new(); - } else { - new = X509_REQ_dup(req); - } - if (!new) { - ossl_raise(eX509ReqError, NULL); - } - SetX509Req(obj, new); - - return obj; -} - X509_REQ * GetX509ReqPtr(VALUE obj) { X509_REQ *req; - SafeGetX509Req(obj, req); + GetX509Req(obj, req); return req; } -X509_REQ * -DupX509ReqPtr(VALUE obj) -{ - X509_REQ *req, *new; - - SafeGetX509Req(obj, req); - if (!(new = X509_REQ_dup(req))) { - ossl_raise(eX509ReqError, NULL); - } - - return new; -} - /* * Private functions */ @@ -145,7 +108,7 @@ ossl_x509req_copy(VALUE self, VALUE other) rb_check_frozen(self); if (self == other) return self; GetX509Req(self, a); - SafeGetX509Req(other, b); + GetX509Req(other, b); if (!(req = X509_REQ_dup(b))) { ossl_raise(eX509ReqError, NULL); } @@ -346,7 +309,7 @@ ossl_x509req_sign(VALUE self, VALUE key, VALUE digest) GetX509Req(self, req); pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ - md = GetDigestPtr(digest); + md = ossl_evp_get_digestbyname(digest); if (!X509_REQ_sign(req, pkey, md)) { ossl_raise(eX509ReqError, NULL); } @@ -457,7 +420,7 @@ Init_ossl_x509req(void) rb_define_alloc_func(cX509Req, ossl_x509req_alloc); rb_define_method(cX509Req, "initialize", ossl_x509req_initialize, -1); - rb_define_copy_func(cX509Req, ossl_x509req_copy); + rb_define_method(cX509Req, "initialize_copy", ossl_x509req_copy, 1); rb_define_method(cX509Req, "to_pem", ossl_x509req_to_pem, 0); rb_define_method(cX509Req, "to_der", ossl_x509req_to_der, 0); diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c index 7960ea34..5fe68534 100644 --- a/ext/openssl/ossl_x509revoked.c +++ b/ext/openssl/ossl_x509revoked.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509Rev(obj, rev) do { \ - OSSL_Check_Kind((obj), cX509Rev); \ - GetX509Rev((obj), (rev)); \ -} while (0) /* * Classes @@ -76,7 +72,7 @@ DupX509RevokedPtr(VALUE obj) { X509_REVOKED *rev, *new; - SafeGetX509Rev(obj, rev); + GetX509Rev(obj, rev); if (!(new = X509_REVOKED_dup(rev))) { ossl_raise(eX509RevError, NULL); } @@ -116,7 +112,7 @@ ossl_x509revoked_initialize_copy(VALUE self, VALUE other) rb_check_frozen(self); GetX509Rev(self, rev); - SafeGetX509Rev(other, rev_other); + GetX509Rev(other, rev_other); rev_new = X509_REVOKED_dup(rev_other); if (!rev_new) @@ -159,10 +155,14 @@ static VALUE ossl_x509revoked_get_time(VALUE self) { X509_REVOKED *rev; + const ASN1_TIME *time; GetX509Rev(self, rev); + time = X509_REVOKED_get0_revocationDate(rev); + if (!time) + return Qnil; - return asn1time_to_time(X509_REVOKED_get0_revocationDate(rev)); + return asn1time_to_time(time); } static VALUE @@ -249,6 +249,26 @@ ossl_x509revoked_add_extension(VALUE self, VALUE ext) return ext; } +static VALUE +ossl_x509revoked_to_der(VALUE self) +{ + X509_REVOKED *rev; + VALUE str; + int len; + unsigned char *p; + + GetX509Rev(self, rev); + len = i2d_X509_REVOKED(rev, NULL); + if (len <= 0) + ossl_raise(eX509RevError, "i2d_X509_REVOKED"); + str = rb_str_new(NULL, len); + p = (unsigned char *)RSTRING_PTR(str); + if (i2d_X509_REVOKED(rev, &p) <= 0) + ossl_raise(eX509RevError, "i2d_X509_REVOKED"); + ossl_str_adjust(str, p); + return str; +} + /* * INIT */ @@ -267,7 +287,7 @@ Init_ossl_x509revoked(void) rb_define_alloc_func(cX509Rev, ossl_x509revoked_alloc); rb_define_method(cX509Rev, "initialize", ossl_x509revoked_initialize, -1); - rb_define_copy_func(cX509Rev, ossl_x509revoked_initialize_copy); + rb_define_method(cX509Rev, "initialize_copy", ossl_x509revoked_initialize_copy, 1); rb_define_method(cX509Rev, "serial", ossl_x509revoked_get_serial, 0); rb_define_method(cX509Rev, "serial=", ossl_x509revoked_set_serial, 1); @@ -276,4 +296,5 @@ Init_ossl_x509revoked(void) rb_define_method(cX509Rev, "extensions", ossl_x509revoked_get_extensions, 0); rb_define_method(cX509Rev, "extensions=", ossl_x509revoked_set_extensions, 1); rb_define_method(cX509Rev, "add_extension", ossl_x509revoked_add_extension, 1); + rb_define_method(cX509Rev, "to_der", ossl_x509revoked_to_der, 0); } diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index 4becc8e3..c6cf67ad 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -23,10 +23,6 @@ ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ } \ } while (0) -#define SafeGetX509Store(obj, st) do { \ - OSSL_Check_Kind((obj), cX509Store); \ - GetX509Store((obj), (st)); \ -} while (0) #define NewX509StCtx(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509stctx_type, 0) @@ -42,10 +38,6 @@ ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \ } \ } while (0) -#define SafeGetX509StCtx(obj, storep) do { \ - OSSL_Check_Kind((obj), cX509StoreContext); \ - GetX509Store((obj), (ctx)); \ -} while (0) /* * Verify callback stuff @@ -130,34 +122,12 @@ static const rb_data_type_t ossl_x509store_type = { /* * Public functions */ -VALUE -ossl_x509store_new(X509_STORE *store) -{ - VALUE obj; - - obj = NewX509Store(cX509Store); - SetX509Store(obj, store); - - return obj; -} - X509_STORE * GetX509StorePtr(VALUE obj) { X509_STORE *store; - SafeGetX509Store(obj, store); - - return store; -} - -X509_STORE * -DupX509StorePtr(VALUE obj) -{ - X509_STORE *store; - - SafeGetX509Store(obj, store); - X509_STORE_up_ref(store); + GetX509Store(obj, store); return store; } @@ -242,9 +212,9 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) /* * call-seq: - * store.flags = flag + * store.flags = flags * - * Sets +flag+ to the Store. +flag+ consists of zero or more of the constants + * Sets _flags_ to the Store. _flags_ consists of zero or more of the constants * defined in with name V_FLAG_* or'ed together. */ static VALUE @@ -263,7 +233,7 @@ ossl_x509store_set_flags(VALUE self, VALUE flags) * call-seq: * store.purpose = purpose * - * Sets the store's purpose to +purpose+. If specified, the verifications on + * Sets the store's purpose to _purpose_. If specified, the verifications on * the store will check every untrusted certificate's extensions are consistent * with the purpose. The purpose is specified by constants: * @@ -322,8 +292,9 @@ ossl_x509store_set_time(VALUE self, VALUE time) * call-seq: * store.add_file(file) -> self * - * Adds the certificates in +file+ to the certificate store. The +file+ can - * contain multiple PEM-encoded certificates. + * Adds the certificates in _file_ to the certificate store. _file_ is the path + * to the file, and the file contains one or more certificates in PEM format + * concatenated together. */ static VALUE ossl_x509store_add_file(VALUE self, VALUE file) @@ -359,7 +330,7 @@ ossl_x509store_add_file(VALUE self, VALUE file) * call-seq: * store.add_path(path) -> self * - * Adds +path+ as the hash dir to be looked up by the store. + * Adds _path_ as the hash dir to be looked up by the store. */ static VALUE ossl_x509store_add_path(VALUE self, VALUE dir) @@ -386,7 +357,7 @@ ossl_x509store_add_path(VALUE self, VALUE dir) * call-seq: * store.set_default_paths * - * Configures +store+ to look up CA certificates from the system default + * Configures _store_ to look up CA certificates from the system default * certificate store as needed basis. The location of the store can usually be * determined by: * @@ -410,7 +381,7 @@ ossl_x509store_set_default_paths(VALUE self) * call-seq: * store.add_cert(cert) * - * Adds the OpenSSL::X509::Certificate +cert+ to the certificate store. + * Adds the OpenSSL::X509::Certificate _cert_ to the certificate store. */ static VALUE ossl_x509store_add_cert(VALUE self, VALUE arg) @@ -431,7 +402,7 @@ ossl_x509store_add_cert(VALUE self, VALUE arg) * call-seq: * store.add_crl(crl) -> self * - * Adds the OpenSSL::X509::CRL +crl+ to the store. + * Adds the OpenSSL::X509::CRL _crl_ to the store. */ static VALUE ossl_x509store_add_crl(VALUE self, VALUE arg) @@ -456,15 +427,15 @@ static VALUE ossl_x509stctx_get_chain(VALUE); * call-seq: * store.verify(cert, chain = nil) -> true | false * - * Performs a certificate verification on the OpenSSL::X509::Certificate +cert+. + * Performs a certificate verification on the OpenSSL::X509::Certificate _cert_. * - * +chain+ can be an array of OpenSSL::X509::Certificate that is used to + * _chain_ can be an array of OpenSSL::X509::Certificate that is used to * construct the certificate chain. * * If a block is given, it overrides the callback set by #verify_callback=. * * After finishing the verification, the error information can be retrieved by - * #error, #error_string, and the resuting complete certificate chain can be + * #error, #error_string, and the resulting complete certificate chain can be * retrieved by #chain. */ static VALUE @@ -561,7 +532,7 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) rb_scan_args(argc, argv, "12", &store, &cert, &chain); GetX509StCtx(self, ctx); - SafeGetX509Store(store, x509st); + GetX509Store(store, x509st); if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */ if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain); if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){ diff --git a/ext/openssl/ruby_missing.h b/ext/openssl/ruby_missing.h index 5b1481ae..069acc8b 100644 --- a/ext/openssl/ruby_missing.h +++ b/ext/openssl/ruby_missing.h @@ -10,11 +10,6 @@ #if !defined(_OSSL_RUBY_MISSING_H_) #define _OSSL_RUBY_MISSING_H_ -#define rb_define_copy_func(klass, func) \ - rb_define_method((klass), "initialize_copy", (func), 1) - -#define FPTR_TO_FD(fptr) ((fptr)->fd) - /* Ruby 2.4 */ #ifndef RB_INTEGER_TYPE_P # define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM)) diff --git a/lib/openssl.rb b/lib/openssl.rb index 26d167a9..09142829 100644 --- a/lib/openssl.rb +++ b/lib/openssl.rb @@ -19,3 +19,4 @@ require 'openssl/config' require 'openssl/digest' require 'openssl/x509' require 'openssl/ssl' +require 'openssl/pkcs5' diff --git a/lib/openssl/bn.rb b/lib/openssl/bn.rb index 6d6c96e4..8d1ebefb 100644 --- a/lib/openssl/bn.rb +++ b/lib/openssl/bn.rb @@ -27,8 +27,9 @@ module OpenSSL end # OpenSSL ## +#-- # Add double dispatch to Integer -# +#++ class Integer # Casts an Integer as an OpenSSL::BN # diff --git a/lib/openssl/buffering.rb b/lib/openssl/buffering.rb index b0dffefd..935f61f0 100644 --- a/lib/openssl/buffering.rb +++ b/lib/openssl/buffering.rb @@ -63,7 +63,7 @@ module OpenSSL::Buffering end ## - # Consumes +size+ bytes from the buffer + # Consumes _size_ bytes from the buffer def consume_rbuff(size=nil) if @rbuffer.empty? @@ -79,7 +79,7 @@ module OpenSSL::Buffering public ## - # Reads +size+ bytes from the stream. If +buf+ is provided it must + # Reads _size_ bytes from the stream. If _buf_ is provided it must # reference a string which will receive the data. # # See IO#read for full details. @@ -106,7 +106,7 @@ module OpenSSL::Buffering end ## - # Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it + # Reads at most _maxlen_ bytes from the stream. If _buf_ is provided it # must reference a string which will receive the data. # # See IO#readpartial for full details. @@ -136,7 +136,7 @@ module OpenSSL::Buffering end ## - # Reads at most +maxlen+ bytes in the non-blocking manner. + # Reads at most _maxlen_ bytes in the non-blocking manner. # # When no data can be read without blocking it raises # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable. @@ -164,9 +164,10 @@ module OpenSSL::Buffering # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for # more details. http://www.openssl.org/support/faq.html # - # By specifying `exception: false`, the options hash allows you to indicate + # By specifying a keyword argument _exception_ to +false+, you can indicate # that read_nonblock should not raise an IO::Wait*able exception, but - # return the symbol :wait_writable or :wait_readable instead. + # return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF, + # it will return +nil+ instead of raising EOFError. def read_nonblock(maxlen, buf=nil, exception: true) if maxlen == 0 @@ -189,11 +190,11 @@ module OpenSSL::Buffering end ## - # Reads the next "line" from the stream. Lines are separated by +eol+. If - # +limit+ is provided the result will not be longer than the given number of + # Reads the next "line" from the stream. Lines are separated by _eol_. If + # _limit_ is provided the result will not be longer than the given number of # bytes. # - # +eol+ may be a String or Regexp. + # _eol_ may be a String or Regexp. # # Unlike IO#gets the line read will not be assigned to +$_+. # @@ -219,7 +220,7 @@ module OpenSSL::Buffering ## # Executes the block for every line in the stream where lines are separated - # by +eol+. + # by _eol_. # # See also #gets @@ -231,7 +232,7 @@ module OpenSSL::Buffering alias each_line each ## - # Reads lines from the stream which are separated by +eol+. + # Reads lines from the stream which are separated by _eol_. # # See also #gets @@ -244,7 +245,7 @@ module OpenSSL::Buffering end ## - # Reads a line from the stream which is separated by +eol+. + # Reads a line from the stream which is separated by _eol_. # # Raises EOFError if at end of file. @@ -280,7 +281,7 @@ module OpenSSL::Buffering end ## - # Pushes character +c+ back onto the stream such that a subsequent buffered + # Pushes character _c_ back onto the stream such that a subsequent buffered # character read will return it. # # Unlike IO#getc multiple bytes may be pushed back onto the stream. @@ -307,7 +308,7 @@ module OpenSSL::Buffering private ## - # Writes +s+ to the buffer. When the buffer is full or #sync is true the + # Writes _s_ to the buffer. When the buffer is full or #sync is true the # buffer is flushed to the underlying socket. def do_write(s) @@ -335,16 +336,18 @@ module OpenSSL::Buffering public ## - # Writes +s+ to the stream. If the argument is not a string it will be - # converted using String#to_s. Returns the number of bytes written. + # Writes _s_ to the stream. If the argument is not a String it will be + # converted using +.to_s+ method. Returns the number of bytes written. - def write(s) - do_write(s) - s.bytesize + def write(*s) + s.inject(0) do |written, str| + do_write(str) + written + str.bytesize + end end ## - # Writes +s+ in the non-blocking manner. + # Writes _s_ in the non-blocking manner. # # If there is buffered data, it is flushed first. This may block. # @@ -376,9 +379,9 @@ module OpenSSL::Buffering # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ # for more details. http://www.openssl.org/support/faq.html # - # By specifying `exception: false`, the options hash allows you to indicate + # By specifying a keyword argument _exception_ to +false+, you can indicate # that write_nonblock should not raise an IO::Wait*able exception, but - # return the symbol :wait_writable or :wait_readable instead. + # return the symbol +:wait_writable+ or +:wait_readable+ instead. def write_nonblock(s, exception: true) flush @@ -386,8 +389,8 @@ module OpenSSL::Buffering end ## - # Writes +s+ to the stream. +s+ will be converted to a String using - # String#to_s. + # Writes _s_ to the stream. _s_ will be converted to a String using + # +.to_s+ method. def <<(s) do_write(s) @@ -395,7 +398,7 @@ module OpenSSL::Buffering end ## - # Writes +args+ to the stream along with a record separator. + # Writes _args_ to the stream along with a record separator. # # See IO#puts for full details. @@ -415,7 +418,7 @@ module OpenSSL::Buffering end ## - # Writes +args+ to the stream. + # Writes _args_ to the stream. # # See IO#print for full details. diff --git a/lib/openssl/config.rb b/lib/openssl/config.rb index 88225451..48d8be00 100644 --- a/lib/openssl/config.rb +++ b/lib/openssl/config.rb @@ -30,7 +30,8 @@ module OpenSSL class << self ## - # Parses a given +string+ as a blob that contains configuration for openssl. + # Parses a given _string_ as a blob that contains configuration for + # OpenSSL. # # If the source of the IO is a file, then consider using #parse_config. def parse(string) @@ -46,7 +47,7 @@ module OpenSSL alias load new ## - # Parses the configuration data read from +io+, see also #parse. + # Parses the configuration data read from _io_, see also #parse. # # Raises a ConfigError on invalid configuration data. def parse_config(io) @@ -236,7 +237,7 @@ module OpenSSL # # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config= # - # If the optional +filename+ parameter is provided, then it is read in and + # If the optional _filename_ parameter is provided, then it is read in and # parsed via #parse_config. # # This can raise IO exceptions based on the access, or availability of the @@ -255,7 +256,7 @@ module OpenSSL end ## - # Gets the value of +key+ from the given +section+ + # Gets the value of _key_ from the given _section_ # # Given the following configurating file being loaded: # @@ -265,8 +266,8 @@ module OpenSSL # #=> [ default ] # # foo=bar # - # You can get a specific value from the config if you know the +section+ - # and +key+ like so: + # You can get a specific value from the config if you know the _section_ + # and _key_ like so: # # config.get_value('default','foo') # #=> "bar" @@ -297,7 +298,7 @@ module OpenSSL end ## - # Set the target +key+ with a given +value+ under a specific +section+. + # Set the target _key_ with a given _value_ under a specific _section_. # # Given the following configurating file being loaded: # @@ -307,7 +308,7 @@ module OpenSSL # #=> [ default ] # # foo=bar # - # You can set the value of +foo+ under the +default+ section to a new + # You can set the value of _foo_ under the _default_ section to a new # value: # # config.add_value('default', 'foo', 'buzz') @@ -322,7 +323,7 @@ module OpenSSL end ## - # Get a specific +section+ from the current configuration + # Get a specific _section_ from the current configuration # # Given the following configurating file being loaded: # @@ -351,7 +352,7 @@ module OpenSSL end ## - # Sets a specific +section+ name with a Hash +pairs+ + # Sets a specific _section_ name with a Hash _pairs_. # # Given the following configuration being created: # @@ -365,7 +366,7 @@ module OpenSSL # # baz=buz # # It's important to note that this will essentially merge any of the keys - # in +pairs+ with the existing +section+. For example: + # in _pairs_ with the existing _section_. For example: # # config['default'] # #=> {"foo"=>"bar", "baz"=>"buz"} diff --git a/lib/openssl/digest.rb b/lib/openssl/digest.rb index 97ccbc95..b6744de6 100644 --- a/lib/openssl/digest.rb +++ b/lib/openssl/digest.rb @@ -15,15 +15,12 @@ module OpenSSL class Digest - alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1) + alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512) if OPENSSL_VERSION_NUMBER < 0x10100000 alg += %w(DSS DSS1 SHA) end - if OPENSSL_VERSION_NUMBER > 0x00908000 - alg += %w(SHA224 SHA256 SHA384 SHA512) - end - # Return the +data+ hash computed with +name+ Digest. +name+ is either the + # Return the hash value computed with _name_ Digest. _name_ is either the # long name or short name of a supported digest algorithm. # # === Examples @@ -59,7 +56,7 @@ module OpenSSL end # Digest - # Returns a Digest subclass by +name+. + # Returns a Digest subclass by _name_ # # require 'openssl' # diff --git a/lib/openssl/pkcs5.rb b/lib/openssl/pkcs5.rb new file mode 100644 index 00000000..959447df --- /dev/null +++ b/lib/openssl/pkcs5.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: false +#-- +# Ruby/OpenSSL Project +# Copyright (C) 2017 Ruby/OpenSSL Project Authors +#++ + +module OpenSSL + module PKCS5 + module_function + + # OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac. + # This method is provided for backwards compatibility. + def pbkdf2_hmac(pass, salt, iter, keylen, digest) + OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter, + length: keylen, hash: digest) + end + + def pbkdf2_hmac_sha1(pass, salt, iter, keylen) + pbkdf2_hmac(pass, salt, iter, keylen, "sha1") + end + end +end diff --git a/lib/openssl/pkey.rb b/lib/openssl/pkey.rb index 9af5f781..dcedd849 100644 --- a/lib/openssl/pkey.rb +++ b/lib/openssl/pkey.rb @@ -1,44 +1,3 @@ # frozen_string_literal: false module OpenSSL - module PKey - if defined?(OpenSSL::PKey::DH) - - class DH - # :nodoc: - DEFAULT_1024 = new <<-_end_of_pem_ ------BEGIN DH PARAMETERS----- -MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ -AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR -T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC ------END DH PARAMETERS----- - _end_of_pem_ - - # :nodoc: - DEFAULT_2048 = new <<-_end_of_pem_ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY -JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab -VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 -YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 -1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD -7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== ------END DH PARAMETERS----- - _end_of_pem_ - end - - # :nodoc: - DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| - warn "using default DH parameters." if $VERBOSE - case keylen - when 1024 then OpenSSL::PKey::DH::DEFAULT_1024 - when 2048 then OpenSSL::PKey::DH::DEFAULT_2048 - else - nil - end - } - - else - DEFAULT_TMP_DH_CALLBACK = nil - end - end end diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb index f40a4514..6a6f2b94 100644 --- a/lib/openssl/ssl.rb +++ b/lib/openssl/ssl.rb @@ -17,18 +17,36 @@ module OpenSSL module SSL class SSLContext DEFAULT_PARAMS = { # :nodoc: - :ssl_version => "SSLv23", + :min_version => OpenSSL::SSL::TLS1_VERSION, :verify_mode => OpenSSL::SSL::VERIFY_PEER, :verify_hostname => true, :options => -> { opts = OpenSSL::SSL::OP_ALL 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 | OpenSSL::SSL::OP_NO_SSLv3 + opts |= OpenSSL::SSL::OP_NO_COMPRESSION opts }.call } + if defined?(OpenSSL::PKey::DH) + DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY +JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab +VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 +YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 +1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD +7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== +-----END DH PARAMETERS----- + _end_of_pem_ + private_constant :DEFAULT_2048 + + DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc: + warn "using default DH parameters." if $VERBOSE + DEFAULT_2048 + } + end + if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") && OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000) DEFAULT_PARAMS.merge!( @@ -87,14 +105,18 @@ module OpenSSL # # The callback is invoked with an SSLSocket and a server name. The # callback must return an SSLContext for the server name or nil. - attr_accessor :servername_cb if ExtConfig::HAVE_TLSEXT_HOST_NAME + attr_accessor :servername_cb # call-seq: - # SSLContext.new => ctx - # SSLContext.new(:TLSv1) => ctx - # SSLContext.new("SSLv23_client") => ctx + # SSLContext.new -> ctx + # SSLContext.new(:TLSv1) -> ctx + # SSLContext.new("SSLv23") -> ctx # - # You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS + # Creates a new SSL context. + # + # If an argument is given, #ssl_version= is called with the value. Note + # that this form is deprecated. New applications should use #min_version= + # and #max_version= as necessary. def initialize(version = nil) self.options |= OpenSSL::SSL::OP_ALL self.ssl_version = version if version @@ -106,14 +128,15 @@ module OpenSSL # # Sets saner defaults optimized for the use with HTTP-like protocols. # - # If a Hash +params+ is given, the parameters are overridden with it. - # The keys in +params+ must be assignment methods on SSLContext. + # If a Hash _params_ is given, the parameters are overridden with it. + # The keys in _params_ must be assignment methods on SSLContext. # # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and # cert_store are not set then the system default certificate store is # used. def set_params(params={}) params = DEFAULT_PARAMS.merge(params) + self.options = params.delete(:options) # set before min_version/max_version params.each{|name, value| self.__send__("#{name}=", value) } if self.verify_mode != OpenSSL::SSL::VERIFY_NONE unless self.ca_file or self.ca_path or self.cert_store @@ -122,6 +145,88 @@ module OpenSSL end return params end + + # call-seq: + # ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION + # ctx.min_version = :TLS1_2 + # ctx.min_version = nil + # + # Sets the lower bound on the supported SSL/TLS protocol version. The + # version may be specified by an integer constant named + # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version". + # + # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v* + # options by #options= once you have called #min_version= or + # #max_version=. + # + # === Example + # ctx = OpenSSL::SSL::SSLContext.new + # ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION + # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + # + # sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx) + # sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2 + def min_version=(version) + set_minmax_proto_version(version, @max_proto_version ||= nil) + @min_proto_version = version + end + + # call-seq: + # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + # ctx.max_version = :TLS1_2 + # ctx.max_version = nil + # + # Sets the upper bound of the supported SSL/TLS protocol version. See + # #min_version= for the possible values. + def max_version=(version) + set_minmax_proto_version(@min_proto_version ||= nil, version) + @max_proto_version = version + end + + # call-seq: + # ctx.ssl_version = :TLSv1 + # ctx.ssl_version = "SSLv23" + # + # Sets the SSL/TLS protocol version for the context. This forces + # connections to use only the specified protocol version. This is + # deprecated and only provided for backwards compatibility. Use + # #min_version= and #max_version= instead. + # + # === History + # As the name hints, this used to call the SSL_CTX_set_ssl_version() + # function which sets the SSL method used for connections created from + # the context. As of Ruby/OpenSSL 2.1, this accessor method is + # implemented to call #min_version= and #max_version= instead. + def ssl_version=(meth) + meth = meth.to_s if meth.is_a?(Symbol) + if /(?<type>_client|_server)\z/ =~ meth + meth = $` + if $VERBOSE + warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored" + end + end + version = METHODS_MAP[meth.intern] or + raise ArgumentError, "unknown SSL method `%s'" % meth + set_minmax_proto_version(version, version) + @min_proto_version = @max_proto_version = version + end + + METHODS_MAP = { + SSLv23: 0, + SSLv2: OpenSSL::SSL::SSL2_VERSION, + SSLv3: OpenSSL::SSL::SSL3_VERSION, + TLSv1: OpenSSL::SSL::TLS1_VERSION, + TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION, + TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION, + }.freeze + private_constant :METHODS_MAP + + # The list of available SSL/TLS methods. This constant is only provided + # for backwards compatibility. + METHODS = METHODS_MAP.flat_map { |name,| + [name, :"#{name}_client", :"#{name}_server"] + }.freeze + deprecate_constant :METHODS end module SocketForwarder @@ -242,9 +347,7 @@ module OpenSSL include Buffering include SocketForwarder - if ExtConfig::HAVE_TLSEXT_HOST_NAME - attr_reader :hostname - end + attr_reader :hostname # The underlying IO object. attr_reader :io @@ -317,7 +420,7 @@ module OpenSSL end def tmp_dh_callback - @context.tmp_dh_callback || OpenSSL::PKey::DEFAULT_TMP_DH_CALLBACK + @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK end def tmp_ecdh_callback @@ -341,8 +444,8 @@ module OpenSSL attr_accessor :start_immediately # Creates a new instance of SSLServer. - # * +srv+ is an instance of TCPServer. - # * +ctx+ is an instance of OpenSSL::SSL::SSLContext. + # * _srv_ is an instance of TCPServer. + # * _ctx_ is an instance of OpenSSL::SSL::SSLContext. def initialize(svr, ctx) @svr = svr @ctx = ctx diff --git a/lib/openssl/x509.rb b/lib/openssl/x509.rb index aef3456e..98358f90 100644 --- a/lib/openssl/x509.rb +++ b/lib/openssl/x509.rb @@ -41,6 +41,11 @@ module OpenSSL end class Extension + def ==(other) + return false unless Extension === other + to_der == other.to_der + end + def to_s # "oid = critical, value" str = self.oid str << " = " @@ -139,7 +144,13 @@ module OpenSSL end def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE) - ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) } + if str.start_with?("/") + # /A=B/C=D format + ary = str[1..-1].split("/").map { |i| i.split("=", 2) } + else + # Comma-separated + ary = str.split(",").map { |i| i.strip.split("=", 2) } + end self.new(ary, template) end @@ -154,6 +165,13 @@ module OpenSSL end end + class Attribute + def ==(other) + return false unless Attribute === other + to_der == other.to_der + end + end + class StoreContext def cleanup warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE @@ -172,5 +190,26 @@ module OpenSSL } end end + + class CRL + def ==(other) + return false unless CRL === other + to_der == other.to_der + end + end + + class Revoked + def ==(other) + return false unless Revoked === other + to_der == other.to_der + end + end + + class Request + def ==(other) + return false unless Request === other + to_der == other.to_der + end + end end end diff --git a/openssl.gemspec b/openssl.gemspec index 34cef6f1..7440e928 100644 --- a/openssl.gemspec +++ b/openssl.gemspec @@ -1,11 +1,11 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "2.0.6" + spec.version = "2.1.0.beta1" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} spec.description = %q{It wraps the OpenSSL library.} - spec.homepage = "https://www.ruby-lang.org/" + spec.homepage = "https://github.com/ruby/openssl" spec.license = "Ruby" spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"] diff --git a/test/envutil.rb b/test/envutil.rb index 89332b35..05d6fe27 100644 --- a/test/envutil.rb +++ b/test/envutil.rb @@ -92,6 +92,18 @@ module EnvUtil end module_function :invoke_ruby + def verbose_warning + class << (stderr = "".dup) + alias write << + end + stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true + yield stderr + return $stderr + ensure + stderr, $stderr, $VERBOSE = $stderr, stderr, verbose + end + module_function :verbose_warning + def suppress_warning verbose, $VERBOSE = $VERBOSE, nil yield @@ -220,6 +232,17 @@ eom raise marshal_error if marshal_error end + def assert_warning(pat, msg = nil) + stderr = EnvUtil.verbose_warning { + yield + } + if Regexp === pat + assert_match pat, stderr, msg + else + assert_equal pat, stderr, msg + end + end + def message msg = nil, ending = ".", &default proc { msg = msg.call.chomp(".") if Proc === msg diff --git a/test/test_asn1.rb b/test/test_asn1.rb index 55cb1f0b..11707037 100644 --- a/test/test_asn1.rb +++ b/test/test_asn1.rb @@ -1,10 +1,10 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestASN1 < OpenSSL::TestCase - def test_decode + def test_decode_x509_certificate subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") key = Fixtures.pkey("rsa1024") now = Time.at(Time.now.to_i) # suppress usec @@ -130,9 +130,9 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase assert_equal(OpenSSL::ASN1::Sequence, spkey.class) assert_equal(2, spkey.value.size) assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class) - assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value) + assert_equal(cert.public_key.n, spkey.value[0].value) assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class) - assert_equal(65537, spkey.value[1].value) + assert_equal(cert.public_key.e, spkey.value[1].value) extensions = tbs_cert.value[7] assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class) @@ -193,66 +193,8 @@ class OpenSSL::TestASN1 < OpenSSL::TestCase assert_equal(cululated_sig, sig_val.value) end - def test_encode_boolean - encode_decode_test(OpenSSL::ASN1::Boolean, [true, false]) - end - - def test_encode_integer - encode_decode_test(OpenSSL::ASN1::Integer, [72, -127, -128, 128, -1, 0, 1, -(2**12345), 2**12345]) - end - - def test_encode_nil - m = OpenSSL::ASN1 - [ - m::Boolean, m::Integer, m::BitString, m::OctetString, - m::ObjectId, m::Enumerated, m::UTF8String, m::UTCTime, - m::GeneralizedTime, m::Sequence, m::Set - ].each do |klass| - #Primitives raise TypeError, Constructives NoMethodError - assert_raise(TypeError, NoMethodError) { klass.send(:new, nil).to_der } - end - end - - def encode_decode_test(type, values) - values.each do |v| - assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value) - end - end - - def test_decode_pem #should fail gracefully (cf. [ruby-dev:44542]) - pem = <<-_EOS_ ------BEGIN CERTIFICATE----- -MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB -GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe -Fw0xMTA5MjUxMzQ4MjZaFw0xMTA5MjUxNDQ4MjZaMD0xEzARBgoJkiaJk/IsZAEZ -FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV9ht9J7k4NBs38jOXvvTKY9 -gW8nLICSno5EETR1cuF7i4pNs9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enen -fzq/t/e/1IRW0wkJUJUFQign4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWm -qbjs07JbuS4QQGGXLc+Su96DkYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v6 -8JkRFIhdGlb6JL8fllf/A/blNwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX -9KZYcU00mOX+fdxOSnGqS/8JDRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wID -AQABMA0GCSqGSIb3DQEBBQUAA4IBAQAiAtrIr1pLX4GYN5klviWKb8HC9ICYuAFI -NfE3FwqzErEVXotuMe3yPVyB3Bv6rjYY/x5EtS5+WPTbHlvHZTkfcsnTpizcn4mW -dJ6dDRaFCHt1YKKjUxqBt9lvvrc3nReYZN/P+s1mrDhWzGf8iPZgf8sFUHgnaK7W -CXRVXmPFgCDRNpDDVQ0MQkr509yYfTH+dujNzqTCwSvkyZFyQ7Oe8Yj0VR6kquG3 -rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm -/93PnPG1IvPjYNd5VlV+sXSnaxQn974HRCsMv7jA8BD6IgSaX6WK ------END CERTIFICATE----- - _EOS_ - assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode(pem) } - assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode_all(pem) } - end - - def test_primitive_cannot_set_infinite_length - prim = OpenSSL::ASN1::Integer.new(50) - assert_equal false, prim.infinite_length - assert_not_respond_to prim, :infinite_length= - end - def test_decode_all - expected = %w{ 02 01 01 02 01 02 02 01 03 } - raw = [expected.join('')].pack('H*') + raw = B(%w{ 02 01 01 02 01 02 02 01 03 }) ary = OpenSSL::ASN1.decode_all(raw) assert_equal(3, ary.size) ary.each_with_index do |asn1, i| @@ -261,288 +203,406 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm end end - def test_decode_utctime - expected = Time.at 1374535380 - assert_equal expected, OpenSSL::ASN1.decode("\x17\v1307222323Z").value - - expected += 17 - assert_equal expected, OpenSSL::ASN1.decode("\x17\r130722232317Z").value + def test_object_id_register + oid = "1.2.34.56789" + pend "OID 1.2.34.56789 is already registered" if OpenSSL::ASN1::ObjectId(oid).sn + assert_equal true, OpenSSL::ASN1::ObjectId.register(oid, "ossl-test-sn", "ossl-test-ln") + obj = OpenSSL::ASN1::ObjectId(oid) + assert_equal oid, obj.oid + assert_equal "ossl-test-sn", obj.sn + assert_equal "ossl-test-ln", obj.ln + obj = encode_decode_test B(%w{ 06 05 2A 22 83 BB 55 }), OpenSSL::ASN1::ObjectId("ossl-test-ln") + assert_equal "ossl-test-sn", obj.value end - def test_encode_utctime_2k38 - encoded = OpenSSL::ASN1::UTCTime(2 ** 31 - 1).to_der - assert_equal 2 ** 31 - 1, OpenSSL::ASN1.decode(encoded).value.to_i - - encoded = OpenSSL::ASN1::UTCTime(2 ** 31).to_der - assert_equal 2 ** 31, OpenSSL::ASN1.decode(encoded).value.to_i + def test_end_of_content + encode_decode_test B(%w{ 00 00 }), OpenSSL::ASN1::EndOfContent.new + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 00 01 00 })) + } end - def test_decode_generalisedtime - expected = Time.at 1481225640 - assert_equal expected, OpenSSL::ASN1.decode("\x18\x0D201612081934Z").value - - expected += 29 - assert_equal expected, OpenSSL::ASN1.decode("\x18\x0F20161208193429Z").value + def test_boolean + encode_decode_test B(%w{ 01 01 00 }), OpenSSL::ASN1::Boolean.new(false) + encode_decode_test B(%w{ 01 01 FF }), OpenSSL::ASN1::Boolean.new(true) + decode_test B(%w{ 01 01 01 }), OpenSSL::ASN1::Boolean.new(true) + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 01 02 00 00 })) + } end - def test_decode_enumerated - encoded = OpenSSL::ASN1.Enumerated(0).to_der - assert_equal "\x0a\x01\x00".b, encoded - assert_equal encoded, OpenSSL::ASN1.decode(encoded).to_der + def test_integer + encode_decode_test B(%w{ 02 01 00 }), OpenSSL::ASN1::Integer.new(0) + encode_decode_test B(%w{ 02 01 48 }), OpenSSL::ASN1::Integer.new(72) + encode_decode_test B(%w{ 02 02 00 80 }), OpenSSL::ASN1::Integer.new(128) + encode_decode_test B(%w{ 02 01 81 }), OpenSSL::ASN1::Integer.new(-127) + encode_decode_test B(%w{ 02 01 80 }), OpenSSL::ASN1::Integer.new(-128) + encode_decode_test B(%w{ 02 01 FF }), OpenSSL::ASN1::Integer.new(-1) + encode_decode_test B(%w{ 02 09 01 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Integer.new(2 ** 64) + encode_decode_test B(%w{ 02 09 FF 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Integer.new(-(2 ** 64)) + # FIXME: OpenSSL < 1.1.0 does not fail + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 02 02 00 7F })) + # } + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 02 02 FF 80 })) + # } + end + + def test_enumerated + encode_decode_test B(%w{ 0A 01 00 }), OpenSSL::ASN1::Enumerated.new(0) + encode_decode_test B(%w{ 0A 01 48 }), OpenSSL::ASN1::Enumerated.new(72) + encode_decode_test B(%w{ 0A 02 00 80 }), OpenSSL::ASN1::Enumerated.new(128) + encode_decode_test B(%w{ 0A 09 01 00 00 00 00 00 00 00 00 }), OpenSSL::ASN1::Enumerated.new(2 ** 64) + end + + def test_bitstring + encode_decode_test B(%w{ 03 01 00 }), OpenSSL::ASN1::BitString.new(B(%w{})) + encode_decode_test B(%w{ 03 02 00 01 }), OpenSSL::ASN1::BitString.new(B(%w{ 01 })) + obj = OpenSSL::ASN1::BitString.new(B(%w{ F0 })) + obj.unused_bits = 4 + encode_decode_test B(%w{ 03 02 04 F0 }), obj + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 03 00 })) + } + # OpenSSL < OpenSSL_1_0_1k and LibreSSL ignore the error + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 03 03 08 FF 00 })) + # } + # OpenSSL does not seem to prohibit this, though X.690 8.6.2.3 (15/08) does + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 03 01 04 })) + # } + assert_raise(OpenSSL::ASN1::ASN1Error) { + obj = OpenSSL::ASN1::BitString.new(B(%w{ FF FF })) + obj.unused_bits = 8 + obj.to_der + } end - def test_create_inf_length_primitive - expected = %w{ 24 80 04 01 61 00 00 } - raw = [expected.join('')].pack('H*') - content = [OpenSSL::ASN1::OctetString.new("a"), OpenSSL::ASN1::EndOfContent.new] - cons = OpenSSL::ASN1::Constructive.new(content, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - cons.infinite_length = true - assert_equal(nil, cons.tagging) - assert_equal(raw, cons.to_der) - asn1 = OpenSSL::ASN1.decode(raw) - assert(asn1.infinite_length) - assert_equal(raw, asn1.to_der) + def test_string_basic + test = -> (tag, klass) { + encode_decode_test tag.chr + B(%w{ 00 }), klass.new(B(%w{})) + encode_decode_test tag.chr + B(%w{ 02 00 01 }), klass.new(B(%w{ 00 01 })) + } + test.(4, OpenSSL::ASN1::OctetString) + test.(12, OpenSSL::ASN1::UTF8String) + test.(18, OpenSSL::ASN1::NumericString) + test.(19, OpenSSL::ASN1::PrintableString) + test.(20, OpenSSL::ASN1::T61String) + test.(21, OpenSSL::ASN1::VideotexString) + test.(22, OpenSSL::ASN1::IA5String) + test.(25, OpenSSL::ASN1::GraphicString) + test.(26, OpenSSL::ASN1::ISO64String) + test.(27, OpenSSL::ASN1::GeneralString) + test.(28, OpenSSL::ASN1::UniversalString) + test.(30, OpenSSL::ASN1::BMPString) + end + + def test_null + encode_decode_test B(%w{ 05 00 }), OpenSSL::ASN1::Null.new(nil) + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 05 01 00 })) + } end - def test_cons_without_inf_length_forbidden - assert_raise(OpenSSL::ASN1::ASN1Error) do - val = OpenSSL::ASN1::OctetString.new('a') - cons = OpenSSL::ASN1::Constructive.new([val], OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - cons.to_der + def test_object_identifier + encode_decode_test B(%w{ 06 01 00 }), OpenSSL::ASN1::ObjectId.new("0.0".b) + encode_decode_test B(%w{ 06 01 28 }), OpenSSL::ASN1::ObjectId.new("1.0".b) + encode_decode_test B(%w{ 06 03 88 37 03 }), OpenSSL::ASN1::ObjectId.new("2.999.3".b) + encode_decode_test B(%w{ 06 05 2A 22 83 BB 55 }), OpenSSL::ASN1::ObjectId.new("1.2.34.56789".b) + obj = encode_decode_test B(%w{ 06 09 60 86 48 01 65 03 04 02 01 }), OpenSSL::ASN1::ObjectId.new("sha256") + assert_equal "2.16.840.1.101.3.4.2.1", obj.oid + assert_equal "SHA256", obj.sn + assert_equal "sha256", obj.ln + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 06 00 })) + } + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.decode(B(%w{ 06 01 80 })) + } + assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("3.0".b).to_der } + assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("0.40".b).to_der } + + begin + oid = (0...100).to_a.join(".").b + obj = OpenSSL::ASN1::ObjectId.new(oid) + assert_equal oid, obj.oid + rescue OpenSSL::ASN1::ASN1Error + pend "OBJ_obj2txt() not working (LibreSSL?)" if $!.message =~ /OBJ_obj2txt/ + raise end end - def test_cons_without_array_forbidden - assert_raise(OpenSSL::ASN1::ASN1Error) do - val = OpenSSL::ASN1::OctetString.new('a') - cons = OpenSSL::ASN1::Constructive.new(val, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - cons.infinite_length = true - cons.to_der + def test_sequence + encode_decode_test B(%w{ 30 00 }), OpenSSL::ASN1::Sequence.new([]) + encode_decode_test B(%w{ 30 07 05 00 30 00 04 01 00 }), OpenSSL::ASN1::Sequence.new([ + OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::Sequence.new([]), + OpenSSL::ASN1::OctetString.new(B(%w{ 00 })) + ]) + + expected = OpenSSL::ASN1::Sequence.new([OpenSSL::ASN1::OctetString.new(B(%w{ 00 }))]) + expected.indefinite_length = true + encode_decode_test B(%w{ 30 80 04 01 00 00 00 }), expected + + # OpenSSL::ASN1::EndOfContent can only be at the end + obj = OpenSSL::ASN1::Sequence.new([ + OpenSSL::ASN1::EndOfContent.new, + OpenSSL::ASN1::OctetString.new(B(%w{ 00 })), + OpenSSL::ASN1::EndOfContent.new, + ]) + obj.indefinite_length = true + assert_raise(OpenSSL::ASN1::ASN1Error) { obj.to_der } + + # The last EOC in value is ignored if indefinite length form is used + expected = OpenSSL::ASN1::Sequence.new([ + OpenSSL::ASN1::OctetString.new(B(%w{ 00 })), + OpenSSL::ASN1::EndOfContent.new + ]) + expected.indefinite_length = true + encode_test B(%w{ 30 80 04 01 00 00 00 }), expected + end + + def test_set + encode_decode_test B(%w{ 31 00 }), OpenSSL::ASN1::Set.new([]) + encode_decode_test B(%w{ 31 07 05 00 30 00 04 01 00 }), OpenSSL::ASN1::Set.new([ + OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::Sequence.new([]), + OpenSSL::ASN1::OctetString.new(B(%w{ 00 })) + ]) + expected = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::OctetString.new(B(%w{ 00 }))]) + expected.indefinite_length = true + encode_decode_test B(%w{ 31 80 04 01 00 00 00 }), expected + end + + def test_utctime + encode_decode_test B(%w{ 17 0D }) + "160908234339Z".b, + OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 39)) + # Seconds is omitted + decode_test B(%w{ 17 0B }) + "1609082343Z".b, + OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 0)) + begin + # possible range of UTCTime is 1969-2068 currently + encode_decode_test B(%w{ 17 0D }) + "690908234339Z".b, + OpenSSL::ASN1::UTCTime.new(Time.utc(1969, 9, 8, 23, 43, 39)) + rescue OpenSSL::ASN1::ASN1Error + pend "No negative time_t support?" end - end + # not implemented + # decode_test B(%w{ 17 11 }) + "500908234339+0930".b, + # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 39, "+09:30")) + # decode_test B(%w{ 17 0F }) + "5009082343-0930".b, + # OpenSSL::ASN1::UTCTime.new(Time.new(1950, 9, 8, 23, 43, 0, "-09:30")) + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 17 0C }) + "500908234339".b) + # } + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 17 0D }) + "500908234339Y".b) + # } + end + + def test_generalizedtime + encode_decode_test B(%w{ 18 0F }) + "20161208193429Z".b, + OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29)) + encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b, + OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39)) + decode_test B(%w{ 18 0D }) + "201612081934Z".b, + OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0)) + # not implemented + # decode_test B(%w{ 18 13 }) + "20161208193439+0930".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30")) + # decode_test B(%w{ 18 11 }) + "201612081934-0930".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30")) + # decode_test B(%w{ 18 11 }) + "201612081934-09".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00")) + # decode_test B(%w{ 18 0D }) + "2016120819.5Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0)) + # decode_test B(%w{ 18 0D }) + "2016120819,5Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0)) + # decode_test B(%w{ 18 0F }) + "201612081934.5Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30)) + # decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b, + # OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5)) + # assert_raise(OpenSSL::ASN1::ASN1Error) { + # OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b) + # } + end + + def test_basic_asn1data + encode_test B(%w{ 00 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 0, :UNIVERSAL) + encode_test B(%w{ 01 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :UNIVERSAL) + encode_decode_test B(%w{ 41 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :APPLICATION) + encode_decode_test B(%w{ 81 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :CONTEXT_SPECIFIC) + encode_decode_test B(%w{ C1 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 1, :PRIVATE) + encode_decode_test B(%w{ 1F 20 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 32, :UNIVERSAL) + encode_decode_test B(%w{ 1F C0 20 00 }), OpenSSL::ASN1::ASN1Data.new(B(%w{}), 8224, :UNIVERSAL) + encode_decode_test B(%w{ 41 02 AB CD }), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 1, :APPLICATION) + encode_decode_test B(%w{ 41 81 80 } + %w{ AB CD } * 64), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD } * 64), 1, :APPLICATION) + encode_decode_test B(%w{ 41 82 01 00 } + %w{ AB CD } * 128), OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD } * 128), 1, :APPLICATION) + encode_decode_test B(%w{ 61 00 }), OpenSSL::ASN1::ASN1Data.new([], 1, :APPLICATION) + obj = OpenSSL::ASN1::ASN1Data.new([OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 2, :PRIVATE)], 1, :APPLICATION) + obj.indefinite_length = true + encode_decode_test B(%w{ 61 80 C2 02 AB CD 00 00 }), obj + obj = OpenSSL::ASN1::ASN1Data.new([ + OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 2, :PRIVATE), + OpenSSL::ASN1::EndOfContent.new + ], 1, :APPLICATION) + obj.indefinite_length = true + encode_test B(%w{ 61 80 C2 02 AB CD 00 00 }), obj + obj = OpenSSL::ASN1::ASN1Data.new(B(%w{ AB CD }), 1, :UNIVERSAL) + obj.indefinite_length = true + assert_raise(OpenSSL::ASN1::ASN1Error) { obj.to_der } + end + + def test_basic_primitive + encode_test B(%w{ 00 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 0) + encode_test B(%w{ 01 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :UNIVERSAL) + encode_test B(%w{ 81 00 }), OpenSSL::ASN1::Primitive.new(B(%w{}), 1, nil, :CONTEXT_SPECIFIC) + encode_test B(%w{ 01 02 AB CD }), OpenSSL::ASN1::Primitive.new(B(%w{ AB CD }), 1) + assert_raise(TypeError) { OpenSSL::ASN1::Primitive.new([], 1).to_der } - def test_parse_empty_sequence - expected = %w{ A0 07 30 02 30 00 02 01 00 } - raw = [expected.join('')].pack('H*') - asn1 = OpenSSL::ASN1.decode(raw) - assert_equal(raw, asn1.to_der) - assert_equal(2, asn1.value.size) - seq = asn1.value[0] - assert_equal(1, seq.value.size) - inner_seq = seq.value[0] - assert_equal(0, inner_seq.value.size) - end - - def test_parse_tagged_0_infinite - expected = %w{ 30 80 02 01 01 80 01 02 00 00 } - raw = [expected.join('')].pack('H*') - asn1 = OpenSSL::ASN1.decode(raw) - assert_equal(3, asn1.value.size) - int = asn1.value[0] - assert_universal(OpenSSL::ASN1::INTEGER, int) - tagged = asn1.value[1] - assert_equal(0, tagged.tag) - assert_universal(OpenSSL::ASN1::EOC, asn1.value[2]) - assert_equal(raw, asn1.to_der) - end - - def test_seq_infinite_length - content = [ OpenSSL::ASN1::Null.new(nil), - OpenSSL::ASN1::EndOfContent.new ] - cons = OpenSSL::ASN1::Sequence.new(content) - cons.infinite_length = true - expected = %w{ 30 80 05 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + prim = OpenSSL::ASN1::Integer.new(50) + assert_equal false, prim.indefinite_length + assert_not_respond_to prim, :indefinite_length= end - def test_set_infinite_length - content = [ OpenSSL::ASN1::Null.new(nil), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Set.new(content) - cons.infinite_length = true - expected = %w{ 31 80 05 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end - - def test_octet_string_infinite_length - octets = [ OpenSSL::ASN1::OctetString.new('aaa'), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - cons.infinite_length = true - expected = %w{ 24 80 04 03 61 61 61 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + def test_basic_constructed + octet_string = OpenSSL::ASN1::OctetString.new(B(%w{ AB CD })) + encode_test B(%w{ 20 00 }), OpenSSL::ASN1::Constructive.new([], 0) + encode_test B(%w{ 21 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :UNIVERSAL) + encode_test B(%w{ A1 00 }), OpenSSL::ASN1::Constructive.new([], 1, nil, :CONTEXT_SPECIFIC) + encode_test B(%w{ 21 04 04 02 AB CD }), OpenSSL::ASN1::Constructive.new([octet_string], 1) + obj = OpenSSL::ASN1::Constructive.new([octet_string], 1) + obj.indefinite_length = true + encode_decode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj + obj = OpenSSL::ASN1::Constructive.new([octet_string, OpenSSL::ASN1::EndOfContent.new], 1) + obj.indefinite_length = true + encode_test B(%w{ 21 80 04 02 AB CD 00 00 }), obj end def test_prim_explicit_tagging oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) - expected = %w{ A0 03 04 01 61 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, oct_str.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + encode_test B(%w{ A0 03 04 01 61 }), oct_str + oct_str2 = OpenSSL::ASN1::OctetString.new("a", 1, :EXPLICIT, :APPLICATION) + encode_test B(%w{ 61 03 04 01 61 }), oct_str2 - def test_prim_explicit_tagging_tag_class - oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) - oct_str2 = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT, :CONTEXT_SPECIFIC) - assert_equal(oct_str.to_der, oct_str2.to_der) + decoded = OpenSSL::ASN1.decode(oct_str2.to_der) + assert_equal :APPLICATION, decoded.tag_class + assert_equal 1, decoded.tag + assert_equal 1, decoded.value.size + inner = decoded.value[0] + assert_equal OpenSSL::ASN1::OctetString, inner.class + assert_equal B(%w{ 61 }), inner.value end def test_prim_implicit_tagging int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) - expected = %w{ 80 01 01 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, int.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + encode_test B(%w{ 80 01 01 }), int + int2 = OpenSSL::ASN1::Integer.new(1, 1, :IMPLICIT, :APPLICATION) + encode_test B(%w{ 41 01 01 }), int2 + decoded = OpenSSL::ASN1.decode(int2.to_der) + assert_equal :APPLICATION, decoded.tag_class + assert_equal 1, decoded.tag + assert_equal B(%w{ 01 }), decoded.value - def test_prim_implicit_tagging_tag_class - int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) - int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC); - assert_equal(int.to_der, int2.to_der) + # Special behavior: Encoding universal types with non-default 'tag' + # attribute and nil tagging method. + int3 = OpenSSL::ASN1::Integer.new(1, 1) + encode_test B(%w{ 01 01 01 }), int3 end def test_cons_explicit_tagging content = [ OpenSSL::ASN1::PrintableString.new('abc') ] seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) - expected = %w{ A2 07 30 05 13 03 61 62 63 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + encode_test B(%w{ A2 07 30 05 13 03 61 62 63 }), seq + seq2 = OpenSSL::ASN1::Sequence.new(content, 3, :EXPLICIT, :APPLICATION) + encode_test B(%w{ 63 07 30 05 13 03 61 62 63 }), seq2 - def test_cons_explicit_tagging_inf_length - content = [ OpenSSL::ASN1::PrintableString.new('abc') , - OpenSSL::ASN1::EndOfContent.new() ] - seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) - seq.infinite_length = true - expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + content3 = [ OpenSSL::ASN1::PrintableString.new('abc'), + OpenSSL::ASN1::EndOfContent.new() ] + seq3 = OpenSSL::ASN1::Sequence.new(content3, 2, :EXPLICIT) + seq3.indefinite_length = true + encode_test B(%w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 }), seq3 end def test_cons_implicit_tagging content = [ OpenSSL::ASN1::Null.new(nil) ] seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) - expected = %w{ A1 02 05 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + encode_test B(%w{ A1 02 05 00 }), seq + seq2 = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT, :APPLICATION) + encode_test B(%w{ 61 02 05 00 }), seq2 - def test_cons_implicit_tagging_inf_length - content = [ OpenSSL::ASN1::Null.new(nil), - OpenSSL::ASN1::EndOfContent.new() ] - seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) - seq.infinite_length = true - expected = %w{ A1 80 05 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, seq.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end + content3 = [ OpenSSL::ASN1::Null.new(nil), + OpenSSL::ASN1::EndOfContent.new() ] + seq3 = OpenSSL::ASN1::Sequence.new(content3, 1, :IMPLICIT) + seq3.indefinite_length = true + encode_test B(%w{ A1 80 05 00 00 00 }), seq3 - def test_octet_string_infinite_length_explicit_tagging - octets = [ OpenSSL::ASN1::OctetString.new('aaa'), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new(octets, 1, :EXPLICIT) - cons.infinite_length = true - expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + # Special behavior: Encoding universal types with non-default 'tag' + # attribute and nil tagging method. + seq4 = OpenSSL::ASN1::Sequence.new([], 1) + encode_test B(%w{ 21 00 }), seq4 end - def test_octet_string_infinite_length_implicit_tagging + def test_octet_string_constructed_tagging + octets = [ OpenSSL::ASN1::OctetString.new('aaa') ] + cons = OpenSSL::ASN1::Constructive.new(octets, 0, :IMPLICIT) + encode_test B(%w{ A0 05 04 03 61 61 61 }), cons + octets = [ OpenSSL::ASN1::OctetString.new('aaa'), OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(octets, 0, :IMPLICIT) - cons.infinite_length = true - expected = %w{ A0 80 04 03 61 61 61 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) + cons.indefinite_length = true + encode_test B(%w{ A0 80 04 03 61 61 61 00 00 }), cons end - def test_recursive_octet_string_infinite_length + def test_recursive_octet_string_indefinite_length octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"), OpenSSL::ASN1::EndOfContent.new() ] octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"), OpenSSL::ASN1::EndOfContent.new() ] container1 = OpenSSL::ASN1::Constructive.new(octets_sub1, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - container1.infinite_length = true + container1.indefinite_length = true container2 = OpenSSL::ASN1::Constructive.new(octets_sub2, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - container2.infinite_length = true + container2.indefinite_length = true octets3 = OpenSSL::ASN1::OctetString.new("\x03") octets = [ container1, container2, octets3, OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) - cons.infinite_length = true - expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } - raw = [expected.join('')].pack('H*') + cons.indefinite_length = true + raw = B(%w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }) assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end - def test_bit_string_infinite_length - content = [ OpenSSL::ASN1::BitString.new("\x01"), - OpenSSL::ASN1::EndOfContent.new() ] - cons = OpenSSL::ASN1::Constructive.new(content, OpenSSL::ASN1::BIT_STRING, nil, :UNIVERSAL) - cons.infinite_length = true - expected = %w{ 23 80 03 02 00 01 00 00 } - raw = [expected.join('')].pack('H*') - assert_equal(raw, cons.to_der) - assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) - end - - def test_primitive_inf_length - assert_raise(OpenSSL::ASN1::ASN1Error) do - spec = %w{ 02 80 02 01 01 00 00 } - raw = [spec.join('')].pack('H*') - OpenSSL::ASN1.decode(raw) - OpenSSL::ASN1.decode_all(raw) - end - end - def test_recursive_octet_string_parse - test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } - raw = [test.join('')].pack('H*') + raw = B(%w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 }) asn1 = OpenSSL::ASN1.decode(raw) assert_equal(OpenSSL::ASN1::Constructive, asn1.class) assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1) - assert_equal(true, asn1.infinite_length) - assert_equal(4, asn1.value.size) + assert_equal(true, asn1.indefinite_length) + assert_equal(3, asn1.value.size) nested1 = asn1.value[0] assert_equal(OpenSSL::ASN1::Constructive, nested1.class) assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1) - assert_equal(true, nested1.infinite_length) - assert_equal(2, nested1.value.size) + assert_equal(true, nested1.indefinite_length) + assert_equal(1, nested1.value.size) oct1 = nested1.value[0] assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1) - assert_equal(false, oct1.infinite_length) - assert_universal(OpenSSL::ASN1::EOC, nested1.value[1]) - assert_equal(false, nested1.value[1].infinite_length) + assert_equal(false, oct1.indefinite_length) nested2 = asn1.value[1] assert_equal(OpenSSL::ASN1::Constructive, nested2.class) assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2) - assert_equal(true, nested2.infinite_length) - assert_equal(2, nested2.value.size) + assert_equal(true, nested2.indefinite_length) + assert_equal(1, nested2.value.size) oct2 = nested2.value[0] assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2) - assert_equal(false, oct2.infinite_length) - assert_universal(OpenSSL::ASN1::EOC, nested2.value[1]) - assert_equal(false, nested2.value[1].infinite_length) + assert_equal(false, oct2.indefinite_length) oct3 = asn1.value[2] assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3) - assert_equal(false, oct3.infinite_length) - assert_universal(OpenSSL::ASN1::EOC, asn1.value[3]) - assert_equal(false, asn1.value[3].infinite_length) + assert_equal(false, oct3.indefinite_length) end def test_decode_constructed_overread @@ -577,6 +637,46 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm private + def B(ary) + [ary.join].pack("H*") + end + + def assert_asn1_equal(a, b) + assert_equal a.class, b.class + assert_equal a.tag, b.tag + assert_equal a.tag_class, b.tag_class + assert_equal a.indefinite_length, b.indefinite_length + assert_equal a.unused_bits, b.unused_bits if a.respond_to?(:unused_bits) + case a.value + when Array + a.value.each_with_index { |ai, i| + assert_asn1_equal ai, b.value[i] + } + else + if OpenSSL::ASN1::ObjectId === a + assert_equal a.oid, b.oid + else + assert_equal a.value, b.value + end + end + assert_equal a.to_der, b.to_der + end + + def encode_test(der, obj) + assert_equal der, obj.to_der + end + + def decode_test(der, obj) + decoded = OpenSSL::ASN1.decode(der) + assert_asn1_equal obj, decoded + decoded + end + + def encode_decode_test(der, obj) + encode_test(der, obj) + decode_test(der, obj) + end + def assert_universal(tag, asn1) assert_equal(tag, asn1.tag) if asn1.respond_to?(:tagging) diff --git a/test/test_bn.rb b/test/test_bn.rb index 5f3ae2b4..274afba3 100644 --- a/test/test_bn.rb +++ b/test/test_bn.rb @@ -1,61 +1,276 @@ +# coding: us-ascii # frozen_string_literal: false require_relative 'utils' +require "prime" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) 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) - assert_equal(e1, OpenSSL::BN.new("999")) - assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s)) - assert_equal(e1, OpenSSL::BN.new("999", 10)) - assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10)) - assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2)) - assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2)) - assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0)) - assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0)) - end - - def test_new_bn - e1 = OpenSSL::BN.new(999.to_s(16), 16) - e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) - assert_equal(e1, OpenSSL::BN.new(e1)) - assert_equal(e2, OpenSSL::BN.new(e2)) - end - - def test_new_integer - assert_equal(999.to_bn, OpenSSL::BN.new(999)) - assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1)) - assert_equal(-999.to_bn, OpenSSL::BN.new(-999)) - assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1))) - end - - def test_to_bn - e1 = OpenSSL::BN.new(999.to_s(16), 16) - e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) - assert_equal(e1, 999.to_bn) - assert_equal(e2, (2**107-1).to_bn) - end - - def test_prime_p - assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?) - assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1)) - end - - 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) - assert_instance_of(String, bn1.hash.to_s) + def setup + super + @e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable + @e2 = OpenSSL::BN.new("-" + 999.to_s(16), 16) + @e3 = OpenSSL::BN.new((2**107-1).to_s(16), 16) + @e4 = OpenSSL::BN.new("-" + (2**107-1).to_s(16), 16) + end + + def test_new + assert_equal(@e1, OpenSSL::BN.new("999")) + assert_equal(@e1, OpenSSL::BN.new("999", 10)) + assert_equal(@e1, OpenSSL::BN.new("\x03\xE7", 2)) + assert_equal(@e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0)) + assert_equal(@e2, OpenSSL::BN.new("-999")) + assert_equal(@e2, OpenSSL::BN.new("-999", 10)) + assert_equal(@e2, OpenSSL::BN.new("\x00\x00\x00\x02\x83\xE7", 0)) + assert_equal(@e3, OpenSSL::BN.new((2**107-1).to_s)) + assert_equal(@e3, OpenSSL::BN.new((2**107-1).to_s, 10)) + assert_equal(@e3, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2)) + assert_equal(@e3, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0)) + assert_equal(@e4, OpenSSL::BN.new("-" + (2**107-1).to_s)) + assert_equal(@e4, OpenSSL::BN.new("-" + (2**107-1).to_s, 10)) + assert_equal(@e4, OpenSSL::BN.new("\x00\x00\x00\x0E\x87\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0)) + + e1copy = OpenSSL::BN.new(@e1) + assert_equal(@e1, e1copy) + e1copy.clear_bit!(0) #=> 998 + assert_not_equal(@e1, e1copy) + + assert_equal(@e1, OpenSSL::BN.new(999)) + assert_equal(@e2, OpenSSL::BN.new(-999)) + assert_equal(@e3, OpenSSL::BN.new(2**107-1)) + assert_equal(@e4, OpenSSL::BN.new(-(2**107-1))) + + assert_equal(@e1, 999.to_bn) + assert_equal(@e2, -999.to_bn) + assert_equal(@e3, (2**107-1).to_bn) + assert_equal(@e4, (-(2**107-1)).to_bn) + end + + def test_to_str + assert_equal("999", @e1.to_s(10)) + assert_equal("-999", @e2.to_s(10)) + assert_equal((2**107-1).to_s, @e3.to_s(10)) + assert_equal((-(2**107-1)).to_s, @e4.to_s(10)) + assert_equal("999", @e1.to_s) + + assert_equal("03E7", @e1.to_s(16)) + assert_equal("-03E7", @e2.to_s(16)) + assert_equal("07FFFFFFFFFFFFFFFFFFFFFFFFFF", @e3.to_s(16)) + assert_equal("-07FFFFFFFFFFFFFFFFFFFFFFFFFF", @e4.to_s(16)) + + assert_equal("\x03\xe7", @e1.to_s(2)) + assert_equal("\x03\xe7", @e2.to_s(2)) + assert_equal("\x07\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e3.to_s(2)) + assert_equal("\x07\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e4.to_s(2)) + + assert_equal("\x00\x00\x00\x02\x03\xe7", @e1.to_s(0)) + assert_equal("\x00\x00\x00\x02\x83\xe7", @e2.to_s(0)) + assert_equal("\x00\x00\x00\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e3.to_s(0)) + assert_equal("\x00\x00\x00\x0e\x87\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", @e4.to_s(0)) + end + + def test_to_int + assert_equal(999, @e1.to_i) + assert_equal(-999, @e2.to_i) + assert_equal(2**107-1, @e3.to_i) + assert_equal(-(2**107-1), @e4.to_i) + + assert_equal(999, @e1.to_int) + end + + def test_coerce + assert_equal(["", "-999"], @e2.coerce("")) + assert_equal([1000, -999], @e2.coerce(1000)) + assert_raise(TypeError) { @e2.coerce(Class.new.new) } + end + + def test_zero_p + assert_equal(true, 0.to_bn.zero?) + assert_equal(false, 1.to_bn.zero?) + end + + def test_one_p + assert_equal(true, 1.to_bn.one?) + assert_equal(false, 2.to_bn.one?) + end + + def test_odd_p + assert_equal(true, 1.to_bn.odd?) + assert_equal(false, 2.to_bn.odd?) + end + + def test_negative_p + assert_equal(false, 0.to_bn.negative?) + assert_equal(false, @e1.negative?) + assert_equal(true, @e2.negative?) + end + + def test_sqr + assert_equal(1, 1.to_bn.sqr) + assert_equal(100, 10.to_bn.sqr) + end + + def test_four_ops + assert_equal(3, 1.to_bn + 2) + assert_equal(-1, 1.to_bn + -2) + assert_equal(-1, 1.to_bn - 2) + assert_equal(3, 1.to_bn - -2) + assert_equal(2, 1.to_bn * 2) + assert_equal(-2, 1.to_bn * -2) + assert_equal([0, 1], 1.to_bn / 2) + assert_equal([2, 0], 2.to_bn / 1) + assert_raise(OpenSSL::BNError) { 1.to_bn / 0 } + end + + def test_unary_plus_minus + assert_equal(999, +@e1) + assert_equal(-999, +@e2) + assert_equal(-999, -@e1) + assert_equal(+999, -@e2) + end + + def test_mod + assert_equal(1, 1.to_bn % 2) + assert_equal(0, 2.to_bn % 1) + assert_equal(-2, -2.to_bn % 7) + end + + def test_exp + assert_equal(1, 1.to_bn ** 5) + assert_equal(32, 2.to_bn ** 5) + end + + def test_gcd + assert_equal(1, 7.to_bn.gcd(5)) + assert_equal(8, 24.to_bn.gcd(16)) + end + + def test_mod_sqr + assert_equal(4, 3.to_bn.mod_sqr(5)) + assert_equal(0, 59.to_bn.mod_sqr(59)) + end + + def test_mod_inverse + assert_equal(2, 3.to_bn.mod_inverse(5)) + assert_raise(OpenSSL::BNError) { 3.to_bn.mod_inverse(6) } + end + + def test_mod_add + assert_equal(1, 3.to_bn.mod_add(5, 7)) + assert_equal(2, 3.to_bn.mod_add(5, 3)) + assert_equal(5, 3.to_bn.mod_add(-5, 7)) + end + + def test_mod_sub + assert_equal(1, 11.to_bn.mod_sub(3, 7)) + assert_equal(2, 11.to_bn.mod_sub(3, 3)) + assert_equal(5, 3.to_bn.mod_sub(5, 7)) + end + + def test_mod_mul + assert_equal(1, 2.to_bn.mod_mul(4, 7)) + assert_equal(5, 2.to_bn.mod_mul(-1, 7)) + end + + def test_mod_exp + assert_equal(1, 3.to_bn.mod_exp(2, 8)) + assert_equal(4, 2.to_bn.mod_exp(5, 7)) + end + + def test_bit_operations + e = 0b10010010.to_bn + assert_equal(0b10010011, e.set_bit!(0)) + assert_equal(0b10010011, e.set_bit!(1)) + assert_equal(0b1010010011, e.set_bit!(9)) + + e = 0b10010010.to_bn + assert_equal(0b10010010, e.clear_bit!(0)) + assert_equal(0b10010000, e.clear_bit!(1)) + + e = 0b10010010.to_bn + assert_equal(0b10010010, e.mask_bits!(8)) + assert_equal(0b10, e.mask_bits!(3)) + + e = 0b10010010.to_bn + assert_equal(false, e.bit_set?(0)) + assert_equal(true, e.bit_set?(1)) + assert_equal(false, e.bit_set?(1000)) + + e = 0b10010010.to_bn + assert_equal(0b1001001000, e << 2) + assert_equal(0b10010010, e) + assert_equal(0b1001001000, e.lshift!(2)) + assert_equal(0b1001001000, e) + + e = 0b10010010.to_bn + assert_equal(0b100100, e >> 2) + assert_equal(0b10010010, e) + assert_equal(0b100100, e.rshift!(2)) + assert_equal(0b100100, e) + end + + def test_random + 10.times { + r1 = OpenSSL::BN.rand(8) + assert_include(128..255, r1) + r2 = OpenSSL::BN.rand(8, -1) + assert_include(0..255, r2) + r3 = OpenSSL::BN.rand(8, 1) + assert_include(192..255, r3) + r4 = OpenSSL::BN.rand(8, 1, true) + assert_include(192..255, r4) + assert_equal(true, r4.odd?) + + r5 = OpenSSL::BN.rand_range(256) + assert_include(0..255, r5) + } + end + + def test_prime + p1 = OpenSSL::BN.generate_prime(32) + assert_include(0...2**32, p1) + assert_equal(true, Prime.prime?(p1.to_i)) + p2 = OpenSSL::BN.generate_prime(32, true) + assert_equal(true, Prime.prime?((p2.to_i - 1) / 2)) + p3 = OpenSSL::BN.generate_prime(32, false, 4) + assert_equal(1, p3 % 4) + p4 = OpenSSL::BN.generate_prime(32, false, 4, 3) + assert_equal(3, p4 % 4) + + assert_equal(true, p1.prime?) + assert_equal(true, p2.prime?) + assert_equal(true, p3.prime?) + assert_equal(true, p4.prime?) + assert_equal(true, @e3.prime?) + assert_equal(true, @e3.prime_fasttest?) + end + + def test_num_bits_bytes + assert_equal(10, @e1.num_bits) + assert_equal(2, @e1.num_bytes) + assert_equal(107, @e3.num_bits) + assert_equal(14, @e3.num_bytes) + assert_equal(0, 0.to_bn.num_bits) + assert_equal(0, 0.to_bn.num_bytes) + assert_equal(9, -256.to_bn.num_bits) + assert_equal(2, -256.to_bn.num_bytes) + end + + def test_comparison + assert_equal(false, @e1 == nil) + assert_equal(false, @e1 == -999) + assert_equal(true, @e1 == 999) + assert_equal(true, @e1 == 999.to_bn) + assert_equal(false, @e1.eql?(nil)) + assert_equal(false, @e1.eql?(999)) + assert_equal(true, @e1.eql?(999.to_bn)) + assert_equal(@e1.hash, 999.to_bn.hash) + assert_not_equal(@e1.hash, @e3.hash) + assert_equal(0, @e1.cmp(999)) + assert_equal(1, @e1.cmp(-999)) + assert_equal(0, @e1.ucmp(999)) + assert_equal(0, @e1.ucmp(-999)) + assert_instance_of(String, @e1.hash.to_s) end end diff --git a/test/test_buffering.rb b/test/test_buffering.rb index 08735add..c85a6f02 100644 --- a/test/test_buffering.rb +++ b/test/test_buffering.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestBuffering < OpenSSL::TestCase class IO diff --git a/test/test_cipher.rb b/test/test_cipher.rb index 48149d41..56061741 100644 --- a/test/test_cipher.rb +++ b/test/test_cipher.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestCipher < OpenSSL::TestCase module Helper @@ -129,7 +129,7 @@ class OpenSSL::TestCipher < OpenSSL::TestCase assert_equal ct, cipher.update(pt) << cipher.final cipher = new_decryptor("aes-128-ctr", key: key, iv: iv, padding: 0) assert_equal pt, cipher.update(ct) << cipher.final - end if has_cipher?('aes-128-ctr') + end def test_ciphers OpenSSL::Cipher.ciphers.each{|name| @@ -165,10 +165,8 @@ class OpenSSL::TestCipher < OpenSSL::TestCase end def test_authenticated - if has_cipher?('aes-128-gcm') - cipher = OpenSSL::Cipher.new('aes-128-gcm') - assert_predicate(cipher, :authenticated?) - end + cipher = OpenSSL::Cipher.new('aes-128-gcm') + assert_predicate(cipher, :authenticated?) cipher = OpenSSL::Cipher.new('aes-128-cbc') assert_not_predicate(cipher, :authenticated?) end @@ -220,7 +218,7 @@ class OpenSSL::TestCipher < OpenSSL::TestCase cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad) cipher.update(ct2) assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } - end if has_cipher?("aes-128-gcm") + end def test_aes_gcm_variable_iv_len # GCM spec Appendix B Test Case 5 @@ -243,7 +241,7 @@ class OpenSSL::TestCipher < OpenSSL::TestCase assert_equal tag, cipher.auth_tag cipher = new_decryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_tag: tag, auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final - end if has_cipher?("aes-128-gcm") + end def test_aes_ocb_tag_len # RFC 7253 Appendix A; the second sample @@ -295,14 +293,14 @@ class OpenSSL::TestCipher < OpenSSL::TestCase assert_equal ct1, ct2 assert_equal tag1, tag2 - end if has_cipher?("aes-128-gcm") + end def test_non_aead_cipher_set_auth_data assert_raise(OpenSSL::Cipher::CipherError) { cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt cipher.auth_data = "123" } - end if has_cipher?("aes-128-gcm") + end private @@ -319,7 +317,6 @@ class OpenSSL::TestCipher < OpenSSL::TestCase kwargs.each {|k, v| cipher.send(:"#{k}=", v) } end end - end end diff --git a/test/test_config.rb b/test/test_config.rb index 99dcc497..8096375c 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestConfig < OpenSSL::TestCase def setup diff --git a/test/test_digest.rb b/test/test_digest.rb index c8817395..2cb878b6 100644 --- a/test/test_digest.rb +++ b/test/test_digest.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestDigest < OpenSSL::TestCase def setup diff --git a/test/test_engine.rb b/test/test_engine.rb index 801d1c8e..bb1123d5 100644 --- a/test/test_engine.rb +++ b/test/test_engine.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::Engine) +if defined?(OpenSSL) && defined?(OpenSSL::Engine) class OpenSSL::TestEngine < OpenSSL::TestCase def test_engines_free # [ruby-dev:44173] diff --git a/test/test_fips.rb b/test/test_fips.rb index 534dade0..a577d789 100644 --- a/test/test_fips.rb +++ b/test/test_fips.rb @@ -1,15 +1,30 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestFIPS < OpenSSL::TestCase - def test_fips_mode_is_reentrant OpenSSL.fips_mode = false OpenSSL.fips_mode = false end + def test_fips_mode_get + return unless OpenSSL::OPENSSL_FIPS + assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;") + require #{__FILE__.dump} + + begin + OpenSSL.fips_mode = true + assert OpenSSL.fips_mode == true, ".fips_mode returns true when .fips_mode=true" + + OpenSSL.fips_mode = false + assert OpenSSL.fips_mode == false, ".fips_mode returns false when .fips_mode=false" + rescue OpenSSL::OpenSSLError + pend "Could not set FIPS mode (OpenSSL::OpenSSLError: \#$!); skipping" + end + end; + end end end diff --git a/test/test_hmac.rb b/test/test_hmac.rb index 4d06e476..831a5b6b 100644 --- a/test/test_hmac.rb +++ b/test/test_hmac.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestHMAC < OpenSSL::TestCase def test_hmac diff --git a/test/test_kdf.rb b/test/test_kdf.rb new file mode 100644 index 00000000..5e1db80c --- /dev/null +++ b/test/test_kdf.rb @@ -0,0 +1,183 @@ +# frozen_string_literal: false +require_relative 'utils' + +if defined?(OpenSSL) + +class OpenSSL::TestKDF < OpenSSL::TestCase + def test_pkcs5_pbkdf2_hmac_compatibility + expected = OpenSSL::KDF.pbkdf2_hmac("password", salt: "salt", iterations: 1, length: 20, hash: "sha1") + assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac("password", "salt", 1, 20, "sha1")) + assert_equal(expected, OpenSSL::PKCS5.pbkdf2_hmac_sha1("password", "salt", 1, 20)) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20 + p ="password" + s = "salt" + c = 1 + dk_len = 20 + raw = %w{ 0c 60 c8 0f 96 1f 0e 71 + f3 a9 b5 24 af 60 12 06 + 2f e0 37 a6 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1") + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20 + p ="password" + s = "salt" + c = 2 + dk_len = 20 + raw = %w{ ea 6c 01 4d c7 2d 6f 8c + cd 1e d9 2a ce 1d 41 f0 + d8 de 89 57 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1") + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20 + p ="password" + s = "salt" + c = 4096 + dk_len = 20 + raw = %w{ 4b 00 79 01 b7 65 48 9a + be ad 49 d9 26 f7 21 d0 + 65 a4 29 c1 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1") + assert_equal(expected, value) + end + +# takes too long! +# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20 +# p ="password" +# s = "salt" +# c = 16777216 +# dk_len = 20 +# raw = %w{ ee fe 3d 61 cd 4d a4 e4 +# e9 94 5b 3d 6b a2 15 8c +# 26 34 e9 84 } +# expected = [raw.join('')].pack('H*') +# value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1") +# assert_equal(expected, value) +# end + + def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25 + p ="passwordPASSWORDpassword" + s = "saltSALTsaltSALTsaltSALTsaltSALTsalt" + c = 4096 + dk_len = 25 + + raw = %w{ 3d 2e ec 4f e4 1c 84 9b + 80 c8 d8 36 62 c0 e4 4a + 8b 29 1a 96 4c f2 f0 70 + 38 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1") + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16 + p ="pass\0word" + s = "sa\0lt" + c = 4096 + dk_len = 16 + raw = %w{ 56 fa 6a a7 55 48 09 9d + cc 37 d7 f0 34 25 e0 c3 } + expected = [raw.join('')].pack('H*') + value = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha1") + assert_equal(expected, value) + end + + def test_pbkdf2_hmac_sha256_c_20000_len_32 + #unfortunately no official test vectors available yet for SHA-2 + p ="password" + s = OpenSSL::Random.random_bytes(16) + c = 20000 + dk_len = 32 + value1 = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha256") + value2 = OpenSSL::KDF.pbkdf2_hmac(p, salt: s, iterations: c, length: dk_len, hash: "sha256") + assert_equal(value1, value2) + end + + def test_scrypt_rfc7914_first + pend "scrypt is not implemented" unless OpenSSL::KDF.respond_to?(:scrypt) # OpenSSL >= 1.1.0 + pass = "" + salt = "" + n = 16 + r = 1 + p = 1 + dklen = 64 + expected = B(%w{ 77 d6 57 62 38 65 7b 20 3b 19 ca 42 c1 8a 04 97 + f1 6b 48 44 e3 07 4a e8 df df fa 3f ed e2 14 42 + fc d0 06 9d ed 09 48 f8 32 6a 75 3a 0f c8 1f 17 + e8 d3 e0 fb 2e 0d 36 28 cf 35 e2 0c 38 d1 89 06 }) + assert_equal(expected, OpenSSL::KDF.scrypt(pass, salt: salt, N: n, r: r, p: p, length: dklen)) + end + + def test_scrypt_rfc7914_second + pend "scrypt is not implemented" unless OpenSSL::KDF.respond_to?(:scrypt) # OpenSSL >= 1.1.0 + pass = "password" + salt = "NaCl" + n = 1024 + r = 8 + p = 16 + dklen = 64 + expected = B(%w{ fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe + 7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62 + 2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da + c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40 }) + assert_equal(expected, OpenSSL::KDF.scrypt(pass, salt: salt, N: n, r: r, p: p, length: dklen)) + end + + def test_hkdf_rfc5869_test_case_1 + pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0 + hash = "sha256" + ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + salt = B("000102030405060708090a0b0c") + info = B("f0f1f2f3f4f5f6f7f8f9") + l = 42 + + okm = B("3cb25f25faacd57a90434f64d0362f2a" \ + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" \ + "34007208d5b887185865") + assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash)) + end + + def test_hkdf_rfc5869_test_case_3 + pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0 + hash = "sha256" + ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + salt = B("") + info = B("") + l = 42 + + okm = B("8da4e775a563c18f715f802a063c5a31" \ + "b8a11f5c5ee1879ec3454e5f3c738d2d" \ + "9d201395faa4b61a96c8") + assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash)) + end + + def test_hkdf_rfc5869_test_case_4 + pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0 + hash = "sha1" + ikm = B("0b0b0b0b0b0b0b0b0b0b0b") + salt = B("000102030405060708090a0b0c") + info = B("f0f1f2f3f4f5f6f7f8f9") + l = 42 + + okm = B("085a01ea1b10f36933068b56efa5ad81" \ + "a4f14b822f5b091568a9cdd4f155fda2" \ + "c22e422478d305f3f896") + assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash)) + end + + private + + def B(ary) + [Array(ary).join].pack("H*") + end +end + +end diff --git a/test/test_ns_spki.rb b/test/test_ns_spki.rb index 58e0f649..aa1e6182 100644 --- a/test/test_ns_spki.rb +++ b/test/test_ns_spki.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestNSSPI < OpenSSL::TestCase def setup diff --git a/test/test_ocsp.rb b/test/test_ocsp.rb index 0440634a..50ad6c31 100644 --- a/test/test_ocsp.rb +++ b/test/test_ocsp.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestOCSP < OpenSSL::TestCase def setup @@ -122,14 +122,29 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase assert_equal true, req.verify([@cert], store, OpenSSL::OCSP::NOINTERN) ret = req.verify([@cert], store) - if ret || openssl?(1, 0, 2) || libressl?(2, 4, 2) + if ret || openssl?(1, 0, 2) assert_equal true, ret else # RT2560; OCSP_request_verify() does not find signer cert from 'certs' when # OCSP_NOINTERN is not specified. - # fixed by OpenSSL 1.0.1j, 1.0.2 and LibreSSL 2.4.2 + # fixed by OpenSSL 1.0.1j, 1.0.2 pend "RT2560: ocsp_req_find_signer" end + + # not signed + req = OpenSSL::OCSP::Request.new.add_certid(cid) + assert_equal false, req.verify([], store) + end + + def test_request_is_signed + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) + req = OpenSSL::OCSP::Request.new + req.add_certid(cid) + assert_equal false, req.signed? + assert_equal false, OpenSSL::OCSP::Request.new(req.to_der).signed? + req.sign(@cert, @cert_key, []) + assert_equal true, req.signed? + assert_equal true, OpenSSL::OCSP::Request.new(req.to_der).signed? end def test_request_nonce @@ -247,11 +262,6 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, []) bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, Time.now + 100, nil, nil) - if bres.responses[2].check_validity # thisUpdate is in future; must fail - # LibreSSL bug; skip for now - pend "OCSP_check_validity() is broken" - end - single1 = bres.responses[0] assert_equal false, single1.check_validity assert_equal false, single1.check_validity(30) @@ -260,6 +270,8 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase assert_equal true, single2.check_validity assert_equal true, single2.check_validity(0, 500) assert_equal false, single2.check_validity(0, 200) + single3 = bres.responses[2] + assert_equal false, single3.check_validity end def test_response diff --git a/test/test_pair.rb b/test/test_pair.rb index ea5f0dcf..29d5c9bb 100644 --- a/test/test_pair.rb +++ b/test/test_pair.rb @@ -2,7 +2,7 @@ require_relative 'utils' require_relative 'ut_eof' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) module OpenSSL::SSLPairM def setup @@ -362,6 +362,15 @@ module OpenSSL::TestPairM } end + def test_write_multiple_arguments + ssl_pair {|s1, s2| + str1 = "foo"; str2 = "bar" + assert_equal 6, s1.write(str1, str2) + s1.close + assert_equal "foobar", s2.read + } + end + def test_partial_tls_record_read_nonblock ssl_pair { |s1, s2| # the beginning of a TLS record diff --git a/test/test_pkcs12.rb b/test/test_pkcs12.rb index 30888234..de8e35ed 100644 --- a/test/test_pkcs12.rb +++ b/test/test_pkcs12.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) module OpenSSL class TestPKCS12 < OpenSSL::TestCase @@ -307,7 +307,6 @@ Kw4DAhoFAAQUYAuwVtGD1TdgbFK4Yal2XBgwUR4ECEawsN3rNaa6AgIIAA== end false end - end end diff --git a/test/test_pkcs5.rb b/test/test_pkcs5.rb deleted file mode 100644 index 0919b9ce..00000000 --- a/test/test_pkcs5.rb +++ /dev/null @@ -1,100 +0,0 @@ -# frozen_string_literal: false -require_relative 'utils' - -if defined?(OpenSSL::TestUtils) - -class OpenSSL::TestPKCS5 < OpenSSL::TestCase - def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20 - p ="password" - s = "salt" - c = 1 - dk_len = 20 - raw = %w{ 0c 60 c8 0f 96 1f 0e 71 - f3 a9 b5 24 af 60 12 06 - 2f e0 37 a6 } - expected = [raw.join('')].pack('H*') - value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) - assert_equal(expected, value) - end - - def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20 - p ="password" - s = "salt" - c = 2 - dk_len = 20 - raw = %w{ ea 6c 01 4d c7 2d 6f 8c - cd 1e d9 2a ce 1d 41 f0 - d8 de 89 57 } - expected = [raw.join('')].pack('H*') - value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) - assert_equal(expected, value) - end - - def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20 - p ="password" - s = "salt" - c = 4096 - dk_len = 20 - raw = %w{ 4b 00 79 01 b7 65 48 9a - be ad 49 d9 26 f7 21 d0 - 65 a4 29 c1 } - expected = [raw.join('')].pack('H*') - value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) - assert_equal(expected, value) - end - -# takes too long! -# def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20 -# p ="password" -# s = "salt" -# c = 16777216 -# dk_len = 20 -# raw = %w{ ee fe 3d 61 cd 4d a4 e4 -# e9 94 5b 3d 6b a2 15 8c -# 26 34 e9 84 } -# expected = [raw.join('')].pack('H*') -# value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) -# assert_equal(expected, value) -# end - - def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25 - p ="passwordPASSWORDpassword" - s = "saltSALTsaltSALTsaltSALTsaltSALTsalt" - c = 4096 - dk_len = 25 - - raw = %w{ 3d 2e ec 4f e4 1c 84 9b - 80 c8 d8 36 62 c0 e4 4a - 8b 29 1a 96 4c f2 f0 70 - 38 } - expected = [raw.join('')].pack('H*') - value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) - assert_equal(expected, value) - end - - def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16 - p ="pass\0word" - s = "sa\0lt" - c = 4096 - dk_len = 16 - raw = %w{ 56 fa 6a a7 55 48 09 9d - cc 37 d7 f0 34 25 e0 c3 } - expected = [raw.join('')].pack('H*') - value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) - assert_equal(expected, value) - end - - def test_pbkdf2_hmac_sha256_c_20000_len_32 - #unfortunately no official test vectors available yet for SHA-2 - p ="password" - s = OpenSSL::Random.random_bytes(16) - c = 20000 - dk_len = 32 - digest = OpenSSL::Digest::SHA256.new - value1 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest) - value2 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest) - assert_equal(value1, value2) - end if OpenSSL::PKCS5.respond_to?(:pbkdf2_hmac) -end - -end diff --git a/test/test_pkcs7.rb b/test/test_pkcs7.rb index 026fe847..149d3b9b 100644 --- a/test/test_pkcs7.rb +++ b/test/test_pkcs7.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestPKCS7 < OpenSSL::TestCase def setup diff --git a/test/test_pkey_dh.rb b/test/test_pkey_dh.rb index fb713813..77cdb0ab 100644 --- a/test/test_pkey_dh.rb +++ b/test/test_pkey_dh.rb @@ -1,27 +1,11 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) && defined?(OpenSSL::PKey::DH) class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase NEW_KEYLEN = 256 - def test_DEFAULT_parameters - list = { - 1024 => OpenSSL::PKey::DH::DEFAULT_1024, - 2048 => OpenSSL::PKey::DH::DEFAULT_2048, - } - - list.each do |expected_size, dh| - assert_equal expected_size, dh.p.num_bits - assert_predicate dh.p, :prime? - result, remainder = (dh.p - 1) / 2 - assert_predicate result, :prime? - assert_equal 0, remainder - assert_no_key dh - end - end - def test_new dh = OpenSSL::PKey::DH.new(NEW_KEYLEN) assert_key(dh) diff --git a/test/test_pkey_dsa.rb b/test/test_pkey_dsa.rb index 474f2388..d6519498 100644 --- a/test/test_pkey_dsa.rb +++ b/test/test_pkey_dsa.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) && defined?(OpenSSL::PKey::DSA) class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase def test_private @@ -41,7 +41,6 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase assert_equal true, dsa512.verify(OpenSSL::Digest::DSS1.new, signature, data) end - return unless openssl?(1, 0, 0) signature = dsa512.sign("SHA1", data) assert_equal true, dsa512.verify("SHA1", signature, data) diff --git a/test/test_pkey_ec.rb b/test/test_pkey_ec.rb index 8119b77c..713c649a 100644 --- a/test/test_pkey_ec.rb +++ b/test/test_pkey_ec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC) +if defined?(OpenSSL) && defined?(OpenSSL::PKey::EC) class OpenSSL::TestEC < OpenSSL::PKeyTestCase def test_ec_key @@ -99,16 +99,9 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase key = OpenSSL::PKey::EC.new("prime256v1").generate_key! size = key.group.order.num_bits / 8 + 1 dgst = (1..size).to_a.pack('C*') - begin - sig = key.dsa_sign_asn1(dgst) - # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m - assert(key.dsa_verify_asn1(dgst + "garbage", sig)) - rescue OpenSSL::PKey::ECError => e - # just an exception for longer dgst before openssl-0.9.8m - assert_equal('ECDSA_sign: data too large for key size', e.message) - # no need to do following tests - return - end + sig = key.dsa_sign_asn1(dgst) + # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m + assert(key.dsa_verify_asn1(dgst + "garbage", sig)) end def test_dh_compute_key diff --git a/test/test_pkey_rsa.rb b/test/test_pkey_rsa.rb index c1205563..d9bea1a6 100644 --- a/test/test_pkey_rsa.rb +++ b/test/test_pkey_rsa.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase def test_padding @@ -113,6 +113,39 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase } end + def test_sign_verify_pss + key = Fixtures.pkey("rsa1024") + data = "Sign me!" + invalid_data = "Sign me?" + + signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA1") + assert_equal 128, signature.bytesize + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1") + assert_equal false, + key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA1") + + signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1") + assert_equal false, + key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1") + + signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: 94, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1") + + assert_raise(OpenSSL::PKey::RSAError) { + key.sign_pss("SHA256", data, salt_length: 95, mgf1_hash: "SHA1") + } + end + def test_RSAPrivateKey rsa1024 = Fixtures.pkey("rsa1024") asn1 = OpenSSL::ASN1::Sequence([ diff --git a/test/test_random.rb b/test/test_random.rb index c0160f9a..d5a37454 100644 --- a/test/test_random.rb +++ b/test/test_random.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestRandom < OpenSSL::TestCase def test_random_bytes diff --git a/test/test_ssl.rb b/test/test_ssl.rb index 7bb32adf..b3f3143f 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -1,10 +1,9 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestSSL < OpenSSL::SSLTestCase - def test_ctx_options ctx = OpenSSL::SSL::SSLContext.new @@ -55,6 +54,87 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase } end + def test_add_certificate + ctx_proc = -> ctx { + # Unset values set by start_server + ctx.cert = ctx.key = ctx.extra_chain_cert = nil + ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA + } + start_server(ctx_proc: ctx_proc) do |port| + server_connect(port) { |ssl| + assert_equal @svr_cert.subject, ssl.peer_cert.subject + assert_equal [@svr_cert.subject, @ca_cert.subject], + ssl.peer_cert_chain.map(&:subject) + } + end + end + + def test_add_certificate_multiple_certs + pend "EC is not supported" unless defined?(OpenSSL::PKey::EC) + pend "TLS 1.2 is not supported" unless tls12_supported? + + # SSL_CTX_set0_chain() is needed for setting multiple certificate chains + add0_chain_supported = openssl?(1, 0, 2) + + if add0_chain_supported + ca2_key = Fixtures.pkey("rsa1024") + ca2_exts = [ + ["basicConstraints", "CA:TRUE", true], + ["keyUsage", "cRLSign, keyCertSign", true], + ] + ca2_dn = OpenSSL::X509::Name.parse_rfc2253("CN=CA2") + ca2_cert = issue_cert(ca2_dn, ca2_key, 123, ca2_exts, nil, nil) + else + # Use the same CA as @svr_cert + ca2_key = @ca_key; ca2_cert = @ca_cert + end + + ecdsa_key = Fixtures.pkey("p256") + exts = [ + ["keyUsage", "digitalSignature", false], + ] + ecdsa_dn = OpenSSL::X509::Name.parse_rfc2253("CN=localhost2") + ecdsa_cert = issue_cert(ecdsa_dn, ecdsa_key, 456, exts, ca2_cert, ca2_key) + + if !add0_chain_supported + # Testing the warning emitted when 'extra' chain is replaced + tctx = OpenSSL::SSL::SSLContext.new + tctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) + assert_warning(/set0_chain/) { + tctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert]) + } + end + + ctx_proc = -> ctx { + # Unset values set by start_server + ctx.cert = ctx.key = ctx.extra_chain_cert = nil + ctx.ecdh_curves = "P-256" unless openssl?(1, 0, 2) + ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA + EnvUtil.suppress_warning do # !add0_chain_supported + ctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert]) + end + } + start_server(ctx_proc: ctx_proc) do |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.max_version = :TLS1_2 # TODO: We need this to force certificate type + ctx.ciphers = "aECDSA" + server_connect(port, ctx) { |ssl| + assert_equal ecdsa_cert.subject, ssl.peer_cert.subject + assert_equal [ecdsa_cert.subject, ca2_cert.subject], + ssl.peer_cert_chain.map(&:subject) + } + + ctx = OpenSSL::SSL::SSLContext.new + ctx.max_version = :TLS1_2 + ctx.ciphers = "aRSA" + server_connect(port, ctx) { |ssl| + assert_equal @svr_cert.subject, ssl.peer_cert.subject + assert_equal [@svr_cert.subject, @ca_cert.subject], + ssl.peer_cert_chain.map(&:subject) + } + end + end + def test_sysread_and_syswrite start_server { |port| server_connect(port) { |ssl| @@ -344,10 +424,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase assert ciphers_names.all?{|v| /A(EC)?DH/ !~ v }, "anon ciphers are disabled" assert ciphers_names.all?{|v| /(RC4|MD5|EXP|DES(?!-EDE|-CBC3))/ !~ v }, "weak ciphers are disabled" assert_equal 0, ctx.options & OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS - if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) # >= 1.0.0 - assert_equal OpenSSL::SSL::OP_NO_COMPRESSION, - ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION - end + assert_equal OpenSSL::SSL::OP_NO_COMPRESSION, + ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION end def test_post_connect_check_with_anon_ciphers @@ -739,6 +817,30 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase end end + def test_connect_certificate_verify_failed_exception_message + start_server(ignore_listener_error: true) { |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params + assert_raise_with_message(OpenSSL::SSL::SSLError, /self signed/) { + server_connect(port, ctx) + } + } + + ctx_proc = proc { |ctx| + ctx.cert = issue_cert(@svr, @svr_key, 30, [], @ca_cert, @ca_key, + not_before: Time.now-100, not_after: Time.now-10) + } + start_server(ignore_listener_error: true, ctx_proc: ctx_proc) { |port| + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params(cert_store: store) + assert_raise_with_message(OpenSSL::SSL::SSLError, /expired/) { + server_connect(port, ctx) + } + } + end + def test_unset_OP_ALL ctx_proc = Proc.new { |ctx| # If OP_DONT_INSERT_EMPTY_FRAGMENTS is not defined, this test is @@ -754,100 +856,228 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase } end -if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1) && OpenSSL::SSL::SSLContext::METHODS.include?(:SSLv3) + def check_supported_protocol_versions + possible_versions = [ + OpenSSL::SSL::SSL3_VERSION, + OpenSSL::SSL::TLS1_VERSION, + OpenSSL::SSL::TLS1_1_VERSION, + OpenSSL::SSL::TLS1_2_VERSION, + # OpenSSL 1.1.1 + defined?(OpenSSL::SSL::TLS1_3_VERSION) && OpenSSL::SSL::TLS1_3_VERSION, + ].compact + + # Prepare for testing & do sanity check + supported = [] + possible_versions.each do |ver| + catch(:unsupported) { + ctx_proc = proc { |ctx| + begin + ctx.min_version = ctx.max_version = ver + rescue ArgumentError, OpenSSL::SSL::SSLError + throw :unsupported + end + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| + begin + server_connect(port) { } + rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET + else + supported << ver + end + end + } + end + assert_not_empty supported - def test_forbid_ssl_v3_for_client - ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 } - start_server_version(:SSLv23, ctx_proc) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :SSLv3 - assert_handshake_error { server_connect(port, ctx) } - } + supported end - def test_forbid_ssl_v3_from_server - start_server_version(:SSLv3) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 - assert_handshake_error { server_connect(port, ctx) } - } + def test_set_params_min_version + supported = check_supported_protocol_versions + store = OpenSSL::X509::Store.new + store.add_cert(@ca_cert) + + if supported.include?(OpenSSL::SSL::SSL3_VERSION) + # SSLContext#set_params properly disables SSL 3.0 by default + ctx_proc = proc { |ctx| + ctx.min_version = ctx.max_version = OpenSSL::SSL::SSL3_VERSION + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.set_params(cert_store: store, verify_hostname: false) + assert_handshake_error { server_connect(port, ctx) { } } + } + end end -end + def test_minmax_version + supported = check_supported_protocol_versions + + # name: The string that would be returned by SSL_get_version() + # method: The version-specific method name (if any) + vmap = { + OpenSSL::SSL::SSL3_VERSION => { name: "SSLv3", method: "SSLv3" }, + OpenSSL::SSL::SSL3_VERSION => { name: "SSLv3", method: "SSLv3" }, + OpenSSL::SSL::TLS1_VERSION => { name: "TLSv1", method: "TLSv1" }, + OpenSSL::SSL::TLS1_1_VERSION => { name: "TLSv1.1", method: "TLSv1_1" }, + OpenSSL::SSL::TLS1_2_VERSION => { name: "TLSv1.2", method: "TLSv1_2" }, + # OpenSSL 1.1.1 + defined?(OpenSSL::SSL::TLS1_3_VERSION) && OpenSSL::SSL::TLS1_3_VERSION => + { name: "TLSv1.3", method: nil }, + } -if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_1) && OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1) + # Server enables a single version + supported.each do |ver| + ctx_proc = proc { |ctx| ctx.min_version = ctx.max_version = ver } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + supported.each do |cver| + # Client enables a single version + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.min_version = ctx1.max_version = cver + if ver == cver + server_connect(port, ctx1) { |ssl| + assert_equal vmap[cver][:name], ssl.ssl_version + } + else + assert_handshake_error { server_connect(port, ctx1) { } } + end - def test_tls_v1_1 - start_server_version(:TLSv1_1) { |port| - ctx = OpenSSL::SSL::SSLContext.new(:TLSv1_1) - server_connect(port, ctx) { |ssl| assert_equal("TLSv1.1", ssl.ssl_version) } - } - end + # There is no version-specific SSL methods for TLS 1.3 + if cver <= OpenSSL::SSL::TLS1_2_VERSION + # Client enables a single version using #ssl_version= + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.ssl_version = vmap[cver][:method] + if ver == cver + server_connect(port, ctx2) { |ssl| + assert_equal vmap[cver][:name], ssl.ssl_version + } + else + assert_handshake_error { server_connect(port, ctx2) { } } + end + end + end - def test_forbid_tls_v1_for_client - ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 } - start_server_version(:SSLv23, ctx_proc) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1 - assert_handshake_error { server_connect(port, ctx) } - } - end + # Client enables all supported versions + ctx3 = OpenSSL::SSL::SSLContext.new + ctx3.min_version = ctx3.max_version = nil + server_connect(port, ctx3) { |ssl| + assert_equal vmap[ver][:name], ssl.ssl_version + } + } + end - def test_forbid_tls_v1_from_server - start_server_version(:TLSv1) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 - assert_handshake_error { server_connect(port, ctx) } - } - end + if supported.size == 1 + pend "More than one protocol version must be supported" + end -end + # Server sets min_version (earliest is disabled) + sver = supported[1] + ctx_proc = proc { |ctx| ctx.min_version = sver } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + supported.each do |cver| + # Client sets min_version + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.min_version = cver + server_connect(port, ctx1) { |ssl| + assert_equal vmap[supported.last][:name], ssl.ssl_version + } -if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) && OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_1) + # Client sets max_version + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.max_version = cver + if cver >= sver + server_connect(port, ctx2) { |ssl| + assert_equal vmap[cver][:name], ssl.ssl_version + } + else + assert_handshake_error { server_connect(port, ctx2) { } } + end + end + } - def test_tls_v1_2 - start_server_version(:TLSv1_2) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_2_client - server_connect(port, ctx) { |ssl| assert_equal("TLSv1.2", ssl.ssl_version) } + # Server sets max_version (latest is disabled) + sver = supported[-2] + ctx_proc = proc { |ctx| ctx.max_version = sver } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + supported.each do |cver| + # Client sets min_version + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.min_version = cver + if cver <= sver + server_connect(port, ctx1) { |ssl| + assert_equal vmap[sver][:name], ssl.ssl_version + } + else + assert_handshake_error { server_connect(port, ctx1) { } } + end + + # Client sets max_version + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.max_version = cver + server_connect(port, ctx2) { |ssl| + if cver >= sver + assert_equal vmap[sver][:name], ssl.ssl_version + else + assert_equal vmap[cver][:name], ssl.ssl_version + end + } + end } end - def test_forbid_tls_v1_1_for_client - ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 } - start_server_version(:SSLv23, ctx_proc) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_1 - assert_handshake_error { server_connect(port, ctx) } - } - end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) + def test_options_disable_versions + # Note: Use of these OP_* flags has been deprecated since OpenSSL 1.1.0. + supported = check_supported_protocol_versions - def test_forbid_tls_v1_1_from_server - start_server_version(:TLSv1_1) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 - assert_handshake_error { server_connect(port, ctx) } - } - end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) + if supported.include?(OpenSSL::SSL::TLS1_1_VERSION) && + supported.include?(OpenSSL::SSL::TLS1_2_VERSION) + # Server disables ~ TLS 1.1 + ctx_proc = proc { |ctx| + ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 | + OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + # Client only supports TLS 1.1 + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_1_VERSION + assert_handshake_error { server_connect(port, ctx1) { } } + + # Client only supports TLS 1.2 + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_2_VERSION + assert_nothing_raised { server_connect(port, ctx2) { } } + } - def test_forbid_tls_v1_2_for_client - ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 } - start_server_version(:SSLv23, ctx_proc) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_2 - assert_handshake_error { server_connect(port, ctx) } - } - end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) + # Server only supports TLS 1.1 + ctx_proc = proc { |ctx| + ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + # Client disables TLS 1.1 + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_1 + assert_handshake_error { server_connect(port, ctx1) { } } + + # Client disables TLS 1.2 + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_2 + assert_nothing_raised { server_connect(port, ctx2) { } } + } + else + pend "TLS 1.1 and TLS 1.2 must be supported; skipping" + end + end - def test_forbid_tls_v1_2_from_server - start_server_version(:TLSv1_2) { |port| - ctx = OpenSSL::SSL::SSLContext.new - ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 - assert_handshake_error { server_connect(port, ctx) } + def test_ssl_methods_constant + EnvUtil.suppress_warning { # Deprecated in v2.1.0 + base = [:TLSv1_2, :TLSv1_1, :TLSv1, :SSLv3, :SSLv2, :SSLv23] + base.each do |name| + assert_include OpenSSL::SSL::SSLContext::METHODS, name + assert_include OpenSSL::SSL::SSLContext::METHODS, :"#{name}_client" + assert_include OpenSSL::SSL::SSLContext::METHODS, :"#{name}_server" + end } - end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) - -end + end def test_renegotiation_cb num_handshakes = 0 @@ -1073,6 +1303,59 @@ end end end + def test_fallback_scsv + pend "Fallback SCSV is not supported" unless OpenSSL::SSL::SSLContext.method_defined?( :enable_fallback_scsv) + + start_server do |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + # Here is OK + # TLS1.2 supported and this is what we ask the first time + server_connect(port, ctx) + end + + ctx_proc = proc { |ctx| + ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION + } + start_server(ctx_proc: ctx_proc) do |port| + ctx = OpenSSL::SSL::SSLContext.new + ctx.enable_fallback_scsv + ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION + # Here is OK too + # TLS1.2 not supported, fallback to TLS1.1 and signaling the fallback + # Server doesn't support better, so connection OK + server_connect(port, ctx) + end + + # Here is not OK + # TLS1.2 is supported, fallback to TLS1.1 (downgrade attack) and signaling the fallback + # Server support better, so refuse the connection + sock1, sock2 = socketpair + begin + ctx1 = OpenSSL::SSL::SSLContext.new + s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) + + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.enable_fallback_scsv + ctx2.max_version = OpenSSL::SSL::TLS1_1_VERSION + s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) + t = Thread.new { + assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) { + s2.connect + } + } + + assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) { + s1.accept + } + + assert t.join + ensure + sock1.close + sock2.close + end + end + def test_dh_callback pend "TLS 1.2 is not supported" unless tls12_supported? @@ -1187,11 +1470,24 @@ end return end assert_equal(1, ctx.security_level) - # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("dsa512") } - # ctx.key = Fixtures.pkey("rsa1024") - # ctx.security_level = 2 - # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("rsa1024") } - pend "FIXME: SSLContext#key= currently does not raise because SSL_CTX_use_certificate() is delayed" + + dsa512 = Fixtures.pkey("dsa512") + dsa512_cert = issue_cert(@svr, dsa512, 50, [], @ca_cert, @ca_key) + rsa1024 = Fixtures.pkey("rsa1024") + rsa1024_cert = issue_cert(@svr, rsa1024, 51, [], @ca_cert, @ca_key) + + assert_raise(OpenSSL::SSL::SSLError) { + # 512 bit DSA key is rejected because it offers < 80 bits of security + ctx.add_certificate(dsa512_cert, dsa512) + } + assert_nothing_raised { + ctx.add_certificate(rsa1024_cert, rsa1024) + } + ctx.security_level = 2 + assert_raise(OpenSSL::SSL::SSLError) { + # < 112 bits of security + ctx.add_certificate(rsa1024_cert, rsa1024) + } end def test_dup diff --git a/test/test_ssl_session.rb b/test/test_ssl_session.rb index af8c65b1..2cb46cd2 100644 --- a/test/test_ssl_session.rb +++ b/test/test_ssl_session.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase def test_session diff --git a/test/test_x509attr.rb b/test/test_x509attr.rb index d7473f1a..c6c48e86 100644 --- a/test/test_x509attr.rb +++ b/test/test_x509attr.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509Attribute < OpenSSL::TestCase def test_new @@ -62,6 +62,23 @@ class OpenSSL::TestX509Attribute < OpenSSL::TestCase attr = OpenSSL::X509::Attribute.new("challengePassword", val) assert_equal(attr.to_der, attr.dup.to_der) end + + def test_eq + val1 = OpenSSL::ASN1::Set([ + OpenSSL::ASN1::UTF8String("abc123") + ]) + attr1 = OpenSSL::X509::Attribute.new("challengePassword", val1) + attr2 = OpenSSL::X509::Attribute.new("challengePassword", val1) + ef = OpenSSL::X509::ExtensionFactory.new + val2 = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([ + ef.create_extension("keyUsage", "keyCertSign", true) + ])]) + attr3 = OpenSSL::X509::Attribute.new("extReq", val2) + + assert_equal false, attr1 == 12345 + assert_equal true, attr1 == attr2 + assert_equal false, attr1 == attr3 + end end end diff --git a/test/test_x509cert.rb b/test/test_x509cert.rb index 03650402..40a5b0ad 100644 --- a/test/test_x509cert.rb +++ b/test/test_x509cert.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509Certificate < OpenSSL::TestCase def setup @@ -30,13 +30,10 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase ["authorityKeyIdentifier","keyid:always",false], ] - sha1 = OpenSSL::Digest::SHA1.new - dsa_digest = OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new - [ - [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dsa_digest], [@dsa512, dsa_digest] - ].each{|pk, digest| - cert = issue_cert(@ca, pk, 1, exts, nil, nil, digest: digest) + @rsa1024, @rsa2048, @dsa256, @dsa512, + ].each{|pk| + cert = issue_cert(@ca, pk, 1, exts, nil, nil) assert_equal(cert.extensions.sort_by(&:to_s)[2].value, OpenSSL::TestUtils.get_subject_key_id(cert)) cert = OpenSSL::X509::Certificate.new(cert.to_der) @@ -148,26 +145,15 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase } end - def test_dsig_algorithm_mismatch - assert_raise(OpenSSL::X509::CertificateError) do - issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: OpenSSL::Digest::DSS1.new) - end if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10001000 # [ruby-core:42949] - end - def test_dsa_with_sha2 - begin - cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha256") - assert_equal("dsa_with_SHA256", cert.signature_algorithm) - rescue OpenSSL::X509::CertificateError - # dsa_with_sha2 not supported. skip following test. - return - end + cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha256") + assert_equal("dsa_with_SHA256", cert.signature_algorithm) # TODO: need more tests for dsa + sha2 # SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1) cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha1") assert_equal("dsaWithSHA1", cert.signature_algorithm) - end if defined?(OpenSSL::Digest::SHA256) + end def test_check_private_key cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) @@ -183,6 +169,26 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase } end + def test_eq + now = Time.now + cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil, + not_before: now, not_after: now + 3600) + cert1 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024, + not_before: now, not_after: now + 3600) + cert2 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024, + not_before: now, not_after: now + 3600) + cert3 = issue_cert(@ee1, @rsa2048, 3, [], cacert, @rsa1024, + not_before: now, not_after: now + 3600) + cert4 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024, + digest: "sha512", not_before: now, not_after: now + 3600) + + assert_equal false, cert1 == 12345 + assert_equal true, cert1 == cert2 + assert_equal false, cert1 == cert3 + assert_equal false, cert1 == cert4 + assert_equal false, cert3 == cert4 + end + private def certificate_error_returns_false diff --git a/test/test_x509crl.rb b/test/test_x509crl.rb index 7d55e3c1..03fdf64d 100644 --- a/test/test_x509crl.rb +++ b/test/test_x509crl.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509CRL < OpenSSL::TestCase def setup @@ -188,7 +188,7 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase cert = issue_cert(@ca, @dsa512, 1, [], nil, nil) crl = issue_crl([], 1, Time.now, Time.now+1600, [], - cert, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + cert, @dsa512, OpenSSL::Digest::SHA1.new) assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) }) assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) }) assert_equal(false, crl.verify(@dsa256)) @@ -197,6 +197,58 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase assert_equal(false, crl.verify(@dsa512)) end + def test_revoked_to_der + # revokedCertificates SEQUENCE OF SEQUENCE { + # userCertificate CertificateSerialNumber, + # revocationDate Time, + # crlEntryExtensions Extensions OPTIONAL + # -- if present, version MUST be v2 + # } OPTIONAL, + + now = Time.utc(2000, 1, 1) + rev1 = OpenSSL::X509::Revoked.new + rev1.serial = 123 + rev1.time = now + ext = OpenSSL::X509::Extension.new("CRLReason", OpenSSL::ASN1::Enumerated(1)) + rev1.extensions = [ext] + asn1 = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(123), + OpenSSL::ASN1::UTCTime(now), + OpenSSL::ASN1::Sequence([ext.to_der]) + ]) + + assert_equal asn1.to_der, rev1.to_der + end + + def test_eq + now = Time.now + + cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil) + crl1 = issue_crl([], 1, now, now + 3600, [], cacert, @rsa1024, "sha256") + rev1 = OpenSSL::X509::Revoked.new.tap { |rev| + rev.serial = 1 + rev.time = now + } + crl1.add_revoked(rev1) + crl2 = OpenSSL::X509::CRL.new(crl1.to_der) + + # CRL + assert_equal false, crl1 == 12345 + assert_equal true, crl1 == crl2 + rev2 = OpenSSL::X509::Revoked.new.tap { |rev| + rev.serial = 2 + rev.time = now + } + crl2.add_revoked(rev2) + assert_equal false, crl1 == crl2 + + # Revoked + assert_equal false, rev1 == 12345 + assert_equal true, rev1 == crl2.revoked[0] + assert_equal false, rev1 == crl2.revoked[1] + assert_equal true, rev2 == crl2.revoked[1] + end + private def crl_error_returns_false diff --git a/test/test_x509ext.rb b/test/test_x509ext.rb index 58f03168..91ce202f 100644 --- a/test/test_x509ext.rb +++ b/test/test_x509ext.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509Extension < OpenSSL::TestCase def setup @@ -75,6 +75,17 @@ class OpenSSL::TestX509Extension < OpenSSL::TestCase assert_equal(@basic_constraints.to_der, ext.to_der) assert_equal(ext.to_der, ext.dup.to_der) end + + def test_eq + ext1 = OpenSSL::X509::Extension.new(@basic_constraints.to_der) + ef = OpenSSL::X509::ExtensionFactory.new + ext2 = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2") + ext3 = ef.create_extension("basicConstraints", "critical, CA:TRUE") + + assert_equal false, ext1 == 12345 + assert_equal true, ext1 == ext2 + assert_equal false, ext1 == ext3 + end end end diff --git a/test/test_x509name.rb b/test/test_x509name.rb index c1dacf4f..2d92e645 100644 --- a/test/test_x509name.rb +++ b/test/test_x509name.rb @@ -1,8 +1,8 @@ -# coding: US-ASCII +# coding: ASCII-8BIT # frozen_string_literal: false require_relative 'utils' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509Name < OpenSSL::TestCase def setup @@ -148,33 +148,28 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase end def test_s_parse - dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" + dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org/1.2.3.4.5.6=A=BCD" name = OpenSSL::X509::Name.parse(dn) assert_equal(dn, name.to_s) ary = name.to_a - assert_equal("DC", ary[0][0]) - assert_equal("DC", ary[1][0]) - assert_equal("CN", ary[2][0]) - assert_equal("org", ary[0][1]) - assert_equal("ruby-lang", ary[1][1]) - assert_equal("www.ruby-lang.org", ary[2][1]) - assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) - assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) - assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) - - dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org" + assert_equal [ + ["DC", "org", OpenSSL::ASN1::IA5STRING], + ["DC", "ruby-lang", OpenSSL::ASN1::IA5STRING], + ["CN", "www.ruby-lang.org", OpenSSL::ASN1::UTF8STRING], + ["1.2.3.4.5.6", "A=BCD", OpenSSL::ASN1::UTF8STRING], + ], ary + + dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org, 1.2.3.4.5.6=A=BCD" name = OpenSSL::X509::Name.parse(dn2) - ary = name.to_a assert_equal(dn, name.to_s) - assert_equal("org", ary[0][1]) - assert_equal("ruby-lang", ary[1][1]) - assert_equal("www.ruby-lang.org", ary[2][1]) + assert_equal ary, name.to_a name = OpenSSL::X509::Name.parse(dn2, @obj_type_tmpl) ary = name.to_a assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) + assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[3][2]) end def test_s_parse_rfc2253 @@ -322,16 +317,87 @@ class OpenSSL::TestX509Name < OpenSSL::TestCase assert_equal("Namiki", ary[5][1]) end + def test_add_entry_placing + der = %w{ 30 2A + 31 12 + 30 10 06 03 55 04 0A 0C 09 72 75 62 79 2D 6C 61 6E 67 + 31 14 + 30 08 06 03 55 04 0B 0C 01 61 + 30 08 06 03 55 04 0B 0C 01 62 } + orig = OpenSSL::X509::Name.new([der.join].pack("H*")) + assert_equal("OU=b+OU=a,O=ruby-lang", orig.to_s(OpenSSL::X509::Name::RFC2253)) + # Skip for now; they do not work + # + # dn = orig.dup + # dn.add_entry("CN", "unya", loc: 0, set: 0) + # assert_equal("OU=b+OU=a,O=ruby-lang,CN=unya", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + # dn = orig.dup + # dn.add_entry("CN", "unya", loc: 0, set: 1) + # assert_equal("OU=b+OU=a,O=ruby-lang+CN=unya", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + dn = orig.dup + dn.add_entry("CN", "unya", loc: 1, set: -1) + assert_equal("OU=b+OU=a,O=ruby-lang+CN=unya", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + # dn = orig.dup + # dn.add_entry("CN", "unya", loc: 1, set: 0) + # assert_equal("OU=b+OU=a,CN=unya,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + dn = orig.dup + dn.add_entry("CN", "unya", loc: 1, set: 1) + assert_equal("CN=unya+OU=b+OU=a,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + dn = orig.dup + dn.add_entry("CN", "unya", loc: -1, set: -1) + assert_equal("CN=unya+OU=b+OU=a,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + dn = orig.dup + dn.add_entry("CN", "unya", loc: -1, set: 0) + assert_equal("CN=unya,OU=b+OU=a,O=ruby-lang", dn.dup.to_s(OpenSSL::X509::Name::RFC2253)) + end + + def test_to_s + dn = [ + ["DC", "org"], + ["DC", "ruby-lang"], + ["CN", "フー, バー"], + ] + name = OpenSSL::X509::Name.new + dn.each { |x| name.add_entry(*x) } + + assert_equal "/DC=org/DC=ruby-lang/" \ + "CN=\\xE3\\x83\\x95\\xE3\\x83\\xBC, \\xE3\\x83\\x90\\xE3\\x83\\xBC", + name.to_s + # OpenSSL escapes characters with MSB by default + assert_equal \ + "CN=\\E3\\83\\95\\E3\\83\\BC\\, \\E3\\83\\90\\E3\\83\\BC," \ + "DC=ruby-lang,DC=org", + name.to_s(OpenSSL::X509::Name::RFC2253) + assert_equal "DC = org, DC = ruby-lang, " \ + "CN = \"\\E3\\83\\95\\E3\\83\\BC, \\E3\\83\\90\\E3\\83\\BC\"", + name.to_s(OpenSSL::X509::Name::ONELINE) + end + + def test_to_utf8 + dn = [ + ["DC", "org"], + ["DC", "ruby-lang"], + ["CN", "フー, バー"], + ] + name = OpenSSL::X509::Name.new + dn.each { |x| name.add_entry(*x) } + + str = name.to_utf8 + expected = "CN=フー\\, バー,DC=ruby-lang,DC=org".force_encoding("UTF-8") + assert_equal expected, str + assert_equal Encoding.find("UTF-8"), str.encoding + end + def test_equals2 - n1 = OpenSSL::X509::Name.parse 'CN=a' - n2 = OpenSSL::X509::Name.parse 'CN=a' + n1 = OpenSSL::X509::Name.parse_rfc2253 'CN=a' + n2 = OpenSSL::X509::Name.parse_rfc2253 'CN=a' assert_equal n1, n2 end def test_spaceship - n1 = OpenSSL::X509::Name.parse 'CN=a' - n2 = OpenSSL::X509::Name.parse 'CN=b' + n1 = OpenSSL::X509::Name.parse_rfc2253 'CN=a' + n2 = OpenSSL::X509::Name.parse_rfc2253 'CN=b' assert_equal(-1, n1 <=> n2) end diff --git a/test/test_x509req.rb b/test/test_x509req.rb index fcc3e2f1..2c447ccd 100644 --- a/test/test_x509req.rb +++ b/test/test_x509req.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509Request < OpenSSL::TestCase def setup @@ -28,7 +28,7 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase req = OpenSSL::X509::Request.new(req.to_der) assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der) - req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new) assert_equal(@dsa512.public_key.to_der, req.public_key.to_der) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(@dsa512.public_key.to_der, req.public_key.to_der) @@ -122,7 +122,7 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase end def test_sign_and_verify_dsa - req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) + req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new) assert_equal(false, request_error_returns_false { req.verify(@rsa1024) }) assert_equal(false, request_error_returns_false { req.verify(@rsa2048) }) assert_equal(false, req.verify(@dsa256)) @@ -131,18 +131,6 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase assert_equal(false, req.verify(@dsa512)) end - def test_sign_and_verify_rsa_dss1 - req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new) - assert_equal(true, req.verify(@rsa1024)) - assert_equal(false, req.verify(@rsa2048)) - assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) - assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) - req.version = 1 - assert_equal(false, req.verify(@rsa1024)) - rescue OpenSSL::X509::RequestError - pend - end if defined?(OpenSSL::Digest::DSS1) - def test_sign_and_verify_dsa_md5 assert_raise(OpenSSL::X509::RequestError){ issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) } @@ -153,6 +141,16 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase assert_equal(req.to_der, req.dup.to_der) end + def test_eq + req1 = issue_csr(0, @dn, @rsa1024, "sha1") + req2 = issue_csr(0, @dn, @rsa1024, "sha1") + req3 = issue_csr(0, @dn, @rsa1024, "sha256") + + assert_equal false, req1 == 12345 + assert_equal true, req1 == req2 + assert_equal false, req1 == req3 + end + private def request_error_returns_false diff --git a/test/test_x509store.rb b/test/test_x509store.rb index b40534c6..6412249b 100644 --- a/test/test_x509store.rb +++ b/test/test_x509store.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require_relative "utils" -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) class OpenSSL::TestX509Store < OpenSSL::TestCase def setup diff --git a/test/ut_eof.rb b/test/ut_eof.rb index a49ad513..bd62fd50 100644 --- a/test/ut_eof.rb +++ b/test/ut_eof.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false require 'test/unit' -if defined?(OpenSSL::TestUtils) +if defined?(OpenSSL) module OpenSSL::TestEOF def test_eof_0 diff --git a/test/utils.rb b/test/utils.rb index 4331d8bd..a3599490 100644 --- a/test/utils.rb +++ b/test/utils.rb @@ -34,7 +34,7 @@ require "tempfile" require "socket" require "envutil" -if defined?(OpenSSL) && OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10000000 +if defined?(OpenSSL) module OpenSSL::TestUtils module Fixtures @@ -56,14 +56,10 @@ module OpenSSL::TestUtils end end - DSA_SIGNATURE_DIGEST = OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000 ? - OpenSSL::Digest::SHA1 : - OpenSSL::Digest::DSS1 - module_function def issue_cert(dn, key, serial, extensions, issuer, issuer_key, - not_before: nil, not_after: nil, digest: nil) + not_before: nil, not_after: nil, digest: "sha256") cert = OpenSSL::X509::Certificate.new issuer = cert unless issuer issuer_key = key unless issuer_key @@ -71,7 +67,7 @@ module OpenSSL::TestUtils cert.serial = serial cert.subject = dn cert.issuer = issuer.subject - cert.public_key = key.public_key + cert.public_key = key now = Time.now cert.not_before = not_before || now - 3600 cert.not_after = not_after || now + 3600 @@ -81,7 +77,6 @@ module OpenSSL::TestUtils extensions.each{|oid, value, critical| cert.add_extension(ef.create_extension(oid, value, critical)) } - digest ||= OpenSSL::PKey::DSA === issuer_key ? DSA_SIGNATURE_DIGEST.new : "sha256" cert.sign(issuer_key, digest) cert end @@ -182,7 +177,10 @@ class OpenSSL::SSLTestCase < OpenSSL::TestCase end def tls12_supported? - OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) + ctx = OpenSSL::SSL::SSLContext.new + ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + true + rescue end def readwrite_loop(ctx, ssl) |