[ruby/openssl] pkey: add PKey::PKey#derive
Add OpenSSL::PKey::PKey#derive as the wrapper for EVP_PKEY_CTX_derive(). This is useful for pkey types that we don't have dedicated classes, such as X25519. https://github.com/ruby/openssl/commit/28f0059bea
This commit is contained in:
parent
b2dc4880f5
commit
fbadb01d6e
Notes:
git
2021-03-16 20:38:51 +09:00
@ -886,6 +886,57 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* INIT
|
||||
*/
|
||||
@ -983,6 +1034,7 @@ Init_ossl_pkey(void)
|
||||
|
||||
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?");
|
||||
|
||||
|
@ -125,4 +125,30 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
|
||||
# Ed25519 pkey type does not support key derivation
|
||||
assert_raise(OpenSSL::PKey::PKeyError) { priv.derive(pub) }
|
||||
end
|
||||
|
||||
def test_x25519
|
||||
# Test vector from RFC 7748 Section 6.1
|
||||
alice_pem = <<~EOF
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MC4CAQAwBQYDK2VuBCIEIHcHbQpzGKV9PBbBclGyZkXfTC+H68CZKrF3+6UduSwq
|
||||
-----END PRIVATE KEY-----
|
||||
EOF
|
||||
bob_pem = <<~EOF
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MCowBQYDK2VuAyEA3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08=
|
||||
-----END PUBLIC KEY-----
|
||||
EOF
|
||||
shared_secret = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"
|
||||
begin
|
||||
alice = OpenSSL::PKey.read(alice_pem)
|
||||
bob = OpenSSL::PKey.read(bob_pem)
|
||||
rescue OpenSSL::PKey::PKeyError
|
||||
# OpenSSL < 1.1.0
|
||||
pend "X25519 is not implemented"
|
||||
end
|
||||
assert_instance_of OpenSSL::PKey::PKey, alice
|
||||
assert_equal alice_pem, alice.private_to_pem
|
||||
assert_equal bob_pem, bob.public_to_pem
|
||||
assert_equal [shared_secret].pack("H*"), alice.derive(bob)
|
||||
end
|
||||
end
|
||||
|
@ -18,6 +18,19 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
|
||||
end
|
||||
end
|
||||
|
||||
def test_derive_key
|
||||
dh1 = Fixtures.pkey("dh1024").generate_key!
|
||||
dh2 = Fixtures.pkey("dh1024").generate_key!
|
||||
dh1_pub = OpenSSL::PKey.read(dh1.public_to_der)
|
||||
dh2_pub = OpenSSL::PKey.read(dh2.public_to_der)
|
||||
z = dh1.g.mod_exp(dh1.priv_key, dh1.p).mod_exp(dh2.priv_key, dh1.p).to_s(2)
|
||||
assert_equal z, dh1.derive(dh2_pub)
|
||||
assert_equal z, dh2.derive(dh1_pub)
|
||||
|
||||
assert_equal z, dh1.compute_key(dh2.pub_key)
|
||||
assert_equal z, dh2.compute_key(dh1.pub_key)
|
||||
end
|
||||
|
||||
def test_DHparams
|
||||
dh1024 = Fixtures.pkey("dh1024")
|
||||
asn1 = OpenSSL::ASN1::Sequence([
|
||||
|
@ -93,6 +93,22 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
|
||||
assert_equal false, p256.verify("SHA256", signature1, data)
|
||||
end
|
||||
|
||||
def test_derive_key
|
||||
# NIST CAVP, KAS_ECC_CDH_PrimitiveTest.txt, P-256 COUNT = 0
|
||||
qCAVSx = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
|
||||
qCAVSy = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac"
|
||||
dIUT = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534"
|
||||
zIUT = "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
|
||||
a = OpenSSL::PKey::EC.new("prime256v1")
|
||||
a.private_key = OpenSSL::BN.new(dIUT, 16)
|
||||
b = OpenSSL::PKey::EC.new("prime256v1")
|
||||
uncompressed = OpenSSL::BN.new("04" + qCAVSx + qCAVSy, 16)
|
||||
b.public_key = OpenSSL::PKey::EC::Point.new(b.group, uncompressed)
|
||||
assert_equal [zIUT].pack("H*"), a.derive(b)
|
||||
|
||||
assert_equal a.derive(b), a.dh_compute_key(b.public_key)
|
||||
end
|
||||
|
||||
def test_dsa_sign_verify
|
||||
data1 = "foo"
|
||||
data2 = "bar"
|
||||
|
Loading…
x
Reference in New Issue
Block a user