Object
An abstract class that bundles signature creation (PKey#sign
) and validation (PKey#verify
) that is common to all implementations except OpenSSL::PKey::DH
Because PKey
is an abstract class, actually calling this method explicitly will raise a NotImplementedError.
static VALUE ossl_pkey_initialize(VALUE self) { if (rb_obj_is_instance_of(self, cPKey)) { ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); } return self; }
Used primarily to check if an OpenSSL::X509::Certificate#public_key
compares to its private key.
x509 = OpenSSL::X509::Certificate.new(pem_encoded_certificate) rsa_key = OpenSSL::PKey::RSA.new(pem_encoded_private_key) rsa_key.compare?(x509.public_key) => true | false
static VALUE ossl_pkey_compare(VALUE self, VALUE other) { int ret; EVP_PKEY *selfPKey; EVP_PKEY *otherPKey; GetPKey(self, selfPKey); GetPKey(other, otherPKey); /* Explicitly check the key type given EVP_PKEY_ASN1_METHOD(3) * docs param_cmp could return any negative number. */ if (EVP_PKEY_id(selfPKey) != EVP_PKEY_id(otherPKey)) ossl_raise(rb_eTypeError, "cannot match different PKey types"); ret = EVP_PKEY_eq(selfPKey, otherPKey); if (ret == 0) return Qfalse; else if (ret == 1) return Qtrue; else ossl_raise(ePKeyError, "EVP_PKEY_eq"); }
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; }
Derives a shared secret from pkey and peer_pkey. pkey must contain the private components, peer_pkey must contain the public components.
static VALUE ossl_pkey_derive(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey, *peer_pkey; EVP_PKEY_CTX *ctx; VALUE peer_pkey_obj, str; size_t keylen; int state; GetPKey(self, pkey); rb_scan_args(argc, argv, "1", &peer_pkey_obj); GetPKey(peer_pkey_obj, peer_pkey); ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL); if (!ctx) ossl_raise(ePKeyError, "EVP_PKEY_CTX_new"); if (EVP_PKEY_derive_init(ctx) <= 0) { EVP_PKEY_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_PKEY_derive_init"); } if (EVP_PKEY_derive_set_peer(ctx, peer_pkey) <= 0) { EVP_PKEY_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_PKEY_derive_set_peer"); } if (EVP_PKEY_derive(ctx, NULL, &keylen) <= 0) { EVP_PKEY_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_PKEY_derive"); } if (keylen > LONG_MAX) rb_raise(ePKeyError, "derived key would be too large"); str = ossl_str_new(NULL, (long)keylen, &state); if (state) { EVP_PKEY_CTX_free(ctx); rb_jump_tag(state); } if (EVP_PKEY_derive(ctx, (unsigned char *)RSTRING_PTR(str), &keylen) <= 0) { EVP_PKEY_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_PKEY_derive"); } EVP_PKEY_CTX_free(ctx); rb_str_set_len(str, keylen); return str; }
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; }
static VALUE ossl_pkey_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey, *pkey_other; TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey); TypedData_Get_Struct(other, EVP_PKEY, &ossl_evp_pkey_type, pkey_other); if (pkey) rb_raise(rb_eTypeError, "pkey already initialized"); if (pkey_other) { pkey = EVP_PKEY_dup(pkey_other); if (!pkey) ossl_raise(ePKeyError, "EVP_PKEY_dup"); RTYPEDDATA_DATA(self) = pkey; } return self; }
Returns a string describing the PKey
object.
static VALUE ossl_pkey_inspect(VALUE self) { EVP_PKEY *pkey; int nid; GetPKey(self, pkey); nid = EVP_PKEY_id(pkey); return rb_sprintf("#<%"PRIsVALUE":%p oid=%s>", rb_class_name(CLASS_OF(self)), (void *)self, OBJ_nid2sn(nid)); }
Returns the short name of the OID associated with pkey.
static VALUE ossl_pkey_oid(VALUE self) { EVP_PKEY *pkey; int nid; GetPKey(self, pkey); nid = EVP_PKEY_id(pkey); return rb_str_new_cstr(OBJ_nid2sn(nid)); }
Serializes the private key to DER-encoded PKCS #8 format. If called without arguments, unencrypted PKCS #8 PrivateKeyInfo format is used. If called with a cipher name and a password, PKCS #8 EncryptedPrivateKeyInfo format with PBES2 encryption scheme is used.
static VALUE ossl_pkey_private_to_der(int argc, VALUE *argv, VALUE self) { return do_pkcs8_export(argc, argv, self, 1); }
Serializes the private key to PEM-encoded PKCS #8 format. See private_to_der
for more details.
static VALUE ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self) { return do_pkcs8_export(argc, argv, self, 0); }
Serializes the public key to DER-encoded X.509 SubjectPublicKeyInfo format.
static VALUE ossl_pkey_public_to_der(VALUE self) { return ossl_pkey_export_spki(self, 1); }
Serializes the public key to PEM-encoded X.509 SubjectPublicKeyInfo format.
static VALUE ossl_pkey_public_to_pem(VALUE self) { return ossl_pkey_export_spki(self, 0); }
Hashes and signs the data
using a message digest algorithm digest
and a private key pkey
.
See verify
for the verification operation.
See also the man page EVP_DigestSign(3).
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 3.0.
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 ossl_pkey_sign(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; VALUE digest, data, options, sig; const EVP_MD *md = NULL; EVP_MD_CTX *ctx; EVP_PKEY_CTX *pctx; size_t siglen; int state; pkey = GetPrivPKeyPtr(self); rb_scan_args(argc, argv, "21", &digest, &data, &options); if (!NIL_P(digest)) md = ossl_evp_get_digestbyname(digest); StringValue(data); ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(ePKeyError, "EVP_MD_CTX_new"); if (EVP_DigestSignInit(ctx, &pctx, md, /* engine */NULL, pkey) < 1) { EVP_MD_CTX_free(ctx); 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 (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_DigestSign"); } if (siglen > LONG_MAX) { EVP_MD_CTX_free(ctx); 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_DigestSign(ctx, (unsigned char *)RSTRING_PTR(sig), &siglen, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_DigestSign"); } #else 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 (siglen > LONG_MAX) { EVP_MD_CTX_free(ctx); 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"); } #endif EVP_MD_CTX_free(ctx); rb_str_set_len(sig, siglen); return sig; }
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; }
Dumps key parameters, public key, and private key components contained in the key into a human-readable text.
This is intended for debugging purpose.
See also the man page EVP_PKEY_print_private(3).
static VALUE ossl_pkey_to_text(VALUE self) { EVP_PKEY *pkey; BIO *bio; GetPKey(self, pkey); if (!(bio = BIO_new(BIO_s_mem()))) ossl_raise(ePKeyError, "BIO_new"); if (EVP_PKEY_print_private(bio, pkey, 0, NULL) == 1) goto out; OSSL_BIO_reset(bio); if (EVP_PKEY_print_public(bio, pkey, 0, NULL) == 1) goto out; OSSL_BIO_reset(bio); if (EVP_PKEY_print_params(bio, pkey, 0, NULL) == 1) goto out; BIO_free(bio); ossl_raise(ePKeyError, "EVP_PKEY_print_params"); out: return ossl_membio2str(bio); }
Verifies the signature
for the data
using a message digest algorithm digest
and a public key pkey
.
Returns true
if the signature is successfully verified, false
otherwise. The caller must check the return value.
See sign
for the signing operation and an example.
See also the man page EVP_DigestVerify(3).
digest
See sign
.
signature
A String containing the signature to be verified.
data
See sign
.
options
See sign
. options
parameter was added in version 3.0.
static VALUE ossl_pkey_verify(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; VALUE digest, sig, data, options; const EVP_MD *md = NULL; EVP_MD_CTX *ctx; EVP_PKEY_CTX *pctx; 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_MD_CTX_new(); if (!ctx) ossl_raise(ePKeyError, "EVP_MD_CTX_new"); if (EVP_DigestVerifyInit(ctx, &pctx, md, /* engine */NULL, pkey) < 1) { EVP_MD_CTX_free(ctx); 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) ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)); EVP_MD_CTX_free(ctx); if (ret < 0) ossl_raise(ePKeyError, "EVP_DigestVerify"); #else if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate"); } ret = EVP_DigestVerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), RSTRING_LEN(sig)); EVP_MD_CTX_free(ctx); if (ret < 0) ossl_raise(ePKeyError, "EVP_DigestVerifyFinal"); #endif if (ret) return Qtrue; else { ossl_clear_error(); return Qfalse; } }
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; } }
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; }