diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/openssl/extconf.rb | 4 | ||||
-rw-r--r-- | ext/openssl/openssl_missing.c | 26 | ||||
-rw-r--r-- | ext/openssl/openssl_missing.h | 10 | ||||
-rw-r--r-- | ext/openssl/ossl.c | 35 | ||||
-rw-r--r-- | ext/openssl/ossl.h | 1 | ||||
-rw-r--r-- | ext/openssl/ossl_bn.c | 70 | ||||
-rw-r--r-- | ext/openssl/ossl_bn.h | 3 | ||||
-rw-r--r-- | ext/openssl/ossl_cipher.c | 14 | ||||
-rw-r--r-- | ext/openssl/ossl_config.c | 453 | ||||
-rw-r--r-- | ext/openssl/ossl_config.h | 11 | ||||
-rw-r--r-- | ext/openssl/ossl_digest.c | 12 | ||||
-rw-r--r-- | ext/openssl/ossl_hmac.c | 183 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey.c | 580 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey.h | 18 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_dh.c | 119 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_dsa.c | 175 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_ec.c | 209 | ||||
-rw-r--r-- | ext/openssl/ossl_pkey_rsa.c | 199 | ||||
-rw-r--r-- | ext/openssl/ossl_ssl.c | 140 | ||||
-rw-r--r-- | ext/openssl/ossl_ts.c | 50 | ||||
-rw-r--r-- | ext/openssl/ossl_x509cert.c | 2 | ||||
-rw-r--r-- | ext/openssl/ossl_x509ext.c | 3 | ||||
-rw-r--r-- | ext/openssl/ossl_x509store.c | 211 |
23 files changed, 1460 insertions, 1068 deletions
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 693e55cd..d5e0470c 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -141,8 +141,7 @@ have_func("BN_GENCB_free") have_func("BN_GENCB_get_arg") have_func("EVP_MD_CTX_new") have_func("EVP_MD_CTX_free") -have_func("HMAC_CTX_new") -have_func("HMAC_CTX_free") +have_func("EVP_MD_CTX_pkey_ctx") have_func("X509_STORE_get_ex_data") have_func("X509_STORE_set_ex_data") have_func("X509_STORE_get_ex_new_index") @@ -161,7 +160,6 @@ have_func("X509_CRL_up_ref") have_func("X509_STORE_up_ref") have_func("SSL_SESSION_up_ref") have_func("EVP_PKEY_up_ref") -have_func("SSL_CTX_set_tmp_ecdh_callback(NULL, NULL)", "openssl/ssl.h") # removed have_func("SSL_CTX_set_min_proto_version(NULL, 0)", "openssl/ssl.h") have_func("SSL_CTX_get_security_level") have_func("X509_get0_notBefore") diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c index b36ef028..010c158d 100644 --- a/ext/openssl/openssl_missing.c +++ b/ext/openssl/openssl_missing.c @@ -13,9 +13,6 @@ #if !defined(OPENSSL_NO_ENGINE) # include <openssl/engine.h> #endif -#if !defined(OPENSSL_NO_HMAC) -# include <openssl/hmac.h> -#endif #include <openssl/x509_vfy.h> #include "openssl_missing.h" @@ -58,29 +55,6 @@ ossl_EC_curve_nist2nid(const char *name) #endif /*** added in 1.1.0 ***/ -#if !defined(HAVE_HMAC_CTX_NEW) -HMAC_CTX * -ossl_HMAC_CTX_new(void) -{ - HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX)); - if (!ctx) - return NULL; - HMAC_CTX_init(ctx); - return ctx; -} -#endif - -#if !defined(HAVE_HMAC_CTX_FREE) -void -ossl_HMAC_CTX_free(HMAC_CTX *ctx) -{ - if (ctx) { - HMAC_CTX_cleanup(ctx); - OPENSSL_free(ctx); - } -} -#endif - #if !defined(HAVE_X509_CRL_GET0_SIGNATURE) void ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 7d218f86..06d2a908 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -54,14 +54,8 @@ int ossl_EC_curve_nist2nid(const char *); # define EVP_MD_CTX_free EVP_MD_CTX_destroy #endif -#if !defined(HAVE_HMAC_CTX_NEW) -HMAC_CTX *ossl_HMAC_CTX_new(void); -# define HMAC_CTX_new ossl_HMAC_CTX_new -#endif - -#if !defined(HAVE_HMAC_CTX_FREE) -void ossl_HMAC_CTX_free(HMAC_CTX *); -# define HMAC_CTX_free ossl_HMAC_CTX_free +#if !defined(HAVE_EVP_MD_CTX_PKEY_CTX) +# define EVP_MD_CTX_pkey_ctx(x) (x)->pctx #endif #if !defined(HAVE_X509_STORE_GET_EX_DATA) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 2f54b861..7bdf3a2e 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -497,8 +497,11 @@ print_mem_leaks(VALUE self) int ret; #endif - BN_CTX_free(ossl_bn_ctx); - ossl_bn_ctx = NULL; +#ifndef HAVE_RB_EXT_RACTOR_SAFE + // for Ruby 2.x + void ossl_bn_ctx_free(void); // ossl_bn.c + ossl_bn_ctx_free(); +#endif #if OPENSSL_VERSION_NUMBER >= 0x10100000 ret = CRYPTO_mem_leaks_fp(stderr); @@ -664,7 +667,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * ahold of the key may use it unless it is encrypted. In order to securely * export a key you may export it with a pass phrase. * - * cipher = OpenSSL::Cipher.new 'AES-256-CBC' + * cipher = OpenSSL::Cipher.new 'aes-256-cbc' * pass_phrase = 'my secure pass phrase goes here' * * key_secure = key.export cipher, pass_phrase @@ -679,13 +682,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * A key can also be loaded from a file. * - * key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem' + * key2 = OpenSSL::PKey.read File.read 'private_key.pem' * key2.public? # => true * key2.private? # => true * * or * - * key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem' + * key3 = OpenSSL::PKey.read File.read 'public_key.pem' * key3.public? # => true * key3.private? # => false * @@ -697,7 +700,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * key4_pem = File.read 'private.secure.pem' * pass_phrase = 'my secure pass phrase goes here' - * key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase + * key4 = OpenSSL::PKey.read key4_pem, pass_phrase * * == RSA Encryption * @@ -772,7 +775,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt, * the number of iterations largely depends on the hardware being used. * - * cipher = OpenSSL::Cipher.new 'AES-256-CBC' + * cipher = OpenSSL::Cipher.new 'aes-256-cbc' * cipher.encrypt * iv = cipher.random_iv * @@ -795,7 +798,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * Use the same steps as before to derive the symmetric AES key, this time * setting the Cipher up for decryption. * - * cipher = OpenSSL::Cipher.new 'AES-256-CBC' + * cipher = OpenSSL::Cipher.new 'aes-256-cbc' * cipher.decrypt * cipher.iv = iv # the one generated with #random_iv * @@ -830,7 +833,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * First set up the cipher for encryption * - * encryptor = OpenSSL::Cipher.new 'AES-256-CBC' + * encryptor = OpenSSL::Cipher.new 'aes-256-cbc' * encryptor.encrypt * encryptor.pkcs5_keyivgen pass_phrase, salt * @@ -843,7 +846,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * Use a new Cipher instance set up for decryption * - * decryptor = OpenSSL::Cipher.new 'AES-256-CBC' + * decryptor = OpenSSL::Cipher.new 'aes-256-cbc' * decryptor.decrypt * decryptor.pkcs5_keyivgen pass_phrase, salt * @@ -931,7 +934,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * ca_key = OpenSSL::PKey::RSA.new 2048 * pass_phrase = 'my secure pass phrase goes here' * - * cipher = OpenSSL::Cipher.new 'AES-256-CBC' + * cipher = OpenSSL::Cipher.new 'aes-256-cbc' * * open 'ca_key.pem', 'w', 0400 do |io| * io.write ca_key.export(cipher, pass_phrase) @@ -1069,13 +1072,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * loop do * ssl_connection = ssl_server.accept * - * data = connection.gets + * data = ssl_connection.gets * * response = "I got #{data.dump}" * puts response * - * connection.puts "I got #{data.dump}" - * connection.close + * ssl_connection.puts "I got #{data.dump}" + * ssl_connection.close * end * * === SSL client @@ -1126,6 +1129,10 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) void Init_openssl(void) { +#if HAVE_RB_EXT_RACTOR_SAFE + rb_ext_ractor_safe(true); +#endif + #undef rb_intern /* * Init timezone info diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index c20f506b..577eb6d6 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -24,7 +24,6 @@ #include <openssl/ssl.h> #include <openssl/pkcs12.h> #include <openssl/pkcs7.h> -#include <openssl/hmac.h> #include <openssl/rand.h> #include <openssl/conf.h> #ifndef OPENSSL_NO_TS diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index f3f2e792..bec37299 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -10,6 +10,10 @@ /* modified by Michal Rokos <m.rokos@sh.cvut.cz> */ #include "ossl.h" +#if HAVE_RB_EXT_RACTOR_SAFE +#include <ruby/ractor.h> +#endif + #define NewBN(klass) \ TypedData_Wrap_Struct((klass), &ossl_bn_type, 0) #define SetBN(obj, bn) do { \ @@ -150,12 +154,58 @@ ossl_bn_value_ptr(volatile VALUE *ptr) /* * Private */ -/* - * BN_CTX - is used in more difficult math. ops - * (Why just 1? Because Ruby itself isn't thread safe, - * we don't need to care about threads) - */ -BN_CTX *ossl_bn_ctx; + +#if HAVE_RB_EXT_RACTOR_SAFE +void +ossl_bn_ctx_free(void *ptr) +{ + BN_CTX *ctx = (BN_CTX *)ptr; + BN_CTX_free(ctx); +} + +struct rb_ractor_local_storage_type ossl_bn_ctx_key_type = { + NULL, // mark + ossl_bn_ctx_free, +}; + +rb_ractor_local_key_t ossl_bn_ctx_key; + +BN_CTX * +ossl_bn_ctx_get(void) +{ + // stored in ractor local storage + + BN_CTX *ctx = rb_ractor_local_storage_ptr(ossl_bn_ctx_key); + if (!ctx) { + if (!(ctx = BN_CTX_new())) { + ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX"); + } + rb_ractor_local_storage_ptr_set(ossl_bn_ctx_key, ctx); + } + return ctx; +} +#else +// for ruby 2.x +static BN_CTX *gv_ossl_bn_ctx; + +BN_CTX * +ossl_bn_ctx_get(void) +{ + if (gv_ossl_bn_ctx == NULL) { + if (!(gv_ossl_bn_ctx = BN_CTX_new())) { + ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX"); + } + } + return gv_ossl_bn_ctx; +} + +void +ossl_bn_ctx_free(void) +{ + BN_CTX_free(gv_ossl_bn_ctx); + gv_ossl_bn_ctx = NULL; +} +#endif static VALUE ossl_bn_alloc(VALUE klass) @@ -1102,9 +1152,11 @@ Init_ossl_bn(void) eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif - if (!(ossl_bn_ctx = BN_CTX_new())) { - ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX"); - } +#ifdef HAVE_RB_EXT_RACTOR_SAFE + ossl_bn_ctx_key = rb_ractor_local_storage_ptr_newkey(&ossl_bn_ctx_key_type); +#else + ossl_bn_ctx_get(); +#endif eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError); diff --git a/ext/openssl/ossl_bn.h b/ext/openssl/ossl_bn.h index a19ba194..1cc041fc 100644 --- a/ext/openssl/ossl_bn.h +++ b/ext/openssl/ossl_bn.h @@ -13,7 +13,8 @@ extern VALUE cBN; extern VALUE eBNError; -extern BN_CTX *ossl_bn_ctx; +BN_CTX *ossl_bn_ctx_get(void); +#define ossl_bn_ctx ossl_bn_ctx_get() #define GetBNPtr(obj) ossl_bn_value_ptr(&(obj)) diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index 5b92fc39..28f5c1b5 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -104,7 +104,7 @@ ossl_cipher_alloc(VALUE klass) * call-seq: * Cipher.new(string) -> cipher * - * The string must contain a valid cipher name like "AES-256-CBC". + * The string must contain a valid cipher name like "aes-256-cbc". * * A list of cipher names is available by calling OpenSSL::Cipher.ciphers. */ @@ -874,7 +874,7 @@ Init_ossl_cipher(void) * individual components name, key length and mode. Either all uppercase * or all lowercase strings may be used, for example: * - * cipher = OpenSSL::Cipher.new('AES-128-CBC') + * cipher = OpenSSL::Cipher.new('aes-128-cbc') * * === Choosing either encryption or decryption mode * @@ -904,7 +904,7 @@ Init_ossl_cipher(void) * without processing the password further. A simple and secure way to * create a key for a particular Cipher is * - * cipher = OpenSSL::Cipher.new('AES-256-CFB') + * cipher = OpenSSL::Cipher.new('aes-256-cfb') * cipher.encrypt * key = cipher.random_key # also sets the generated key on the Cipher * @@ -972,14 +972,14 @@ Init_ossl_cipher(void) * * data = "Very, very confidential data" * - * cipher = OpenSSL::Cipher.new('AES-128-CBC') + * cipher = OpenSSL::Cipher.new('aes-128-cbc') * cipher.encrypt * key = cipher.random_key * iv = cipher.random_iv * * encrypted = cipher.update(data) + cipher.final * ... - * decipher = OpenSSL::Cipher.new('AES-128-CBC') + * decipher = OpenSSL::Cipher.new('aes-128-cbc') * decipher.decrypt * decipher.key = key * decipher.iv = iv @@ -1015,7 +1015,7 @@ Init_ossl_cipher(void) * not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the * security guarantees of GCM mode. * - * cipher = OpenSSL::Cipher.new('AES-128-GCM').encrypt + * cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt * cipher.key = key * cipher.iv = nonce * cipher.auth_data = auth_data @@ -1031,7 +1031,7 @@ Init_ossl_cipher(void) * ciphertext with a probability of 1/256. * * raise "tag is truncated!" unless tag.bytesize == 16 - * decipher = OpenSSL::Cipher.new('AES-128-GCM').decrypt + * decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt * decipher.key = key * decipher.iv = nonce * decipher.auth_tag = tag diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c index 28392e20..21c327b2 100644 --- a/ext/openssl/ossl_config.c +++ b/ext/openssl/ossl_config.c @@ -9,81 +9,452 @@ */ #include "ossl.h" +static VALUE cConfig, eConfigError; + +static void +nconf_free(void *conf) +{ + NCONF_free(conf); +} + +static const rb_data_type_t ossl_config_type = { + "OpenSSL/CONF", + { + 0, nconf_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +CONF * +GetConfig(VALUE obj) +{ + CONF *conf; + + TypedData_Get_Struct(obj, CONF, &ossl_config_type, conf); + if (!conf) + rb_raise(rb_eRuntimeError, "CONF is not initialized"); + return conf; +} + +static VALUE +config_s_alloc(VALUE klass) +{ + VALUE obj; + CONF *conf; + + obj = TypedData_Wrap_Struct(klass, &ossl_config_type, 0); + conf = NCONF_new(NULL); + if (!conf) + ossl_raise(eConfigError, "NCONF_new"); + RTYPEDDATA_DATA(obj) = conf; + return obj; +} + +static void +config_load_bio(CONF *conf, BIO *bio) +{ + long eline = -1; + + if (!NCONF_load_bio(conf, bio, &eline)) { + BIO_free(bio); + if (eline <= 0) + ossl_raise(eConfigError, "wrong config format"); + else + ossl_raise(eConfigError, "error in line %d", eline); + } + BIO_free(bio); + + /* + * Clear the error queue even if it is parsed successfully. + * Particularly, when the .include directive refers to a non-existent file, + * it is only reported in the error queue. + */ + ossl_clear_error(); +} /* - * Classes - */ -VALUE cConfig; -/* Document-class: OpenSSL::ConfigError + * call-seq: + * Config.parse(string) -> OpenSSL::Config * - * General error for openssl library configuration files. Including formatting, - * parsing errors, etc. + * Parses a given _string_ as a blob that contains configuration for OpenSSL. */ -VALUE eConfigError; +static VALUE +config_s_parse(VALUE klass, VALUE str) +{ + VALUE obj = config_s_alloc(klass); + CONF *conf = GetConfig(obj); + BIO *bio; + + bio = ossl_obj2bio(&str); + config_load_bio(conf, bio); /* Consumes BIO */ + return obj; +} + +static VALUE config_get_sections(VALUE self); +static VALUE config_get_section(VALUE self, VALUE section); /* - * Public + * call-seq: + * Config.parse_config(io) -> hash + * + * Parses the configuration data read from _io_ and returns the whole content + * as a Hash. */ +static VALUE +config_s_parse_config(VALUE klass, VALUE io) +{ + VALUE obj, sections, ret; + long i; + + obj = config_s_parse(klass, io); + sections = config_get_sections(obj); + ret = rb_hash_new(); + for (i = 0; i < RARRAY_LEN(sections); i++) { + VALUE section = rb_ary_entry(sections, i); + rb_hash_aset(ret, section, config_get_section(obj, section)); + } + return ret; +} /* - * DupConfigPtr is a public C-level function for getting OpenSSL CONF struct - * from an OpenSSL::Config(eConfig) instance. We decided to implement - * OpenSSL::Config in Ruby level but we need to pass native CONF struct for - * some OpenSSL features such as X509V3_EXT_*. + * call-seq: + * Config.new(filename) -> OpenSSL::Config + * + * Creates an instance of OpenSSL::Config from the content of the file + * specified by _filename_. + * + * This can be used in contexts like OpenSSL::X509::ExtensionFactory.config= + * + * This can raise IO exceptions based on the access, or availability of the + * file. A ConfigError exception may be raised depending on the validity of + * the data being configured. */ -CONF * -DupConfigPtr(VALUE obj) +static VALUE +config_initialize(int argc, VALUE *argv, VALUE self) { - CONF *conf; + CONF *conf = GetConfig(self); + VALUE filename; + + /* 0-arguments call has no use-case, but is kept for compatibility */ + rb_scan_args(argc, argv, "01", &filename); + rb_check_frozen(self); + if (!NIL_P(filename)) { + BIO *bio = BIO_new_file(StringValueCStr(filename), "rb"); + if (!bio) + ossl_raise(eConfigError, "BIO_new_file"); + config_load_bio(conf, bio); /* Consumes BIO */ + } + return self; +} + +static VALUE +config_initialize_copy(VALUE self, VALUE other) +{ + CONF *conf = GetConfig(self); VALUE str; BIO *bio; - long eline = -1; - OSSL_Check_Kind(obj, cConfig); - str = rb_funcall(obj, rb_intern("to_s"), 0); + str = rb_funcall(other, rb_intern("to_s"), 0); + rb_check_frozen(self); bio = ossl_obj2bio(&str); - conf = NCONF_new(NULL); - if(!conf){ - BIO_free(bio); - ossl_raise(eConfigError, NULL); + config_load_bio(conf, bio); /* Consumes BIO */ + return self; +} + +/* + * call-seq: + * config.get_value(section, key) -> string + * + * Gets the value of _key_ from the given _section_. + * + * Given the following configurating file being loaded: + * + * config = OpenSSL::Config.load('foo.cnf') + * #=> #<OpenSSL::Config sections=["default"]> + * puts config.to_s + * #=> [ default ] + * # foo=bar + * + * You can get a specific value from the config if you know the _section_ + * and _key_ like so: + * + * config.get_value('default','foo') + * #=> "bar" + */ +static VALUE +config_get_value(VALUE self, VALUE section, VALUE key) +{ + CONF *conf = GetConfig(self); + const char *str, *sectionp; + + StringValueCStr(section); + StringValueCStr(key); + /* For compatibility; NULL means "default". */ + sectionp = RSTRING_LEN(section) ? RSTRING_PTR(section) : NULL; + str = NCONF_get_string(conf, sectionp, RSTRING_PTR(key)); + if (!str) { + ossl_clear_error(); + return Qnil; + } + return rb_str_new_cstr(str); +} + +/* + * call-seq: + * config[section] -> hash + * + * Gets all key-value pairs in a specific _section_ from the current + * configuration. + * + * Given the following configurating file being loaded: + * + * config = OpenSSL::Config.load('foo.cnf') + * #=> #<OpenSSL::Config sections=["default"]> + * puts config.to_s + * #=> [ default ] + * # foo=bar + * + * You can get a hash of the specific section like so: + * + * config['default'] + * #=> {"foo"=>"bar"} + * + */ +static VALUE +config_get_section(VALUE self, VALUE section) +{ + CONF *conf = GetConfig(self); + STACK_OF(CONF_VALUE) *sk; + int i, entries; + VALUE hash; + + hash = rb_hash_new(); + StringValueCStr(section); + if (!(sk = NCONF_get_section(conf, RSTRING_PTR(section)))) { + ossl_clear_error(); + return hash; } - if(!NCONF_load_bio(conf, bio, &eline)){ - BIO_free(bio); - NCONF_free(conf); - if (eline <= 0) - ossl_raise(eConfigError, "wrong config format"); - else - ossl_raise(eConfigError, "error in line %d", eline); + entries = sk_CONF_VALUE_num(sk); + for (i = 0; i < entries; i++) { + CONF_VALUE *entry = sk_CONF_VALUE_value(sk, i); + rb_hash_aset(hash, rb_str_new_cstr(entry->name), + rb_str_new_cstr(entry->value)); } - BIO_free(bio); + return hash; +} - return conf; +static void +get_conf_section_doall_arg(CONF_VALUE *cv, VALUE *aryp) +{ + if (cv->name) + return; + rb_ary_push(*aryp, rb_str_new_cstr(cv->section)); } -/* Document-const: DEFAULT_CONFIG_FILE +/* IMPLEMENT_LHASH_DOALL_ARG_CONST() requires >= OpenSSL 1.1.0 */ +static IMPLEMENT_LHASH_DOALL_ARG_FN(get_conf_section, CONF_VALUE, VALUE) + +/* + * call-seq: + * config.sections -> array of string * - * The default system configuration file for openssl + * Get the names of all sections in the current configuration. */ +static VALUE +config_get_sections(VALUE self) +{ + CONF *conf = GetConfig(self); + VALUE ary; + + ary = rb_ary_new(); + lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(get_conf_section), + &ary); + return ary; +} + +static void +dump_conf_value_doall_arg(CONF_VALUE *cv, VALUE *strp) +{ + VALUE str = *strp; + STACK_OF(CONF_VALUE) *sk; + int i, num; + + if (cv->name) + return; + sk = (STACK_OF(CONF_VALUE) *)cv->value; + num = sk_CONF_VALUE_num(sk); + rb_str_cat_cstr(str, "[ "); + rb_str_cat_cstr(str, cv->section); + rb_str_cat_cstr(str, " ]\n"); + for (i = 0; i < num; i++){ + CONF_VALUE *v = sk_CONF_VALUE_value(sk, i); + rb_str_cat_cstr(str, v->name ? v->name : "None"); + rb_str_cat_cstr(str, "="); + rb_str_cat_cstr(str, v->value ? v->value : "None"); + rb_str_cat_cstr(str, "\n"); + } + rb_str_cat_cstr(str, "\n"); +} + +static IMPLEMENT_LHASH_DOALL_ARG_FN(dump_conf_value, CONF_VALUE, VALUE) /* - * INIT + * call-seq: + * config.to_s -> string + * + * + * Gets the parsable form of the current configuration. + * + * Given the following configuration being created: + * + * config = OpenSSL::Config.new + * #=> #<OpenSSL::Config sections=[]> + * config['default'] = {"foo"=>"bar","baz"=>"buz"} + * #=> {"foo"=>"bar", "baz"=>"buz"} + * puts config.to_s + * #=> [ default ] + * # foo=bar + * # baz=buz + * + * You can parse get the serialized configuration using #to_s and then parse + * it later: + * + * serialized_config = config.to_s + * # much later... + * new_config = OpenSSL::Config.parse(serialized_config) + * #=> #<OpenSSL::Config sections=["default"]> + * puts new_config + * #=> [ default ] + * foo=bar + * baz=buz */ +static VALUE +config_to_s(VALUE self) +{ + CONF *conf = GetConfig(self); + VALUE str; + + str = rb_str_new(NULL, 0); + lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(dump_conf_value), + &str); + return str; +} + +static void +each_conf_value_doall_arg(CONF_VALUE *cv, void *unused) +{ + STACK_OF(CONF_VALUE) *sk; + VALUE section; + int i, num; + + if (cv->name) + return; + sk = (STACK_OF(CONF_VALUE) *)cv->value; + num = sk_CONF_VALUE_num(sk); + section = rb_str_new_cstr(cv->section); + for (i = 0; i < num; i++){ + CONF_VALUE *v = sk_CONF_VALUE_value(sk, i); + VALUE name = v->name ? rb_str_new_cstr(v->name) : Qnil; + VALUE value = v->value ? rb_str_new_cstr(v->value) : Qnil; + rb_yield(rb_ary_new3(3, section, name, value)); + } +} + +static IMPLEMENT_LHASH_DOALL_ARG_FN(each_conf_value, CONF_VALUE, void) + +/* + * call-seq: + * config.each { |section, key, value| } + * + * Retrieves the section and its pairs for the current configuration. + * + * config.each do |section, key, value| + * # ... + * end + */ +static VALUE +config_each(VALUE self) +{ + CONF *conf = GetConfig(self); + + RETURN_ENUMERATOR(self, 0, 0); + + lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(each_conf_value), + NULL); + return self; +} + +/* + * call-seq: + * config.inspect -> string + * + * String representation of this configuration object, including the class + * name and its sections. + */ +static VALUE +config_inspect(VALUE self) +{ + VALUE str, ary = config_get_sections(self); + const char *cname = rb_class2name(rb_obj_class(self)); + + str = rb_str_new_cstr("#<"); + rb_str_cat_cstr(str, cname); + rb_str_cat_cstr(str, " sections="); + rb_str_append(str, rb_inspect(ary)); + rb_str_cat_cstr(str, ">"); + + return str; +} + void Init_ossl_config(void) { - char *default_config_file; + char *path; + VALUE path_str; #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif - eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError); + /* Document-class: OpenSSL::Config + * + * Configuration for the openssl library. + * + * Many system's installation of openssl library will depend on your system + * configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for + * the location of the file for your host. + * + * See also http://www.openssl.org/docs/apps/config.html + */ cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject); - default_config_file = CONF_get1_default_config_file(); - rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", - rb_str_new2(default_config_file)); - OPENSSL_free(default_config_file); - /* methods are defined by openssl/config.rb */ + /* Document-class: OpenSSL::ConfigError + * + * General error for openssl library configuration files. Including formatting, + * parsing errors, etc. + */ + eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError); + + rb_include_module(cConfig, rb_mEnumerable); + rb_define_singleton_method(cConfig, "parse", config_s_parse, 1); + rb_define_singleton_method(cConfig, "parse_config", config_s_parse_config, 1); + rb_define_alias(CLASS_OF(cConfig), "load", "new"); + rb_define_alloc_func(cConfig, config_s_alloc); + rb_define_method(cConfig, "initialize", config_initialize, -1); + rb_define_method(cConfig, "initialize_copy", config_initialize_copy, 1); + rb_define_method(cConfig, "get_value", config_get_value, 2); + rb_define_method(cConfig, "[]", config_get_section, 1); + rb_define_method(cConfig, "sections", config_get_sections, 0); + rb_define_method(cConfig, "to_s", config_to_s, 0); + rb_define_method(cConfig, "each", config_each, 0); + rb_define_method(cConfig, "inspect", config_inspect, 0); + + /* Document-const: DEFAULT_CONFIG_FILE + * + * The default system configuration file for OpenSSL. + */ + path = CONF_get1_default_config_file(); + path_str = ossl_buf2str(path, rb_long2int(strlen(path))); + rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", path_str); } diff --git a/ext/openssl/ossl_config.h b/ext/openssl/ossl_config.h index 627d297b..4e604f1a 100644 --- a/ext/openssl/ossl_config.h +++ b/ext/openssl/ossl_config.h @@ -7,13 +7,10 @@ * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ -#if !defined(_OSSL_CONFIG_H_) -#define _OSSL_CONFIG_H_ +#ifndef OSSL_CONFIG_H +#define OSSL_CONFIG_H -extern VALUE cConfig; -extern VALUE eConfigError; - -CONF* DupConfigPtr(VALUE obj); +CONF *GetConfig(VALUE obj); void Init_ossl_config(void); -#endif /* _OSSL_CONFIG_H_ */ +#endif /* OSSL_CONFIG_H */ diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c index e2157cb0..d327f718 100644 --- a/ext/openssl/ossl_digest.c +++ b/ext/openssl/ossl_digest.c @@ -372,15 +372,15 @@ Init_ossl_digest(void) * * === Hashing a file * - * data = File.read('document') + * data = File.binread('document') * sha256 = OpenSSL::Digest.new('SHA256') * digest = sha256.digest(data) * * === Hashing several pieces of data at once * - * data1 = File.read('file1') - * data2 = File.read('file2') - * data3 = File.read('file3') + * data1 = File.binread('file1') + * data2 = File.binread('file2') + * data3 = File.binread('file3') * sha256 = OpenSSL::Digest.new('SHA256') * sha256 << data1 * sha256 << data2 @@ -389,11 +389,11 @@ Init_ossl_digest(void) * * === Reuse a Digest instance * - * data1 = File.read('file1') + * data1 = File.binread('file1') * sha256 = OpenSSL::Digest.new('SHA256') * digest1 = sha256.digest(data1) * - * data2 = File.read('file2') + * data2 = File.binread('file2') * sha256.reset * digest2 = sha256.digest(data2) * diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index e831cff5..a21db6c4 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -7,14 +7,12 @@ * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ -#if !defined(OPENSSL_NO_HMAC) - #include "ossl.h" #define NewHMAC(klass) \ TypedData_Wrap_Struct((klass), &ossl_hmac_type, 0) #define GetHMAC(obj, ctx) do { \ - TypedData_Get_Struct((obj), HMAC_CTX, &ossl_hmac_type, (ctx)); \ + TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_hmac_type, (ctx)); \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ } \ @@ -36,7 +34,7 @@ VALUE eHMACError; static void ossl_hmac_free(void *ctx) { - HMAC_CTX_free(ctx); + EVP_MD_CTX_free(ctx); } static const rb_data_type_t ossl_hmac_type = { @@ -51,12 +49,12 @@ static VALUE ossl_hmac_alloc(VALUE klass) { VALUE obj; - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; obj = NewHMAC(klass); - ctx = HMAC_CTX_new(); + ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(eHMACError, NULL); + ossl_raise(eHMACError, "EVP_MD_CTX"); RTYPEDDATA_DATA(obj) = ctx; return obj; @@ -76,8 +74,7 @@ ossl_hmac_alloc(VALUE klass) * === Example * * key = 'key' - * digest = OpenSSL::Digest.new('sha1') - * instance = OpenSSL::HMAC.new(key, digest) + * instance = OpenSSL::HMAC.new(key, 'SHA1') * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance.class * #=> OpenSSL::HMAC @@ -86,7 +83,7 @@ ossl_hmac_alloc(VALUE klass) * * Two instances can be securely compared with #== in constant time: * - * other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) + * other_instance = OpenSSL::HMAC.new('key', 'SHA1') * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance == other_instance * #=> true @@ -95,12 +92,23 @@ ossl_hmac_alloc(VALUE klass) static VALUE ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) { - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; + EVP_PKEY *pkey; - StringValue(key); GetHMAC(self, ctx); - HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key), - ossl_evp_get_digestbyname(digest), NULL); + StringValue(key); + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, + (unsigned char *)RSTRING_PTR(key), + RSTRING_LENINT(key)); + if (!pkey) + ossl_raise(eHMACError, "EVP_PKEY_new_mac_key"); + if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest), + NULL, pkey) != 1) { + EVP_PKEY_free(pkey); + ossl_raise(eHMACError, "EVP_DigestSignInit"); + } + /* Decrement reference counter; EVP_MD_CTX still keeps it */ + EVP_PKEY_free(pkey); return self; } @@ -108,16 +116,15 @@ ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) static VALUE ossl_hmac_copy(VALUE self, VALUE other) { - HMAC_CTX *ctx1, *ctx2; + EVP_MD_CTX *ctx1, *ctx2; rb_check_frozen(self); if (self == other) return self; GetHMAC(self, ctx1); GetHMAC(other, ctx2); - - if (!HMAC_CTX_copy(ctx1, ctx2)) - ossl_raise(eHMACError, "HMAC_CTX_copy"); + if (EVP_MD_CTX_copy(ctx1, ctx2) != 1) + ossl_raise(eHMACError, "EVP_MD_CTX_copy"); return self; } @@ -142,33 +149,16 @@ ossl_hmac_copy(VALUE self, VALUE other) static VALUE ossl_hmac_update(VALUE self, VALUE data) { - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; StringValue(data); GetHMAC(self, ctx); - HMAC_Update(ctx, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)); + if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + ossl_raise(eHMACError, "EVP_DigestSignUpdate"); return self; } -static void -hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len) -{ - HMAC_CTX *final; - - final = HMAC_CTX_new(); - if (!final) - ossl_raise(eHMACError, "HMAC_CTX_new"); - - if (!HMAC_CTX_copy(final, ctx)) { - HMAC_CTX_free(final); - ossl_raise(eHMACError, "HMAC_CTX_copy"); - } - - HMAC_Final(final, buf, buf_len); - HMAC_CTX_free(final); -} - /* * call-seq: * hmac.digest -> string @@ -176,7 +166,7 @@ hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len) * Returns the authentication code an instance represents as a binary string. * * === Example - * instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) + * instance = OpenSSL::HMAC.new('key', 'SHA1') * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance.digest * #=> "\xF4+\xB0\xEE\xB0\x18\xEB\xBDE\x97\xAEr\x13q\x1E\xC6\a`\x84?" @@ -184,15 +174,16 @@ hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len) static VALUE ossl_hmac_digest(VALUE self) { - HMAC_CTX *ctx; - unsigned int buf_len; + EVP_MD_CTX *ctx; + size_t buf_len; VALUE ret; GetHMAC(self, ctx); ret = rb_str_new(NULL, EVP_MAX_MD_SIZE); - hmac_final(ctx, (unsigned char *)RSTRING_PTR(ret), &buf_len); - assert(buf_len <= EVP_MAX_MD_SIZE); - rb_str_set_len(ret, buf_len); + if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(ret), + &buf_len) != 1) + ossl_raise(eHMACError, "EVP_DigestSignFinal"); + rb_str_set_len(ret, (long)buf_len); return ret; } @@ -207,13 +198,14 @@ ossl_hmac_digest(VALUE self) static VALUE ossl_hmac_hexdigest(VALUE self) { - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; unsigned char buf[EVP_MAX_MD_SIZE]; - unsigned int buf_len; + size_t buf_len; VALUE ret; GetHMAC(self, ctx); - hmac_final(ctx, buf, &buf_len); + if (EVP_DigestSignFinal(ctx, buf, &buf_len) != 1) + ossl_raise(eHMACError, "EVP_DigestSignFinal"); ret = rb_str_new(NULL, buf_len * 2); ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len); @@ -230,7 +222,7 @@ ossl_hmac_hexdigest(VALUE self) * === Example * * data = "The quick brown fox jumps over the lazy dog" - * instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) + * instance = OpenSSL::HMAC.new('key', 'SHA1') * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * * instance.update(data) @@ -242,85 +234,18 @@ ossl_hmac_hexdigest(VALUE self) static VALUE ossl_hmac_reset(VALUE self) { - HMAC_CTX *ctx; + EVP_MD_CTX *ctx; + EVP_PKEY *pkey; GetHMAC(self, ctx); - HMAC_Init_ex(ctx, NULL, 0, NULL, NULL); + pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(ctx)); + if (EVP_DigestSignInit(ctx, NULL, EVP_MD_CTX_md(ctx), NULL, pkey) != 1) + ossl_raise(eHMACError, "EVP_DigestSignInit"); return self; } /* - * call-seq: - * HMAC.digest(digest, key, data) -> aString - * - * 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' - * - * hmac = OpenSSL::HMAC.digest('sha1', key, data) - * #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9" - * - */ -static VALUE -ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data) -{ - unsigned char *buf; - unsigned int buf_len; - - StringValue(key); - StringValue(data); - 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); -} - -/* - * call-seq: - * HMAC.hexdigest(digest, key, data) -> aString - * - * 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' - * - * hmac = OpenSSL::HMAC.hexdigest('sha1', key, data) - * #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9" - * - */ -static VALUE -ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data) -{ - unsigned char buf[EVP_MAX_MD_SIZE]; - unsigned int buf_len; - VALUE ret; - - StringValue(key); - StringValue(data); - - 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); - ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len); - - return ret; -} - -/* * INIT */ void @@ -350,11 +275,10 @@ Init_ossl_hmac(void) * * === HMAC-SHA256 using incremental interface * - * data1 = File.read("file1") - * data2 = File.read("file2") + * data1 = File.binread("file1") + * data2 = File.binread("file2") * key = "key" - * digest = OpenSSL::Digest.new('SHA256') - * hmac = OpenSSL::HMAC.new(key, digest) + * hmac = OpenSSL::HMAC.new(key, 'SHA256') * hmac << data1 * hmac << data2 * mac = hmac.digest @@ -364,8 +288,6 @@ Init_ossl_hmac(void) cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject); rb_define_alloc_func(cHMAC, ossl_hmac_alloc); - rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3); - rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3); rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2); rb_define_method(cHMAC, "initialize_copy", ossl_hmac_copy, 1); @@ -378,12 +300,3 @@ Init_ossl_hmac(void) rb_define_alias(cHMAC, "inspect", "hexdigest"); rb_define_alias(cHMAC, "to_s", "hexdigest"); } - -#else /* NO_HMAC */ -# warning >>> OpenSSL is compiled without HMAC support <<< -void -Init_ossl_hmac(void) -{ - rb_warning("HMAC is not available: OpenSSL is compiled without HMAC."); -} -#endif /* NO_HMAC */ diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 23204087..1c1f80bf 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -95,7 +95,7 @@ const rb_data_type_t ossl_evp_pkey_type = { static VALUE pkey_new0(EVP_PKEY *pkey) { - VALUE obj; + VALUE klass, obj; int type; if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE) @@ -103,26 +103,22 @@ pkey_new0(EVP_PKEY *pkey) switch (type) { #if !defined(OPENSSL_NO_RSA) - case EVP_PKEY_RSA: - return ossl_rsa_new(pkey); + case EVP_PKEY_RSA: klass = cRSA; break; #endif #if !defined(OPENSSL_NO_DSA) - case EVP_PKEY_DSA: - return ossl_dsa_new(pkey); + case EVP_PKEY_DSA: klass = cDSA; break; #endif #if !defined(OPENSSL_NO_DH) - case EVP_PKEY_DH: - return ossl_dh_new(pkey); + case EVP_PKEY_DH: klass = cDH; break; #endif #if !defined(OPENSSL_NO_EC) - case EVP_PKEY_EC: - return ossl_ec_new(pkey); + case EVP_PKEY_EC: klass = cEC; break; #endif - default: - obj = NewPKey(cPKey); - SetPKey(obj, pkey); - return obj; + default: klass = cPKey; break; } + obj = NewPKey(klass); + SetPKey(obj, pkey); + return obj; } VALUE @@ -140,6 +136,35 @@ ossl_pkey_new(EVP_PKEY *pkey) return obj; } +EVP_PKEY * +ossl_pkey_read_generic(BIO *bio, VALUE pass) +{ + void *ppass = (void *)pass; + EVP_PKEY *pkey; + + if ((pkey = d2i_PrivateKey_bio(bio, NULL))) + goto out; + OSSL_BIO_reset(bio); + if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, ppass))) + goto out; + OSSL_BIO_reset(bio); + if ((pkey = d2i_PUBKEY_bio(bio, NULL))) + goto out; + OSSL_BIO_reset(bio); + /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */ + if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, ppass))) + goto out; + OSSL_BIO_reset(bio); + if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL))) + goto out; + OSSL_BIO_reset(bio); + if ((pkey = PEM_read_bio_Parameters(bio, NULL))) + goto out; + + out: + return pkey; +} + /* * call-seq: * OpenSSL::PKey.read(string [, pwd ]) -> PKey @@ -149,7 +174,7 @@ ossl_pkey_new(EVP_PKEY *pkey) * 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 * arbitrary private or public key. @@ -164,33 +189,234 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) VALUE data, pass; rb_scan_args(argc, argv, "11", &data, &pass); - pass = ossl_pem_passwd_value(pass); - bio = ossl_obj2bio(&data); - if ((pkey = d2i_PrivateKey_bio(bio, NULL))) - goto ok; - OSSL_BIO_reset(bio); - if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) - goto ok; - OSSL_BIO_reset(bio); - if ((pkey = d2i_PUBKEY_bio(bio, NULL))) - goto ok; - OSSL_BIO_reset(bio); - /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */ - if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) - goto ok; - OSSL_BIO_reset(bio); - if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL))) - goto ok; - - BIO_free(bio); - ossl_raise(ePKeyError, "Could not parse PKey"); - -ok: + pkey = ossl_pkey_read_generic(bio, ossl_pem_passwd_value(pass)); BIO_free(bio); + if (!pkey) + ossl_raise(ePKeyError, "Could not parse PKey"); return ossl_pkey_new(pkey); } +static VALUE +pkey_gen_apply_options_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ctx_v)) +{ + VALUE key = rb_ary_entry(i, 0), value = rb_ary_entry(i, 1); + EVP_PKEY_CTX *ctx = (EVP_PKEY_CTX *)ctx_v; + + if (SYMBOL_P(key)) + key = rb_sym2str(key); + value = rb_String(value); + + if (EVP_PKEY_CTX_ctrl_str(ctx, StringValueCStr(key), StringValueCStr(value)) <= 0) + ossl_raise(ePKeyError, "EVP_PKEY_CTX_ctrl_str(ctx, %+"PRIsVALUE", %+"PRIsVALUE")", + key, value); + return Qnil; +} + +static VALUE +pkey_gen_apply_options0(VALUE args_v) +{ + VALUE *args = (VALUE *)args_v; + + rb_block_call(args[1], rb_intern("each"), 0, NULL, + pkey_gen_apply_options_i, args[0]); + return Qnil; +} + +struct pkey_blocking_generate_arg { + EVP_PKEY_CTX *ctx; + EVP_PKEY *pkey; + int state; + int yield: 1; + int genparam: 1; + int stop: 1; +}; + +static VALUE +pkey_gen_cb_yield(VALUE ctx_v) +{ + EVP_PKEY_CTX *ctx = (void *)ctx_v; + int i, info_num; + VALUE *argv; + + info_num = EVP_PKEY_CTX_get_keygen_info(ctx, -1); + argv = ALLOCA_N(VALUE, info_num); + for (i = 0; i < info_num; i++) + argv[i] = INT2NUM(EVP_PKEY_CTX_get_keygen_info(ctx, i)); + + return rb_yield_values2(info_num, argv); +} + +static int +pkey_gen_cb(EVP_PKEY_CTX *ctx) +{ + struct pkey_blocking_generate_arg *arg = EVP_PKEY_CTX_get_app_data(ctx); + + if (arg->yield) { + int state; + rb_protect(pkey_gen_cb_yield, (VALUE)ctx, &state); + if (state) { + arg->stop = 1; + arg->state = state; + } + } + return !arg->stop; +} + +static void +pkey_blocking_gen_stop(void *ptr) +{ + struct pkey_blocking_generate_arg *arg = ptr; + arg->stop = 1; +} + +static void * +pkey_blocking_gen(void *ptr) +{ + struct pkey_blocking_generate_arg *arg = ptr; + + if (arg->genparam && EVP_PKEY_paramgen(arg->ctx, &arg->pkey) <= 0) + return NULL; + if (!arg->genparam && EVP_PKEY_keygen(arg->ctx, &arg->pkey) <= 0) + return NULL; + return arg->pkey; +} + +static VALUE +pkey_generate(int argc, VALUE *argv, VALUE self, int genparam) +{ + EVP_PKEY_CTX *ctx; + VALUE alg, options; + struct pkey_blocking_generate_arg gen_arg = { 0 }; + int state; + + rb_scan_args(argc, argv, "11", &alg, &options); + if (rb_obj_is_kind_of(alg, cPKey)) { + EVP_PKEY *base_pkey; + + GetPKey(alg, base_pkey); + ctx = EVP_PKEY_CTX_new(base_pkey, NULL/* engine */); + if (!ctx) + ossl_raise(ePKeyError, "EVP_PKEY_CTX_new"); + } + else { + const EVP_PKEY_ASN1_METHOD *ameth; + ENGINE *tmpeng; + int pkey_id; + + StringValue(alg); + ameth = EVP_PKEY_asn1_find_str(&tmpeng, RSTRING_PTR(alg), + RSTRING_LENINT(alg)); + if (!ameth) + ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", alg); + EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth); +#if !defined(OPENSSL_NO_ENGINE) + if (tmpeng) + ENGINE_finish(tmpeng); +#endif + + ctx = EVP_PKEY_CTX_new_id(pkey_id, NULL/* engine */); + if (!ctx) + ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_id"); + } + + if (genparam && EVP_PKEY_paramgen_init(ctx) <= 0) { + EVP_PKEY_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_PKEY_paramgen_init"); + } + if (!genparam && EVP_PKEY_keygen_init(ctx) <= 0) { + EVP_PKEY_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_PKEY_keygen_init"); + } + + if (!NIL_P(options)) { + VALUE args[2]; + + args[0] = (VALUE)ctx; + args[1] = options; + rb_protect(pkey_gen_apply_options0, (VALUE)args, &state); + if (state) { + EVP_PKEY_CTX_free(ctx); + rb_jump_tag(state); + } + } + + gen_arg.genparam = genparam; + gen_arg.ctx = ctx; + gen_arg.yield = rb_block_given_p(); + EVP_PKEY_CTX_set_app_data(ctx, &gen_arg); + EVP_PKEY_CTX_set_cb(ctx, pkey_gen_cb); + if (gen_arg.yield) + pkey_blocking_gen(&gen_arg); + else + rb_thread_call_without_gvl(pkey_blocking_gen, &gen_arg, + pkey_blocking_gen_stop, &gen_arg); + EVP_PKEY_CTX_free(ctx); + if (!gen_arg.pkey) { + if (gen_arg.state) { + ossl_clear_error(); + rb_jump_tag(gen_arg.state); + } + else { + ossl_raise(ePKeyError, genparam ? "EVP_PKEY_paramgen" : "EVP_PKEY_keygen"); + } + } + + return ossl_pkey_new(gen_arg.pkey); +} + +/* + * call-seq: + * OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey + * + * Generates new parameters for the algorithm. _algo_name_ is a String that + * represents the algorithm. The optional argument _options_ is a Hash that + * specifies the options specific to the algorithm. The order of the options + * can be important. + * + * A block can be passed optionally. The meaning of the arguments passed to + * the block varies depending on the implementation of the algorithm. The block + * may be called once or multiple times, or may not even be called. + * + * For the supported options, see the documentation for the 'openssl genpkey' + * utility command. + * + * == Example + * pkey = OpenSSL::PKey.generate_parameters("DSA", "dsa_paramgen_bits" => 2048) + * p pkey.p.num_bits #=> 2048 + */ +static VALUE +ossl_pkey_s_generate_parameters(int argc, VALUE *argv, VALUE self) +{ + return pkey_generate(argc, argv, self, 1); +} + +/* + * call-seq: + * OpenSSL::PKey.generate_key(algo_name [, options]) -> pkey + * OpenSSL::PKey.generate_key(pkey [, options]) -> pkey + * + * Generates a new key (pair). + * + * If a String is given as the first argument, it generates a new random key + * for the algorithm specified by the name just as ::generate_parameters does. + * If an OpenSSL::PKey::PKey is given instead, it generates a new random key + * for the same algorithm as the key, using the parameters the key contains. + * + * See ::generate_parameters for the details of _options_ and the given block. + * + * == Example + * pkey_params = OpenSSL::PKey.generate_parameters("DSA", "dsa_paramgen_bits" => 2048) + * pkey_params.priv_key #=> nil + * pkey = OpenSSL::PKey.generate_key(pkey_params) + * pkey.priv_key #=> #<OpenSSL::BN 6277... + */ +static VALUE +ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self) +{ + return pkey_generate(argc, argv, self, 0); +} + void ossl_pkey_check_public_key(const EVP_PKEY *pkey) { @@ -246,12 +472,19 @@ GetPrivPKeyPtr(VALUE obj) { EVP_PKEY *pkey; - if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) { - ossl_raise(rb_eArgError, "Private key is needed."); - } GetPKey(obj, pkey); + if (OSSL_PKEY_IS_PRIVATE(obj)) + return pkey; + /* + * The EVP API does not provide a way to check if the EVP_PKEY has private + * components. Assuming it does... + */ + if (!rb_respond_to(obj, id_private_q)) + return pkey; + if (RTEST(rb_funcallv(obj, id_private_q, 0, NULL))) + return pkey; - return pkey; + rb_raise(rb_eArgError, "private key is needed"); } EVP_PKEY * @@ -335,6 +568,52 @@ ossl_pkey_inspect(VALUE self) OBJ_nid2sn(nid)); } +VALUE +ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der) +{ + EVP_PKEY *pkey; + VALUE cipher, pass; + const EVP_CIPHER *enc = NULL; + BIO *bio; + + GetPKey(self, pkey); + rb_scan_args(argc, argv, "02", &cipher, &pass); + if (!NIL_P(cipher)) { + enc = ossl_evp_get_cipherbyname(cipher); + pass = ossl_pem_passwd_value(pass); + } + + bio = BIO_new(BIO_s_mem()); + if (!bio) + ossl_raise(ePKeyError, "BIO_new"); + if (to_der) { + if (!i2d_PrivateKey_bio(bio, pkey)) { + BIO_free(bio); + ossl_raise(ePKeyError, "i2d_PrivateKey_bio"); + } + } + else { +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) + if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0, + ossl_pem_passwd_cb, + (void *)pass)) { +#else + char pem_str[80]; + const char *aname; + + EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &aname, pkey->ameth); + snprintf(pem_str, sizeof(pem_str), "%s PRIVATE KEY", aname); + if (!PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey, pem_str, bio, + pkey, enc, NULL, 0, ossl_pem_passwd_cb, + (void *)pass)) { +#endif + BIO_free(bio); + ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional"); + } + } + return ossl_membio2str(bio); +} + static VALUE do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der) { @@ -404,8 +683,8 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self) return do_pkcs8_export(argc, argv, self, 0); } -static VALUE -do_spki_export(VALUE self, int to_der) +VALUE +ossl_pkey_export_spki(VALUE self, int to_der) { EVP_PKEY *pkey; BIO *bio; @@ -438,7 +717,7 @@ do_spki_export(VALUE self, int to_der) static VALUE ossl_pkey_public_to_der(VALUE self) { - return do_spki_export(self, 1); + return ossl_pkey_export_spki(self, 1); } /* @@ -450,7 +729,45 @@ ossl_pkey_public_to_der(VALUE self) static VALUE ossl_pkey_public_to_pem(VALUE self) { - return do_spki_export(self, 0); + return ossl_pkey_export_spki(self, 0); +} + +/* + * call-seq: + * pkey.compare?(another_pkey) -> true | false + * + * Used primarily to check if an OpenSSL::X509::Certificate#public_key compares to its private key. + * + * == Example + * x509 = OpenSSL::X509::Certificate.new(pem_encoded_certificate) + * rsa_key = OpenSSL::PKey::RSA.new(pem_encoded_private_key) + * + * rsa_key.compare?(x509.public_key) => true | false + */ +static VALUE +ossl_pkey_compare(VALUE self, VALUE other) +{ + int ret; + EVP_PKEY *selfPKey; + EVP_PKEY *otherPKey; + + GetPKey(self, selfPKey); + GetPKey(other, otherPKey); + + /* Explicitly check the key type given EVP_PKEY_ASN1_METHOD(3) + * docs param_cmp could return any negative number. + */ + if (EVP_PKEY_id(selfPKey) != EVP_PKEY_id(otherPKey)) + ossl_raise(rb_eTypeError, "cannot match different PKey types"); + + ret = EVP_PKEY_cmp(selfPKey, otherPKey); + + if (ret == 0) + return Qfalse; + else if (ret == 1) + return Qtrue; + else + ossl_raise(ePKeyError, "EVP_PKEY_cmp"); } /* @@ -474,35 +791,68 @@ static VALUE ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) { EVP_PKEY *pkey; - const EVP_MD *md; + const EVP_MD *md = NULL; EVP_MD_CTX *ctx; - unsigned int buf_len; - VALUE str; - int result; + size_t siglen; + int state; + VALUE sig; pkey = GetPrivPKeyPtr(self); - md = ossl_evp_get_digestbyname(digest); + if (!NIL_P(digest)) + md = ossl_evp_get_digestbyname(digest); StringValue(data); - str = rb_str_new(0, EVP_PKEY_size(pkey)); ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(ePKeyError, "EVP_MD_CTX_new"); - if (!EVP_SignInit_ex(ctx, md, NULL)) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_SignInit_ex"); + ossl_raise(ePKeyError, "EVP_MD_CTX_new"); + if (EVP_DigestSignInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignInit"); + } +#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) + if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data), + RSTRING_LEN(data)) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSign"); } - if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_SignUpdate"); + if (siglen > LONG_MAX) + rb_raise(ePKeyError, "signature would be too large"); + sig = ossl_str_new(NULL, (long)siglen, &state); + if (state) { + EVP_MD_CTX_free(ctx); + rb_jump_tag(state); } - result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey); + if (EVP_DigestSign(ctx, (unsigned char *)RSTRING_PTR(sig), &siglen, + (unsigned char *)RSTRING_PTR(data), + RSTRING_LEN(data)) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSign"); + } +#else + if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignUpdate"); + } + if (EVP_DigestSignFinal(ctx, NULL, &siglen) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignFinal"); + } + if (siglen > LONG_MAX) + rb_raise(ePKeyError, "signature would be too large"); + sig = ossl_str_new(NULL, (long)siglen, &state); + if (state) { + EVP_MD_CTX_free(ctx); + rb_jump_tag(state); + } + if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(sig), + &siglen) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignFinal"); + } +#endif EVP_MD_CTX_free(ctx); - if (!result) - ossl_raise(ePKeyError, "EVP_SignFinal"); - rb_str_set_len(str, buf_len); - - return str; + rb_str_set_len(sig, siglen); + return sig; } /* @@ -530,39 +880,99 @@ static VALUE ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) { EVP_PKEY *pkey; - const EVP_MD *md; + const EVP_MD *md = NULL; EVP_MD_CTX *ctx; - int siglen, result; + int ret; GetPKey(self, pkey); ossl_pkey_check_public_key(pkey); - md = ossl_evp_get_digestbyname(digest); + if (!NIL_P(digest)) + md = ossl_evp_get_digestbyname(digest); StringValue(sig); - siglen = RSTRING_LENINT(sig); StringValue(data); ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(ePKeyError, "EVP_MD_CTX_new"); - if (!EVP_VerifyInit_ex(ctx, md, NULL)) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_VerifyInit_ex"); + ossl_raise(ePKeyError, "EVP_MD_CTX_new"); + if (EVP_DigestVerifyInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestVerifyInit"); } - if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_VerifyUpdate"); +#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER) + ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig), + RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data), + RSTRING_LEN(data)); + EVP_MD_CTX_free(ctx); + if (ret < 0) + ossl_raise(ePKeyError, "EVP_DigestVerify"); +#else + if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate"); } - result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey); + ret = EVP_DigestVerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), + RSTRING_LEN(sig)); EVP_MD_CTX_free(ctx); - switch (result) { - case 0: - ossl_clear_error(); - return Qfalse; - case 1: - return Qtrue; - default: - ossl_raise(ePKeyError, "EVP_VerifyFinal"); + if (ret < 0) + ossl_raise(ePKeyError, "EVP_DigestVerifyFinal"); +#endif + if (ret) + return Qtrue; + else { + ossl_clear_error(); + return Qfalse; + } +} + +/* + * call-seq: + * pkey.derive(peer_pkey) -> string + * + * Derives a shared secret from _pkey_ and _peer_pkey_. _pkey_ must contain + * the private components, _peer_pkey_ must contain the public components. + */ +static VALUE +ossl_pkey_derive(int argc, VALUE *argv, VALUE self) +{ + EVP_PKEY *pkey, *peer_pkey; + EVP_PKEY_CTX *ctx; + VALUE peer_pkey_obj, str; + size_t keylen; + int state; + + GetPKey(self, pkey); + rb_scan_args(argc, argv, "1", &peer_pkey_obj); + GetPKey(peer_pkey_obj, peer_pkey); + + ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); + if (!ctx) + ossl_raise(ePKeyError, "EVP_PKEY_CTX_new"); + if (EVP_PKEY_derive_init(ctx) <= 0) { + EVP_PKEY_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_PKEY_derive_init"); + } + if (EVP_PKEY_derive_set_peer(ctx, peer_pkey) <= 0) { + EVP_PKEY_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_PKEY_derive_set_peer"); + } + if (EVP_PKEY_derive(ctx, NULL, &keylen) <= 0) { + EVP_PKEY_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_PKEY_derive"); } + if (keylen > LONG_MAX) + rb_raise(ePKeyError, "derived key would be too large"); + str = ossl_str_new(NULL, (long)keylen, &state); + if (state) { + EVP_PKEY_CTX_free(ctx); + rb_jump_tag(state); + } + if (EVP_PKEY_derive(ctx, (unsigned char *)RSTRING_PTR(str), &keylen) <= 0) { + EVP_PKEY_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_PKEY_derive"); + } + EVP_PKEY_CTX_free(ctx); + rb_str_set_len(str, keylen); + return str; } /* @@ -648,6 +1058,8 @@ Init_ossl_pkey(void) cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); + rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1); + rb_define_module_function(mPKey, "generate_key", ossl_pkey_s_generate_key, -1); rb_define_alloc_func(cPKey, ossl_pkey_alloc); rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); @@ -657,9 +1069,11 @@ Init_ossl_pkey(void) rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1); rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0); rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0); + rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1); rb_define_method(cPKey, "sign", ossl_pkey_sign, 2); rb_define_method(cPKey, "verify", ossl_pkey_verify, 3); + rb_define_method(cPKey, "derive", ossl_pkey_derive, -1); id_private_q = rb_intern("private?"); diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index 0db59305..7dbaed47 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -45,9 +45,24 @@ void ossl_generate_cb_stop(void *ptr); VALUE ossl_pkey_new(EVP_PKEY *); void ossl_pkey_check_public_key(const EVP_PKEY *); +EVP_PKEY *ossl_pkey_read_generic(BIO *, VALUE); EVP_PKEY *GetPKeyPtr(VALUE); EVP_PKEY *DupPKeyPtr(VALUE); EVP_PKEY *GetPrivPKeyPtr(VALUE); + +/* + * Serializes _self_ in X.509 SubjectPublicKeyInfo format and returns the + * resulting String. Sub-classes use this when overriding #to_der. + */ +VALUE ossl_pkey_export_spki(VALUE self, int to_der); +/* + * Serializes the private key _self_ in the traditional private key format + * and returns the resulting String. Sub-classes use this when overriding + * #to_der. + */ +VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, + int to_der); + void Init_ossl_pkey(void); /* @@ -56,7 +71,6 @@ void Init_ossl_pkey(void); extern VALUE cRSA; extern VALUE eRSAError; -VALUE ossl_rsa_new(EVP_PKEY *); void Init_ossl_rsa(void); /* @@ -65,7 +79,6 @@ void Init_ossl_rsa(void); extern VALUE cDSA; extern VALUE eDSAError; -VALUE ossl_dsa_new(EVP_PKEY *); void Init_ossl_dsa(void); /* @@ -74,7 +87,6 @@ void Init_ossl_dsa(void); extern VALUE cDH; extern VALUE eDHError; -VALUE ossl_dh_new(EVP_PKEY *); void Init_ossl_dh(void); /* diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c index bf4e3f93..5bc1c49c 100644 --- a/ext/openssl/ossl_pkey_dh.c +++ b/ext/openssl/ossl_pkey_dh.c @@ -30,52 +30,6 @@ VALUE cDH; VALUE eDHError; /* - * Public - */ -static VALUE -dh_instance(VALUE klass, DH *dh) -{ - EVP_PKEY *pkey; - VALUE obj; - - if (!dh) { - return Qfalse; - } - obj = NewPKey(klass); - if (!(pkey = EVP_PKEY_new())) { - return Qfalse; - } - if (!EVP_PKEY_assign_DH(pkey, dh)) { - EVP_PKEY_free(pkey); - return Qfalse; - } - SetPKey(obj, pkey); - - return obj; -} - -VALUE -ossl_dh_new(EVP_PKEY *pkey) -{ - VALUE obj; - - if (!pkey) { - obj = dh_instance(cDH, DH_new()); - } else { - obj = NewPKey(cDH); - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) { - ossl_raise(rb_eTypeError, "Not a DH key!"); - } - SetPKey(obj, pkey); - } - if (obj == Qfalse) { - ossl_raise(eDHError, NULL); - } - - return obj; -} - -/* * Private */ struct dh_blocking_gen_arg { @@ -105,7 +59,7 @@ dh_generate(int size, int gen) if (!dh || !cb) { DH_free(dh); BN_GENCB_free(cb); - return NULL; + ossl_raise(eDHError, "malloc failure"); } if (rb_block_given_p()) @@ -131,12 +85,12 @@ dh_generate(int size, int gen) ossl_clear_error(); rb_jump_tag(cb_arg.state); } - return NULL; + ossl_raise(eDHError, "DH_generate_parameters_ex"); } if (!DH_generate_key(dh)) { DH_free(dh); - return NULL; + ossl_raise(eDHError, "DH_generate_key"); } return dh; @@ -157,6 +111,7 @@ dh_generate(int size, int gen) static VALUE ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass) { + EVP_PKEY *pkey; DH *dh ; int g = 2; VALUE size, gen, obj; @@ -164,13 +119,14 @@ ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass) if (rb_scan_args(argc, argv, "11", &size, &gen) == 2) { g = NUM2INT(gen); } + obj = rb_obj_alloc(klass); + GetPKey(obj, pkey); + dh = dh_generate(NUM2INT(size), g); - obj = dh_instance(klass, dh); - if (obj == Qfalse) { - DH_free(dh); - ossl_raise(eDHError, NULL); + if (!EVP_PKEY_assign_DH(pkey, dh)) { + DH_free(dh); + ossl_raise(eDHError, "EVP_PKEY_assign_DH"); } - return obj; } @@ -216,9 +172,7 @@ ossl_dh_initialize(int argc, VALUE *argv, VALUE self) if (!NIL_P(gen)) { g = NUM2INT(gen); } - if (!(dh = dh_generate(NUM2INT(arg), g))) { - ossl_raise(eDHError, NULL); - } + dh = dh_generate(NUM2INT(arg), g); } else { arg = ossl_to_der_if_possible(arg); @@ -455,17 +409,21 @@ ossl_dh_to_text(VALUE self) static VALUE ossl_dh_to_public_key(VALUE self) { + EVP_PKEY *pkey; DH *orig_dh, *dh; VALUE obj; + obj = rb_obj_alloc(rb_obj_class(self)); + GetPKey(obj, pkey); + GetDH(self, orig_dh); - dh = DHparams_dup(orig_dh); /* err check perfomed by dh_instance */ - obj = dh_instance(rb_obj_class(self), dh); - if (obj == Qfalse) { - DH_free(dh); - ossl_raise(eDHError, NULL); + dh = DHparams_dup(orig_dh); + if (!dh) + ossl_raise(eDHError, "DHparams_dup"); + if (!EVP_PKEY_assign_DH(pkey, dh)) { + DH_free(dh); + ossl_raise(eDHError, "EVP_PKEY_assign_DH"); } - return obj; } @@ -519,40 +477,6 @@ ossl_dh_generate_key(VALUE self) } /* - * call-seq: - * dh.compute_key(pub_bn) -> aString - * - * Returns a String containing a shared secret computed from the other party's public value. - * See DH_compute_key() for further information. - * - * === Parameters - * * _pub_bn_ is a OpenSSL::BN, *not* the DH instance returned by - * DH#public_key as that contains the DH parameters only. - */ -static VALUE -ossl_dh_compute_key(VALUE self, VALUE pub) -{ - DH *dh; - const BIGNUM *pub_key, *dh_p; - VALUE str; - int len; - - GetDH(self, dh); - DH_get0_pqg(dh, &dh_p, NULL, NULL); - if (!dh_p) - ossl_raise(eDHError, "incomplete DH"); - pub_key = GetBNPtr(pub); - len = DH_size(dh); - str = rb_str_new(0, len); - if ((len = DH_compute_key((unsigned char *)RSTRING_PTR(str), pub_key, dh)) < 0) { - ossl_raise(eDHError, NULL); - } - rb_str_set_len(str, len); - - return str; -} - -/* * Document-method: OpenSSL::PKey::DH#set_pqg * call-seq: * dh.set_pqg(p, q, g) -> self @@ -629,7 +553,6 @@ Init_ossl_dh(void) rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0); rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0); rb_define_method(cDH, "generate_key!", ossl_dh_generate_key, 0); - rb_define_method(cDH, "compute_key", ossl_dh_compute_key, 1); DEF_OSSL_PKEY_BN(cDH, dh, p); DEF_OSSL_PKEY_BN(cDH, dh, q); diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c index 431c20e0..0e68f7f2 100644 --- a/ext/openssl/ossl_pkey_dsa.c +++ b/ext/openssl/ossl_pkey_dsa.c @@ -44,52 +44,6 @@ VALUE cDSA; VALUE eDSAError; /* - * Public - */ -static VALUE -dsa_instance(VALUE klass, DSA *dsa) -{ - EVP_PKEY *pkey; - VALUE obj; - - if (!dsa) { - return Qfalse; - } - obj = NewPKey(klass); - if (!(pkey = EVP_PKEY_new())) { - return Qfalse; - } - if (!EVP_PKEY_assign_DSA(pkey, dsa)) { - EVP_PKEY_free(pkey); - return Qfalse; - } - SetPKey(obj, pkey); - - return obj; -} - -VALUE -ossl_dsa_new(EVP_PKEY *pkey) -{ - VALUE obj; - - if (!pkey) { - obj = dsa_instance(cDSA, DSA_new()); - } else { - obj = NewPKey(cDSA); - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { - ossl_raise(rb_eTypeError, "Not a DSA key!"); - } - SetPKey(obj, pkey); - } - if (obj == Qfalse) { - ossl_raise(eDSAError, NULL); - } - - return obj; -} - -/* * Private */ struct dsa_blocking_gen_arg { @@ -121,9 +75,9 @@ dsa_generate(int size) unsigned long h; if (!dsa || !cb) { - DSA_free(dsa); - BN_GENCB_free(cb); - return NULL; + DSA_free(dsa); + BN_GENCB_free(cb); + ossl_raise(eDSAError, "malloc failure"); } if (rb_block_given_p()) @@ -153,12 +107,12 @@ dsa_generate(int size) ossl_clear_error(); rb_jump_tag(cb_arg.state); } - return NULL; + ossl_raise(eDSAError, "DSA_generate_parameters_ex"); } if (!DSA_generate_key(dsa)) { - DSA_free(dsa); - return NULL; + DSA_free(dsa); + ossl_raise(eDSAError, "DSA_generate_key"); } return dsa; @@ -178,14 +132,18 @@ dsa_generate(int size) static VALUE ossl_dsa_s_generate(VALUE klass, VALUE size) { - DSA *dsa = dsa_generate(NUM2INT(size)); /* err handled by dsa_instance */ - VALUE obj = dsa_instance(klass, dsa); + EVP_PKEY *pkey; + DSA *dsa; + VALUE obj; - if (obj == Qfalse) { - DSA_free(dsa); - ossl_raise(eDSAError, NULL); - } + obj = rb_obj_alloc(klass); + GetPKey(obj, pkey); + dsa = dsa_generate(NUM2INT(size)); + if (!EVP_PKEY_assign_DSA(pkey, dsa)) { + DSA_free(dsa); + ossl_raise(eDSAError, "EVP_PKEY_assign_DSA"); + } return obj; } @@ -212,37 +170,34 @@ ossl_dsa_s_generate(VALUE klass, VALUE size) static VALUE ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) { - EVP_PKEY *pkey; - DSA *dsa; + EVP_PKEY *pkey, *tmp; + DSA *dsa = NULL; BIO *in; VALUE arg, pass; GetPKey(self, pkey); - if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) { + rb_scan_args(argc, argv, "02", &arg, &pass); + if (argc == 0) { dsa = DSA_new(); + if (!dsa) + ossl_raise(eDSAError, "DSA_new"); } - else if (RB_INTEGER_TYPE_P(arg)) { - if (!(dsa = dsa_generate(NUM2INT(arg)))) { - ossl_raise(eDSAError, NULL); - } + else if (argc == 1 && RB_INTEGER_TYPE_P(arg)) { + dsa = dsa_generate(NUM2INT(arg)); } else { pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); - dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); - if (!dsa) { - OSSL_BIO_reset(in); - dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL); - } - if (!dsa) { - OSSL_BIO_reset(in); - dsa = d2i_DSAPrivateKey_bio(in, NULL); - } - if (!dsa) { - OSSL_BIO_reset(in); - dsa = d2i_DSA_PUBKEY_bio(in, NULL); - } + + tmp = ossl_pkey_read_generic(in, pass); + if (tmp) { + if (EVP_PKEY_base_id(tmp) != EVP_PKEY_DSA) + rb_raise(eDSAError, "incorrect pkey type: %s", + OBJ_nid2sn(EVP_PKEY_base_id(tmp))); + dsa = EVP_PKEY_get1_DSA(tmp); + EVP_PKEY_free(tmp); + } if (!dsa) { OSSL_BIO_reset(in); #define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ @@ -341,34 +296,12 @@ static VALUE ossl_dsa_export(int argc, VALUE *argv, VALUE self) { DSA *dsa; - BIO *out; - const EVP_CIPHER *ciph = NULL; - VALUE cipher, pass, str; GetDSA(self, dsa); - rb_scan_args(argc, argv, "02", &cipher, &pass); - if (!NIL_P(cipher)) { - ciph = ossl_evp_get_cipherbyname(cipher); - pass = ossl_pem_passwd_value(pass); - } - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eDSAError, NULL); - } - if (DSA_HAS_PRIVATE(dsa)) { - if (!PEM_write_bio_DSAPrivateKey(out, dsa, ciph, NULL, 0, - ossl_pem_passwd_cb, (void *)pass)){ - BIO_free(out); - ossl_raise(eDSAError, NULL); - } - } else { - if (!PEM_write_bio_DSA_PUBKEY(out, dsa)) { - BIO_free(out); - ossl_raise(eDSAError, NULL); - } - } - str = ossl_membio2str(out); - - return str; + if (DSA_HAS_PRIVATE(dsa)) + return ossl_pkey_export_traditional(argc, argv, self, 0); + else + return ossl_pkey_export_spki(self, 0); } /* @@ -382,25 +315,12 @@ static VALUE ossl_dsa_to_der(VALUE self) { DSA *dsa; - int (*i2d_func)(DSA *, unsigned char **); - unsigned char *p; - long len; - VALUE str; GetDSA(self, dsa); - if(DSA_HAS_PRIVATE(dsa)) - i2d_func = (int (*)(DSA *,unsigned char **))i2d_DSAPrivateKey; + if (DSA_HAS_PRIVATE(dsa)) + return ossl_pkey_export_traditional(0, NULL, self, 1); else - i2d_func = i2d_DSA_PUBKEY; - if((len = i2d_func(dsa, NULL)) <= 0) - ossl_raise(eDSAError, NULL); - str = rb_str_new(0, len); - p = (unsigned char *)RSTRING_PTR(str); - if(i2d_func(dsa, &p) < 0) - ossl_raise(eDSAError, NULL); - ossl_str_adjust(str, p); - - return str; + return ossl_pkey_export_spki(self, 1); } @@ -481,20 +401,23 @@ ossl_dsa_to_text(VALUE self) static VALUE ossl_dsa_to_public_key(VALUE self) { - EVP_PKEY *pkey; + EVP_PKEY *pkey, *pkey_new; DSA *dsa; VALUE obj; GetPKeyDSA(self, pkey); - /* err check performed by dsa_instance */ + obj = rb_obj_alloc(rb_obj_class(self)); + GetPKey(obj, pkey_new); + #define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup( \ (i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa)) dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey)); #undef DSAPublicKey_dup - obj = dsa_instance(rb_obj_class(self), dsa); - if (obj == Qfalse) { - DSA_free(dsa); - ossl_raise(eDSAError, NULL); + if (!dsa) + ossl_raise(eDSAError, "DSAPublicKey_dup"); + if (!EVP_PKEY_assign_DSA(pkey_new, dsa)) { + DSA_free(dsa); + ossl_raise(eDSAError, "EVP_PKEY_assign_DSA"); } return obj; } diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index fc2bc6c8..dfb46f8b 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -63,47 +63,6 @@ static ID id_i_group; static VALUE ec_group_new(const EC_GROUP *group); static VALUE ec_point_new(const EC_POINT *point, const EC_GROUP *group); -static VALUE ec_instance(VALUE klass, EC_KEY *ec) -{ - EVP_PKEY *pkey; - VALUE obj; - - if (!ec) { - return Qfalse; - } - obj = NewPKey(klass); - if (!(pkey = EVP_PKEY_new())) { - return Qfalse; - } - if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) { - EVP_PKEY_free(pkey); - return Qfalse; - } - SetPKey(obj, pkey); - - return obj; -} - -VALUE ossl_ec_new(EVP_PKEY *pkey) -{ - VALUE obj; - - if (!pkey) { - obj = ec_instance(cEC, EC_KEY_new()); - } else { - obj = NewPKey(cEC); - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { - ossl_raise(rb_eTypeError, "Not a EC key!"); - } - SetPKey(obj, pkey); - } - if (obj == Qfalse) { - ossl_raise(eECError, NULL); - } - - return obj; -} - /* * Creates a new EC_KEY on the EC group obj. arg can be an EC::Group or a String * representing an OID. @@ -150,17 +109,18 @@ ec_key_new_from_group(VALUE arg) static VALUE ossl_ec_key_s_generate(VALUE klass, VALUE arg) { + EVP_PKEY *pkey; EC_KEY *ec; VALUE obj; - ec = ec_key_new_from_group(arg); + obj = rb_obj_alloc(klass); + GetPKey(obj, pkey); - obj = ec_instance(klass, ec); - if (obj == Qfalse) { - EC_KEY_free(ec); - ossl_raise(eECError, NULL); + ec = ec_key_new_from_group(arg); + if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) { + EC_KEY_free(ec); + ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); } - if (!EC_KEY_generate_key(ec)) ossl_raise(eECError, "EC_KEY_generate_key"); @@ -181,7 +141,7 @@ ossl_ec_key_s_generate(VALUE klass, VALUE arg) static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; - EC_KEY *ec; + EC_KEY *ec = NULL; VALUE arg, pass; GetPKey(self, pkey); @@ -202,24 +162,17 @@ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) { ec = ec_key_new_from_group(arg); } else { - BIO *in; - - pass = ossl_pem_passwd_value(pass); - in = ossl_obj2bio(&arg); - - ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); - if (!ec) { - OSSL_BIO_reset(in); - ec = PEM_read_bio_EC_PUBKEY(in, NULL, ossl_pem_passwd_cb, (void *)pass); - } - if (!ec) { - OSSL_BIO_reset(in); - ec = d2i_ECPrivateKey_bio(in, NULL); - } - if (!ec) { - OSSL_BIO_reset(in); - ec = d2i_EC_PUBKEY_bio(in, NULL); - } + BIO *in = ossl_obj2bio(&arg); + EVP_PKEY *tmp; + pass = ossl_pem_passwd_value(pass); + tmp = ossl_pkey_read_generic(in, pass); + if (tmp) { + if (EVP_PKEY_base_id(tmp) != EVP_PKEY_EC) + rb_raise(eECError, "incorrect pkey type: %s", + OBJ_nid2sn(EVP_PKEY_base_id(tmp))); + ec = EVP_PKEY_get1_EC_KEY(tmp); + EVP_PKEY_free(tmp); + } BIO_free(in); if (!ec) { @@ -425,66 +378,6 @@ static VALUE ossl_ec_key_is_private(VALUE self) return EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse; } -static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int format) -{ - EC_KEY *ec; - BIO *out; - int i = -1; - int private = 0; - VALUE str; - const EVP_CIPHER *cipher = NULL; - - GetEC(self, ec); - - if (EC_KEY_get0_public_key(ec) == NULL) - ossl_raise(eECError, "can't export - no public key set"); - - if (EC_KEY_check_key(ec) != 1) - ossl_raise(eECError, "can't export - EC_KEY_check_key failed"); - - if (EC_KEY_get0_private_key(ec)) - private = 1; - - if (!NIL_P(ciph)) { - cipher = ossl_evp_get_cipherbyname(ciph); - pass = ossl_pem_passwd_value(pass); - } - - if (!(out = BIO_new(BIO_s_mem()))) - ossl_raise(eECError, "BIO_new(BIO_s_mem())"); - - switch(format) { - case EXPORT_PEM: - if (private) { - i = PEM_write_bio_ECPrivateKey(out, ec, cipher, NULL, 0, ossl_pem_passwd_cb, (void *)pass); - } else { - i = PEM_write_bio_EC_PUBKEY(out, ec); - } - - break; - case EXPORT_DER: - if (private) { - i = i2d_ECPrivateKey_bio(out, ec); - } else { - i = i2d_EC_PUBKEY_bio(out, ec); - } - - break; - default: - BIO_free(out); - ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); - } - - if (i != 1) { - BIO_free(out); - ossl_raise(eECError, "outlen=%d", i); - } - - str = ossl_membio2str(out); - - return str; -} - /* * call-seq: * key.export([cipher, pass_phrase]) => String @@ -495,11 +388,16 @@ static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int forma * instance. Note that encryption will only be effective for a private key, * public keys will always be encoded in plain text. */ -static VALUE ossl_ec_key_export(int argc, VALUE *argv, VALUE self) +static VALUE +ossl_ec_key_export(int argc, VALUE *argv, VALUE self) { - VALUE cipher, passwd; - rb_scan_args(argc, argv, "02", &cipher, &passwd); - return ossl_ec_key_to_string(self, cipher, passwd, EXPORT_PEM); + EC_KEY *ec; + + GetEC(self, ec); + if (EC_KEY_get0_private_key(ec)) + return ossl_pkey_export_traditional(argc, argv, self, 0); + else + return ossl_pkey_export_spki(self, 0); } /* @@ -508,9 +406,16 @@ static VALUE ossl_ec_key_export(int argc, VALUE *argv, VALUE self) * * See the OpenSSL documentation for i2d_ECPrivateKey_bio() */ -static VALUE ossl_ec_key_to_der(VALUE self) +static VALUE +ossl_ec_key_to_der(VALUE self) { - return ossl_ec_key_to_string(self, Qnil, Qnil, EXPORT_DER); + EC_KEY *ec; + + GetEC(self, ec); + if (EC_KEY_get0_private_key(ec)) + return ossl_pkey_export_traditional(0, NULL, self, 1); + else + return ossl_pkey_export_spki(self, 1); } /* @@ -584,37 +489,6 @@ static VALUE ossl_ec_key_check_key(VALUE self) /* * call-seq: - * key.dh_compute_key(pubkey) => String - * - * See the OpenSSL documentation for ECDH_compute_key() - */ -static VALUE ossl_ec_key_dh_compute_key(VALUE self, VALUE pubkey) -{ - EC_KEY *ec; - EC_POINT *point; - int buf_len; - VALUE str; - - GetEC(self, ec); - GetECPoint(pubkey, point); - -/* BUG: need a way to figure out the maximum string size */ - buf_len = 1024; - str = rb_str_new(0, buf_len); -/* BUG: take KDF as a block */ - buf_len = ECDH_compute_key(RSTRING_PTR(str), buf_len, point, ec, NULL); - if (buf_len < 0) - ossl_raise(eECError, "ECDH_compute_key"); - - rb_str_resize(str, buf_len); - - return str; -} - -/* sign_setup */ - -/* - * call-seq: * key.dsa_sign_asn1(data) => String * * See the OpenSSL documentation for ECDSA_sign() @@ -1631,6 +1505,10 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) if (EC_POINT_mul(group, point_result, bn_g, point_self, bn, ossl_bn_ctx) != 1) ossl_raise(eEC_POINT, NULL); } else { +#if OPENSSL_VERSION_MAJOR+0 >= 3 || defined(LIBRESSL_VERSION_NUMBER) + rb_raise(rb_eNotImpError, "calling #mul with arrays is not" \ + "supported by this OpenSSL version"); +#else /* * bignums | arg1[0] | arg1[1] | arg1[2] | ... * points | self | arg2[0] | arg2[1] | ... @@ -1645,6 +1523,9 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) if (RARRAY_LEN(arg1) != RARRAY_LEN(arg2) + 1) /* arg2 must be 1 larger */ ossl_raise(rb_eArgError, "bns must be 1 longer than points; see the documentation"); + rb_warning("OpenSSL::PKey::EC::Point#mul(ary, ary) is deprecated; " \ + "use #mul(bn) form instead"); + num = RARRAY_LEN(arg1); bns_tmp = rb_ary_tmp_new(num); bignums = ALLOCV_N(const BIGNUM *, tmp_b, num); @@ -1670,6 +1551,7 @@ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) ALLOCV_END(tmp_b); ALLOCV_END(tmp_p); +#endif } return result; @@ -1752,7 +1634,6 @@ void Init_ossl_ec(void) rb_define_alias(cEC, "generate_key", "generate_key!"); rb_define_method(cEC, "check_key", ossl_ec_key_check_key, 0); - rb_define_method(cEC, "dh_compute_key", ossl_ec_key_dh_compute_key, 1); rb_define_method(cEC, "dsa_sign_asn1", ossl_ec_key_dsa_sign_asn1, 1); rb_define_method(cEC, "dsa_verify_asn1", ossl_ec_key_dsa_verify_asn1, 2); /* do_sign/do_verify */ diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 761866c6..3c298a2a 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -45,53 +45,6 @@ VALUE cRSA; VALUE eRSAError; /* - * Public - */ -static VALUE -rsa_instance(VALUE klass, RSA *rsa) -{ - EVP_PKEY *pkey; - VALUE obj; - - if (!rsa) { - return Qfalse; - } - obj = NewPKey(klass); - if (!(pkey = EVP_PKEY_new())) { - return Qfalse; - } - if (!EVP_PKEY_assign_RSA(pkey, rsa)) { - EVP_PKEY_free(pkey); - return Qfalse; - } - SetPKey(obj, pkey); - - return obj; -} - -VALUE -ossl_rsa_new(EVP_PKEY *pkey) -{ - VALUE obj; - - if (!pkey) { - obj = rsa_instance(cRSA, RSA_new()); - } - else { - obj = NewPKey(cRSA); - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { - ossl_raise(rb_eTypeError, "Not a RSA key!"); - } - SetPKey(obj, pkey); - } - if (obj == Qfalse) { - ossl_raise(eRSAError, NULL); - } - - return obj; -} - -/* * Private */ struct rsa_blocking_gen_arg { @@ -124,7 +77,7 @@ rsa_generate(int size, unsigned long exp) RSA_free(rsa); BN_free(e); BN_GENCB_free(cb); - return NULL; + ossl_raise(eRSAError, "malloc failure"); } for (i = 0; i < (int)sizeof(exp) * 8; ++i) { if (exp & (1UL << i)) { @@ -132,7 +85,7 @@ rsa_generate(int size, unsigned long exp) BN_free(e); RSA_free(rsa); BN_GENCB_free(cb); - return NULL; + ossl_raise(eRSAError, "BN_set_bit"); } } } @@ -161,7 +114,7 @@ rsa_generate(int size, unsigned long exp) ossl_clear_error(); rb_jump_tag(cb_arg.state); } - return NULL; + ossl_raise(eRSAError, "RSA_generate_key_ex"); } return rsa; @@ -180,26 +133,26 @@ static VALUE ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass) { /* why does this method exist? why can't initialize take an optional exponent? */ + EVP_PKEY *pkey; RSA *rsa; VALUE size, exp; VALUE obj; rb_scan_args(argc, argv, "11", &size, &exp); + obj = rb_obj_alloc(klass); + GetPKey(obj, pkey); - rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2ULONG(exp)); /* err handled by rsa_instance */ - obj = rsa_instance(klass, rsa); - - if (obj == Qfalse) { - RSA_free(rsa); - ossl_raise(eRSAError, NULL); + rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2ULONG(exp)); + if (!EVP_PKEY_assign_RSA(pkey, rsa)) { + RSA_free(rsa); + ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); } - return obj; } /* * call-seq: - * RSA.new(key_size) => RSA instance + * RSA.new(size [, exponent]) => RSA instance * RSA.new(encoded_key) => RSA instance * RSA.new(encoded_key, pass_phrase) => RSA instance * @@ -220,36 +173,34 @@ ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass) static VALUE ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) { - EVP_PKEY *pkey; - RSA *rsa; + EVP_PKEY *pkey, *tmp; + RSA *rsa = NULL; BIO *in; VALUE arg, pass; GetPKey(self, pkey); - if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) { + rb_scan_args(argc, argv, "02", &arg, &pass); + if (argc == 0) { rsa = RSA_new(); + if (!rsa) + ossl_raise(eRSAError, "RSA_new"); } else if (RB_INTEGER_TYPE_P(arg)) { rsa = rsa_generate(NUM2INT(arg), NIL_P(pass) ? RSA_F4 : NUM2ULONG(pass)); - if (!rsa) ossl_raise(eRSAError, NULL); } else { pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); - rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); - if (!rsa) { - OSSL_BIO_reset(in); - rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL); - } - if (!rsa) { - OSSL_BIO_reset(in); - rsa = d2i_RSAPrivateKey_bio(in, NULL); - } - if (!rsa) { - OSSL_BIO_reset(in); - rsa = d2i_RSA_PUBKEY_bio(in, NULL); - } + + tmp = ossl_pkey_read_generic(in, pass); + if (tmp) { + if (EVP_PKEY_base_id(tmp) != EVP_PKEY_RSA) + rb_raise(eRSAError, "incorrect pkey type: %s", + OBJ_nid2sn(EVP_PKEY_base_id(tmp))); + rsa = EVP_PKEY_get1_RSA(tmp); + EVP_PKEY_free(tmp); + } if (!rsa) { OSSL_BIO_reset(in); rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL); @@ -260,12 +211,13 @@ ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) } BIO_free(in); if (!rsa) { + ossl_clear_error(); ossl_raise(eRSAError, "Neither PUB key nor PRIV key"); } } if (!EVP_PKEY_assign_RSA(pkey, rsa)) { RSA_free(rsa); - ossl_raise(eRSAError, NULL); + ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); } return self; @@ -327,6 +279,21 @@ ossl_rsa_is_private(VALUE self) return RSA_PRIVATE(self, rsa) ? Qtrue : Qfalse; } +static int +can_export_rsaprivatekey(VALUE self) +{ + RSA *rsa; + const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; + + GetRSA(self, rsa); + + RSA_get0_key(rsa, &n, &e, &d); + RSA_get0_factors(rsa, &p, &q); + RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); + + return n && e && d && p && q && dmp1 && dmq1 && iqmp; +} + /* * call-seq: * rsa.export([cipher, pass_phrase]) => PEM-format String @@ -340,41 +307,10 @@ ossl_rsa_is_private(VALUE self) static VALUE ossl_rsa_export(int argc, VALUE *argv, VALUE self) { - RSA *rsa; - const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; - BIO *out; - const EVP_CIPHER *ciph = NULL; - VALUE cipher, pass, str; - - GetRSA(self, rsa); - - rb_scan_args(argc, argv, "02", &cipher, &pass); - - if (!NIL_P(cipher)) { - ciph = ossl_evp_get_cipherbyname(cipher); - pass = ossl_pem_passwd_value(pass); - } - if (!(out = BIO_new(BIO_s_mem()))) { - ossl_raise(eRSAError, NULL); - } - RSA_get0_key(rsa, &n, &e, &d); - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); - if (n && e && d && p && q && dmp1 && dmq1 && iqmp) { - if (!PEM_write_bio_RSAPrivateKey(out, rsa, ciph, NULL, 0, - ossl_pem_passwd_cb, (void *)pass)) { - BIO_free(out); - ossl_raise(eRSAError, NULL); - } - } else { - if (!PEM_write_bio_RSA_PUBKEY(out, rsa)) { - BIO_free(out); - ossl_raise(eRSAError, NULL); - } - } - str = ossl_membio2str(out); - - return str; + if (can_export_rsaprivatekey(self)) + return ossl_pkey_export_traditional(argc, argv, self, 0); + else + return ossl_pkey_export_spki(self, 0); } /* @@ -386,30 +322,10 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self) static VALUE ossl_rsa_to_der(VALUE self) { - RSA *rsa; - const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; - int (*i2d_func)(const RSA *, unsigned char **); - unsigned char *ptr; - long len; - VALUE str; - - GetRSA(self, rsa); - RSA_get0_key(rsa, &n, &e, &d); - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); - if (n && e && d && p && q && dmp1 && dmq1 && iqmp) - i2d_func = i2d_RSAPrivateKey; + if (can_export_rsaprivatekey(self)) + return ossl_pkey_export_traditional(0, NULL, self, 1); else - i2d_func = (int (*)(const RSA *, unsigned char **))i2d_RSA_PUBKEY; - if((len = i2d_func(rsa, NULL)) <= 0) - ossl_raise(eRSAError, NULL); - str = rb_str_new(0, len); - ptr = (unsigned char *)RSTRING_PTR(str); - if(i2d_func(rsa, &ptr) < 0) - ossl_raise(eRSAError, NULL); - ossl_str_adjust(str, ptr); - - return str; + return ossl_pkey_export_spki(self, 1); } /* @@ -809,17 +725,20 @@ ossl_rsa_to_text(VALUE self) static VALUE ossl_rsa_to_public_key(VALUE self) { - EVP_PKEY *pkey; + EVP_PKEY *pkey, *pkey_new; RSA *rsa; VALUE obj; GetPKeyRSA(self, pkey); - /* err check performed by rsa_instance */ + obj = rb_obj_alloc(rb_obj_class(self)); + GetPKey(obj, pkey_new); + rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); - obj = rsa_instance(rb_obj_class(self), rsa); - if (obj == Qfalse) { - RSA_free(rsa); - ossl_raise(eRSAError, NULL); + if (!rsa) + ossl_raise(eRSAError, "RSAPublicKey_dup"); + if (!EVP_PKEY_assign_RSA(pkey_new, rsa)) { + RSA_free(rsa); + ossl_raise(eRSAError, "EVP_PKEY_assign_RSA"); } return obj; } diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index b76757fe..c38142bf 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -13,6 +13,12 @@ #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) +#if !defined(TLS1_3_VERSION) && \ + defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER >= 0x3020000fL +# define TLS1_3_VERSION 0x0304 +#endif + #ifdef _WIN32 # define TO_SOCKET(s) _get_osfhandle(s) #else @@ -32,14 +38,14 @@ VALUE cSSLSocket; static VALUE eSSLErrorWaitReadable; static VALUE eSSLErrorWaitWritable; -static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback, +static ID id_call, ID_callback_state, id_tmp_dh_callback, id_npn_protocols_encoded; static VALUE sym_exception, sym_wait_readable, sym_wait_writable; static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, id_i_verify_depth, id_i_verify_callback, id_i_client_ca, id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert, - id_i_client_cert_cb, id_i_tmp_ecdh_callback, id_i_timeout, + id_i_client_cert_cb, id_i_timeout, id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, @@ -231,8 +237,7 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) return 1; } -#if !defined(OPENSSL_NO_DH) || \ - !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) +#if !defined(OPENSSL_NO_DH) struct tmp_dh_callback_args { VALUE ssl_obj; ID id; @@ -289,35 +294,6 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) } #endif /* OPENSSL_NO_DH */ -#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) -static EC_KEY * -ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength) -{ - VALUE rb_ssl; - EVP_PKEY *pkey; - struct tmp_dh_callback_args args; - int state; - - rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - args.ssl_obj = rb_ssl; - args.id = id_tmp_ecdh_callback; - args.is_export = is_export; - args.keylength = keylength; - args.type = EVP_PKEY_EC; - - pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback, - (VALUE)&args, &state); - if (state) { - rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); - return NULL; - } - if (!pkey) - return NULL; - - return EVP_PKEY_get0_EC_KEY(pkey); -} -#endif - static VALUE call_verify_certificate_identity(VALUE ctx_v) { @@ -797,26 +773,6 @@ ossl_sslctx_setup(VALUE self) SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback); #endif -#if !defined(OPENSSL_NO_EC) - /* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0, - * but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */ - if (RTEST(rb_attr_get(self, id_i_tmp_ecdh_callback))) { -# if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) - rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead"); - SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback); -# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) - /* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores - * tmp_ecdh_callback. So disable ecdh_auto. */ - if (!SSL_CTX_set_ecdh_auto(ctx, 0)) - ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); -# endif -# else - ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; " - "use #ecdh_curves= instead"); -# endif - } -#endif /* OPENSSL_NO_EC */ - #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH SSL_CTX_set_post_handshake_auth(ctx, 1); #endif @@ -2471,8 +2427,6 @@ ossl_ssl_tmp_key(VALUE self) # endif /* defined(HAVE_SSL_GET_SERVER_TMP_KEY) */ #endif /* !defined(OPENSSL_NO_SOCK) */ -#undef rb_intern -#define rb_intern(s) rb_intern_const(s) void Init_ossl_ssl(void) { @@ -2483,8 +2437,8 @@ 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"); + id_call = rb_intern_const("call"); + ID_callback_state = rb_intern_const("callback_state"); 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) @@ -2551,7 +2505,7 @@ Init_ossl_ssl(void) * 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); + rb_attr(cSSLContext, rb_intern_const("cert"), 1, 1, Qfalse); /* * Context private key @@ -2559,29 +2513,29 @@ Init_ossl_ssl(void) * 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); + rb_attr(cSSLContext, rb_intern_const("key"), 1, 1, Qfalse); /* * A certificate or Array of certificates that will be sent to the client. */ - rb_attr(cSSLContext, rb_intern("client_ca"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("client_ca"), 1, 1, Qfalse); /* * The path to a file containing a PEM-format CA certificate */ - rb_attr(cSSLContext, rb_intern("ca_file"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("ca_file"), 1, 1, Qfalse); /* * The path to a directory containing CA certificates in PEM format. * * Files are looked up by subject's X509 name's hash value. */ - rb_attr(cSSLContext, rb_intern("ca_path"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("ca_path"), 1, 1, Qfalse); /* * Maximum session lifetime in seconds. */ - rb_attr(cSSLContext, rb_intern("timeout"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("timeout"), 1, 1, Qfalse); /* * Session verification mode. @@ -2594,12 +2548,12 @@ Init_ossl_ssl(void) * * See SSL_CTX_set_verify(3) for details. */ - rb_attr(cSSLContext, rb_intern("verify_mode"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("verify_mode"), 1, 1, Qfalse); /* * Number of CA certificates to walk when verifying a certificate chain. */ - rb_attr(cSSLContext, rb_intern("verify_depth"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("verify_depth"), 1, 1, Qfalse); /* * A callback for additional certificate verification. The callback is @@ -2613,7 +2567,7 @@ Init_ossl_ssl(void) * 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); + rb_attr(cSSLContext, rb_intern_const("verify_callback"), 1, 1, Qfalse); /* * Whether to check the server certificate is valid for the hostname. @@ -2621,12 +2575,12 @@ Init_ossl_ssl(void) * In order to make this work, verify_mode must be set to VERIFY_PEER and * the server hostname must be given by OpenSSL::SSL::SSLSocket#hostname=. */ - rb_attr(cSSLContext, rb_intern("verify_hostname"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("verify_hostname"), 1, 1, Qfalse); /* * An OpenSSL::X509::Store used for certificate verification. */ - rb_attr(cSSLContext, rb_intern("cert_store"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("cert_store"), 1, 1, Qfalse); /* * An Array of extra X509 certificates to be added to the certificate @@ -2635,7 +2589,7 @@ Init_ossl_ssl(void) * 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); + rb_attr(cSSLContext, rb_intern_const("extra_chain_cert"), 1, 1, Qfalse); /* * A callback invoked when a client certificate is requested by a server @@ -2645,28 +2599,14 @@ Init_ossl_ssl(void) * containing an OpenSSL::X509::Certificate and an OpenSSL::PKey. If any * other value is returned the handshake is suspended. */ - rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse); - -#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) - /* - * A callback invoked when ECDH parameters are required. - * - * The callback is invoked with the Session for the key exchange, an - * flag indicating the use of an export cipher and the keylength - * required. - * - * The callback is deprecated. This does not work with recent versions of - * OpenSSL. Use OpenSSL::SSL::SSLContext#ecdh_curves= instead. - */ - rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse); -#endif + rb_attr(cSSLContext, rb_intern_const("client_cert_cb"), 1, 1, Qfalse); /* * Sets the context in which a session can be reused. This allows * sessions for multiple applications to be distinguished, for example, by * name. */ - rb_attr(cSSLContext, rb_intern("session_id_context"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("session_id_context"), 1, 1, Qfalse); /* * A callback invoked on a server when a session is proposed by the client @@ -2675,7 +2615,7 @@ Init_ossl_ssl(void) * The callback is invoked with the SSLSocket and session id. The * callback may return a Session from an external cache. */ - rb_attr(cSSLContext, rb_intern("session_get_cb"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("session_get_cb"), 1, 1, Qfalse); /* * A callback invoked when a new session was negotiated. @@ -2683,7 +2623,7 @@ Init_ossl_ssl(void) * 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); + rb_attr(cSSLContext, rb_intern_const("session_new_cb"), 1, 1, Qfalse); /* * A callback invoked when a session is removed from the internal cache. @@ -2694,7 +2634,7 @@ Init_ossl_ssl(void) * multi-threaded application. The callback is called inside a global lock * and it can randomly cause deadlock on Ruby thread switching. */ - rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("session_remove_cb"), 1, 1, Qfalse); rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue); @@ -2717,7 +2657,7 @@ Init_ossl_ssl(void) * raise RuntimeError, "Client renegotiation disabled" * end */ - rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("renegotiation_cb"), 1, 1, Qfalse); #ifndef OPENSSL_NO_NEXTPROTONEG /* * An Enumerable of Strings. Each String represents a protocol to be @@ -2730,7 +2670,7 @@ Init_ossl_ssl(void) * * ctx.npn_protocols = ["http/1.1", "spdy/2"] */ - rb_attr(cSSLContext, rb_intern("npn_protocols"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("npn_protocols"), 1, 1, Qfalse); /* * A callback invoked on the client side when the client needs to select * a protocol from the list sent by the server. Supported in OpenSSL 1.0.1 @@ -2747,7 +2687,7 @@ Init_ossl_ssl(void) * protocols.first * end */ - rb_attr(cSSLContext, rb_intern("npn_select_cb"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("npn_select_cb"), 1, 1, Qfalse); #endif #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB @@ -2762,7 +2702,7 @@ Init_ossl_ssl(void) * * ctx.alpn_protocols = ["http/1.1", "spdy/2", "h2"] */ - rb_attr(cSSLContext, rb_intern("alpn_protocols"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("alpn_protocols"), 1, 1, Qfalse); /* * A callback invoked on the server side when the server needs to select * a protocol from the list sent by the client. Supported in OpenSSL 1.0.2 @@ -2779,7 +2719,7 @@ Init_ossl_ssl(void) * protocols.first * end */ - rb_attr(cSSLContext, rb_intern("alpn_select_cb"), 1, 1, Qfalse); + rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse); #endif rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); @@ -3007,16 +2947,15 @@ Init_ossl_ssl(void) #endif - sym_exception = ID2SYM(rb_intern("exception")); - sym_wait_readable = ID2SYM(rb_intern("wait_readable")); - sym_wait_writable = ID2SYM(rb_intern("wait_writable")); + sym_exception = ID2SYM(rb_intern_const("exception")); + sym_wait_readable = ID2SYM(rb_intern_const("wait_readable")); + sym_wait_writable = ID2SYM(rb_intern_const("wait_writable")); - id_tmp_dh_callback = rb_intern("tmp_dh_callback"); - id_tmp_ecdh_callback = rb_intern("tmp_ecdh_callback"); - id_npn_protocols_encoded = rb_intern("npn_protocols_encoded"); + id_tmp_dh_callback = rb_intern_const("tmp_dh_callback"); + id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded"); #define DefIVarID(name) do \ - id_i_##name = rb_intern("@"#name); while (0) + id_i_##name = rb_intern_const("@"#name); while (0) DefIVarID(cert_store); DefIVarID(ca_file); @@ -3030,7 +2969,6 @@ Init_ossl_ssl(void) DefIVarID(key); DefIVarID(extra_chain_cert); DefIVarID(client_cert_cb); - DefIVarID(tmp_ecdh_callback); DefIVarID(timeout); DefIVarID(session_id_context); DefIVarID(session_get_cb); diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c index 160ec0d8..9450e435 100644 --- a/ext/openssl/ossl_ts.c +++ b/ext/openssl/ossl_ts.c @@ -68,9 +68,9 @@ static VALUE cTimestampRequest; static VALUE cTimestampResponse; static VALUE cTimestampTokenInfo; static VALUE cTimestampFactory; -static ID sBAD_ALG, sBAD_REQUEST, sBAD_DATA_FORMAT, sTIME_NOT_AVAILABLE; -static ID sUNACCEPTED_POLICY, sUNACCEPTED_EXTENSION, sADD_INFO_NOT_AVAILABLE; -static ID sSYSTEM_FAILURE; +static VALUE sBAD_ALG, sBAD_REQUEST, sBAD_DATA_FORMAT, sTIME_NOT_AVAILABLE; +static VALUE sUNACCEPTED_POLICY, sUNACCEPTED_EXTENSION, sADD_INFO_NOT_AVAILABLE; +static VALUE sSYSTEM_FAILURE; static void ossl_ts_req_free(void *ptr) @@ -1247,24 +1247,24 @@ Init_ossl_ts(void) * timestamp server rejects the message imprint algorithm used in the * +Request+ */ - sBAD_ALG = rb_intern("BAD_ALG"); + sBAD_ALG = ID2SYM(rb_intern_const("BAD_ALG")); /* * Possible return value for +Response#failure_info+. Indicates that the * timestamp server was not able to process the +Request+ properly. */ - sBAD_REQUEST = rb_intern("BAD_REQUEST"); + sBAD_REQUEST = ID2SYM(rb_intern_const("BAD_REQUEST")); /* * Possible return value for +Response#failure_info+. Indicates that the * timestamp server was not able to parse certain data in the +Request+. */ - sBAD_DATA_FORMAT = rb_intern("BAD_DATA_FORMAT"); + sBAD_DATA_FORMAT = ID2SYM(rb_intern_const("BAD_DATA_FORMAT")); - sTIME_NOT_AVAILABLE = rb_intern("TIME_NOT_AVAILABLE"); - sUNACCEPTED_POLICY = rb_intern("UNACCEPTED_POLICY"); - sUNACCEPTED_EXTENSION = rb_intern("UNACCEPTED_EXTENSION"); - sADD_INFO_NOT_AVAILABLE = rb_intern("ADD_INFO_NOT_AVAILABLE"); - sSYSTEM_FAILURE = rb_intern("SYSTEM_FAILURE"); + sTIME_NOT_AVAILABLE = ID2SYM(rb_intern_const("TIME_NOT_AVAILABLE")); + sUNACCEPTED_POLICY = ID2SYM(rb_intern_const("UNACCEPTED_POLICY")); + sUNACCEPTED_EXTENSION = ID2SYM(rb_intern_const("UNACCEPTED_EXTENSION")); + sADD_INFO_NOT_AVAILABLE = ID2SYM(rb_intern_const("ADD_INFO_NOT_AVAILABLE")); + sSYSTEM_FAILURE = ID2SYM(rb_intern_const("SYSTEM_FAILURE")); /* Document-class: OpenSSL::Timestamp * Provides classes and methods to request, create and validate @@ -1280,7 +1280,7 @@ Init_ossl_ts(void) * ===Create a Response: * #Assumes ts.p12 is a PKCS#12-compatible file with a private key * #and a certificate that has an extended key usage of 'timeStamping' - * p12 = OpenSSL::PKCS12.new(File.open('ts.p12', 'rb'), 'pwd') + * p12 = OpenSSL::PKCS12.new(File.binread('ts.p12'), 'pwd') * md = OpenSSL::Digest.new('SHA1') * hash = md.digest(data) #some binary data to be timestamped * req = OpenSSL::Timestamp::Request.new @@ -1295,16 +1295,16 @@ Init_ossl_ts(void) * * ===Verify a timestamp response: * #Assume we have a timestamp token in a file called ts.der - * ts = OpenSSL::Timestamp::Response.new(File.open('ts.der', 'rb') + * ts = OpenSSL::Timestamp::Response.new(File.binread('ts.der')) * #Assume we have the Request for this token in a file called req.der - * req = OpenSSL::Timestamp::Request.new(File.open('req.der', 'rb') + * req = OpenSSL::Timestamp::Request.new(File.binread('req.der')) * # Assume the associated root CA certificate is contained in a * # DER-encoded file named root.cer - * root = OpenSSL::X509::Certificate.new(File.open('root.cer', 'rb') + * root = OpenSSL::X509::Certificate.new(File.binread('root.cer')) * # get the necessary intermediate certificates, available in * # DER-encoded form in inter1.cer and inter2.cer - * inter1 = OpenSSL::X509::Certificate.new(File.open('inter1.cer', 'rb') - * inter2 = OpenSSL::X509::Certificate.new(File.open('inter2.cer', 'rb') + * inter1 = OpenSSL::X509::Certificate.new(File.binread('inter1.cer')) + * inter2 = OpenSSL::X509::Certificate.new(File.binread('inter2.cer')) * ts.verify(req, root, inter1, inter2) -> ts or raises an exception if validation fails * */ @@ -1437,9 +1437,9 @@ Init_ossl_ts(void) * timestamping certificate. * * req = OpenSSL::Timestamp::Request.new(raw_bytes) - * p12 = OpenSSL::PKCS12.new(File.open('ts.p12', 'rb'), 'pwd') - * inter1 = OpenSSL::X509::Certificate.new(File.open('inter1.cer', 'rb') - * inter2 = OpenSSL::X509::Certificate.new(File.open('inter2.cer', 'rb') + * p12 = OpenSSL::PKCS12.new(File.binread('ts.p12'), 'pwd') + * inter1 = OpenSSL::X509::Certificate.new(File.binread('inter1.cer')) + * inter2 = OpenSSL::X509::Certificate.new(File.binread('inter2.cer')) * fac = OpenSSL::Timestamp::Factory.new * fac.gen_time = Time.now * fac.serial_number = 1 @@ -1503,11 +1503,11 @@ Init_ossl_ts(void) * */ cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject); - rb_attr(cTimestampFactory, rb_intern("allowed_digests"), 1, 1, 0); - rb_attr(cTimestampFactory, rb_intern("default_policy_id"), 1, 1, 0); - rb_attr(cTimestampFactory, rb_intern("serial_number"), 1, 1, 0); - rb_attr(cTimestampFactory, rb_intern("gen_time"), 1, 1, 0); - rb_attr(cTimestampFactory, rb_intern("additional_certs"), 1, 1, 0); + rb_attr(cTimestampFactory, rb_intern_const("allowed_digests"), 1, 1, 0); + rb_attr(cTimestampFactory, rb_intern_const("default_policy_id"), 1, 1, 0); + rb_attr(cTimestampFactory, rb_intern_const("serial_number"), 1, 1, 0); + rb_attr(cTimestampFactory, rb_intern_const("gen_time"), 1, 1, 0); + rb_attr(cTimestampFactory, rb_intern_const("additional_certs"), 1, 1, 0); rb_define_method(cTimestampFactory, "create_timestamp", ossl_tsfac_create_ts, 3); } diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index e3766b1b..5376bff0 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -730,7 +730,7 @@ Init_ossl_x509cert(void) * Certificate is capable of handling DER-encoded certificates and * certificates encoded in OpenSSL's PEM format. * - * raw = File.read "cert.cer" # DER- or PEM-encoded + * raw = File.binread "cert.cer" # DER- or PEM-encoded * certificate = OpenSSL::X509::Certificate.new raw * * === Saving a certificate to a file diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c index 5eb9bd75..e54102c7 100644 --- a/ext/openssl/ossl_x509ext.c +++ b/ext/openssl/ossl_x509ext.c @@ -226,11 +226,10 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self) GetX509ExtFactory(self, ctx); obj = NewX509Ext(cX509Ext); rconf = rb_iv_get(self, "@config"); - conf = NIL_P(rconf) ? NULL : DupConfigPtr(rconf); + conf = NIL_P(rconf) ? NULL : GetConfig(rconf); X509V3_set_nconf(ctx, conf); ext = X509V3_EXT_nconf_nid(conf, ctx, nid, RSTRING_PTR(valstr)); X509V3_set_ctx_nodb(ctx); - NCONF_free(conf); if (!ext){ ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr); } diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c index 6c5f1c79..5e0ab8d8 100644 --- a/ext/openssl/ossl_x509store.c +++ b/ext/openssl/ossl_x509store.c @@ -157,9 +157,8 @@ ossl_x509store_alloc(VALUE klass) VALUE obj; obj = NewX509Store(klass); - if((store = X509_STORE_new()) == NULL){ - ossl_raise(eX509StoreError, NULL); - } + if ((store = X509_STORE_new()) == NULL) + ossl_raise(eX509StoreError, "X509_STORE_new"); SetX509Store(obj, store); return obj; @@ -192,8 +191,9 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) { X509_STORE *store; -/* BUG: This method takes any number of arguments but appears to ignore them. */ GetX509Store(self, store); + if (argc != 0) + rb_warn("OpenSSL::X509::Store.new does not take any arguments"); #if !defined(HAVE_OPAQUE_OPENSSL) /* [Bug #405] [Bug #1678] [Bug #3000]; already fixed? */ store->ex_data.sk = NULL; @@ -214,8 +214,16 @@ ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) * call-seq: * store.flags = flags * - * Sets _flags_ to the Store. _flags_ consists of zero or more of the constants - * defined in with name V_FLAG_* or'ed together. + * Sets the default flags used by certificate chain verification performed with + * the Store. + * + * _flags_ consists of zero or more of the constants defined in OpenSSL::X509 + * with name V_FLAG_* or'ed together. + * + * OpenSSL::X509::StoreContext#flags= can be used to change the flags for a + * single verification operation. + * + * See also the man page X509_VERIFY_PARAM_set_flags(3). */ static VALUE ossl_x509store_set_flags(VALUE self, VALUE flags) @@ -233,9 +241,9 @@ 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 - * the store will check every untrusted certificate's extensions are consistent - * with the purpose. The purpose is specified by constants: + * Sets the store's default verification purpose. If specified, + * the verifications on the store will check every certificate's extensions are + * consistent with the purpose. The purpose is specified by constants: * * * X509::PURPOSE_SSL_CLIENT * * X509::PURPOSE_SSL_SERVER @@ -246,6 +254,11 @@ ossl_x509store_set_flags(VALUE self, VALUE flags) * * X509::PURPOSE_ANY * * X509::PURPOSE_OCSP_HELPER * * X509::PURPOSE_TIMESTAMP_SIGN + * + * OpenSSL::X509::StoreContext#purpose= can be used to change the value for a + * single verification operation. + * + * See also the man page X509_VERIFY_PARAM_set_purpose(3). */ static VALUE ossl_x509store_set_purpose(VALUE self, VALUE purpose) @@ -262,6 +275,14 @@ ossl_x509store_set_purpose(VALUE self, VALUE purpose) /* * call-seq: * store.trust = trust + * + * Sets the default trust settings used by the certificate verification with + * the store. + * + * OpenSSL::X509::StoreContext#trust= can be used to change the value for a + * single verification operation. + * + * See also the man page X509_VERIFY_PARAM_set_trust(3). */ static VALUE ossl_x509store_set_trust(VALUE self, VALUE trust) @@ -279,7 +300,13 @@ ossl_x509store_set_trust(VALUE self, VALUE trust) * call-seq: * store.time = time * - * Sets the time to be used in verifications. + * Sets the time to be used in the certificate verifications with the store. + * By default, if not specified, the current system time is used. + * + * OpenSSL::X509::StoreContext#time= can be used to change the value for a + * single verification operation. + * + * See also the man page X509_VERIFY_PARAM_set_time(3). */ static VALUE ossl_x509store_set_time(VALUE self, VALUE time) @@ -295,23 +322,23 @@ ossl_x509store_set_time(VALUE self, VALUE time) * 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. + * + * See also the man page X509_LOOKUP_file(3). */ static VALUE ossl_x509store_add_file(VALUE self, VALUE file) { X509_STORE *store; X509_LOOKUP *lookup; - char *path = NULL; + const char *path; - if(file != Qnil){ - path = StringValueCStr(file); - } GetX509Store(self, store); + path = StringValueCStr(file); lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); - if(lookup == NULL) ossl_raise(eX509StoreError, NULL); - if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){ - ossl_raise(eX509StoreError, NULL); - } + if (!lookup) + ossl_raise(eX509StoreError, "X509_STORE_add_lookup"); + if (X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1) + ossl_raise(eX509StoreError, "X509_LOOKUP_load_file"); #if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER) /* * X509_load_cert_crl_file() which is called from X509_LOOKUP_load_file() @@ -330,23 +357,23 @@ ossl_x509store_add_file(VALUE self, VALUE file) * store.add_path(path) -> self * * Adds _path_ as the hash dir to be looked up by the store. + * + * See also the man page X509_LOOKUP_hash_dir(3). */ static VALUE ossl_x509store_add_path(VALUE self, VALUE dir) { X509_STORE *store; X509_LOOKUP *lookup; - char *path = NULL; + const char *path; - if(dir != Qnil){ - path = StringValueCStr(dir); - } GetX509Store(self, store); + path = StringValueCStr(dir); lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); - if(lookup == NULL) ossl_raise(eX509StoreError, NULL); - if(X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1){ - ossl_raise(eX509StoreError, NULL); - } + if (!lookup) + ossl_raise(eX509StoreError, "X509_STORE_add_lookup"); + if (X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1) + ossl_raise(eX509StoreError, "X509_LOOKUP_add_dir"); return self; } @@ -361,6 +388,8 @@ ossl_x509store_add_path(VALUE self, VALUE dir) * * * OpenSSL::X509::DEFAULT_CERT_FILE * * OpenSSL::X509::DEFAULT_CERT_DIR + * + * See also the man page X509_STORE_set_default_paths(3). */ static VALUE ossl_x509store_set_default_paths(VALUE self) @@ -368,18 +397,19 @@ ossl_x509store_set_default_paths(VALUE self) X509_STORE *store; GetX509Store(self, store); - if (X509_STORE_set_default_paths(store) != 1){ - ossl_raise(eX509StoreError, NULL); - } + if (X509_STORE_set_default_paths(store) != 1) + ossl_raise(eX509StoreError, "X509_STORE_set_default_paths"); return Qnil; } /* * call-seq: - * store.add_cert(cert) + * store.add_cert(cert) -> self * * Adds the OpenSSL::X509::Certificate _cert_ to the certificate store. + * + * See also the man page X509_STORE_add_cert(3). */ static VALUE ossl_x509store_add_cert(VALUE self, VALUE arg) @@ -389,9 +419,8 @@ ossl_x509store_add_cert(VALUE self, VALUE arg) cert = GetX509CertPtr(arg); /* NO NEED TO DUP */ GetX509Store(self, store); - if (X509_STORE_add_cert(store, cert) != 1){ - ossl_raise(eX509StoreError, NULL); - } + if (X509_STORE_add_cert(store, cert) != 1) + ossl_raise(eX509StoreError, "X509_STORE_add_cert"); return self; } @@ -401,6 +430,8 @@ ossl_x509store_add_cert(VALUE self, VALUE arg) * store.add_crl(crl) -> self * * Adds the OpenSSL::X509::CRL _crl_ to the store. + * + * See also the man page X509_STORE_add_crl(3). */ static VALUE ossl_x509store_add_crl(VALUE self, VALUE arg) @@ -410,9 +441,8 @@ ossl_x509store_add_crl(VALUE self, VALUE arg) crl = GetX509CRLPtr(arg); /* NO NEED TO DUP */ GetX509Store(self, store); - if (X509_STORE_add_crl(store, crl) != 1){ - ossl_raise(eX509StoreError, NULL); - } + if (X509_STORE_add_crl(store, crl) != 1) + ossl_raise(eX509StoreError, "X509_STORE_add_crl"); return self; } @@ -491,9 +521,8 @@ ossl_x509stctx_alloc(VALUE klass) VALUE obj; obj = NewX509StCtx(klass); - if((ctx = X509_STORE_CTX_new()) == NULL){ - ossl_raise(eX509StoreError, NULL); - } + if ((ctx = X509_STORE_CTX_new()) == NULL) + ossl_raise(eX509StoreError, "X509_STORE_CTX_new"); SetX509StCtx(obj, ctx); return obj; @@ -559,6 +588,10 @@ ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) /* * call-seq: * stctx.verify -> true | false + * + * Performs the certificate verification using the parameters set to _stctx_. + * + * See also the man page X509_verify_cert(3). */ static VALUE ossl_x509stctx_verify(VALUE self) @@ -571,48 +604,45 @@ ossl_x509stctx_verify(VALUE self) switch (X509_verify_cert(ctx)) { case 1: - return Qtrue; + return Qtrue; case 0: - ossl_clear_error(); - return Qfalse; + ossl_clear_error(); + return Qfalse; default: - ossl_raise(eX509CertError, NULL); + ossl_raise(eX509CertError, "X509_verify_cert"); } } /* * call-seq: - * stctx.chain -> Array of X509::Certificate + * stctx.chain -> nil | Array of X509::Certificate + * + * Returns the verified chain. + * + * See also the man page X509_STORE_CTX_set0_verified_chain(3). */ static VALUE ossl_x509stctx_get_chain(VALUE self) { X509_STORE_CTX *ctx; - STACK_OF(X509) *chain; - X509 *x509; - int i, num; - VALUE ary; + const STACK_OF(X509) *chain; GetX509StCtx(self, ctx); - if((chain = X509_STORE_CTX_get0_chain(ctx)) == NULL){ - return Qnil; - } - if((num = sk_X509_num(chain)) < 0){ - OSSL_Debug("certs in chain < 0???"); - return rb_ary_new(); - } - ary = rb_ary_new2(num); - for(i = 0; i < num; i++) { - x509 = sk_X509_value(chain, i); - rb_ary_push(ary, ossl_x509_new(x509)); - } - - return ary; + chain = X509_STORE_CTX_get0_chain(ctx); + if (!chain) + return Qnil; /* Could be an empty array instead? */ + return ossl_x509_sk2ary(chain); } /* * call-seq: * stctx.error -> Integer + * + * Returns the error code of _stctx_. This is typically called after #verify + * is done, or from the verification callback set to + * OpenSSL::X509::Store#verify_callback=. + * + * See also the man page X509_STORE_CTX_get_error(3). */ static VALUE ossl_x509stctx_get_err(VALUE self) @@ -627,6 +657,11 @@ ossl_x509stctx_get_err(VALUE self) /* * call-seq: * stctx.error = error_code + * + * Sets the error code of _stctx_. This is used by the verification callback + * set to OpenSSL::X509::Store#verify_callback=. + * + * See also the man page X509_STORE_CTX_set_error(3). */ static VALUE ossl_x509stctx_set_error(VALUE self, VALUE err) @@ -643,7 +678,10 @@ ossl_x509stctx_set_error(VALUE self, VALUE err) * call-seq: * stctx.error_string -> String * - * Returns the error string corresponding to the error code retrieved by #error. + * Returns the human readable error string corresponding to the error code + * retrieved by #error. + * + * See also the man page X509_verify_cert_error_string(3). */ static VALUE ossl_x509stctx_get_err_string(VALUE self) @@ -660,6 +698,10 @@ ossl_x509stctx_get_err_string(VALUE self) /* * call-seq: * stctx.error_depth -> Integer + * + * Returns the depth of the chain. This is used in combination with #error. + * + * See also the man page X509_STORE_CTX_get_error_depth(3). */ static VALUE ossl_x509stctx_get_err_depth(VALUE self) @@ -674,6 +716,10 @@ ossl_x509stctx_get_err_depth(VALUE self) /* * call-seq: * stctx.current_cert -> X509::Certificate + * + * Returns the certificate which caused the error. + * + * See also the man page X509_STORE_CTX_get_current_cert(3). */ static VALUE ossl_x509stctx_get_curr_cert(VALUE self) @@ -688,6 +734,10 @@ ossl_x509stctx_get_curr_cert(VALUE self) /* * call-seq: * stctx.current_crl -> X509::CRL + * + * Returns the CRL which caused the error. + * + * See also the man page X509_STORE_CTX_get_current_crl(3). */ static VALUE ossl_x509stctx_get_curr_crl(VALUE self) @@ -707,7 +757,10 @@ ossl_x509stctx_get_curr_crl(VALUE self) * call-seq: * stctx.flags = flags * - * Sets the verification flags to the context. See Store#flags=. + * Sets the verification flags to the context. This overrides the default value + * set by Store#flags=. + * + * See also the man page X509_VERIFY_PARAM_set_flags(3). */ static VALUE ossl_x509stctx_set_flags(VALUE self, VALUE flags) @@ -725,7 +778,10 @@ ossl_x509stctx_set_flags(VALUE self, VALUE flags) * call-seq: * stctx.purpose = purpose * - * Sets the purpose of the context. See Store#purpose=. + * Sets the purpose of the context. This overrides the default value set by + * Store#purpose=. + * + * See also the man page X509_VERIFY_PARAM_set_purpose(3). */ static VALUE ossl_x509stctx_set_purpose(VALUE self, VALUE purpose) @@ -742,6 +798,11 @@ ossl_x509stctx_set_purpose(VALUE self, VALUE purpose) /* * call-seq: * stctx.trust = trust + * + * Sets the trust settings of the context. This overrides the default value set + * by Store#trust=. + * + * See also the man page X509_VERIFY_PARAM_set_trust(3). */ static VALUE ossl_x509stctx_set_trust(VALUE self, VALUE trust) @@ -760,6 +821,8 @@ ossl_x509stctx_set_trust(VALUE self, VALUE trust) * stctx.time = time * * Sets the time used in the verification. If not set, the current time is used. + * + * See also the man page X509_VERIFY_PARAM_set_time(3). */ static VALUE ossl_x509stctx_set_time(VALUE self, VALUE time) @@ -835,23 +898,37 @@ Init_ossl_x509store(void) cX509Store = rb_define_class_under(mX509, "Store", rb_cObject); /* * The callback for additional certificate verification. It is invoked for - * each untrusted certificate in the chain. + * each certificate in the chain and can be used to implement custom + * certificate verification conditions. * * The callback is invoked with two values, a boolean that indicates if the * pre-verification by OpenSSL has succeeded or not, and the StoreContext in - * use. The callback must return either true or false. + * use. + * + * The callback can use StoreContext#error= to change the error code as + * needed. The callback must return either true or false. + * + * NOTE: any exception raised within the callback will be ignored. + * + * See also the man page X509_STORE_CTX_set_verify_cb(3). */ rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse); /* * The error code set by the last call of #verify. + * + * See also StoreContext#error. */ rb_attr(cX509Store, rb_intern("error"), 1, 0, Qfalse); /* * The description for the error code set by the last call of #verify. + * + * See also StoreContext#error_string. */ rb_attr(cX509Store, rb_intern("error_string"), 1, 0, Qfalse); /* * The certificate chain constructed by the last call of #verify. + * + * See also StoreContext#chain. */ rb_attr(cX509Store, rb_intern("chain"), 1, 0, Qfalse); rb_define_alloc_func(cX509Store, ossl_x509store_alloc); |