Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 35 additions & 49 deletions ext/openssl/lib/openssl/pkey.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
require_relative 'marshal'

module OpenSSL::PKey
# Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError.
DHError = PKeyError

class DH
include OpenSSL::Marshal

Expand Down Expand Up @@ -102,7 +105,7 @@ def compute_key(pub_bn)
# puts dh0.pub_key == dh.pub_key #=> false
def generate_key!
if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x30000000
raise DHError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \
raise PKeyError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \
"use OpenSSL::PKey.generate_key instead"
end

Expand Down Expand Up @@ -147,6 +150,9 @@ def new(*args, &blk) # :nodoc:
end
end

# Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError.
DSAError = PKeyError

class DSA
include OpenSSL::Marshal

Expand Down Expand Up @@ -242,13 +248,9 @@ def new(*args, &blk) # :nodoc:
# sig = dsa.sign_raw(nil, digest)
# p dsa.verify_raw(nil, sig, digest) #=> true
def syssign(string)
q or raise OpenSSL::PKey::DSAError, "incomplete DSA"
private? or raise OpenSSL::PKey::DSAError, "Private DSA key needed!"
begin
sign_raw(nil, string)
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::DSAError, $!.message
end
q or raise PKeyError, "incomplete DSA"
private? or raise PKeyError, "Private DSA key needed!"
sign_raw(nil, string)
end

# :call-seq:
Expand All @@ -266,12 +268,13 @@ def syssign(string)
# A \DSA signature value.
def sysverify(digest, sig)
verify_raw(nil, sig, digest)
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::DSAError, $!.message
end
end

if defined?(EC)
# Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError.
ECError = PKeyError

class EC
include OpenSSL::Marshal

Expand All @@ -282,8 +285,6 @@ class EC
# Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
def dsa_sign_asn1(data)
sign_raw(nil, data)
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::ECError, $!.message
end

# :call-seq:
Expand All @@ -293,8 +294,6 @@ def dsa_sign_asn1(data)
# Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
def dsa_verify_asn1(data, sig)
verify_raw(nil, sig, data)
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::ECError, $!.message
end

# :call-seq:
Expand Down Expand Up @@ -334,6 +333,9 @@ def to_bn(conversion_form = group.point_conversion_form)
end
end

# Alias of PKeyError. Before version 4.0.0, this was a subclass of PKeyError.
RSAError = PKeyError

class RSA
include OpenSSL::Marshal

Expand Down Expand Up @@ -407,15 +409,11 @@ def new(*args, &blk) # :nodoc:
# Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
# PKey::PKey#verify_recover instead.
def private_encrypt(string, padding = PKCS1_PADDING)
n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
private? or raise OpenSSL::PKey::RSAError, "private key needed."
begin
sign_raw(nil, string, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::RSAError, $!.message
end
n or raise PKeyError, "incomplete RSA"
private? or raise PKeyError, "private key needed."
sign_raw(nil, string, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
end

# :call-seq:
Expand All @@ -430,14 +428,10 @@ def private_encrypt(string, padding = PKCS1_PADDING)
# Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
# PKey::PKey#verify_recover instead.
def public_decrypt(string, padding = PKCS1_PADDING)
n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
begin
verify_recover(nil, string, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::RSAError, $!.message
end
n or raise PKeyError, "incomplete RSA"
verify_recover(nil, string, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
end

# :call-seq:
Expand All @@ -452,14 +446,10 @@ def public_decrypt(string, padding = PKCS1_PADDING)
# <b>Deprecated in version 3.0</b>.
# Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
def public_encrypt(data, padding = PKCS1_PADDING)
n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
begin
encrypt(data, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::RSAError, $!.message
end
n or raise PKeyError, "incomplete RSA"
encrypt(data, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
end

# :call-seq:
Expand All @@ -473,15 +463,11 @@ def public_encrypt(data, padding = PKCS1_PADDING)
# <b>Deprecated in version 3.0</b>.
# Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
def private_decrypt(data, padding = PKCS1_PADDING)
n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
private? or raise OpenSSL::PKey::RSAError, "private key needed."
begin
decrypt(data, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
rescue OpenSSL::PKey::PKeyError
raise OpenSSL::PKey::RSAError, $!.message
end
n or raise PKeyError, "incomplete RSA"
private? or raise PKeyError, "private key needed."
decrypt(data, {
"rsa_padding_mode" => translate_padding_mode(padding),
})
end

PKCS1_PADDING = 1
Expand All @@ -500,7 +486,7 @@ def private_decrypt(data, padding = PKCS1_PADDING)
when PKCS1_OAEP_PADDING
"oaep"
else
raise OpenSSL::PKey::PKeyError, "unsupported padding mode"
raise PKeyError, "unsupported padding mode"
end
end
end
Expand Down
78 changes: 54 additions & 24 deletions ext/openssl/ossl_cipher.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
static VALUE cCipher;
static VALUE eCipherError;
static VALUE eAuthTagError;
static ID id_auth_tag_len, id_key_set;
static ID id_auth_tag_len, id_key_set, id_cipher_holder;

static VALUE ossl_cipher_alloc(VALUE klass);
static void ossl_cipher_free(void *ptr);
Expand All @@ -46,30 +46,58 @@ static const rb_data_type_t ossl_cipher_type = {
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};

#ifdef OSSL_USE_PROVIDER
static void
ossl_evp_cipher_free(void *ptr)
{
// This is safe to call against const EVP_CIPHER * returned by
// EVP_get_cipherbyname()
EVP_CIPHER_free(ptr);
}

static const rb_data_type_t ossl_evp_cipher_holder_type = {
"OpenSSL/EVP_CIPHER",
{
.dfree = ossl_evp_cipher_free,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
#endif

/*
* PUBLIC
*/
const EVP_CIPHER *
ossl_evp_get_cipherbyname(VALUE obj)
ossl_evp_cipher_fetch(VALUE obj, volatile VALUE *holder)
{
*holder = Qnil;
if (rb_obj_is_kind_of(obj, cCipher)) {
EVP_CIPHER_CTX *ctx;

GetCipher(obj, ctx);

return EVP_CIPHER_CTX_cipher(ctx);
EVP_CIPHER_CTX *ctx;
GetCipher(obj, ctx);
EVP_CIPHER *cipher = (EVP_CIPHER *)EVP_CIPHER_CTX_cipher(ctx);
#ifdef OSSL_USE_PROVIDER
*holder = TypedData_Wrap_Struct(0, &ossl_evp_cipher_holder_type, NULL);
if (!EVP_CIPHER_up_ref(cipher))
ossl_raise(eCipherError, "EVP_CIPHER_up_ref");
RTYPEDDATA_DATA(*holder) = cipher;
#endif
return cipher;
}
else {
const EVP_CIPHER *cipher;

StringValueCStr(obj);
cipher = EVP_get_cipherbyname(RSTRING_PTR(obj));
if (!cipher)
ossl_raise(rb_eArgError,
"unsupported cipher algorithm: %"PRIsVALUE, obj);

return cipher;
const char *name = StringValueCStr(obj);
EVP_CIPHER *cipher = (EVP_CIPHER *)EVP_get_cipherbyname(name);
#ifdef OSSL_USE_PROVIDER
if (!cipher) {
ossl_clear_error();
*holder = TypedData_Wrap_Struct(0, &ossl_evp_cipher_holder_type, NULL);
cipher = EVP_CIPHER_fetch(NULL, name, NULL);
RTYPEDDATA_DATA(*holder) = cipher;
}
#endif
if (!cipher)
ossl_raise(eCipherError, "unsupported cipher algorithm: %"PRIsVALUE,
obj);
return cipher;
}

VALUE
Expand All @@ -78,6 +106,9 @@ ossl_cipher_new(const EVP_CIPHER *cipher)
VALUE ret;
EVP_CIPHER_CTX *ctx;

// NOTE: This does not set id_cipher_holder because this function should
// only be called from ossl_engine.c, which will not use any
// reference-counted ciphers.
ret = ossl_cipher_alloc(cCipher);
AllocCipher(ret, ctx);
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
Expand Down Expand Up @@ -114,19 +145,17 @@ ossl_cipher_initialize(VALUE self, VALUE str)
{
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *cipher;
char *name;
VALUE cipher_holder;

name = StringValueCStr(str);
GetCipherInit(self, ctx);
if (ctx) {
ossl_raise(rb_eRuntimeError, "Cipher already initialized!");
}
cipher = ossl_evp_cipher_fetch(str, &cipher_holder);
AllocCipher(self, ctx);
if (!(cipher = EVP_get_cipherbyname(name))) {
ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str);
}
if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1)
ossl_raise(eCipherError, NULL);
ossl_raise(eCipherError, "EVP_CipherInit_ex");
rb_ivar_set(self, id_cipher_holder, cipher_holder);

return self;
}
Expand Down Expand Up @@ -268,7 +297,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
{
EVP_CIPHER_CTX *ctx;
const EVP_MD *digest;
VALUE vpass, vsalt, viter, vdigest;
VALUE vpass, vsalt, viter, vdigest, md_holder;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
int iter;

Expand All @@ -283,7 +312,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
if (iter <= 0)
rb_raise(rb_eArgError, "iterations must be a positive integer");
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_get_digestbyname(vdigest);
digest = NIL_P(vdigest) ? EVP_md5() : ossl_evp_md_fetch(vdigest, &md_holder);
GetCipher(self, ctx);
EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt,
(unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv);
Expand Down Expand Up @@ -1110,4 +1139,5 @@ Init_ossl_cipher(void)

id_auth_tag_len = rb_intern_const("auth_tag_len");
id_key_set = rb_intern_const("key_set");
id_cipher_holder = rb_intern_const("EVP_CIPHER_holder");
}
11 changes: 10 additions & 1 deletion ext/openssl/ossl_cipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@
#if !defined(_OSSL_CIPHER_H_)
#define _OSSL_CIPHER_H_

const EVP_CIPHER *ossl_evp_get_cipherbyname(VALUE);
/*
* Gets EVP_CIPHER from a String or an OpenSSL::Digest instance (discouraged,
* but still supported for compatibility). A holder object is created if the
* EVP_CIPHER is a "fetched" algorithm.
*/
const EVP_CIPHER *ossl_evp_cipher_fetch(VALUE obj, volatile VALUE *holder);
/*
* This is meant for OpenSSL::Engine#cipher. EVP_CIPHER must not be a fetched
* one.
*/
VALUE ossl_cipher_new(const EVP_CIPHER *);
void Init_ossl_cipher(void);

Expand Down
Loading