aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2020-05-17 22:14:03 +0900
committerKazuki Yamaguchi <k@rhe.jp>2021-04-05 00:41:42 +0900
commit1800a8d5ebafa47f14a604a3f12a3a6397b0b4ad (patch)
tree48629bd91007182e24195baf06f6e1856e5f6e06
parent363fd107134fd2dbdf405fedec50dad13411057d (diff)
downloadruby-openssl-1800a8d5ebafa47f14a604a3f12a3a6397b0b4ad.tar.gz
pkey/dsa: use high level EVP interface to generate parameters and keys
Implement PKey::DSA.new(size) and PKey::DSA.generate using OpenSSL::PKey.generate_parameters and .generate_key instead of the low level DSA functions.
-rw-r--r--ext/openssl/ossl_pkey_dsa.c140
-rw-r--r--lib/openssl/pkey.rb30
-rw-r--r--test/openssl/test_pkey_dsa.rb23
3 files changed, 64 insertions, 129 deletions
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
index 0e68f7f2..1c5a8a73 100644
--- a/ext/openssl/ossl_pkey_dsa.c
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -46,126 +46,39 @@ VALUE eDSAError;
/*
* Private
*/
-struct dsa_blocking_gen_arg {
- DSA *dsa;
- int size;
- int *counter;
- unsigned long *h;
- BN_GENCB *cb;
- int result;
-};
-
-static void *
-dsa_blocking_gen(void *arg)
-{
- struct dsa_blocking_gen_arg *gen = (struct dsa_blocking_gen_arg *)arg;
- gen->result = DSA_generate_parameters_ex(gen->dsa, gen->size, NULL, 0,
- gen->counter, gen->h, gen->cb);
- return 0;
-}
-
-static DSA *
-dsa_generate(int size)
-{
- struct ossl_generate_cb_arg cb_arg = { 0 };
- struct dsa_blocking_gen_arg gen_arg;
- DSA *dsa = DSA_new();
- BN_GENCB *cb = BN_GENCB_new();
- int counter;
- unsigned long h;
-
- if (!dsa || !cb) {
- DSA_free(dsa);
- BN_GENCB_free(cb);
- ossl_raise(eDSAError, "malloc failure");
- }
-
- if (rb_block_given_p())
- cb_arg.yield = 1;
- BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg);
- gen_arg.dsa = dsa;
- gen_arg.size = size;
- gen_arg.counter = &counter;
- gen_arg.h = &h;
- gen_arg.cb = cb;
- if (cb_arg.yield == 1) {
- /* we cannot release GVL when callback proc is supplied */
- dsa_blocking_gen(&gen_arg);
- } else {
- /* there's a chance to unblock */
- rb_thread_call_without_gvl(dsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg);
- }
-
- BN_GENCB_free(cb);
- if (!gen_arg.result) {
- DSA_free(dsa);
- if (cb_arg.state) {
- /* Clear OpenSSL error queue before re-raising. By the way, the
- * documentation of DSA_generate_parameters_ex() says the error code
- * can be obtained by ERR_get_error(), but the default
- * implementation, dsa_builtin_paramgen() doesn't put any error... */
- ossl_clear_error();
- rb_jump_tag(cb_arg.state);
- }
- ossl_raise(eDSAError, "DSA_generate_parameters_ex");
- }
-
- if (!DSA_generate_key(dsa)) {
- DSA_free(dsa);
- ossl_raise(eDSAError, "DSA_generate_key");
- }
-
- return dsa;
-}
-
-/*
- * call-seq:
- * DSA.generate(size) -> dsa
- *
- * Creates a new DSA instance by generating a private/public key pair
- * from scratch.
- *
- * === Parameters
- * * _size_ is an integer representing the desired key size.
- *
- */
-static VALUE
-ossl_dsa_s_generate(VALUE klass, VALUE size)
-{
- EVP_PKEY *pkey;
- DSA *dsa;
- VALUE obj;
-
- 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;
-}
-
/*
* call-seq:
* DSA.new -> dsa
- * DSA.new(size) -> dsa
* DSA.new(string [, pass]) -> dsa
+ * DSA.new(size) -> dsa
*
* Creates a new DSA instance by reading an existing key from _string_.
*
- * === Parameters
- * * _size_ is an integer representing the desired key size.
- * * _string_ contains a DER or PEM encoded key.
- * * _pass_ is a string that contains an optional password.
+ * If called without arguments, creates a new instance with no key components
+ * set. They can be set individually by #set_pqg and #set_key.
*
- * === Examples
- * DSA.new -> dsa
- * DSA.new(1024) -> dsa
- * DSA.new(File.read('dsa.pem')) -> dsa
- * DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa
+ * If called with a String, tries to parse as DER or PEM encoding of a \DSA key.
+ * See also OpenSSL::PKey.read which can parse keys of any kinds.
+ *
+ * If called with a number, generates random parameters and a key pair. This
+ * form works as an alias of DSA.generate.
+ *
+ * +string+::
+ * A String that contains a DER or PEM encoded key.
+ * +pass+::
+ * A String that contains an optional password.
+ * +size+::
+ * See DSA.generate.
*
+ * Examples:
+ * p OpenSSL::PKey::DSA.new(1024)
+ * #=> #<OpenSSL::PKey::DSA:0x000055a8d6025bf0 oid=DSA>
+ *
+ * p OpenSSL::PKey::DSA.new(File.read('dsa.pem'))
+ * #=> #<OpenSSL::PKey::DSA:0x000055555d6b8110 oid=DSA>
+ *
+ * p OpenSSL::PKey::DSA.new(File.read('dsa.pem'), 'mypassword')
+ * #=> #<OpenSSL::PKey::DSA:0x0000556f973c40b8 oid=DSA>
*/
static VALUE
ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
@@ -176,15 +89,13 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
VALUE arg, pass;
GetPKey(self, pkey);
+ /* The DSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
rb_scan_args(argc, argv, "02", &arg, &pass);
if (argc == 0) {
dsa = DSA_new();
if (!dsa)
ossl_raise(eDSAError, "DSA_new");
}
- 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);
@@ -553,7 +464,6 @@ Init_ossl_dsa(void)
*/
cDSA = rb_define_class_under(mPKey, "DSA", cPKey);
- rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1);
rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
rb_define_method(cDSA, "initialize_copy", ossl_dsa_initialize_copy, 1);
diff --git a/lib/openssl/pkey.rb b/lib/openssl/pkey.rb
index 3bef06e3..53ee52f9 100644
--- a/lib/openssl/pkey.rb
+++ b/lib/openssl/pkey.rb
@@ -88,6 +88,36 @@ module OpenSSL::PKey
class DSA
include OpenSSL::Marshal
+
+ class << self
+ # :call-seq:
+ # DSA.generate(size) -> dsa
+ #
+ # Creates a new DSA instance by generating a private/public key pair
+ # from scratch.
+ #
+ # See also OpenSSL::PKey.generate_parameters and
+ # OpenSSL::PKey.generate_key.
+ #
+ # +size+::
+ # The desired key size in bits.
+ def generate(size, &blk)
+ dsaparams = OpenSSL::PKey.generate_parameters("DSA", {
+ "dsa_paramgen_bits" => size,
+ }, &blk)
+ OpenSSL::PKey.generate_key(dsaparams)
+ end
+
+ # Handle DSA.new(size) form here; new(str) and new() forms
+ # are handled by #initialize
+ def new(*args, &blk) # :nodoc:
+ if args[0].is_a?(Integer)
+ generate(*args, &blk)
+ else
+ super
+ end
+ end
+ end
end
if defined?(EC)
diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb
index 4bf8a7b3..85bb6ec0 100644
--- a/test/openssl/test_pkey_dsa.rb
+++ b/test/openssl/test_pkey_dsa.rb
@@ -5,31 +5,26 @@ if defined?(OpenSSL) && defined?(OpenSSL::PKey::DSA)
class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
def test_private
- key = OpenSSL::PKey::DSA.new(256)
- assert(key.private?)
+ key = Fixtures.pkey("dsa1024")
+ assert_equal true, key.private?
key2 = OpenSSL::PKey::DSA.new(key.to_der)
- assert(key2.private?)
+ assert_equal true, key2.private?
key3 = key.public_key
- assert(!key3.private?)
+ assert_equal false, key3.private?
key4 = OpenSSL::PKey::DSA.new(key3.to_der)
- assert(!key4.private?)
+ assert_equal false, key4.private?
end
def test_new
- key = OpenSSL::PKey::DSA.new 256
+ key = OpenSSL::PKey::DSA.new(2048)
pem = key.public_key.to_pem
OpenSSL::PKey::DSA.new pem
- if $0 == __FILE__
- assert_nothing_raised {
- key = OpenSSL::PKey::DSA.new 2048
- }
- end
end
def test_new_break
- assert_nil(OpenSSL::PKey::DSA.new(512) { break })
+ assert_nil(OpenSSL::PKey::DSA.new(2048) { break })
assert_raise(RuntimeError) do
- OpenSSL::PKey::DSA.new(512) { raise }
+ OpenSSL::PKey::DSA.new(2048) { raise }
end
end
@@ -184,7 +179,7 @@ fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ==
end
def test_dup
- key = OpenSSL::PKey::DSA.new(256)
+ key = Fixtures.pkey("dsa1024")
key2 = key.dup
assert_equal key.params, key2.params
key2.set_pqg(key2.p + 1, key2.q, key2.g)