6daba0
From 3e6cd88c621d8834712d2279f925b9af83c2bb34 Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Mon, 18 May 2020 20:06:16 +0900
6daba0
Subject: [PATCH 1/6] pkey: implement PKey#encrypt and #decrypt
6daba0
6daba0
Support public key encryption and decryption operations using the EVP
6daba0
API.
6daba0
---
6daba0
 ext/openssl/ossl_pkey.c       | 141 ++++++++++++++++++++++++++++++++++
6daba0
 test/openssl/test_pkey_rsa.rb |  34 ++++++++
6daba0
 2 files changed, 175 insertions(+)
6daba0
6daba0
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
6daba0
index 21cd4b2cda..baf8ce9f20 100644
6daba0
--- a/ext/openssl/ossl_pkey.c
6daba0
+++ b/ext/openssl/ossl_pkey.c
6daba0
@@ -986,6 +986,145 @@ ossl_pkey_derive(int argc, VALUE *argv, VALUE self)
6daba0
     return str;
6daba0
 }
6daba0
 
6daba0
+/*
6daba0
+ * call-seq:
6daba0
+ *    pkey.encrypt(data [, options]) -> string
6daba0
+ *
6daba0
+ * Performs a public key encryption operation using +pkey+.
6daba0
+ *
6daba0
+ * See #decrypt for the reverse operation.
6daba0
+ *
6daba0
+ * Added in version 3.0. See also the man page EVP_PKEY_encrypt(3).
6daba0
+ *
6daba0
+ * +data+::
6daba0
+ *   A String to be encrypted.
6daba0
+ * +options+::
6daba0
+ *   A Hash that contains algorithm specific control operations to \OpenSSL.
6daba0
+ *   See OpenSSL's man page EVP_PKEY_CTX_ctrl_str(3) for details.
6daba0
+ *
6daba0
+ * Example:
6daba0
+ *   pkey = OpenSSL::PKey.generate_key("RSA", rsa_keygen_bits: 2048)
6daba0
+ *   data = "secret data"
6daba0
+ *   encrypted = pkey.encrypt(data, rsa_padding_mode: "oaep")
6daba0
+ *   decrypted = pkey.decrypt(data, rsa_padding_mode: "oaep")
6daba0
+ *   p decrypted #=> "secret data"
6daba0
+ */
6daba0
+static VALUE
6daba0
+ossl_pkey_encrypt(int argc, VALUE *argv, VALUE self)
6daba0
+{
6daba0
+    EVP_PKEY *pkey;
6daba0
+    EVP_PKEY_CTX *ctx;
6daba0
+    VALUE data, options, str;
6daba0
+    size_t outlen;
6daba0
+    int state;
6daba0
+
6daba0
+    GetPKey(self, pkey);
6daba0
+    rb_scan_args(argc, argv, "11", &data, &options);
6daba0
+    StringValue(data);
6daba0
+
6daba0
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
6daba0
+    if (!ctx)
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
6daba0
+    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_encrypt_init");
6daba0
+    }
6daba0
+    if (!NIL_P(options)) {
6daba0
+        pkey_ctx_apply_options(ctx, options, &state);
6daba0
+        if (state) {
6daba0
+            EVP_PKEY_CTX_free(ctx);
6daba0
+            rb_jump_tag(state);
6daba0
+        }
6daba0
+    }
6daba0
+    if (EVP_PKEY_encrypt(ctx, NULL, &outlen,
6daba0
+                         (unsigned char *)RSTRING_PTR(data),
6daba0
+                         RSTRING_LEN(data)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_encrypt");
6daba0
+    }
6daba0
+    if (outlen > LONG_MAX) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_raise(ePKeyError, "encrypted data would be too large");
6daba0
+    }
6daba0
+    str = ossl_str_new(NULL, (long)outlen, &state);
6daba0
+    if (state) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_jump_tag(state);
6daba0
+    }
6daba0
+    if (EVP_PKEY_encrypt(ctx, (unsigned char *)RSTRING_PTR(str), &outlen,
6daba0
+                         (unsigned char *)RSTRING_PTR(data),
6daba0
+                         RSTRING_LEN(data)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_encrypt");
6daba0
+    }
6daba0
+    EVP_PKEY_CTX_free(ctx);
6daba0
+    rb_str_set_len(str, outlen);
6daba0
+    return str;
6daba0
+}
6daba0
+
6daba0
+/*
6daba0
+ * call-seq:
6daba0
+ *    pkey.decrypt(data [, options]) -> string
6daba0
+ *
6daba0
+ * Performs a public key decryption operation using +pkey+.
6daba0
+ *
6daba0
+ * See #encrypt for a description of the parameters and an example.
6daba0
+ *
6daba0
+ * Added in version 3.0. See also the man page EVP_PKEY_decrypt(3).
6daba0
+ */
6daba0
+static VALUE
6daba0
+ossl_pkey_decrypt(int argc, VALUE *argv, VALUE self)
6daba0
+{
6daba0
+    EVP_PKEY *pkey;
6daba0
+    EVP_PKEY_CTX *ctx;
6daba0
+    VALUE data, options, str;
6daba0
+    size_t outlen;
6daba0
+    int state;
6daba0
+
6daba0
+    GetPKey(self, pkey);
6daba0
+    rb_scan_args(argc, argv, "11", &data, &options);
6daba0
+    StringValue(data);
6daba0
+
6daba0
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
6daba0
+    if (!ctx)
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
6daba0
+    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_decrypt_init");
6daba0
+    }
6daba0
+    if (!NIL_P(options)) {
6daba0
+        pkey_ctx_apply_options(ctx, options, &state);
6daba0
+        if (state) {
6daba0
+            EVP_PKEY_CTX_free(ctx);
6daba0
+            rb_jump_tag(state);
6daba0
+        }
6daba0
+    }
6daba0
+    if (EVP_PKEY_decrypt(ctx, NULL, &outlen,
6daba0
+                         (unsigned char *)RSTRING_PTR(data),
6daba0
+                         RSTRING_LEN(data)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_decrypt");
6daba0
+    }
6daba0
+    if (outlen > LONG_MAX) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_raise(ePKeyError, "decrypted data would be too large");
6daba0
+    }
6daba0
+    str = ossl_str_new(NULL, (long)outlen, &state);
6daba0
+    if (state) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_jump_tag(state);
6daba0
+    }
6daba0
+    if (EVP_PKEY_decrypt(ctx, (unsigned char *)RSTRING_PTR(str), &outlen,
6daba0
+                         (unsigned char *)RSTRING_PTR(data),
6daba0
+                         RSTRING_LEN(data)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_decrypt");
6daba0
+    }
6daba0
+    EVP_PKEY_CTX_free(ctx);
6daba0
+    rb_str_set_len(str, outlen);
6daba0
+    return str;
6daba0
+}
6daba0
+
6daba0
 /*
6daba0
  * INIT
6daba0
  */
6daba0
@@ -1085,6 +1224,8 @@ Init_ossl_pkey(void)
6daba0
     rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);
6daba0
     rb_define_method(cPKey, "verify", ossl_pkey_verify, -1);
6daba0
     rb_define_method(cPKey, "derive", ossl_pkey_derive, -1);
6daba0
+    rb_define_method(cPKey, "encrypt", ossl_pkey_encrypt, -1);
6daba0
+    rb_define_method(cPKey, "decrypt", ossl_pkey_decrypt, -1);
6daba0
 
6daba0
     id_private_q = rb_intern("private?");
6daba0
 
6daba0
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
6daba0
index 5f8d04e754..d6bfca3ac5 100644
6daba0
--- a/test/openssl/test_pkey_rsa.rb
6daba0
+++ b/test/openssl/test_pkey_rsa.rb
6daba0
@@ -173,6 +173,40 @@ def test_sign_verify_pss
6daba0
     }
6daba0
   end
6daba0
 
6daba0
+  def test_encrypt_decrypt
6daba0
+    rsapriv = Fixtures.pkey("rsa-1")
6daba0
+    rsapub = dup_public(rsapriv)
6daba0
+
6daba0
+    # Defaults to PKCS #1 v1.5
6daba0
+    raw = "data"
6daba0
+    enc = rsapub.encrypt(raw)
6daba0
+    assert_equal raw, rsapriv.decrypt(enc)
6daba0
+
6daba0
+    # Invalid options
6daba0
+    assert_raise(OpenSSL::PKey::PKeyError) {
6daba0
+      rsapub.encrypt(raw, { "nonexistent" => "option" })
6daba0
+    }
6daba0
+  end
6daba0
+
6daba0
+  def test_encrypt_decrypt_legacy
6daba0
+    rsapriv = Fixtures.pkey("rsa-1")
6daba0
+    rsapub = dup_public(rsapriv)
6daba0
+
6daba0
+    # Defaults to PKCS #1 v1.5
6daba0
+    raw = "data"
6daba0
+    enc_legacy = rsapub.public_encrypt(raw)
6daba0
+    assert_equal raw, rsapriv.decrypt(enc_legacy)
6daba0
+    enc_new = rsapub.encrypt(raw)
6daba0
+    assert_equal raw, rsapriv.private_decrypt(enc_new)
6daba0
+
6daba0
+    # OAEP with default parameters
6daba0
+    raw = "data"
6daba0
+    enc_legacy = rsapub.public_encrypt(raw, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
6daba0
+    assert_equal raw, rsapriv.decrypt(enc_legacy, { "rsa_padding_mode" => "oaep" })
6daba0
+    enc_new = rsapub.encrypt(raw, { "rsa_padding_mode" => "oaep" })
6daba0
+    assert_equal raw, rsapriv.private_decrypt(enc_legacy, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
6daba0
+  end
6daba0
+
6daba0
   def test_export
6daba0
     rsa1024 = Fixtures.pkey("rsa1024")
6daba0
     key = OpenSSL::PKey::RSA.new
6daba0
-- 
6daba0
2.32.0
6daba0
6daba0
6daba0
From 6f5c75b06967b5b2db1d13646d74310e1cdc563e Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Tue, 25 May 2021 18:43:29 +0900
6daba0
Subject: [PATCH 2/6] pkey: update version reference in #sign and #verify
6daba0
 documentation
6daba0
6daba0
The next release is decided to be 3.0 rather than 2.3.
6daba0
---
6daba0
 ext/openssl/ossl_pkey.c | 4 ++--
6daba0
 1 file changed, 2 insertions(+), 2 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
6daba0
index baf8ce9f20..d08f6f7e60 100644
6daba0
--- a/ext/openssl/ossl_pkey.c
6daba0
+++ b/ext/openssl/ossl_pkey.c
6daba0
@@ -761,7 +761,7 @@ ossl_pkey_public_to_pem(VALUE self)
6daba0
  * +options+::
6daba0
  *   A Hash that contains algorithm specific control operations to \OpenSSL.
6daba0
  *   See OpenSSL's man page EVP_PKEY_CTX_ctrl_str(3) for details.
6daba0
- *   +options+ parameter was added in version 2.3.
6daba0
+ *   +options+ parameter was added in version 3.0.
6daba0
  *
6daba0
  * Example:
6daba0
  *   data = "Sign me!"
6daba0
@@ -875,7 +875,7 @@ ossl_pkey_sign(int argc, VALUE *argv, VALUE self)
6daba0
  * +data+::
6daba0
  *   See #sign.
6daba0
  * +options+::
6daba0
- *   See #sign. +options+ parameter was added in version 2.3.
6daba0
+ *   See #sign. +options+ parameter was added in version 3.0.
6daba0
  */
6daba0
 static VALUE
6daba0
 ossl_pkey_verify(int argc, VALUE *argv, VALUE self)
6daba0
-- 
6daba0
2.32.0
6daba0
6daba0
6daba0
From 99fc31a9b4843b7f8923b5ce8b36115b2c66f4db Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Fri, 22 May 2020 16:10:35 +0900
6daba0
Subject: [PATCH 3/6] pkey: implement PKey#sign_raw, #verify_raw, and
6daba0
 #verify_recover
6daba0
6daba0
Add a variant of PKey#sign and #verify that do not hash the data
6daba0
automatically.
6daba0
6daba0
Sometimes the caller has the hashed data only, but not the plaintext
6daba0
to be signed. In that case, users would have to use the low-level API
6daba0
such as RSA#private_encrypt or #public_decrypt directly.
6daba0
6daba0
OpenSSL 1.0.0 and later supports EVP_PKEY_sign() and EVP_PKEY_verify()
6daba0
which provide the same functionality as part of the EVP API. This patch
6daba0
adds wrappers for them.
6daba0
---
6daba0
 ext/openssl/ossl_pkey.c       | 232 ++++++++++++++++++++++++++++++++++
6daba0
 test/openssl/test_pkey_dsa.rb |  25 +++-
6daba0
 test/openssl/test_pkey_ec.rb  |  21 ++-
6daba0
 test/openssl/test_pkey_rsa.rb |  78 ++++++++----
6daba0
 4 files changed, 325 insertions(+), 31 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
6daba0
index d08f6f7e60..ba909c7632 100644
6daba0
--- a/ext/openssl/ossl_pkey.c
6daba0
+++ b/ext/openssl/ossl_pkey.c
6daba0
@@ -935,6 +935,235 @@ ossl_pkey_verify(int argc, VALUE *argv, VALUE self)
6daba0
     }
6daba0
 }
6daba0
 
6daba0
+/*
6daba0
+ * call-seq:
6daba0
+ *    pkey.sign_raw(digest, data [, options]) -> string
6daba0
+ *
6daba0
+ * Signs +data+ using a private key +pkey+. Unlike #sign, +data+ will not be
6daba0
+ * hashed by +digest+ automatically.
6daba0
+ *
6daba0
+ * See #verify_raw for the verification operation.
6daba0
+ *
6daba0
+ * Added in version 3.0. See also the man page EVP_PKEY_sign(3).
6daba0
+ *
6daba0
+ * +digest+::
6daba0
+ *   A String that represents the message digest algorithm name, or +nil+
6daba0
+ *   if the PKey type requires no digest algorithm.
6daba0
+ *   Although this method will not hash +data+ with it, this parameter may still
6daba0
+ *   be required depending on the signature algorithm.
6daba0
+ * +data+::
6daba0
+ *   A String. The data to be signed.
6daba0
+ * +options+::
6daba0
+ *   A Hash that contains algorithm specific control operations to \OpenSSL.
6daba0
+ *   See OpenSSL's man page EVP_PKEY_CTX_ctrl_str(3) for details.
6daba0
+ *
6daba0
+ * Example:
6daba0
+ *   data = "Sign me!"
6daba0
+ *   hash = OpenSSL::Digest.digest("SHA256", data)
6daba0
+ *   pkey = OpenSSL::PKey.generate_key("RSA", rsa_keygen_bits: 2048)
6daba0
+ *   signopts = { rsa_padding_mode: "pss" }
6daba0
+ *   signature = pkey.sign_raw("SHA256", hash, signopts)
6daba0
+ *
6daba0
+ *   # Creates a copy of the RSA key pkey, but without the private components
6daba0
+ *   pub_key = pkey.public_key
6daba0
+ *   puts pub_key.verify_raw("SHA256", signature, hash, signopts) # => true
6daba0
+ */
6daba0
+static VALUE
6daba0
+ossl_pkey_sign_raw(int argc, VALUE *argv, VALUE self)
6daba0
+{
6daba0
+    EVP_PKEY *pkey;
6daba0
+    VALUE digest, data, options, sig;
6daba0
+    const EVP_MD *md = NULL;
6daba0
+    EVP_PKEY_CTX *ctx;
6daba0
+    size_t outlen;
6daba0
+    int state;
6daba0
+
6daba0
+    GetPKey(self, pkey);
6daba0
+    rb_scan_args(argc, argv, "21", &digest, &data, &options);
6daba0
+    if (!NIL_P(digest))
6daba0
+        md = ossl_evp_get_digestbyname(digest);
6daba0
+    StringValue(data);
6daba0
+
6daba0
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
6daba0
+    if (!ctx)
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
6daba0
+    if (EVP_PKEY_sign_init(ctx) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_sign_init");
6daba0
+    }
6daba0
+    if (md && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_set_signature_md");
6daba0
+    }
6daba0
+    if (!NIL_P(options)) {
6daba0
+        pkey_ctx_apply_options(ctx, options, &state);
6daba0
+        if (state) {
6daba0
+            EVP_PKEY_CTX_free(ctx);
6daba0
+            rb_jump_tag(state);
6daba0
+        }
6daba0
+    }
6daba0
+    if (EVP_PKEY_sign(ctx, NULL, &outlen, (unsigned char *)RSTRING_PTR(data),
6daba0
+                      RSTRING_LEN(data)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_sign");
6daba0
+    }
6daba0
+    if (outlen > LONG_MAX) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_raise(ePKeyError, "signature would be too large");
6daba0
+    }
6daba0
+    sig = ossl_str_new(NULL, (long)outlen, &state);
6daba0
+    if (state) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_jump_tag(state);
6daba0
+    }
6daba0
+    if (EVP_PKEY_sign(ctx, (unsigned char *)RSTRING_PTR(sig), &outlen,
6daba0
+                      (unsigned char *)RSTRING_PTR(data),
6daba0
+                      RSTRING_LEN(data)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_sign");
6daba0
+    }
6daba0
+    EVP_PKEY_CTX_free(ctx);
6daba0
+    rb_str_set_len(sig, outlen);
6daba0
+    return sig;
6daba0
+}
6daba0
+
6daba0
+/*
6daba0
+ * call-seq:
6daba0
+ *    pkey.verify_raw(digest, signature, data [, options]) -> true or false
6daba0
+ *
6daba0
+ * Verifies the +signature+ for the +data+ using a public key +pkey+. Unlike
6daba0
+ * #verify, this method will not hash +data+ with +digest+ automatically.
6daba0
+ *
6daba0
+ * Returns +true+ if the signature is successfully verified, +false+ otherwise.
6daba0
+ * The caller must check the return value.
6daba0
+ *
6daba0
+ * See #sign_raw for the signing operation and an example code.
6daba0
+ *
6daba0
+ * Added in version 3.0. See also the man page EVP_PKEY_verify(3).
6daba0
+ *
6daba0
+ * +signature+::
6daba0
+ *   A String containing the signature to be verified.
6daba0
+ */
6daba0
+static VALUE
6daba0
+ossl_pkey_verify_raw(int argc, VALUE *argv, VALUE self)
6daba0
+{
6daba0
+    EVP_PKEY *pkey;
6daba0
+    VALUE digest, sig, data, options;
6daba0
+    const EVP_MD *md = NULL;
6daba0
+    EVP_PKEY_CTX *ctx;
6daba0
+    int state, ret;
6daba0
+
6daba0
+    GetPKey(self, pkey);
6daba0
+    rb_scan_args(argc, argv, "31", &digest, &sig, &data, &options);
6daba0
+    ossl_pkey_check_public_key(pkey);
6daba0
+    if (!NIL_P(digest))
6daba0
+        md = ossl_evp_get_digestbyname(digest);
6daba0
+    StringValue(sig);
6daba0
+    StringValue(data);
6daba0
+
6daba0
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
6daba0
+    if (!ctx)
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
6daba0
+    if (EVP_PKEY_verify_init(ctx) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_init");
6daba0
+    }
6daba0
+    if (md && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_set_signature_md");
6daba0
+    }
6daba0
+    if (!NIL_P(options)) {
6daba0
+        pkey_ctx_apply_options(ctx, options, &state);
6daba0
+        if (state) {
6daba0
+            EVP_PKEY_CTX_free(ctx);
6daba0
+            rb_jump_tag(state);
6daba0
+        }
6daba0
+    }
6daba0
+    ret = EVP_PKEY_verify(ctx, (unsigned char *)RSTRING_PTR(sig),
6daba0
+                          RSTRING_LEN(sig),
6daba0
+                          (unsigned char *)RSTRING_PTR(data),
6daba0
+                          RSTRING_LEN(data));
6daba0
+    EVP_PKEY_CTX_free(ctx);
6daba0
+    if (ret < 0)
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_verify");
6daba0
+
6daba0
+    if (ret)
6daba0
+        return Qtrue;
6daba0
+    else {
6daba0
+        ossl_clear_error();
6daba0
+        return Qfalse;
6daba0
+    }
6daba0
+}
6daba0
+
6daba0
+/*
6daba0
+ * call-seq:
6daba0
+ *    pkey.verify_recover(digest, signature [, options]) -> string
6daba0
+ *
6daba0
+ * Recovers the signed data from +signature+ using a public key +pkey+. Not all
6daba0
+ * signature algorithms support this operation.
6daba0
+ *
6daba0
+ * Added in version 3.0. See also the man page EVP_PKEY_verify_recover(3).
6daba0
+ *
6daba0
+ * +signature+::
6daba0
+ *   A String containing the signature to be verified.
6daba0
+ */
6daba0
+static VALUE
6daba0
+ossl_pkey_verify_recover(int argc, VALUE *argv, VALUE self)
6daba0
+{
6daba0
+    EVP_PKEY *pkey;
6daba0
+    VALUE digest, sig, options, out;
6daba0
+    const EVP_MD *md = NULL;
6daba0
+    EVP_PKEY_CTX *ctx;
6daba0
+    int state;
6daba0
+    size_t outlen;
6daba0
+
6daba0
+    GetPKey(self, pkey);
6daba0
+    rb_scan_args(argc, argv, "21", &digest, &sig, &options);
6daba0
+    ossl_pkey_check_public_key(pkey);
6daba0
+    if (!NIL_P(digest))
6daba0
+        md = ossl_evp_get_digestbyname(digest);
6daba0
+    StringValue(sig);
6daba0
+
6daba0
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
6daba0
+    if (!ctx)
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
6daba0
+    if (EVP_PKEY_verify_recover_init(ctx) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_recover_init");
6daba0
+    }
6daba0
+    if (md && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_set_signature_md");
6daba0
+    }
6daba0
+    if (!NIL_P(options)) {
6daba0
+        pkey_ctx_apply_options(ctx, options, &state);
6daba0
+        if (state) {
6daba0
+            EVP_PKEY_CTX_free(ctx);
6daba0
+            rb_jump_tag(state);
6daba0
+        }
6daba0
+    }
6daba0
+    if (EVP_PKEY_verify_recover(ctx, NULL, &outlen,
6daba0
+                                (unsigned char *)RSTRING_PTR(sig),
6daba0
+                                RSTRING_LEN(sig)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_recover");
6daba0
+    }
6daba0
+    out = ossl_str_new(NULL, (long)outlen, &state);
6daba0
+    if (state) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        rb_jump_tag(state);
6daba0
+    }
6daba0
+    if (EVP_PKEY_verify_recover(ctx, (unsigned char *)RSTRING_PTR(out), &outlen,
6daba0
+                                (unsigned char *)RSTRING_PTR(sig),
6daba0
+                                RSTRING_LEN(sig)) <= 0) {
6daba0
+        EVP_PKEY_CTX_free(ctx);
6daba0
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_recover");
6daba0
+    }
6daba0
+    EVP_PKEY_CTX_free(ctx);
6daba0
+    rb_str_set_len(out, outlen);
6daba0
+    return out;
6daba0
+}
6daba0
+
6daba0
 /*
6daba0
  * call-seq:
6daba0
  *    pkey.derive(peer_pkey) -> string
6daba0
@@ -1223,6 +1452,9 @@ Init_ossl_pkey(void)
6daba0
 
6daba0
     rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);
6daba0
     rb_define_method(cPKey, "verify", ossl_pkey_verify, -1);
6daba0
+    rb_define_method(cPKey, "sign_raw", ossl_pkey_sign_raw, -1);
6daba0
+    rb_define_method(cPKey, "verify_raw", ossl_pkey_verify_raw, -1);
6daba0
+    rb_define_method(cPKey, "verify_recover", ossl_pkey_verify_recover, -1);
6daba0
     rb_define_method(cPKey, "derive", ossl_pkey_derive, -1);
6daba0
     rb_define_method(cPKey, "encrypt", ossl_pkey_encrypt, -1);
6daba0
     rb_define_method(cPKey, "decrypt", ossl_pkey_decrypt, -1);
6daba0
diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb
6daba0
index 85bb6ec0ae..147e50176b 100644
6daba0
--- a/test/openssl/test_pkey_dsa.rb
6daba0
+++ b/test/openssl/test_pkey_dsa.rb
6daba0
@@ -48,12 +48,31 @@ def test_sign_verify
6daba0
     assert_equal false, dsa512.verify("SHA256", signature1, data)
6daba0
   end
6daba0
 
6daba0
-  def test_sys_sign_verify
6daba0
-    key = Fixtures.pkey("dsa256")
6daba0
+  def test_sign_verify_raw
6daba0
+    key = Fixtures.pkey("dsa512")
6daba0
     data = 'Sign me!'
6daba0
     digest = OpenSSL::Digest.digest('SHA1', data)
6daba0
+
6daba0
+    invalid_sig = key.sign_raw(nil, digest.succ)
6daba0
+    malformed_sig = "*" * invalid_sig.bytesize
6daba0
+
6daba0
+    # Sign by #syssign
6daba0
     sig = key.syssign(digest)
6daba0
-    assert(key.sysverify(digest, sig))
6daba0
+    assert_equal true, key.sysverify(digest, sig)
6daba0
+    assert_equal false, key.sysverify(digest, invalid_sig)
6daba0
+    assert_raise(OpenSSL::PKey::DSAError) { key.sysverify(digest, malformed_sig) }
6daba0
+    assert_equal true, key.verify_raw(nil, sig, digest)
6daba0
+    assert_equal false, key.verify_raw(nil, invalid_sig, digest)
6daba0
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, digest) }
6daba0
+
6daba0
+    # Sign by #sign_raw
6daba0
+    sig = key.sign_raw(nil, digest)
6daba0
+    assert_equal true, key.sysverify(digest, sig)
6daba0
+    assert_equal false, key.sysverify(digest, invalid_sig)
6daba0
+    assert_raise(OpenSSL::PKey::DSAError) { key.sysverify(digest, malformed_sig) }
6daba0
+    assert_equal true, key.verify_raw(nil, sig, digest)
6daba0
+    assert_equal false, key.verify_raw(nil, invalid_sig, digest)
6daba0
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, digest) }
6daba0
   end
6daba0
 
6daba0
   def test_DSAPrivateKey
6daba0
diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb
6daba0
index 95d4338a51..4b6df0290f 100644
6daba0
--- a/test/openssl/test_pkey_ec.rb
6daba0
+++ b/test/openssl/test_pkey_ec.rb
6daba0
@@ -109,13 +109,30 @@ def test_derive_key
6daba0
     assert_equal a.derive(b), a.dh_compute_key(b.public_key)
6daba0
   end
6daba0
 
6daba0
-  def test_dsa_sign_verify
6daba0
+  def test_sign_verify_raw
6daba0
+    key = Fixtures.pkey("p256")
6daba0
     data1 = "foo"
6daba0
     data2 = "bar"
6daba0
-    key = OpenSSL::PKey::EC.new("prime256v1").generate_key!
6daba0
+
6daba0
+    malformed_sig = "*" * 30
6daba0
+
6daba0
+    # Sign by #dsa_sign_asn1
6daba0
     sig = key.dsa_sign_asn1(data1)
6daba0
     assert_equal true, key.dsa_verify_asn1(data1, sig)
6daba0
     assert_equal false, key.dsa_verify_asn1(data2, sig)
6daba0
+    assert_raise(OpenSSL::PKey::ECError) { key.dsa_verify_asn1(data1, malformed_sig) }
6daba0
+    assert_equal true, key.verify_raw(nil, sig, data1)
6daba0
+    assert_equal false, key.verify_raw(nil, sig, data2)
6daba0
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, data1) }
6daba0
+
6daba0
+    # Sign by #sign_raw
6daba0
+    sig = key.sign_raw(nil, data1)
6daba0
+    assert_equal true, key.dsa_verify_asn1(data1, sig)
6daba0
+    assert_equal false, key.dsa_verify_asn1(data2, sig)
6daba0
+    assert_raise(OpenSSL::PKey::ECError) { key.dsa_verify_asn1(data1, malformed_sig) }
6daba0
+    assert_equal true, key.verify_raw(nil, sig, data1)
6daba0
+    assert_equal false, key.verify_raw(nil, sig, data2)
6daba0
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, data1) }
6daba0
   end
6daba0
 
6daba0
   def test_dsa_sign_asn1_FIPS186_3
6daba0
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
6daba0
index d6bfca3ac5..5e127f5407 100644
6daba0
--- a/test/openssl/test_pkey_rsa.rb
6daba0
+++ b/test/openssl/test_pkey_rsa.rb
6daba0
@@ -13,32 +13,6 @@ def test_no_private_exp
6daba0
     assert_raise(OpenSSL::PKey::RSAError){ key.private_decrypt("foo") }
6daba0
   end
6daba0
 
6daba0
-  def test_padding
6daba0
-    key = OpenSSL::PKey::RSA.new(512, 3)
6daba0
-
6daba0
-    # Need right size for raw mode
6daba0
-    plain0 = "x" * (512/8)
6daba0
-    cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
6daba0
-    plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
6daba0
-    assert_equal(plain0, plain1)
6daba0
-
6daba0
-    # Need smaller size for pkcs1 mode
6daba0
-    plain0 = "x" * (512/8 - 11)
6daba0
-    cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
6daba0
-    plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
6daba0
-    assert_equal(plain0, plain1)
6daba0
-
6daba0
-    cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
6daba0
-    plain1 = key.public_decrypt(cipherdef)
6daba0
-    assert_equal(plain0, plain1)
6daba0
-    assert_equal(cipher1, cipherdef)
6daba0
-
6daba0
-    # Failure cases
6daba0
-    assert_raise(ArgumentError){ key.private_encrypt() }
6daba0
-    assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
6daba0
-    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
6daba0
-  end
6daba0
-
6daba0
   def test_private
6daba0
     # Generated by key size and public exponent
6daba0
     key = OpenSSL::PKey::RSA.new(512, 3)
6daba0
@@ -133,6 +107,58 @@ def test_sign_verify_options
6daba0
     assert_equal false, key.verify("SHA256", sig_pss, data)
6daba0
   end
6daba0
 
6daba0
+  def test_sign_verify_raw
6daba0
+    key = Fixtures.pkey("rsa-1")
6daba0
+    data = "Sign me!"
6daba0
+    hash = OpenSSL::Digest.digest("SHA1", data)
6daba0
+    signature = key.sign_raw("SHA1", hash)
6daba0
+    assert_equal true, key.verify_raw("SHA1", signature, hash)
6daba0
+    assert_equal true, key.verify("SHA1", signature, data)
6daba0
+
6daba0
+    # Too long data
6daba0
+    assert_raise(OpenSSL::PKey::PKeyError) {
6daba0
+      key.sign_raw("SHA1", "x" * (key.n.num_bytes + 1))
6daba0
+    }
6daba0
+
6daba0
+    # With options
6daba0
+    pssopts = {
6daba0
+      "rsa_padding_mode" => "pss",
6daba0
+      "rsa_pss_saltlen" => 20,
6daba0
+      "rsa_mgf1_md" => "SHA256"
6daba0
+    }
6daba0
+    sig_pss = key.sign_raw("SHA1", hash, pssopts)
6daba0
+    assert_equal true, key.verify("SHA1", sig_pss, data, pssopts)
6daba0
+    assert_equal true, key.verify_raw("SHA1", sig_pss, hash, pssopts)
6daba0
+  end
6daba0
+
6daba0
+  def test_sign_verify_raw_legacy
6daba0
+    key = Fixtures.pkey("rsa-1")
6daba0
+    bits = key.n.num_bits
6daba0
+
6daba0
+    # Need right size for raw mode
6daba0
+    plain0 = "x" * (bits/8)
6daba0
+    cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
6daba0
+    plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
6daba0
+    assert_equal(plain0, plain1)
6daba0
+
6daba0
+    # Need smaller size for pkcs1 mode
6daba0
+    plain0 = "x" * (bits/8 - 11)
6daba0
+    cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
6daba0
+    plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
6daba0
+    assert_equal(plain0, plain1)
6daba0
+
6daba0
+    cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
6daba0
+    plain1 = key.public_decrypt(cipherdef)
6daba0
+    assert_equal(plain0, plain1)
6daba0
+    assert_equal(cipher1, cipherdef)
6daba0
+
6daba0
+    # Failure cases
6daba0
+    assert_raise(ArgumentError){ key.private_encrypt() }
6daba0
+    assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
6daba0
+    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
6daba0
+  end
6daba0
+
6daba0
+
6daba0
   def test_verify_empty_rsa
6daba0
     rsa = OpenSSL::PKey::RSA.new
6daba0
     assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") {
6daba0
-- 
6daba0
2.32.0
6daba0
6daba0
6daba0
From 4330b1b9661fcab1172473f4fdd9986602c1e78c Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Mon, 18 May 2020 20:24:08 +0900
6daba0
Subject: [PATCH 4/6] pkey/rsa: port RSA#{private,public}_{encrypt,decrypt} to
6daba0
 the EVP API
6daba0
6daba0
Implement these methods using the new OpenSSL::PKey::PKey#{encrypt,sign}
6daba0
family. The definitions are now in lib/openssl/pkey.rb.
6daba0
6daba0
Also, recommend using those generic methods in the documentation.
6daba0
---
6daba0
 ext/openssl/lib/openssl/pkey.rb | 106 ++++++++++++++++++++++++
6daba0
 ext/openssl/ossl_pkey_rsa.c     | 141 --------------------------------
6daba0
 2 files changed, 106 insertions(+), 141 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
6daba0
index 569559e1ce..dd8c7c0b09 100644
6daba0
--- a/ext/openssl/lib/openssl/pkey.rb
6daba0
+++ b/ext/openssl/lib/openssl/pkey.rb
6daba0
@@ -243,5 +243,111 @@ def new(*args, &blk) # :nodoc:
6daba0
         end
6daba0
       end
6daba0
     end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    rsa.private_encrypt(string)          -> String
6daba0
+    #    rsa.private_encrypt(string, padding) -> String
6daba0
+    #
6daba0
+    # Encrypt +string+ with the private key.  +padding+ defaults to
6daba0
+    # PKCS1_PADDING. The encrypted string output can be decrypted using
6daba0
+    # #public_decrypt.
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
6daba0
+    # PKey::PKey#verify_recover instead.
6daba0
+    def private_encrypt(string, padding = PKCS1_PADDING)
6daba0
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
6daba0
+      private? or raise OpenSSL::PKey::RSAError, "private key needed."
6daba0
+      begin
6daba0
+        sign_raw(nil, string, {
6daba0
+          "rsa_padding_mode" => translate_padding_mode(padding),
6daba0
+        })
6daba0
+      rescue OpenSSL::PKey::PKeyError
6daba0
+        raise OpenSSL::PKey::RSAError, $!.message
6daba0
+      end
6daba0
+    end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    rsa.public_decrypt(string)          -> String
6daba0
+    #    rsa.public_decrypt(string, padding) -> String
6daba0
+    #
6daba0
+    # Decrypt +string+, which has been encrypted with the private key, with the
6daba0
+    # public key.  +padding+ defaults to PKCS1_PADDING.
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
6daba0
+    # PKey::PKey#verify_recover instead.
6daba0
+    def public_decrypt(string, padding = PKCS1_PADDING)
6daba0
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
6daba0
+      begin
6daba0
+        verify_recover(nil, string, {
6daba0
+          "rsa_padding_mode" => translate_padding_mode(padding),
6daba0
+        })
6daba0
+      rescue OpenSSL::PKey::PKeyError
6daba0
+        raise OpenSSL::PKey::RSAError, $!.message
6daba0
+      end
6daba0
+    end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    rsa.public_encrypt(string)          -> String
6daba0
+    #    rsa.public_encrypt(string, padding) -> String
6daba0
+    #
6daba0
+    # Encrypt +string+ with the public key.  +padding+ defaults to
6daba0
+    # PKCS1_PADDING. The encrypted string output can be decrypted using
6daba0
+    # #private_decrypt.
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
6daba0
+    def public_encrypt(data, padding = PKCS1_PADDING)
6daba0
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
6daba0
+      begin
6daba0
+        encrypt(data, {
6daba0
+          "rsa_padding_mode" => translate_padding_mode(padding),
6daba0
+        })
6daba0
+      rescue OpenSSL::PKey::PKeyError
6daba0
+        raise OpenSSL::PKey::RSAError, $!.message
6daba0
+      end
6daba0
+    end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    rsa.private_decrypt(string)          -> String
6daba0
+    #    rsa.private_decrypt(string, padding) -> String
6daba0
+    #
6daba0
+    # Decrypt +string+, which has been encrypted with the public key, with the
6daba0
+    # private key. +padding+ defaults to PKCS1_PADDING.
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
6daba0
+    def private_decrypt(data, padding = PKCS1_PADDING)
6daba0
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
6daba0
+      private? or raise OpenSSL::PKey::RSAError, "private key needed."
6daba0
+      begin
6daba0
+        decrypt(data, {
6daba0
+          "rsa_padding_mode" => translate_padding_mode(padding),
6daba0
+        })
6daba0
+      rescue OpenSSL::PKey::PKeyError
6daba0
+        raise OpenSSL::PKey::RSAError, $!.message
6daba0
+      end
6daba0
+    end
6daba0
+
6daba0
+    PKCS1_PADDING = 1
6daba0
+    SSLV23_PADDING = 2
6daba0
+    NO_PADDING = 3
6daba0
+    PKCS1_OAEP_PADDING = 4
6daba0
+
6daba0
+    private def translate_padding_mode(num)
6daba0
+      case num
6daba0
+      when PKCS1_PADDING
6daba0
+        "pkcs1"
6daba0
+      when SSLV23_PADDING
6daba0
+        "sslv23"
6daba0
+      when NO_PADDING
6daba0
+        "none"
6daba0
+      when PKCS1_OAEP_PADDING
6daba0
+        "oaep"
6daba0
+      else
6daba0
+        raise OpenSSL::PKey::PKeyError, "unsupported padding mode"
6daba0
+      end
6daba0
+    end
6daba0
   end
6daba0
 end
6daba0
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
6daba0
index 1c5476cdcd..8ebd3ec559 100644
6daba0
--- a/ext/openssl/ossl_pkey_rsa.c
6daba0
+++ b/ext/openssl/ossl_pkey_rsa.c
6daba0
@@ -229,138 +229,6 @@ ossl_rsa_to_der(VALUE self)
6daba0
         return ossl_pkey_export_spki(self, 1);
6daba0
 }
6daba0
 
6daba0
-/*
6daba0
- * call-seq:
6daba0
- *   rsa.public_encrypt(string)          => String
6daba0
- *   rsa.public_encrypt(string, padding) => String
6daba0
- *
6daba0
- * Encrypt _string_ with the public key.  _padding_ defaults to PKCS1_PADDING.
6daba0
- * The encrypted string output can be decrypted using #private_decrypt.
6daba0
- */
6daba0
-static VALUE
6daba0
-ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self)
6daba0
-{
6daba0
-    RSA *rsa;
6daba0
-    const BIGNUM *rsa_n;
6daba0
-    int buf_len, pad;
6daba0
-    VALUE str, buffer, padding;
6daba0
-
6daba0
-    GetRSA(self, rsa);
6daba0
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
6daba0
-    if (!rsa_n)
6daba0
-	ossl_raise(eRSAError, "incomplete RSA");
6daba0
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
6daba0
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
6daba0
-    StringValue(buffer);
6daba0
-    str = rb_str_new(0, RSA_size(rsa));
6daba0
-    buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
6daba0
-				 (unsigned char *)RSTRING_PTR(str), rsa, pad);
6daba0
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
6daba0
-    rb_str_set_len(str, buf_len);
6daba0
-
6daba0
-    return str;
6daba0
-}
6daba0
-
6daba0
-/*
6daba0
- * call-seq:
6daba0
- *   rsa.public_decrypt(string)          => String
6daba0
- *   rsa.public_decrypt(string, padding) => String
6daba0
- *
6daba0
- * Decrypt _string_, which has been encrypted with the private key, with the
6daba0
- * public key.  _padding_ defaults to PKCS1_PADDING.
6daba0
- */
6daba0
-static VALUE
6daba0
-ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
6daba0
-{
6daba0
-    RSA *rsa;
6daba0
-    const BIGNUM *rsa_n;
6daba0
-    int buf_len, pad;
6daba0
-    VALUE str, buffer, padding;
6daba0
-
6daba0
-    GetRSA(self, rsa);
6daba0
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
6daba0
-    if (!rsa_n)
6daba0
-	ossl_raise(eRSAError, "incomplete RSA");
6daba0
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
6daba0
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
6daba0
-    StringValue(buffer);
6daba0
-    str = rb_str_new(0, RSA_size(rsa));
6daba0
-    buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
6daba0
-				 (unsigned char *)RSTRING_PTR(str), rsa, pad);
6daba0
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
6daba0
-    rb_str_set_len(str, buf_len);
6daba0
-
6daba0
-    return str;
6daba0
-}
6daba0
-
6daba0
-/*
6daba0
- * call-seq:
6daba0
- *   rsa.private_encrypt(string)          => String
6daba0
- *   rsa.private_encrypt(string, padding) => String
6daba0
- *
6daba0
- * Encrypt _string_ with the private key.  _padding_ defaults to PKCS1_PADDING.
6daba0
- * The encrypted string output can be decrypted using #public_decrypt.
6daba0
- */
6daba0
-static VALUE
6daba0
-ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self)
6daba0
-{
6daba0
-    RSA *rsa;
6daba0
-    const BIGNUM *rsa_n;
6daba0
-    int buf_len, pad;
6daba0
-    VALUE str, buffer, padding;
6daba0
-
6daba0
-    GetRSA(self, rsa);
6daba0
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
6daba0
-    if (!rsa_n)
6daba0
-	ossl_raise(eRSAError, "incomplete RSA");
6daba0
-    if (!RSA_PRIVATE(self, rsa))
6daba0
-	ossl_raise(eRSAError, "private key needed.");
6daba0
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
6daba0
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
6daba0
-    StringValue(buffer);
6daba0
-    str = rb_str_new(0, RSA_size(rsa));
6daba0
-    buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
6daba0
-				  (unsigned char *)RSTRING_PTR(str), rsa, pad);
6daba0
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
6daba0
-    rb_str_set_len(str, buf_len);
6daba0
-
6daba0
-    return str;
6daba0
-}
6daba0
-
6daba0
-/*
6daba0
- * call-seq:
6daba0
- *   rsa.private_decrypt(string)          => String
6daba0
- *   rsa.private_decrypt(string, padding) => String
6daba0
- *
6daba0
- * Decrypt _string_, which has been encrypted with the public key, with the
6daba0
- * private key.  _padding_ defaults to PKCS1_PADDING.
6daba0
- */
6daba0
-static VALUE
6daba0
-ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
6daba0
-{
6daba0
-    RSA *rsa;
6daba0
-    const BIGNUM *rsa_n;
6daba0
-    int buf_len, pad;
6daba0
-    VALUE str, buffer, padding;
6daba0
-
6daba0
-    GetRSA(self, rsa);
6daba0
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
6daba0
-    if (!rsa_n)
6daba0
-	ossl_raise(eRSAError, "incomplete RSA");
6daba0
-    if (!RSA_PRIVATE(self, rsa))
6daba0
-	ossl_raise(eRSAError, "private key needed.");
6daba0
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
6daba0
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
6daba0
-    StringValue(buffer);
6daba0
-    str = rb_str_new(0, RSA_size(rsa));
6daba0
-    buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
6daba0
-				  (unsigned char *)RSTRING_PTR(str), rsa, pad);
6daba0
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
6daba0
-    rb_str_set_len(str, buf_len);
6daba0
-
6daba0
-    return str;
6daba0
-}
6daba0
-
6daba0
 /*
6daba0
  * call-seq:
6daba0
  *    rsa.sign_pss(digest, data, salt_length:, mgf1_hash:) -> String
6daba0
@@ -657,10 +525,6 @@ Init_ossl_rsa(void)
6daba0
     rb_define_alias(cRSA, "to_pem", "export");
6daba0
     rb_define_alias(cRSA, "to_s", "export");
6daba0
     rb_define_method(cRSA, "to_der", ossl_rsa_to_der, 0);
6daba0
-    rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, -1);
6daba0
-    rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1);
6daba0
-    rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1);
6daba0
-    rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1);
6daba0
     rb_define_method(cRSA, "sign_pss", ossl_rsa_sign_pss, -1);
6daba0
     rb_define_method(cRSA, "verify_pss", ossl_rsa_verify_pss, -1);
6daba0
 
6daba0
@@ -678,11 +542,6 @@ Init_ossl_rsa(void)
6daba0
 
6daba0
     rb_define_method(cRSA, "params", ossl_rsa_get_params, 0);
6daba0
 
6daba0
-    DefRSAConst(PKCS1_PADDING);
6daba0
-    DefRSAConst(SSLV23_PADDING);
6daba0
-    DefRSAConst(NO_PADDING);
6daba0
-    DefRSAConst(PKCS1_OAEP_PADDING);
6daba0
-
6daba0
 /*
6daba0
  * TODO: Test it
6daba0
     rb_define_method(cRSA, "blinding_on!", ossl_rsa_blinding_on, 0);
6daba0
-- 
6daba0
2.32.0
6daba0
6daba0
6daba0
From d45a31cf70f5a55d7f6cf5082efc4dbb68d1169d Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Fri, 10 Jul 2020 13:43:20 +0900
6daba0
Subject: [PATCH 5/6] pkey/ec: refactor EC#dsa_{sign,verify}_asn1 with
6daba0
 PKey#{sign,verify}_raw
6daba0
6daba0
With the newly added OpenSSL::PKey::PKey#{sign,verify}_raw,
6daba0
OpenSSL::PKey::EC's low level signing operation methods can be
6daba0
implemented in Ruby. The definitions are now in lib/openssl/pkey.rb.
6daba0
---
6daba0
 ext/openssl/lib/openssl/pkey.rb | 22 +++++++++++++
6daba0
 ext/openssl/ossl_pkey_ec.c      | 55 ---------------------------------
6daba0
 2 files changed, 22 insertions(+), 55 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
6daba0
index dd8c7c0b09..e587109694 100644
6daba0
--- a/ext/openssl/lib/openssl/pkey.rb
6daba0
+++ b/ext/openssl/lib/openssl/pkey.rb
6daba0
@@ -164,6 +164,28 @@ def new(*args, &blk) # :nodoc:
6daba0
   class EC
6daba0
     include OpenSSL::Marshal
6daba0
 
6daba0
+    # :call-seq:
6daba0
+    #    key.dsa_sign_asn1(data) -> String
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
6daba0
+    def dsa_sign_asn1(data)
6daba0
+      sign_raw(nil, data)
6daba0
+    rescue OpenSSL::PKey::PKeyError
6daba0
+      raise OpenSSL::PKey::ECError, $!.message
6daba0
+    end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    key.dsa_verify_asn1(data, sig) -> true | false
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
6daba0
+    def dsa_verify_asn1(data, sig)
6daba0
+      verify_raw(nil, sig, data)
6daba0
+    rescue OpenSSL::PKey::PKeyError
6daba0
+      raise OpenSSL::PKey::ECError, $!.message
6daba0
+    end
6daba0
+
6daba0
     # :call-seq:
6daba0
     #    ec.dh_compute_key(pubkey) -> string
6daba0
     #
6daba0
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
6daba0
index 829529d4b9..f52e67079d 100644
6daba0
--- a/ext/openssl/ossl_pkey_ec.c
6daba0
+++ b/ext/openssl/ossl_pkey_ec.c
6daba0
@@ -476,57 +476,6 @@ static VALUE ossl_ec_key_check_key(VALUE self)
6daba0
     return Qtrue;
6daba0
 }
6daba0
 
6daba0
-/*
6daba0
- *  call-seq:
6daba0
- *     key.dsa_sign_asn1(data)   => String
6daba0
- *
6daba0
- *  See the OpenSSL documentation for ECDSA_sign()
6daba0
- */
6daba0
-static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data)
6daba0
-{
6daba0
-    EC_KEY *ec;
6daba0
-    unsigned int buf_len;
6daba0
-    VALUE str;
6daba0
-
6daba0
-    GetEC(self, ec);
6daba0
-    StringValue(data);
6daba0
-
6daba0
-    if (EC_KEY_get0_private_key(ec) == NULL)
6daba0
-	ossl_raise(eECError, "Private EC key needed!");
6daba0
-
6daba0
-    str = rb_str_new(0, ECDSA_size(ec));
6daba0
-    if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1)
6daba0
-	ossl_raise(eECError, "ECDSA_sign");
6daba0
-    rb_str_set_len(str, buf_len);
6daba0
-
6daba0
-    return str;
6daba0
-}
6daba0
-
6daba0
-/*
6daba0
- *  call-seq:
6daba0
- *     key.dsa_verify_asn1(data, sig)   => true or false
6daba0
- *
6daba0
- *  See the OpenSSL documentation for ECDSA_verify()
6daba0
- */
6daba0
-static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig)
6daba0
-{
6daba0
-    EC_KEY *ec;
6daba0
-
6daba0
-    GetEC(self, ec);
6daba0
-    StringValue(data);
6daba0
-    StringValue(sig);
6daba0
-
6daba0
-    switch (ECDSA_verify(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
6daba0
-                         (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), ec)) {
6daba0
-      case 1:
6daba0
-        return Qtrue;
6daba0
-      case 0:
6daba0
-        return Qfalse;
6daba0
-      default:
6daba0
-        ossl_raise(eECError, "ECDSA_verify");
6daba0
-    }
6daba0
-}
6daba0
-
6daba0
 /*
6daba0
  * OpenSSL::PKey::EC::Group
6daba0
  */
6daba0
@@ -1615,10 +1564,6 @@ void Init_ossl_ec(void)
6daba0
     rb_define_alias(cEC, "generate_key", "generate_key!");
6daba0
     rb_define_method(cEC, "check_key", ossl_ec_key_check_key, 0);
6daba0
 
6daba0
-    rb_define_method(cEC, "dsa_sign_asn1", ossl_ec_key_dsa_sign_asn1, 1);
6daba0
-    rb_define_method(cEC, "dsa_verify_asn1", ossl_ec_key_dsa_verify_asn1, 2);
6daba0
-/* do_sign/do_verify */
6daba0
-
6daba0
     rb_define_method(cEC, "export", ossl_ec_key_export, -1);
6daba0
     rb_define_alias(cEC, "to_pem", "export");
6daba0
     rb_define_method(cEC, "to_der", ossl_ec_key_to_der, 0);
6daba0
-- 
6daba0
2.32.0
6daba0
6daba0
6daba0
From 2494043e302c920e90e06cce443c5cd428e183f7 Mon Sep 17 00:00:00 2001
6daba0
From: Kazuki Yamaguchi <k@rhe.jp>
6daba0
Date: Fri, 10 Jul 2020 13:51:18 +0900
6daba0
Subject: [PATCH 6/6] pkey/dsa: refactor DSA#sys{sign,verify} with
6daba0
 PKey#{sign,verify}_raw
6daba0
6daba0
With the newly added OpenSSL::PKey::PKey#{sign,verify}_raw,
6daba0
OpenSSL::PKey::DSA's low level signing operation methods can be
6daba0
implemented in Ruby. The definitions are now in lib/openssl/pkey.rb.
6daba0
---
6daba0
 ext/openssl/lib/openssl/pkey.rb | 54 ++++++++++++++++++++
6daba0
 ext/openssl/ossl_pkey_dsa.c     | 88 ---------------------------------
6daba0
 2 files changed, 54 insertions(+), 88 deletions(-)
6daba0
6daba0
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
6daba0
index e587109694..f6bf5892b0 100644
6daba0
--- a/ext/openssl/lib/openssl/pkey.rb
6daba0
+++ b/ext/openssl/lib/openssl/pkey.rb
6daba0
@@ -158,6 +158,60 @@ def new(*args, &blk) # :nodoc:
6daba0
         end
6daba0
       end
6daba0
     end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    dsa.syssign(string) -> string
6daba0
+    #
6daba0
+    # Computes and returns the \DSA signature of +string+, where +string+ is
6daba0
+    # expected to be an already-computed message digest of the original input
6daba0
+    # data. The signature is issued using the private key of this DSA instance.
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
6daba0
+    #
6daba0
+    # +string+::
6daba0
+    #   A message digest of the original input data to be signed.
6daba0
+    #
6daba0
+    # Example:
6daba0
+    #   dsa = OpenSSL::PKey::DSA.new(2048)
6daba0
+    #   doc = "Sign me"
6daba0
+    #   digest = OpenSSL::Digest.digest('SHA1', doc)
6daba0
+    #
6daba0
+    #   # With legacy #syssign and #sysverify:
6daba0
+    #   sig = dsa.syssign(digest)
6daba0
+    #   p dsa.sysverify(digest, sig) #=> true
6daba0
+    #
6daba0
+    #   # With #sign_raw and #verify_raw:
6daba0
+    #   sig = dsa.sign_raw(nil, digest)
6daba0
+    #   p dsa.verify_raw(nil, sig, digest) #=> true
6daba0
+    def syssign(string)
6daba0
+      q or raise OpenSSL::PKey::DSAError, "incomplete DSA"
6daba0
+      private? or raise OpenSSL::PKey::DSAError, "Private DSA key needed!"
6daba0
+      begin
6daba0
+        sign_raw(nil, string)
6daba0
+      rescue OpenSSL::PKey::PKeyError
6daba0
+        raise OpenSSL::PKey::DSAError, $!.message
6daba0
+      end
6daba0
+    end
6daba0
+
6daba0
+    # :call-seq:
6daba0
+    #    dsa.sysverify(digest, sig) -> true | false
6daba0
+    #
6daba0
+    # Verifies whether the signature is valid given the message digest input.
6daba0
+    # It does so by validating +sig+ using the public key of this DSA instance.
6daba0
+    #
6daba0
+    # Deprecated in version 3.0.
6daba0
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
6daba0
+    #
6daba0
+    # +digest+::
6daba0
+    #   A message digest of the original input data to be signed.
6daba0
+    # +sig+::
6daba0
+    #   A \DSA signature value.
6daba0
+    def sysverify(digest, sig)
6daba0
+      verify_raw(nil, sig, digest)
6daba0
+    rescue OpenSSL::PKey::PKeyError
6daba0
+      raise OpenSSL::PKey::DSAError, $!.message
6daba0
+    end
6daba0
   end
6daba0
 
6daba0
   if defined?(EC)
6daba0
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
6daba0
index ab9ac781e8..7af00eebec 100644
6daba0
--- a/ext/openssl/ossl_pkey_dsa.c
6daba0
+++ b/ext/openssl/ossl_pkey_dsa.c
6daba0
@@ -264,92 +264,6 @@ ossl_dsa_get_params(VALUE self)
6daba0
     return hash;
6daba0
 }
6daba0
 
6daba0
-/*
6daba0
- *  call-seq:
6daba0
- *    dsa.syssign(string) -> aString
6daba0
- *
6daba0
- * Computes and returns the DSA signature of _string_, where _string_ is
6daba0
- * expected to be an already-computed message digest of the original input
6daba0
- * data. The signature is issued using the private key of this DSA instance.
6daba0
- *
6daba0
- * === Parameters
6daba0
- * * _string_ is a message digest of the original input data to be signed.
6daba0
- *
6daba0
- * === Example
6daba0
- *  dsa = OpenSSL::PKey::DSA.new(2048)
6daba0
- *  doc = "Sign me"
6daba0
- *  digest = OpenSSL::Digest.digest('SHA1', doc)
6daba0
- *  sig = dsa.syssign(digest)
6daba0
- *
6daba0
- *
6daba0
- */
6daba0
-static VALUE
6daba0
-ossl_dsa_sign(VALUE self, VALUE data)
6daba0
-{
6daba0
-    DSA *dsa;
6daba0
-    const BIGNUM *dsa_q;
6daba0
-    unsigned int buf_len;
6daba0
-    VALUE str;
6daba0
-
6daba0
-    GetDSA(self, dsa);
6daba0
-    DSA_get0_pqg(dsa, NULL, &dsa_q, NULL);
6daba0
-    if (!dsa_q)
6daba0
-	ossl_raise(eDSAError, "incomplete DSA");
6daba0
-    if (!DSA_PRIVATE(self, dsa))
6daba0
-	ossl_raise(eDSAError, "Private DSA key needed!");
6daba0
-    StringValue(data);
6daba0
-    str = rb_str_new(0, DSA_size(dsa));
6daba0
-    if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
6daba0
-		  (unsigned char *)RSTRING_PTR(str),
6daba0
-		  &buf_len, dsa)) { /* type is ignored (0) */
6daba0
-	ossl_raise(eDSAError, NULL);
6daba0
-    }
6daba0
-    rb_str_set_len(str, buf_len);
6daba0
-
6daba0
-    return str;
6daba0
-}
6daba0
-
6daba0
-/*
6daba0
- *  call-seq:
6daba0
- *    dsa.sysverify(digest, sig) -> true | false
6daba0
- *
6daba0
- * Verifies whether the signature is valid given the message digest input. It
6daba0
- * does so by validating _sig_ using the public key of this DSA instance.
6daba0
- *
6daba0
- * === Parameters
6daba0
- * * _digest_ is a message digest of the original input data to be signed
6daba0
- * * _sig_ is a DSA signature value
6daba0
- *
6daba0
- * === Example
6daba0
- *  dsa = OpenSSL::PKey::DSA.new(2048)
6daba0
- *  doc = "Sign me"
6daba0
- *  digest = OpenSSL::Digest.digest('SHA1', doc)
6daba0
- *  sig = dsa.syssign(digest)
6daba0
- *  puts dsa.sysverify(digest, sig) # => true
6daba0
- *
6daba0
- */
6daba0
-static VALUE
6daba0
-ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig)
6daba0
-{
6daba0
-    DSA *dsa;
6daba0
-    int ret;
6daba0
-
6daba0
-    GetDSA(self, dsa);
6daba0
-    StringValue(digest);
6daba0
-    StringValue(sig);
6daba0
-    /* type is ignored (0) */
6daba0
-    ret = DSA_verify(0, (unsigned char *)RSTRING_PTR(digest), RSTRING_LENINT(digest),
6daba0
-		     (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), dsa);
6daba0
-    if (ret < 0) {
6daba0
-	ossl_raise(eDSAError, NULL);
6daba0
-    }
6daba0
-    else if (ret == 1) {
6daba0
-	return Qtrue;
6daba0
-    }
6daba0
-
6daba0
-    return Qfalse;
6daba0
-}
6daba0
-
6daba0
 /*
6daba0
  * Document-method: OpenSSL::PKey::DSA#set_pqg
6daba0
  * call-seq:
6daba0
@@ -404,8 +318,6 @@ Init_ossl_dsa(void)
6daba0
     rb_define_alias(cDSA, "to_pem", "export");
6daba0
     rb_define_alias(cDSA, "to_s", "export");
6daba0
     rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0);
6daba0
-    rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
6daba0
-    rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);
6daba0
 
6daba0
     DEF_OSSL_PKEY_BN(cDSA, dsa, p);
6daba0
     DEF_OSSL_PKEY_BN(cDSA, dsa, q);
6daba0
-- 
6daba0
2.32.0
6daba0