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