[前][次][番号順一覧][スレッド一覧]

ruby-changes:65582

From: Kazuki <ko1@a...>
Date: Tue, 16 Mar 2021 20:38:46 +0900 (JST)
Subject: [ruby-changes:65582] fbadb01d6e (master): [ruby/openssl] pkey: add PKey::PKey#derive

https://git.ruby-lang.org/ruby.git/commit/?id=fbadb01d6e

From fbadb01d6e0881ef6c6e5e105b8ac20fe663c817 Mon Sep 17 00:00:00 2001
From: Kazuki Yamaguchi <k@r...>
Date: Sat, 18 Mar 2017 21:58:46 +0900
Subject: [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
---
 ext/openssl/ossl_pkey.c      | 52 ++++++++++++++++++++++++++++++++++++++++++++
 test/openssl/test_pkey.rb    | 26 ++++++++++++++++++++++
 test/openssl/test_pkey_dh.rb | 13 +++++++++++
 test/openssl/test_pkey_ec.rb | 16 ++++++++++++++
 4 files changed, 107 insertions(+)

diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 19544ec..df8b425 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -887,6 +887,57 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_pkey.c#L887
 }
 
 /*
+ * 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
  */
 void
@@ -983,6 +1034,7 @@ Init_ossl_pkey(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_pkey.c#L1034
 
     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/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb
index d811b9c..5307fe5 100644
--- a/test/openssl/test_pkey.rb
+++ b/test/openssl/test_pkey.rb
@@ -125,4 +125,30 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase https://github.com/ruby/ruby/blob/trunk/test/openssl/test_pkey.rb#L125
     # 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
diff --git a/test/openssl/test_pkey_dh.rb b/test/openssl/test_pkey_dh.rb
index 4a05626..9efc3ba 100644
--- a/test/openssl/test_pkey_dh.rb
+++ b/test/openssl/test_pkey_dh.rb
@@ -18,6 +18,19 @@ class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase https://github.com/ruby/ruby/blob/trunk/test/openssl/test_pkey_dh.rb#L18
     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([
diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb
index a0e6a23..95d4338 100644
--- a/test/openssl/test_pkey_ec.rb
+++ b/test/openssl/test_pkey_ec.rb
@@ -93,6 +93,22 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase https://github.com/ruby/ruby/blob/trunk/test/openssl/test_pkey_ec.rb#L93
     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"
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]