Skip to content

Commit 1b71851

Browse files
committed
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.
1 parent 6e61dc3 commit 1b71851

File tree

3 files changed

+64
-129
lines changed

3 files changed

+64
-129
lines changed

ext/openssl/ossl_pkey_dsa.c

+25-115
Original file line numberDiff line numberDiff line change
@@ -46,126 +46,39 @@ VALUE eDSAError;
4646
/*
4747
* Private
4848
*/
49-
struct dsa_blocking_gen_arg {
50-
DSA *dsa;
51-
int size;
52-
int *counter;
53-
unsigned long *h;
54-
BN_GENCB *cb;
55-
int result;
56-
};
57-
58-
static void *
59-
dsa_blocking_gen(void *arg)
60-
{
61-
struct dsa_blocking_gen_arg *gen = (struct dsa_blocking_gen_arg *)arg;
62-
gen->result = DSA_generate_parameters_ex(gen->dsa, gen->size, NULL, 0,
63-
gen->counter, gen->h, gen->cb);
64-
return 0;
65-
}
66-
67-
static DSA *
68-
dsa_generate(int size)
69-
{
70-
struct ossl_generate_cb_arg cb_arg = { 0 };
71-
struct dsa_blocking_gen_arg gen_arg;
72-
DSA *dsa = DSA_new();
73-
BN_GENCB *cb = BN_GENCB_new();
74-
int counter;
75-
unsigned long h;
76-
77-
if (!dsa || !cb) {
78-
DSA_free(dsa);
79-
BN_GENCB_free(cb);
80-
ossl_raise(eDSAError, "malloc failure");
81-
}
82-
83-
if (rb_block_given_p())
84-
cb_arg.yield = 1;
85-
BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg);
86-
gen_arg.dsa = dsa;
87-
gen_arg.size = size;
88-
gen_arg.counter = &counter;
89-
gen_arg.h = &h;
90-
gen_arg.cb = cb;
91-
if (cb_arg.yield == 1) {
92-
/* we cannot release GVL when callback proc is supplied */
93-
dsa_blocking_gen(&gen_arg);
94-
} else {
95-
/* there's a chance to unblock */
96-
rb_thread_call_without_gvl(dsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg);
97-
}
98-
99-
BN_GENCB_free(cb);
100-
if (!gen_arg.result) {
101-
DSA_free(dsa);
102-
if (cb_arg.state) {
103-
/* Clear OpenSSL error queue before re-raising. By the way, the
104-
* documentation of DSA_generate_parameters_ex() says the error code
105-
* can be obtained by ERR_get_error(), but the default
106-
* implementation, dsa_builtin_paramgen() doesn't put any error... */
107-
ossl_clear_error();
108-
rb_jump_tag(cb_arg.state);
109-
}
110-
ossl_raise(eDSAError, "DSA_generate_parameters_ex");
111-
}
112-
113-
if (!DSA_generate_key(dsa)) {
114-
DSA_free(dsa);
115-
ossl_raise(eDSAError, "DSA_generate_key");
116-
}
117-
118-
return dsa;
119-
}
120-
121-
/*
122-
* call-seq:
123-
* DSA.generate(size) -> dsa
124-
*
125-
* Creates a new DSA instance by generating a private/public key pair
126-
* from scratch.
127-
*
128-
* === Parameters
129-
* * _size_ is an integer representing the desired key size.
130-
*
131-
*/
132-
static VALUE
133-
ossl_dsa_s_generate(VALUE klass, VALUE size)
134-
{
135-
EVP_PKEY *pkey;
136-
DSA *dsa;
137-
VALUE obj;
138-
139-
obj = rb_obj_alloc(klass);
140-
GetPKey(obj, pkey);
141-
142-
dsa = dsa_generate(NUM2INT(size));
143-
if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
144-
DSA_free(dsa);
145-
ossl_raise(eDSAError, "EVP_PKEY_assign_DSA");
146-
}
147-
return obj;
148-
}
149-
15049
/*
15150
* call-seq:
15251
* DSA.new -> dsa
153-
* DSA.new(size) -> dsa
15452
* DSA.new(string [, pass]) -> dsa
53+
* DSA.new(size) -> dsa
15554
*
15655
* Creates a new DSA instance by reading an existing key from _string_.
15756
*
158-
* === Parameters
159-
* * _size_ is an integer representing the desired key size.
160-
* * _string_ contains a DER or PEM encoded key.
161-
* * _pass_ is a string that contains an optional password.
57+
* If called without arguments, creates a new instance with no key components
58+
* set. They can be set individually by #set_pqg and #set_key.
16259
*
163-
* === Examples
164-
* DSA.new -> dsa
165-
* DSA.new(1024) -> dsa
166-
* DSA.new(File.read('dsa.pem')) -> dsa
167-
* DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa
60+
* If called with a String, tries to parse as DER or PEM encoding of a \DSA key.
61+
* See also OpenSSL::PKey.read which can parse keys of any kinds.
62+
*
63+
* If called with a number, generates random parameters and a key pair. This
64+
* form works as an alias of DSA.generate.
65+
*
66+
* +string+::
67+
* A String that contains a DER or PEM encoded key.
68+
* +pass+::
69+
* A String that contains an optional password.
70+
* +size+::
71+
* See DSA.generate.
16872
*
73+
* Examples:
74+
* p OpenSSL::PKey::DSA.new(1024)
75+
* #=> #<OpenSSL::PKey::DSA:0x000055a8d6025bf0 oid=DSA>
76+
*
77+
* p OpenSSL::PKey::DSA.new(File.read('dsa.pem'))
78+
* #=> #<OpenSSL::PKey::DSA:0x000055555d6b8110 oid=DSA>
79+
*
80+
* p OpenSSL::PKey::DSA.new(File.read('dsa.pem'), 'mypassword')
81+
* #=> #<OpenSSL::PKey::DSA:0x0000556f973c40b8 oid=DSA>
16982
*/
17083
static VALUE
17184
ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
@@ -176,15 +89,13 @@ ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
17689
VALUE arg, pass;
17790

17891
GetPKey(self, pkey);
92+
/* The DSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
17993
rb_scan_args(argc, argv, "02", &arg, &pass);
18094
if (argc == 0) {
18195
dsa = DSA_new();
18296
if (!dsa)
18397
ossl_raise(eDSAError, "DSA_new");
18498
}
185-
else if (argc == 1 && RB_INTEGER_TYPE_P(arg)) {
186-
dsa = dsa_generate(NUM2INT(arg));
187-
}
18899
else {
189100
pass = ossl_pem_passwd_value(pass);
190101
arg = ossl_to_der_if_possible(arg);
@@ -553,7 +464,6 @@ Init_ossl_dsa(void)
553464
*/
554465
cDSA = rb_define_class_under(mPKey, "DSA", cPKey);
555466

556-
rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1);
557467
rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
558468
rb_define_method(cDSA, "initialize_copy", ossl_dsa_initialize_copy, 1);
559469

lib/openssl/pkey.rb

+30
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,36 @@ def new(*args, &blk) # :nodoc:
8888

8989
class DSA
9090
include OpenSSL::Marshal
91+
92+
class << self
93+
# :call-seq:
94+
# DSA.generate(size) -> dsa
95+
#
96+
# Creates a new DSA instance by generating a private/public key pair
97+
# from scratch.
98+
#
99+
# See also OpenSSL::PKey.generate_parameters and
100+
# OpenSSL::PKey.generate_key.
101+
#
102+
# +size+::
103+
# The desired key size in bits.
104+
def generate(size, &blk)
105+
dsaparams = OpenSSL::PKey.generate_parameters("DSA", {
106+
"dsa_paramgen_bits" => size,
107+
}, &blk)
108+
OpenSSL::PKey.generate_key(dsaparams)
109+
end
110+
111+
# Handle DSA.new(size) form here; new(str) and new() forms
112+
# are handled by #initialize
113+
def new(*args, &blk) # :nodoc:
114+
if args[0].is_a?(Integer)
115+
generate(*args, &blk)
116+
else
117+
super
118+
end
119+
end
120+
end
91121
end
92122

93123
if defined?(EC)

test/openssl/test_pkey_dsa.rb

+9-14
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,26 @@
55

66
class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
77
def test_private
8-
key = OpenSSL::PKey::DSA.new(256)
9-
assert(key.private?)
8+
key = Fixtures.pkey("dsa1024")
9+
assert_equal true, key.private?
1010
key2 = OpenSSL::PKey::DSA.new(key.to_der)
11-
assert(key2.private?)
11+
assert_equal true, key2.private?
1212
key3 = key.public_key
13-
assert(!key3.private?)
13+
assert_equal false, key3.private?
1414
key4 = OpenSSL::PKey::DSA.new(key3.to_der)
15-
assert(!key4.private?)
15+
assert_equal false, key4.private?
1616
end
1717

1818
def test_new
19-
key = OpenSSL::PKey::DSA.new 256
19+
key = OpenSSL::PKey::DSA.new(2048)
2020
pem = key.public_key.to_pem
2121
OpenSSL::PKey::DSA.new pem
22-
if $0 == __FILE__
23-
assert_nothing_raised {
24-
key = OpenSSL::PKey::DSA.new 2048
25-
}
26-
end
2722
end
2823

2924
def test_new_break
30-
assert_nil(OpenSSL::PKey::DSA.new(512) { break })
25+
assert_nil(OpenSSL::PKey::DSA.new(2048) { break })
3126
assert_raise(RuntimeError) do
32-
OpenSSL::PKey::DSA.new(512) { raise }
27+
OpenSSL::PKey::DSA.new(2048) { raise }
3328
end
3429
end
3530

@@ -184,7 +179,7 @@ def test_read_DSAPublicKey_pem
184179
end
185180

186181
def test_dup
187-
key = OpenSSL::PKey::DSA.new(256)
182+
key = Fixtures.pkey("dsa1024")
188183
key2 = key.dup
189184
assert_equal key.params, key2.params
190185
key2.set_pqg(key2.p + 1, key2.q, key2.g)

0 commit comments

Comments
 (0)