use crate::crypto::{KeyDecryptor, KeyEncryptor, KeyWrapAlgorithm};
use super::cms::{oid_to_cipher, OpensslDecryptorError, OpensslEncryptorError};
use super::OpensslKeyError;
use native_ossl::params::ParamBuilder;
use native_ossl::pkey::{Pkey, PkeyDecryptCtx, PkeyEncryptCtx, Private, Public};
use native_ossl::rand::Rand;
fn hash_alg_name(alg: &str) -> Result<&'static std::ffi::CStr, OpensslKeyError> {
match alg {
"sha1" => Ok(c"SHA1"),
"sha224" => Ok(c"SHA2-224"),
"sha256" => Ok(c"SHA2-256"),
"sha384" => Ok(c"SHA2-384"),
"sha512" => Ok(c"SHA2-512"),
other => Err(OpensslKeyError(format!(
"unsupported hash algorithm: {other}; \
expected sha1, sha224, sha256, sha384, or sha512"
))),
}
}
pub(super) fn rsa_oaep_encrypt_with_key(
key: &Pkey<Public>,
plaintext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, OpensslKeyError> {
let hash_name = hash_alg_name(hash_alg)?;
let params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"oaep")?
.push_utf8_string(c"oaep-digest", hash_name)?
.push_utf8_string(c"mgf1-digest", hash_name)?
.build()?;
let mut ctx = PkeyEncryptCtx::new(key, Some(¶ms))?;
let outlen = ctx.encrypt_len(plaintext.len())?;
let mut out = vec![0u8; outlen];
let n = ctx.encrypt(plaintext, &mut out)?;
out.truncate(n);
Ok(out)
}
pub(super) fn rsa_pkcs1_encrypt_with_key(
key: &Pkey<Public>,
plaintext: &[u8],
) -> Result<Vec<u8>, OpensslKeyError> {
let params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"pkcs1")?
.build()?;
let mut ctx = PkeyEncryptCtx::new(key, Some(¶ms))?;
let outlen = ctx.encrypt_len(plaintext.len())?;
let mut out = vec![0u8; outlen];
let n = ctx.encrypt(plaintext, &mut out)?;
out.truncate(n);
Ok(out)
}
pub(super) fn rsa_oaep_decrypt_with_key(
key: &Pkey<Private>,
ciphertext: &[u8],
hash_alg: &str,
) -> Result<Vec<u8>, OpensslKeyError> {
let hash_name = hash_alg_name(hash_alg)?;
let params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"oaep")?
.push_utf8_string(c"oaep-digest", hash_name)?
.push_utf8_string(c"mgf1-digest", hash_name)?
.build()?;
let mut ctx = PkeyDecryptCtx::new(key, Some(¶ms))?;
let mut out = vec![0u8; ciphertext.len()];
let n = ctx.decrypt(ciphertext, &mut out)?;
out.truncate(n);
Ok(out)
}
pub(super) fn rsa_pkcs1_decrypt_with_key(
key: &Pkey<Private>,
ciphertext: &[u8],
) -> Result<Vec<u8>, OpensslKeyError> {
let params = ParamBuilder::new()?
.push_utf8_string(c"pad-mode", c"pkcs1")?
.build()?;
let mut ctx = PkeyDecryptCtx::new(key, Some(¶ms))?;
let mut out = vec![0u8; ciphertext.len()];
let n = ctx.decrypt(ciphertext, &mut out)?;
out.truncate(n);
Ok(out)
}
pub struct OpensslRsaOaepEncryptor {
key: Pkey<Public>,
hash_algorithm: String,
}
impl OpensslRsaOaepEncryptor {
pub fn new(spki_der: &[u8], hash_algorithm: &str) -> Result<Self, OpensslKeyError> {
let key = Pkey::<Public>::from_der(spki_der)
.map_err(|e| OpensslKeyError(format!("failed to load public key: {e}")))?;
if !key.is_a(c"RSA") {
return Err(OpensslKeyError(
"RSA-OAEP encryption requires an RSA public key".to_string(),
));
}
Ok(Self {
key,
hash_algorithm: hash_algorithm.to_owned(),
})
}
}
impl KeyEncryptor for OpensslRsaOaepEncryptor {
type Error = OpensslKeyError;
fn encrypt_key(&self, plaintext: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
rsa_oaep_encrypt_with_key(&self.key, plaintext, &self.hash_algorithm)
}
}
pub struct OpensslRsaOaepDecryptor {
key: Pkey<Private>,
hash_algorithm: String,
}
impl OpensslRsaOaepDecryptor {
pub fn new(pkcs8_der: &[u8], hash_algorithm: &str) -> Result<Self, OpensslKeyError> {
let key = Pkey::<Private>::from_der(pkcs8_der)
.map_err(|e| OpensslKeyError(format!("failed to load private key: {e}")))?;
if !key.is_a(c"RSA") {
return Err(OpensslKeyError(
"RSA-OAEP decryption requires an RSA private key".to_string(),
));
}
Ok(Self {
key,
hash_algorithm: hash_algorithm.to_owned(),
})
}
}
impl KeyDecryptor for OpensslRsaOaepDecryptor {
type Error = OpensslKeyError;
fn decrypt_key(&self, ciphertext: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
rsa_oaep_decrypt_with_key(&self.key, ciphertext, &self.hash_algorithm)
}
}
pub struct OpensslRsaPkcs1Encryptor {
key: Pkey<Public>,
}
impl OpensslRsaPkcs1Encryptor {
pub fn new(spki_der: &[u8]) -> Result<Self, OpensslKeyError> {
let key = Pkey::<Public>::from_der(spki_der)
.map_err(|e| OpensslKeyError(format!("failed to load public key: {e}")))?;
if !key.is_a(c"RSA") {
return Err(OpensslKeyError(
"RSA PKCS#1 v1.5 encryption requires an RSA public key".to_string(),
));
}
Ok(Self { key })
}
}
impl KeyEncryptor for OpensslRsaPkcs1Encryptor {
type Error = OpensslKeyError;
fn encrypt_key(&self, plaintext: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
rsa_pkcs1_encrypt_with_key(&self.key, plaintext)
}
}
pub struct OpensslRsaPkcs1Decryptor {
key: Pkey<Private>,
}
impl OpensslRsaPkcs1Decryptor {
pub fn new(pkcs8_der: &[u8]) -> Result<Self, OpensslKeyError> {
let key = Pkey::<Private>::from_der(pkcs8_der)
.map_err(|e| OpensslKeyError(format!("failed to load private key: {e}")))?;
if !key.is_a(c"RSA") {
return Err(OpensslKeyError(
"RSA PKCS#1 v1.5 decryption requires an RSA private key".to_string(),
));
}
Ok(Self { key })
}
}
impl KeyDecryptor for OpensslRsaPkcs1Decryptor {
type Error = OpensslKeyError;
fn decrypt_key(&self, ciphertext: &[u8]) -> Result<Vec<u8>, OpensslKeyError> {
rsa_pkcs1_decrypt_with_key(&self.key, ciphertext)
}
}
impl From<OpensslKeyError> for OpensslEncryptorError {
fn from(e: OpensslKeyError) -> Self {
OpensslEncryptorError::UnsupportedAlgorithm(e.0)
}
}
fn build_rsa_oaep_sha256_alg_id() -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::pkcs1_types::{RsaesOaepParams, ID_MGF1, ID_RSAES_OAEP};
use crate::AlgorithmIdentifier;
use synta::{Decoder, Element, Encoding, ObjectIdentifier};
let sha256_alg_der = {
let oid = ObjectIdentifier::new(crate::ID_SHA256).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm("invalid SHA-256 OID".into())
})?;
AlgorithmIdentifier {
algorithm: oid,
parameters: None,
}
.to_der()?
};
let mgf1_alg_der = {
let oid = ObjectIdentifier::new(ID_MGF1).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm("invalid id-mgf1 OID".into())
})?;
let sha256_elem: Element<'_> = Decoder::new(&sha256_alg_der, Encoding::Der).decode()?;
AlgorithmIdentifier {
algorithm: oid,
parameters: Some(sha256_elem),
}
.to_der()?
};
let oaep_params_der = {
let hash_alg: AlgorithmIdentifier<'_> =
Decoder::new(&sha256_alg_der, Encoding::Der).decode()?;
let mask_gen: AlgorithmIdentifier<'_> =
Decoder::new(&mgf1_alg_der, Encoding::Der).decode()?;
RsaesOaepParams {
hash_algorithm: Some(hash_alg),
mask_gen_algorithm: Some(mask_gen),
p_source_algorithm: None,
}
.to_der()?
};
let oaep_oid = ObjectIdentifier::new(ID_RSAES_OAEP).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm("invalid id-RSAES-OAEP OID".into())
})?;
let oaep_elem: Element<'_> = Decoder::new(&oaep_params_der, Encoding::Der).decode()?;
Ok(AlgorithmIdentifier {
algorithm: oaep_oid,
parameters: Some(oaep_elem),
}
.to_der()?)
}
fn build_rsa_pkcs1v15_alg_id() -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::AlgorithmIdentifier;
use synta::{Element, Null, ObjectIdentifier};
let oid = ObjectIdentifier::new(crate::RSA_ENCRYPTION).map_err(|_| {
OpensslEncryptorError::UnsupportedAlgorithm("invalid rsaEncryption OID".into())
})?;
Ok(AlgorithmIdentifier {
algorithm: oid,
parameters: Some(Element::Null(Null)),
}
.to_der()?)
}
fn build_ktri(
cert_der: &[u8],
key_wrap: KeyWrapAlgorithm,
cek: &[u8],
) -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::cms_2010_types::IssuerAndSerialNumber;
use crate::cms_rfc5652_types::KeyTransRecipientInfo;
use synta::{Decoder, Encoding, Integer, OctetStringRef, RawDer};
let synta_cert: crate::Certificate<'_> = Decoder::new(cert_der, Encoding::Der).decode()?;
let spki_der: Vec<u8> = synta_cert
.tbs_certificate
.subject_public_key_info
.to_der()?;
let key_enc_alg_der = match key_wrap {
KeyWrapAlgorithm::RsaOaepSha256 => build_rsa_oaep_sha256_alg_id()?,
KeyWrapAlgorithm::RsaPkcs1v15 => build_rsa_pkcs1v15_alg_id()?,
};
let encrypted_cek: Vec<u8> = match key_wrap {
KeyWrapAlgorithm::RsaOaepSha256 => {
OpensslRsaOaepEncryptor::new(&spki_der, "sha256")?.encrypt_key(cek)?
}
KeyWrapAlgorithm::RsaPkcs1v15 => {
OpensslRsaPkcs1Encryptor::new(&spki_der)?.encrypt_key(cek)?
}
};
let serial_number: Integer = synta_cert.tbs_certificate.serial_number;
let issuer: crate::Name<'_> =
Decoder::new(synta_cert.tbs_certificate.issuer.as_bytes(), Encoding::Der).decode()?;
let isn_der = IssuerAndSerialNumber {
issuer,
serial_number,
}
.to_der()?;
let key_encryption_algorithm: crate::AlgorithmIdentifier<'_> =
Decoder::new(&key_enc_alg_der, Encoding::Der).decode()?;
Ok(KeyTransRecipientInfo {
version: Integer::from_i64(0),
rid: RawDer(&isn_der),
key_encryption_algorithm,
encrypted_key: OctetStringRef::new(&encrypted_cek),
}
.to_der()?)
}
pub fn prepare_enveloped_data(
plaintext: &[u8],
recipients: &[(&[u8], KeyWrapAlgorithm)],
content_enc_alg_oid: &[u32],
) -> Result<crate::EnvelopedDataBuilder, OpensslEncryptorError> {
use super::cms::OpensslEncryptor;
use crate::crypto::Encryptor as _;
if recipients.is_empty() {
return Err(OpensslEncryptorError::UnsupportedAlgorithm(
"EnvelopedData requires at least one recipient".into(),
));
}
let (_, cek_len) = oid_to_cipher(content_enc_alg_oid)
.map_err(|e| OpensslEncryptorError::UnsupportedAlgorithm(e.to_string()))?;
let mut cek = vec![0u8; cek_len];
Rand::fill(&mut cek)?;
let (enc_alg_id_der, ciphertext) =
OpensslEncryptor.encrypt(content_enc_alg_oid, plaintext, &cek)?;
let ktri_ders: Result<Vec<Vec<u8>>, _> = recipients
.iter()
.map(|(cert_der, key_wrap)| build_ktri(cert_der, *key_wrap, &cek))
.collect();
let ktri_ders = ktri_ders?;
for b in cek.iter_mut() {
unsafe { std::ptr::write_volatile(b, 0) };
}
let mut builder = crate::EnvelopedDataBuilder::new(enc_alg_id_der, ciphertext);
for ktri_der in ktri_ders {
builder = builder.add_recipient_info(ktri_der);
}
Ok(builder)
}
pub fn create_enveloped_data(
plaintext: &[u8],
recipients: &[(&[u8], KeyWrapAlgorithm)],
content_enc_alg_oid: &[u32],
) -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::enveloped_data_builder::EnvelopedDataBuilderError;
prepare_enveloped_data(plaintext, recipients, content_enc_alg_oid)?
.build()
.map_err(|e| match e {
EnvelopedDataBuilderError::Encode(se) => OpensslEncryptorError::Encode(se),
EnvelopedDataBuilderError::InvalidOid(s) => {
OpensslEncryptorError::UnsupportedAlgorithm(format!("invalid OID: {s}"))
}
EnvelopedDataBuilderError::NoRecipients => OpensslEncryptorError::UnsupportedAlgorithm(
"EnvelopedData requires at least one recipient".into(),
),
})
}
pub struct OpensslEnvelopedDataDecryptor {
key: Pkey<Private>,
}
impl OpensslEnvelopedDataDecryptor {
pub fn new(pkcs8_der: &[u8]) -> Result<Self, OpensslKeyError> {
let key = Pkey::<Private>::from_der(pkcs8_der)
.map_err(|e| OpensslKeyError(format!("failed to load private key: {e}")))?;
if !key.is_a(c"RSA") {
return Err(OpensslKeyError(
"EnvelopedData decryption requires an RSA private key".to_string(),
));
}
Ok(Self { key })
}
}
impl crate::EnvelopedDataDecryptor for OpensslEnvelopedDataDecryptor {
type Error = OpensslEncryptorError;
fn decrypt_enveloped(
&self,
ed: &crate::cms_rfc5652_types::EnvelopedData<'_>,
) -> Result<Vec<u8>, OpensslEncryptorError> {
use crate::cms_rfc5652_types::KeyTransRecipientInfo;
use synta::tag::{TAG_SEQUENCE, TAG_SET};
use synta::{Encoding, Tag, TagClass};
let ri_raw = ed.recipient_infos.as_bytes();
let set_tag = Tag::universal_constructed(TAG_SET);
let mut outer = synta::Decoder::new(ri_raw, Encoding::Ber);
let mut inner = outer.enter_constructed(set_tag)?;
let mut cek: Option<Vec<u8>> = None;
while !inner.is_empty() {
let before = inner.remaining();
let elem_tag = inner.peek_tag()?;
inner.read_tag()?;
let elem_len = inner.read_length()?.definite()?;
inner.read_bytes(elem_len)?;
let after = inner.remaining();
let elem_tlv = &before[..before.len() - after.len()];
if elem_tag.class() != TagClass::Universal
|| elem_tag.number() != TAG_SEQUENCE
|| !elem_tag.is_constructed()
{
continue;
}
let ktri: KeyTransRecipientInfo<'_> =
match synta::Decoder::new(elem_tlv, Encoding::Ber).decode() {
Ok(k) => k,
Err(_) => continue,
};
let alg_oid = ktri.key_encryption_algorithm.algorithm.components();
let enc_key_bytes = ktri.encrypted_key.as_bytes();
let decrypted: Result<Vec<u8>, OpensslKeyError> = if alg_oid == crate::oids::RSAES_OAEP
{
rsa_oaep_decrypt_with_key(&self.key, enc_key_bytes, "sha256")
} else if alg_oid == crate::RSA_ENCRYPTION {
rsa_pkcs1_decrypt_with_key(&self.key, enc_key_bytes)
} else {
continue;
};
if let Ok(unwrapped) = decrypted {
cek = Some(unwrapped);
break;
}
}
let cek = cek.ok_or_else(|| {
OpensslEncryptorError::UnsupportedAlgorithm(
"no KeyTransRecipientInfo could be decrypted with the given private key"
.to_string(),
)
})?;
let alg_der = ed
.encrypted_content_info
.content_encryption_algorithm
.to_der()?;
let ciphertext = ed
.encrypted_content_info
.encrypted_content
.as_ref()
.ok_or_else(|| {
OpensslEncryptorError::UnsupportedAlgorithm(
"EnvelopedData has no encryptedContent field".to_string(),
)
})?
.as_bytes();
use super::cms::OpensslDecryptor;
crate::crypto::CmsDecryptor::decrypt(&OpensslDecryptor, &alg_der, ciphertext, &cek).map_err(
|e| match e {
OpensslDecryptorError::Parse(se) => OpensslEncryptorError::Encode(se),
OpensslDecryptorError::UnsupportedAlgorithm(s) => {
OpensslEncryptorError::UnsupportedAlgorithm(s)
}
OpensslDecryptorError::Openssl(e) => OpensslEncryptorError::Openssl(e),
},
)
}
}