зеркало из https://github.com/github/ruby.git
[ruby/openssl] pkey: allow setting algorithm-specific options in #sign and #verify
Similarly to OpenSSL::PKey.generate_key and .generate_parameters, let OpenSSL::PKey::PKey#sign and #verify take an optional parameter for specifying control strings for EVP_PKEY_CTX_ctrl_str(). https://github.com/ruby/openssl/commit/faf85d7c1d
This commit is contained in:
Родитель
e2014d0354
Коммит
8cfe92b8a2
|
@ -777,33 +777,51 @@ ossl_pkey_compare(VALUE self, VALUE other)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* pkey.sign(digest, data) -> String
|
* pkey.sign(digest, data [, options]) -> string
|
||||||
*
|
*
|
||||||
* To sign the String _data_, _digest_, an instance of OpenSSL::Digest, must
|
* Hashes and signs the +data+ using a message digest algorithm +digest+ and
|
||||||
* be provided. The return value is again a String containing the signature.
|
* a private key +pkey+.
|
||||||
* A PKeyError is raised should errors occur.
|
|
||||||
* Any previous state of the Digest instance is irrelevant to the signature
|
|
||||||
* outcome, the digest instance is reset to its initial state during the
|
|
||||||
* operation.
|
|
||||||
*
|
*
|
||||||
* == Example
|
* See #verify for the verification operation.
|
||||||
* data = 'Sign me!'
|
*
|
||||||
* digest = OpenSSL::Digest.new('SHA256')
|
* See also the man page EVP_DigestSign(3).
|
||||||
* pkey = OpenSSL::PKey::RSA.new(2048)
|
*
|
||||||
* signature = pkey.sign(digest, data)
|
* +digest+::
|
||||||
|
* A String that represents the message digest algorithm name, or +nil+
|
||||||
|
* if the PKey type requires no digest algorithm.
|
||||||
|
* For backwards compatibility, this can be an instance of OpenSSL::Digest.
|
||||||
|
* Its state will not affect the signature.
|
||||||
|
* +data+::
|
||||||
|
* A String. The data to be hashed and 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.
|
||||||
|
* +options+ parameter was added in version 2.3.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* data = "Sign me!"
|
||||||
|
* pkey = OpenSSL::PKey.generate_key("RSA", rsa_keygen_bits: 2048)
|
||||||
|
* signopts = { rsa_padding_mode: "pss" }
|
||||||
|
* signature = pkey.sign("SHA256", data, signopts)
|
||||||
|
*
|
||||||
|
* # Creates a copy of the RSA key pkey, but without the private components
|
||||||
|
* pub_key = pkey.public_key
|
||||||
|
* puts pub_key.verify("SHA256", signature, data, signopts) # => true
|
||||||
*/
|
*/
|
||||||
static VALUE
|
static VALUE
|
||||||
ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
|
ossl_pkey_sign(int argc, VALUE *argv, VALUE self)
|
||||||
{
|
{
|
||||||
EVP_PKEY *pkey;
|
EVP_PKEY *pkey;
|
||||||
|
VALUE digest, data, options, sig;
|
||||||
const EVP_MD *md = NULL;
|
const EVP_MD *md = NULL;
|
||||||
EVP_MD_CTX *ctx;
|
EVP_MD_CTX *ctx;
|
||||||
|
EVP_PKEY_CTX *pctx;
|
||||||
size_t siglen;
|
size_t siglen;
|
||||||
int state;
|
int state;
|
||||||
VALUE sig;
|
|
||||||
|
|
||||||
pkey = GetPrivPKeyPtr(self);
|
pkey = GetPrivPKeyPtr(self);
|
||||||
|
rb_scan_args(argc, argv, "21", &digest, &data, &options);
|
||||||
if (!NIL_P(digest))
|
if (!NIL_P(digest))
|
||||||
md = ossl_evp_get_digestbyname(digest);
|
md = ossl_evp_get_digestbyname(digest);
|
||||||
StringValue(data);
|
StringValue(data);
|
||||||
|
@ -811,10 +829,17 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
|
||||||
ctx = EVP_MD_CTX_new();
|
ctx = EVP_MD_CTX_new();
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
|
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
|
||||||
if (EVP_DigestSignInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) {
|
if (EVP_DigestSignInit(ctx, &pctx, md, /* engine */NULL, pkey) < 1) {
|
||||||
EVP_MD_CTX_free(ctx);
|
EVP_MD_CTX_free(ctx);
|
||||||
ossl_raise(ePKeyError, "EVP_DigestSignInit");
|
ossl_raise(ePKeyError, "EVP_DigestSignInit");
|
||||||
}
|
}
|
||||||
|
if (!NIL_P(options)) {
|
||||||
|
pkey_ctx_apply_options(pctx, options, &state);
|
||||||
|
if (state) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
rb_jump_tag(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
|
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
|
||||||
if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data),
|
if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data),
|
||||||
RSTRING_LEN(data)) < 1) {
|
RSTRING_LEN(data)) < 1) {
|
||||||
|
@ -866,35 +891,40 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* pkey.verify(digest, signature, data) -> String
|
* pkey.verify(digest, signature, data [, options]) -> true or false
|
||||||
*
|
*
|
||||||
* To verify the String _signature_, _digest_, an instance of
|
* Verifies the +signature+ for the +data+ using a message digest algorithm
|
||||||
* OpenSSL::Digest, must be provided to re-compute the message digest of the
|
* +digest+ and a public key +pkey+.
|
||||||
* original _data_, also a String. The return value is +true+ if the
|
|
||||||
* signature is valid, +false+ otherwise. A PKeyError is raised should errors
|
|
||||||
* occur.
|
|
||||||
* Any previous state of the Digest instance is irrelevant to the validation
|
|
||||||
* outcome, the digest instance is reset to its initial state during the
|
|
||||||
* operation.
|
|
||||||
*
|
*
|
||||||
* == Example
|
* Returns +true+ if the signature is successfully verified, +false+ otherwise.
|
||||||
* data = 'Sign me!'
|
* The caller must check the return value.
|
||||||
* digest = OpenSSL::Digest.new('SHA256')
|
*
|
||||||
* pkey = OpenSSL::PKey::RSA.new(2048)
|
* See #sign for the signing operation and an example.
|
||||||
* signature = pkey.sign(digest, data)
|
*
|
||||||
* pub_key = pkey.public_key
|
* See also the man page EVP_DigestVerify(3).
|
||||||
* puts pub_key.verify(digest, signature, data) # => true
|
*
|
||||||
|
* +digest+::
|
||||||
|
* See #sign.
|
||||||
|
* +signature+::
|
||||||
|
* A String containing the signature to be verified.
|
||||||
|
* +data+::
|
||||||
|
* See #sign.
|
||||||
|
* +options+::
|
||||||
|
* See #sign. +options+ parameter was added in version 2.3.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
static VALUE
|
||||||
ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
|
ossl_pkey_verify(int argc, VALUE *argv, VALUE self)
|
||||||
{
|
{
|
||||||
EVP_PKEY *pkey;
|
EVP_PKEY *pkey;
|
||||||
|
VALUE digest, sig, data, options;
|
||||||
const EVP_MD *md = NULL;
|
const EVP_MD *md = NULL;
|
||||||
EVP_MD_CTX *ctx;
|
EVP_MD_CTX *ctx;
|
||||||
int ret;
|
EVP_PKEY_CTX *pctx;
|
||||||
|
int state, ret;
|
||||||
|
|
||||||
GetPKey(self, pkey);
|
GetPKey(self, pkey);
|
||||||
|
rb_scan_args(argc, argv, "31", &digest, &sig, &data, &options);
|
||||||
ossl_pkey_check_public_key(pkey);
|
ossl_pkey_check_public_key(pkey);
|
||||||
if (!NIL_P(digest))
|
if (!NIL_P(digest))
|
||||||
md = ossl_evp_get_digestbyname(digest);
|
md = ossl_evp_get_digestbyname(digest);
|
||||||
|
@ -904,10 +934,17 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
|
||||||
ctx = EVP_MD_CTX_new();
|
ctx = EVP_MD_CTX_new();
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
|
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
|
||||||
if (EVP_DigestVerifyInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) {
|
if (EVP_DigestVerifyInit(ctx, &pctx, md, /* engine */NULL, pkey) < 1) {
|
||||||
EVP_MD_CTX_free(ctx);
|
EVP_MD_CTX_free(ctx);
|
||||||
ossl_raise(ePKeyError, "EVP_DigestVerifyInit");
|
ossl_raise(ePKeyError, "EVP_DigestVerifyInit");
|
||||||
}
|
}
|
||||||
|
if (!NIL_P(options)) {
|
||||||
|
pkey_ctx_apply_options(pctx, options, &state);
|
||||||
|
if (state) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
rb_jump_tag(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
|
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
|
||||||
ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig),
|
ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig),
|
||||||
RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data),
|
RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data),
|
||||||
|
@ -1081,8 +1118,8 @@ Init_ossl_pkey(void)
|
||||||
rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
|
rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
|
||||||
rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1);
|
rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1);
|
||||||
|
|
||||||
rb_define_method(cPKey, "sign", ossl_pkey_sign, 2);
|
rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);
|
||||||
rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
|
rb_define_method(cPKey, "verify", ossl_pkey_verify, -1);
|
||||||
rb_define_method(cPKey, "derive", ossl_pkey_derive, -1);
|
rb_define_method(cPKey, "derive", ossl_pkey_derive, -1);
|
||||||
|
|
||||||
id_private_q = rb_intern("private?");
|
id_private_q = rb_intern("private?");
|
||||||
|
|
|
@ -117,27 +117,21 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
|
||||||
assert_equal false, rsa1024.verify("SHA256", signature1, data)
|
assert_equal false, rsa1024.verify("SHA256", signature1, data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_digest_state_irrelevant_sign
|
def test_sign_verify_options
|
||||||
key = Fixtures.pkey("rsa1024")
|
key = Fixtures.pkey("rsa1024")
|
||||||
digest1 = OpenSSL::Digest.new('SHA1')
|
data = "Sign me!"
|
||||||
digest2 = OpenSSL::Digest.new('SHA1')
|
pssopts = {
|
||||||
data = 'Sign me!'
|
"rsa_padding_mode" => "pss",
|
||||||
digest1 << 'Change state of digest1'
|
"rsa_pss_saltlen" => 20,
|
||||||
sig1 = key.sign(digest1, data)
|
"rsa_mgf1_md" => "SHA1"
|
||||||
sig2 = key.sign(digest2, data)
|
}
|
||||||
assert_equal(sig1, sig2)
|
sig_pss = key.sign("SHA256", data, pssopts)
|
||||||
end
|
assert_equal 128, sig_pss.bytesize
|
||||||
|
assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
|
||||||
def test_digest_state_irrelevant_verify
|
assert_equal true, key.verify_pss("SHA256", sig_pss, data,
|
||||||
key = Fixtures.pkey("rsa1024")
|
salt_length: 20, mgf1_hash: "SHA1")
|
||||||
digest1 = OpenSSL::Digest.new('SHA1')
|
# Defaults to PKCS #1 v1.5 padding => verification failure
|
||||||
digest2 = OpenSSL::Digest.new('SHA1')
|
assert_equal false, key.verify("SHA256", sig_pss, data)
|
||||||
data = 'Sign me!'
|
|
||||||
sig = key.sign(digest1, data)
|
|
||||||
digest1.reset
|
|
||||||
digest1 << 'Change state of digest1'
|
|
||||||
assert(key.verify(digest1, sig, data))
|
|
||||||
assert(key.verify(digest2, sig, data))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_verify_empty_rsa
|
def test_verify_empty_rsa
|
||||||
|
|
Загрузка…
Ссылка в новой задаче