Skip to content

Commit ae19454

Browse files
committed
pkey: support 'one-shot' signing and verification
OpenSSL 1.1.1 added EVP_DigestSign() and EVP_DigestVerify() functions to the interface. Some EVP_PKEY methods such as PureEdDSA algorithms do not support the streaming mechanism and require us to use them.
1 parent 9ff6e51 commit ae19454

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

ext/openssl/ossl_pkey.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,26 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
719719
EVP_MD_CTX_free(ctx);
720720
ossl_raise(ePKeyError, "EVP_DigestSignInit");
721721
}
722+
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
723+
if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data),
724+
RSTRING_LEN(data)) < 1) {
725+
EVP_MD_CTX_free(ctx);
726+
ossl_raise(ePKeyError, "EVP_DigestSign");
727+
}
728+
if (siglen > LONG_MAX)
729+
rb_raise(ePKeyError, "signature would be too large");
730+
sig = ossl_str_new(NULL, (long)siglen, &state);
731+
if (state) {
732+
EVP_MD_CTX_free(ctx);
733+
rb_jump_tag(state);
734+
}
735+
if (EVP_DigestSign(ctx, (unsigned char *)RSTRING_PTR(sig), &siglen,
736+
(unsigned char *)RSTRING_PTR(data),
737+
RSTRING_LEN(data)) < 1) {
738+
EVP_MD_CTX_free(ctx);
739+
ossl_raise(ePKeyError, "EVP_DigestSign");
740+
}
741+
#else
722742
if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) {
723743
EVP_MD_CTX_free(ctx);
724744
ossl_raise(ePKeyError, "EVP_DigestSignUpdate");
@@ -739,6 +759,7 @@ ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
739759
EVP_MD_CTX_free(ctx);
740760
ossl_raise(ePKeyError, "EVP_DigestSignFinal");
741761
}
762+
#endif
742763
EVP_MD_CTX_free(ctx);
743764
rb_str_set_len(sig, siglen);
744765
return sig;
@@ -787,6 +808,14 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
787808
EVP_MD_CTX_free(ctx);
788809
ossl_raise(ePKeyError, "EVP_DigestVerifyInit");
789810
}
811+
#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
812+
ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig),
813+
RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data),
814+
RSTRING_LEN(data));
815+
EVP_MD_CTX_free(ctx);
816+
if (ret < 0)
817+
ossl_raise(ePKeyError, "EVP_DigestVerify");
818+
#else
790819
if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) {
791820
EVP_MD_CTX_free(ctx);
792821
ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate");
@@ -796,6 +825,7 @@ ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
796825
EVP_MD_CTX_free(ctx);
797826
if (ret < 0)
798827
ossl_raise(ePKeyError, "EVP_DigestVerifyFinal");
828+
#endif
799829
if (ret)
800830
return Qtrue;
801831
else {

test/openssl/test_pkey.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,49 @@ def test_hmac_sign_verify
8080
pkey.verify("SHA256", "data", hmac)
8181
}
8282
end
83+
84+
def test_ed25519
85+
# Test vector from RFC 8032 Section 7.1 TEST 2
86+
priv_pem = <<~EOF
87+
-----BEGIN PRIVATE KEY-----
88+
MC4CAQAwBQYDK2VwBCIEIEzNCJso/5banbbDRuwRTg9bijGfNaumJNqM9u1PuKb7
89+
-----END PRIVATE KEY-----
90+
EOF
91+
pub_pem = <<~EOF
92+
-----BEGIN PUBLIC KEY-----
93+
MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
94+
-----END PUBLIC KEY-----
95+
EOF
96+
begin
97+
priv = OpenSSL::PKey.read(priv_pem)
98+
pub = OpenSSL::PKey.read(pub_pem)
99+
rescue OpenSSL::PKey::PKeyError
100+
# OpenSSL < 1.1.1
101+
pend "Ed25519 is not implemented"
102+
end
103+
assert_instance_of OpenSSL::PKey::PKey, priv
104+
assert_instance_of OpenSSL::PKey::PKey, pub
105+
assert_equal priv_pem, priv.private_to_pem
106+
assert_equal pub_pem, priv.public_to_pem
107+
assert_equal pub_pem, pub.public_to_pem
108+
109+
sig = [<<~EOF.gsub(/[^0-9a-f]/, "")].pack("H*")
110+
92a009a9f0d4cab8720e820b5f642540
111+
a2b27b5416503f8fb3762223ebdb69da
112+
085ac1e43e15996e458f3613d0f11d8c
113+
387b2eaeb4302aeeb00d291612bb0c00
114+
EOF
115+
data = ["72"].pack("H*")
116+
assert_equal sig, priv.sign(nil, data)
117+
assert_equal true, priv.verify(nil, sig, data)
118+
assert_equal true, pub.verify(nil, sig, data)
119+
assert_equal false, pub.verify(nil, sig, data.succ)
120+
121+
# PureEdDSA wants nil as the message digest
122+
assert_raise(OpenSSL::PKey::PKeyError) { priv.sign("SHA512", data) }
123+
assert_raise(OpenSSL::PKey::PKeyError) { pub.verify("SHA512", sig, data) }
124+
125+
# Ed25519 pkey type does not support key derivation
126+
assert_raise(OpenSSL::PKey::PKeyError) { priv.derive(pub) }
127+
end
83128
end

0 commit comments

Comments
 (0)