use super::signature::{RsaEncoding, RsaPadding};
use super::{encoding, RsaParameters};
use crate::encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der};
use crate::error::{KeyRejected, Unspecified};
#[cfg(feature = "ring-io")]
use crate::io;
use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr};
use crate::sealed::Sealed;
use crate::wolfcrypt_rs::{
EVP_PKEY_CTX_set_rsa_keygen_bits, EVP_PKEY_CTX_set_signature_md, EVP_PKEY_assign_RSA,
EVP_PKEY_new, RSA_new, RSA_set0_key, RSA_size, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_RSA,
EVP_PKEY_RSA_PSS,
};
#[cfg(feature = "ring-io")]
use crate::wolfcrypt_rs::{RSA_get0_key, BIGNUM, RSA};
use crate::{hex, rand};
#[cfg(feature = "fips")]
use crate::wolfcrypt_rs::RSA_check_key;
use core::fmt::{self, Debug, Formatter};
use core::ptr::null_mut;
use core::ffi::c_int;
use crate::digest::{match_digest_type, Digest};
use crate::pkcs8::Version;
use crate::rsa::encoding::{rfc5280, rfc8017};
use crate::rsa::signature::configure_rsa_pkcs1_pss_padding;
#[cfg(feature = "ring-io")]
use untrusted::Input;
use zeroize::Zeroize;
#[cfg(not(feature = "std"))]
use crate::prelude::*;
#[allow(clippy::module_name_repetitions)]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeySize {
Rsa2048,
Rsa3072,
Rsa4096,
Rsa8192,
}
#[allow(clippy::len_without_is_empty)]
impl KeySize {
#[inline]
#[must_use]
pub fn len(self) -> usize {
match self {
Self::Rsa2048 => 256,
Self::Rsa3072 => 384,
Self::Rsa4096 => 512,
Self::Rsa8192 => 1024,
}
}
#[inline]
pub(super) fn bits(self) -> i32 {
match self {
Self::Rsa2048 => 2048,
Self::Rsa3072 => 3072,
Self::Rsa4096 => 4096,
Self::Rsa8192 => 8192,
}
}
}
#[allow(clippy::module_name_repetitions)]
pub struct KeyPair {
pub(super) evp_pkey: LcPtr<EVP_PKEY>,
pub(super) serialized_public_key: PublicKey,
}
impl Sealed for KeyPair {}
unsafe impl Send for KeyPair {}
unsafe impl Sync for KeyPair {}
impl KeyPair {
fn new(evp_pkey: LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
KeyPair::validate_private_key(&evp_pkey)?;
let serialized_public_key = PublicKey::new(&evp_pkey)?;
Ok(KeyPair {
evp_pkey,
serialized_public_key,
})
}
pub fn generate(size: KeySize) -> Result<Self, Unspecified> {
let private_key = generate_rsa_key(size.bits())?;
Ok(Self::new(private_key)?)
}
#[cfg(feature = "fips")]
#[deprecated]
pub fn generate_fips(size: KeySize) -> Result<Self, Unspecified> {
Self::generate(size)
}
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
let key = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_RSA)?;
Self::new(key)
}
pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
let key = encoding::rfc8017::decode_private_key_der(input)?;
Self::new(key)
}
#[cfg(feature = "fips")]
#[must_use]
pub fn is_valid_fips_key(&self) -> bool {
is_valid_fips_key(&self.evp_pkey)
}
fn validate_private_key(key: &LcPtr<EVP_PKEY>) -> Result<(), KeyRejected> {
if !is_rsa_key(key) {
return Err(KeyRejected::unspecified());
}
match key.as_const().key_size_bits() {
2048..=8192 => Ok(()),
_ => Err(KeyRejected::unspecified()),
}
}
pub fn sign(
&self,
padding_alg: &'static dyn RsaEncoding,
_rng: &dyn rand::SecureRandom,
msg: &[u8],
signature: &mut [u8],
) -> Result<(), Unspecified> {
let encoding = padding_alg.encoding();
let padding_fn = if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
Some(configure_rsa_pkcs1_pss_padding)
} else {
None
};
let sig_bytes = self
.evp_pkey
.sign(msg, Some(encoding.digest_algorithm()), padding_fn)?;
signature.copy_from_slice(&sig_bytes);
Ok(())
}
pub fn sign_digest(
&self,
padding_alg: &'static dyn RsaEncoding,
digest: &Digest,
signature: &mut [u8],
) -> Result<(), Unspecified> {
let encoding = padding_alg.encoding();
if encoding.digest_algorithm() != digest.algorithm() {
return Err(Unspecified);
}
let padding_fn = Some({
|pctx: *mut EVP_PKEY_CTX| {
let evp_md = match_digest_type(&digest.algorithm().id);
if 1 != unsafe { EVP_PKEY_CTX_set_signature_md(pctx, evp_md.as_const_ptr()) } {
return Err(());
}
if let RsaPadding::RSA_PKCS1_PSS_PADDING = encoding.padding() {
configure_rsa_pkcs1_pss_padding(pctx)
} else {
Ok(())
}
}
});
let sig_bytes = self.evp_pkey.sign_digest(digest, padding_fn)?;
signature.copy_from_slice(&sig_bytes);
Ok(())
}
#[must_use]
pub fn public_modulus_len(&self) -> usize {
match self.evp_pkey.as_const().get_rsa() {
Ok(rsa) => {
unsafe { RSA_size(rsa.as_const_ptr()) as usize }
}
Err(_) => unreachable!(),
}
}
}
impl Debug for KeyPair {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(&format!(
"RsaKeyPair {{ public_key: {:?} }}",
self.serialized_public_key
))
}
}
impl crate::signature::KeyPair for KeyPair {
type PublicKey = PublicKey;
fn public_key(&self) -> &Self::PublicKey {
&self.serialized_public_key
}
}
impl AsDer<Pkcs8V1Der<'static>> for KeyPair {
fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
Ok(Pkcs8V1Der::new(
self.evp_pkey
.as_const()
.marshal_rfc5208_private_key(Version::V1)?,
))
}
}
#[derive(Clone)]
#[allow(clippy::module_name_repetitions)]
pub struct PublicKey {
key: Box<[u8]>,
#[cfg(feature = "ring-io")]
modulus: Box<[u8]>,
#[cfg(feature = "ring-io")]
exponent: Box<[u8]>,
}
impl Drop for PublicKey {
fn drop(&mut self) {
self.key.zeroize();
#[cfg(feature = "ring-io")]
self.modulus.zeroize();
#[cfg(feature = "ring-io")]
self.exponent.zeroize();
}
}
impl PublicKey {
pub(super) fn new(evp_pkey: &LcPtr<EVP_PKEY>) -> Result<Self, KeyRejected> {
let key = encoding::rfc8017::encode_public_key_der(evp_pkey)?;
#[cfg(feature = "ring-io")]
{
let evp_pkey = evp_pkey.as_const();
let pubkey = evp_pkey.get_rsa()?;
let modulus = pubkey.project_const_lifetime(rsa_get0_n)?;
let modulus = modulus.to_be_bytes().into_boxed_slice();
let exponent = pubkey.project_const_lifetime(rsa_get0_e)?;
let exponent = exponent.to_be_bytes().into_boxed_slice();
Ok(PublicKey {
key,
modulus,
exponent,
})
}
#[cfg(not(feature = "ring-io"))]
Ok(PublicKey { key })
}
pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
PublicKey::new(
&rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))?,
)
}
}
pub(crate) fn parse_rsa_public_key(input: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
rfc8017::decode_public_key_der(input).or(rfc5280::decode_public_key_der(input))
}
impl Debug for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(&format!(
"RsaPublicKey(\"{}\")",
hex::encode(self.key.as_ref())
))
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
self.key.as_ref()
}
}
impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
let evp_pkey = rfc8017::decode_public_key_der(self.as_ref())?;
rfc5280::encode_public_key_der(&evp_pkey)
}
}
#[cfg(feature = "ring-io")]
impl PublicKey {
#[must_use]
pub fn modulus(&self) -> io::Positive<'_> {
io::Positive::new_non_empty_without_leading_zeros(Input::from(self.modulus.as_ref()))
}
#[must_use]
pub fn exponent(&self) -> io::Positive<'_> {
io::Positive::new_non_empty_without_leading_zeros(Input::from(self.exponent.as_ref()))
}
#[must_use]
pub fn modulus_len(&self) -> usize {
self.modulus.len()
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Clone)]
pub struct PublicKeyComponents<B>
where
B: AsRef<[u8]> + Debug,
{
pub n: B,
pub e: B,
}
impl<B: AsRef<[u8]> + Debug> Debug for PublicKeyComponents<B> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("RsaPublicKeyComponents")
.field("n", &self.n)
.field("e", &self.e)
.finish()
}
}
impl<B: Copy + AsRef<[u8]> + Debug> Copy for PublicKeyComponents<B> {}
impl<B> PublicKeyComponents<B>
where
B: AsRef<[u8]> + Debug,
{
#[inline]
fn build_rsa(&self) -> Result<LcPtr<EVP_PKEY>, ()> {
let n_bytes = self.n.as_ref();
if n_bytes.is_empty() || n_bytes[0] == 0u8 {
return Err(());
}
let mut n_bn = DetachableLcPtr::try_from(n_bytes)?;
let e_bytes = self.e.as_ref();
if e_bytes.is_empty() || e_bytes[0] == 0u8 {
return Err(());
}
let mut e_bn = DetachableLcPtr::try_from(e_bytes)?;
let mut rsa = DetachableLcPtr::new(unsafe { RSA_new() })?;
if 1 != unsafe {
RSA_set0_key(
rsa.as_mut_ptr(),
n_bn.as_mut_ptr(),
e_bn.as_mut_ptr(),
null_mut(),
)
} {
return Err(());
}
n_bn.detach();
e_bn.detach();
let mut pkey = LcPtr::new(unsafe { EVP_PKEY_new() })?;
if 1 != unsafe { EVP_PKEY_assign_RSA(pkey.as_mut_ptr(), rsa.as_mut_ptr()) } {
return Err(());
}
rsa.detach();
Ok(pkey)
}
pub fn verify(
&self,
params: &RsaParameters,
message: &[u8],
signature: &[u8],
) -> Result<(), Unspecified> {
let rsa = self.build_rsa()?;
super::signature::verify_rsa_signature(
params.digest_algorithm(),
params.padding(),
&rsa,
message,
signature,
params.bit_size_range(),
)
}
}
#[cfg(feature = "ring-io")]
impl From<&PublicKey> for PublicKeyComponents<Vec<u8>> {
fn from(public_key: &PublicKey) -> Self {
PublicKeyComponents {
n: public_key.modulus.to_vec(),
e: public_key.exponent.to_vec(),
}
}
}
pub(super) fn generate_rsa_key(size: c_int) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
let params_fn = |ctx| {
if 1 == unsafe { EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, size) } {
Ok(())
} else {
Err(())
}
};
LcPtr::<EVP_PKEY>::generate(EVP_PKEY_RSA, Some(params_fn))
}
#[cfg(feature = "fips")]
#[must_use]
pub(super) fn is_valid_fips_key(key: &LcPtr<EVP_PKEY>) -> bool {
let evp_pkey = key.as_const();
let Ok(rsa_key) = evp_pkey.get_rsa() else {
return false;
};
1 == unsafe { RSA_check_key(rsa_key.as_const_ptr()) }
}
pub(super) fn is_rsa_key(key: &LcPtr<EVP_PKEY>) -> bool {
let id = key.as_const().id();
id == EVP_PKEY_RSA || id == EVP_PKEY_RSA_PSS
}
#[cfg(feature = "ring-io")]
unsafe fn rsa_get0_n(rsa: &ConstPointer<'_, RSA>) -> *const BIGNUM {
let mut n: *const BIGNUM = core::ptr::null();
let mut e: *const BIGNUM = core::ptr::null();
let mut d: *const BIGNUM = core::ptr::null();
RSA_get0_key(rsa.as_const_ptr(), &mut n, &mut e, &mut d);
n
}
#[cfg(feature = "ring-io")]
unsafe fn rsa_get0_e(rsa: &ConstPointer<'_, RSA>) -> *const BIGNUM {
let mut n: *const BIGNUM = core::ptr::null();
let mut e: *const BIGNUM = core::ptr::null();
let mut d: *const BIGNUM = core::ptr::null();
RSA_get0_key(rsa.as_const_ptr(), &mut n, &mut e, &mut d);
e
}