From 5cae289682c6b8fea6324ae8f26dbcc90ebaaa2f Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Mon, 15 May 2017 23:47:47 +0900 Subject: [ruby/openssl] pkey: port PKey::PKey#sign and #verify to the EVP_Digest* interface Use EVP_DigestSign*() and EVP_DigestVerify*() interface instead of the old EVP_Sign*() and EVP_Verify*() functions. They were added in OpenSSL 1.0.0. Also, allow the digest to be specified as nil, as certain EVP_PKEY types don't expect a digest algorithm. https://github.com/ruby/openssl/commit/9ff6e5143b --- ext/openssl/ossl_pkey.c | 90 +++++++++++++++++++++++++++-------------------- test/openssl/test_pkey.rb | 12 +++++++ 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 1f3dd39b9b..a0d73f5821 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -753,35 +753,47 @@ static VALUE ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) { EVP_PKEY *pkey; - const EVP_MD *md; + const EVP_MD *md = NULL; EVP_MD_CTX *ctx; - unsigned int buf_len; - VALUE str; - int result; + size_t siglen; + int state; + VALUE sig; pkey = GetPrivPKeyPtr(self); - md = ossl_evp_get_digestbyname(digest); + if (!NIL_P(digest)) + md = ossl_evp_get_digestbyname(digest); StringValue(data); - str = rb_str_new(0, EVP_PKEY_size(pkey)); ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(ePKeyError, "EVP_MD_CTX_new"); - if (!EVP_SignInit_ex(ctx, md, NULL)) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_SignInit_ex"); + ossl_raise(ePKeyError, "EVP_MD_CTX_new"); + if (EVP_DigestSignInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignInit"); + } + if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignUpdate"); + } + if (EVP_DigestSignFinal(ctx, NULL, &siglen) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignFinal"); } - if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_SignUpdate"); + if (siglen > LONG_MAX) + rb_raise(ePKeyError, "signature would be too large"); + sig = ossl_str_new(NULL, (long)siglen, &state); + if (state) { + EVP_MD_CTX_free(ctx); + rb_jump_tag(state); + } + if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(sig), + &siglen) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestSignFinal"); } - result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey); EVP_MD_CTX_free(ctx); - if (!result) - ossl_raise(ePKeyError, "EVP_SignFinal"); - rb_str_set_len(str, buf_len); - - return str; + rb_str_set_len(sig, siglen); + return sig; } /* @@ -809,38 +821,38 @@ static VALUE ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) { EVP_PKEY *pkey; - const EVP_MD *md; + const EVP_MD *md = NULL; EVP_MD_CTX *ctx; - int siglen, result; + int ret; GetPKey(self, pkey); ossl_pkey_check_public_key(pkey); - md = ossl_evp_get_digestbyname(digest); + if (!NIL_P(digest)) + md = ossl_evp_get_digestbyname(digest); StringValue(sig); - siglen = RSTRING_LENINT(sig); StringValue(data); ctx = EVP_MD_CTX_new(); if (!ctx) - ossl_raise(ePKeyError, "EVP_MD_CTX_new"); - if (!EVP_VerifyInit_ex(ctx, md, NULL)) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_VerifyInit_ex"); + ossl_raise(ePKeyError, "EVP_MD_CTX_new"); + if (EVP_DigestVerifyInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestVerifyInit"); } - if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { - EVP_MD_CTX_free(ctx); - ossl_raise(ePKeyError, "EVP_VerifyUpdate"); + if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { + EVP_MD_CTX_free(ctx); + ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate"); } - result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey); + ret = EVP_DigestVerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), + RSTRING_LEN(sig)); EVP_MD_CTX_free(ctx); - switch (result) { - case 0: - ossl_clear_error(); - return Qfalse; - case 1: - return Qtrue; - default: - ossl_raise(ePKeyError, "EVP_VerifyFinal"); + if (ret < 0) + ossl_raise(ePKeyError, "EVP_DigestVerifyFinal"); + if (ret) + return Qtrue; + else { + ossl_clear_error(); + return Qfalse; } } diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb index a325a1ea2b..247ba84f83 100644 --- a/test/openssl/test_pkey.rb +++ b/test/openssl/test_pkey.rb @@ -68,4 +68,16 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase assert_equal 512, pkey.p.num_bits assert_not_equal nil, pkey.priv_key end + + def test_hmac_sign_verify + pkey = OpenSSL::PKey.generate_key("HMAC", { "key" => "abcd" }) + + hmac = OpenSSL::HMAC.new("abcd", "SHA256").update("data").digest + assert_equal hmac, pkey.sign("SHA256", "data") + + # EVP_PKEY_HMAC does not support verify + assert_raise(OpenSSL::PKey::PKeyError) { + pkey.verify("SHA256", "data", hmac) + } + end end -- cgit v1.2.3