Skip to content

Commit 9ff6e51

Browse files
committed
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.
1 parent d8e8e57 commit 9ff6e51

File tree

2 files changed

+63
-39
lines changed

2 files changed

+63
-39
lines changed

ext/openssl/ossl_pkey.c

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -701,35 +701,47 @@ static VALUE
701701
ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
702702
{
703703
EVP_PKEY *pkey;
704-
const EVP_MD *md;
704+
const EVP_MD *md = NULL;
705705
EVP_MD_CTX *ctx;
706-
unsigned int buf_len;
707-
VALUE str;
708-
int result;
706+
size_t siglen;
707+
int state;
708+
VALUE sig;
709709

710710
pkey = GetPrivPKeyPtr(self);
711-
md = ossl_evp_get_digestbyname(digest);
711+
if (!NIL_P(digest))
712+
md = ossl_evp_get_digestbyname(digest);
712713
StringValue(data);
713-
str = rb_str_new(0, EVP_PKEY_size(pkey));
714714

715715
ctx = EVP_MD_CTX_new();
716716
if (!ctx)
717-
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
718-
if (!EVP_SignInit_ex(ctx, md, NULL)) {
719-
EVP_MD_CTX_free(ctx);
720-
ossl_raise(ePKeyError, "EVP_SignInit_ex");
717+
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
718+
if (EVP_DigestSignInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) {
719+
EVP_MD_CTX_free(ctx);
720+
ossl_raise(ePKeyError, "EVP_DigestSignInit");
721+
}
722+
if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) {
723+
EVP_MD_CTX_free(ctx);
724+
ossl_raise(ePKeyError, "EVP_DigestSignUpdate");
725+
}
726+
if (EVP_DigestSignFinal(ctx, NULL, &siglen) < 1) {
727+
EVP_MD_CTX_free(ctx);
728+
ossl_raise(ePKeyError, "EVP_DigestSignFinal");
729+
}
730+
if (siglen > LONG_MAX)
731+
rb_raise(ePKeyError, "signature would be too large");
732+
sig = ossl_str_new(NULL, (long)siglen, &state);
733+
if (state) {
734+
EVP_MD_CTX_free(ctx);
735+
rb_jump_tag(state);
721736
}
722-
if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
723-
EVP_MD_CTX_free(ctx);
724-
ossl_raise(ePKeyError, "EVP_SignUpdate");
737+
if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(sig),
738+
&siglen) < 1) {
739+
EVP_MD_CTX_free(ctx);
740+
ossl_raise(ePKeyError, "EVP_DigestSignFinal");
725741
}
726-
result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey);
727742
EVP_MD_CTX_free(ctx);
728-
if (!result)
729-
ossl_raise(ePKeyError, "EVP_SignFinal");
730-
rb_str_set_len(str, buf_len);
731-
732-
return str;
743+
rb_str_set_len(sig, siglen);
744+
return sig;
733745
}
734746

735747
/*
@@ -757,38 +769,38 @@ static VALUE
757769
ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
758770
{
759771
EVP_PKEY *pkey;
760-
const EVP_MD *md;
772+
const EVP_MD *md = NULL;
761773
EVP_MD_CTX *ctx;
762-
int siglen, result;
774+
int ret;
763775

764776
GetPKey(self, pkey);
765777
ossl_pkey_check_public_key(pkey);
766-
md = ossl_evp_get_digestbyname(digest);
778+
if (!NIL_P(digest))
779+
md = ossl_evp_get_digestbyname(digest);
767780
StringValue(sig);
768-
siglen = RSTRING_LENINT(sig);
769781
StringValue(data);
770782

771783
ctx = EVP_MD_CTX_new();
772784
if (!ctx)
773-
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
774-
if (!EVP_VerifyInit_ex(ctx, md, NULL)) {
775-
EVP_MD_CTX_free(ctx);
776-
ossl_raise(ePKeyError, "EVP_VerifyInit_ex");
785+
ossl_raise(ePKeyError, "EVP_MD_CTX_new");
786+
if (EVP_DigestVerifyInit(ctx, NULL, md, /* engine */NULL, pkey) < 1) {
787+
EVP_MD_CTX_free(ctx);
788+
ossl_raise(ePKeyError, "EVP_DigestVerifyInit");
777789
}
778-
if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
779-
EVP_MD_CTX_free(ctx);
780-
ossl_raise(ePKeyError, "EVP_VerifyUpdate");
790+
if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) {
791+
EVP_MD_CTX_free(ctx);
792+
ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate");
781793
}
782-
result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey);
794+
ret = EVP_DigestVerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig),
795+
RSTRING_LEN(sig));
783796
EVP_MD_CTX_free(ctx);
784-
switch (result) {
785-
case 0:
786-
ossl_clear_error();
787-
return Qfalse;
788-
case 1:
789-
return Qtrue;
790-
default:
791-
ossl_raise(ePKeyError, "EVP_VerifyFinal");
797+
if (ret < 0)
798+
ossl_raise(ePKeyError, "EVP_DigestVerifyFinal");
799+
if (ret)
800+
return Qtrue;
801+
else {
802+
ossl_clear_error();
803+
return Qfalse;
792804
}
793805
}
794806

test/openssl/test_pkey.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,16 @@ def test_s_generate_key
6868
assert_equal 512, pkey.p.num_bits
6969
assert_not_equal nil, pkey.priv_key
7070
end
71+
72+
def test_hmac_sign_verify
73+
pkey = OpenSSL::PKey.generate_key("HMAC", { "key" => "abcd" })
74+
75+
hmac = OpenSSL::HMAC.new("abcd", "SHA256").update("data").digest
76+
assert_equal hmac, pkey.sign("SHA256", "data")
77+
78+
# EVP_PKEY_HMAC does not support verify
79+
assert_raise(OpenSSL::PKey::PKeyError) {
80+
pkey.verify("SHA256", "data", hmac)
81+
}
82+
end
7183
end

0 commit comments

Comments
 (0)