use crate::Result;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
#[non_exhaustive]
pub enum DigestAlgorithm {
Sha256,
}
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
#[non_exhaustive]
pub enum SigningAlgorithm {
RS256,
}
pub trait CryptoProvider: std::fmt::Debug + Send + Sync {
fn digest(&self, algorithm: DigestAlgorithm) -> Result<Box<dyn DigestContext>>;
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result<Box<dyn HmacContext>>;
fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result<Box<dyn Signer>>;
}
pub trait DigestContext: Send {
fn update(&mut self, data: &[u8]);
fn finish(&mut self) -> Result<&[u8]>;
}
pub trait HmacContext: Send {
fn update(&mut self, data: &[u8]);
fn finish(&mut self) -> Result<&[u8]>;
}
pub trait Signer: Send + Sync {
fn sign(&self, string_to_sign: &[u8]) -> Result<Vec<u8>>;
}
pub(crate) fn crypto_provider(custom: Option<&dyn CryptoProvider>) -> Result<&dyn CryptoProvider> {
if let Some(x) = custom {
return Ok(x);
}
#[cfg(feature = "aws-lc-rs")]
{
Ok(&aws_lc_rs::PROVIDER)
}
#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
{
Ok(&ring::PROVIDER)
}
#[cfg(not(any(feature = "ring", feature = "aws-lc-rs")))]
{
Err(crate::Error::NotSupported {
source: "Must enable aws-lc-rs, ring, or specify custom CryptoProvider"
.to_string()
.into(),
})
}
}
#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
pub(crate) mod ring {
use super::*;
use ::ring::{digest, hmac, rand, signature};
use thiserror::Error;
#[derive(Debug, Error)]
pub(crate) enum RingError {
#[error("No RSA key found in pem file")]
MissingKey,
#[error("Invalid RSA key: {}", source)]
InvalidKey {
#[from]
source: ::ring::error::KeyRejected,
},
#[error("Error reading pem file: {}", source)]
ReadPem {
source: rustls_pki_types::pem::Error,
},
#[error("Error signing: {}", source)]
Sign { source: ::ring::error::Unspecified },
}
impl From<RingError> for crate::Error {
fn from(value: RingError) -> Self {
Self::Generic {
store: "RingCryptoProvider",
source: Box::new(value),
}
}
}
pub(crate) const PROVIDER: RingCryptoProvider = RingCryptoProvider { _private: () };
#[derive(Debug, Default)]
pub(crate) struct RingCryptoProvider {
_private: (),
}
impl CryptoProvider for RingCryptoProvider {
fn digest(&self, algorithm: DigestAlgorithm) -> Result<Box<dyn DigestContext>> {
let algorithm = match algorithm {
DigestAlgorithm::Sha256 => &digest::SHA256,
};
let ctx = digest::Context::new(algorithm);
Ok(Box::new(RingDigestContext {
ctx: Some(ctx),
out: None,
}))
}
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result<Box<dyn HmacContext>> {
let algorithm = match algorithm {
DigestAlgorithm::Sha256 => hmac::HMAC_SHA256,
};
let ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret));
Ok(Box::new(RingHmacContext {
ctx: Some(ctx),
out: None,
}))
}
fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result<Box<dyn Signer>> {
match algorithm {
SigningAlgorithm::RS256 => Ok(Box::new(RsaKeyPair::from_pem(pem)?)),
}
}
}
struct RingDigestContext {
ctx: Option<digest::Context>,
out: Option<digest::Digest>,
}
impl DigestContext for RingDigestContext {
fn update(&mut self, data: &[u8]) {
self.ctx.as_mut().unwrap().update(data);
}
fn finish(&mut self) -> Result<&[u8]> {
let digest = self.ctx.take().unwrap().finish();
Ok(digest::Digest::as_ref(self.out.insert(digest)))
}
}
struct RingHmacContext {
ctx: Option<hmac::Context>,
out: Option<hmac::Tag>,
}
impl HmacContext for RingHmacContext {
fn update(&mut self, data: &[u8]) {
self.ctx.as_mut().unwrap().update(data);
}
fn finish(&mut self) -> Result<&[u8]> {
let tag = self.ctx.take().unwrap().sign();
Ok(hmac::Tag::as_ref(self.out.insert(tag)))
}
}
#[derive(Debug)]
pub(crate) struct RsaKeyPair(signature::RsaKeyPair);
impl RsaKeyPair {
pub(crate) fn from_pem(encoded: &[u8]) -> Result<Self, RingError> {
use rustls_pki_types::PrivateKeyDer;
use rustls_pki_types::pem::PemObject;
match PrivateKeyDer::from_pem_slice(encoded) {
Ok(PrivateKeyDer::Pkcs8(key)) => Self::from_pkcs8(key.secret_pkcs8_der()),
Ok(PrivateKeyDer::Pkcs1(key)) => Self::from_der(key.secret_pkcs1_der()),
Ok(_) => Err(RingError::MissingKey),
Err(source) => Err(RingError::ReadPem { source }),
}
}
pub(crate) fn from_pkcs8(key: &[u8]) -> Result<Self, RingError> {
Ok(Self(signature::RsaKeyPair::from_pkcs8(key)?))
}
pub(crate) fn from_der(key: &[u8]) -> Result<Self, RingError> {
Ok(Self(signature::RsaKeyPair::from_der(key)?))
}
}
impl Signer for RsaKeyPair {
fn sign(&self, string_to_sign: &[u8]) -> Result<Vec<u8>> {
let mut signature = vec![0; self.0.public().modulus_len()];
self.0
.sign(
&signature::RSA_PKCS1_SHA256,
&rand::SystemRandom::new(),
string_to_sign,
&mut signature,
)
.map_err(|source| RingError::Sign { source })?;
Ok(signature)
}
}
}
#[cfg(feature = "aws-lc-rs")]
pub(crate) mod aws_lc_rs {
use super::*;
use ::aws_lc_rs::{digest, hmac, rand, signature};
use thiserror::Error;
#[derive(Debug, Error)]
pub(crate) enum AwsLcError {
#[error("No RSA key found in pem file")]
MissingKey,
#[error("Invalid RSA key: {}", source)]
InvalidKey {
#[from]
source: ::aws_lc_rs::error::KeyRejected,
},
#[error("Error reading pem file: {}", source)]
ReadPem {
source: rustls_pki_types::pem::Error,
},
#[error("Error signing: {}", source)]
Sign {
source: ::aws_lc_rs::error::Unspecified,
},
}
impl From<AwsLcError> for crate::Error {
fn from(value: AwsLcError) -> Self {
Self::Generic {
store: "AwsLcCryptoProvider",
source: Box::new(value),
}
}
}
pub(crate) const PROVIDER: AwsLcCryptoProvider = AwsLcCryptoProvider { _private: () };
#[derive(Debug, Default)]
pub(crate) struct AwsLcCryptoProvider {
_private: (),
}
impl CryptoProvider for AwsLcCryptoProvider {
fn digest(&self, algorithm: DigestAlgorithm) -> Result<Box<dyn DigestContext>> {
let algorithm = match algorithm {
DigestAlgorithm::Sha256 => &digest::SHA256,
};
let ctx = digest::Context::new(algorithm);
Ok(Box::new(AwsLcDigestContext {
ctx: Some(ctx),
out: None,
}))
}
fn hmac(&self, algorithm: DigestAlgorithm, secret: &[u8]) -> Result<Box<dyn HmacContext>> {
let algorithm = match algorithm {
DigestAlgorithm::Sha256 => hmac::HMAC_SHA256,
};
let ctx = hmac::Context::with_key(&hmac::Key::new(algorithm, secret));
Ok(Box::new(AwsLcHmacContext {
ctx: Some(ctx),
out: None,
}))
}
fn sign(&self, algorithm: SigningAlgorithm, pem: &[u8]) -> Result<Box<dyn Signer>> {
match algorithm {
SigningAlgorithm::RS256 => Ok(Box::new(RsaKeyPair::from_pem(pem)?)),
}
}
}
struct AwsLcDigestContext {
ctx: Option<digest::Context>,
out: Option<digest::Digest>,
}
impl DigestContext for AwsLcDigestContext {
fn update(&mut self, data: &[u8]) {
self.ctx.as_mut().unwrap().update(data);
}
fn finish(&mut self) -> Result<&[u8]> {
let digest = self.ctx.take().unwrap().finish();
Ok(digest::Digest::as_ref(self.out.insert(digest)))
}
}
struct AwsLcHmacContext {
ctx: Option<hmac::Context>,
out: Option<hmac::Tag>,
}
impl HmacContext for AwsLcHmacContext {
fn update(&mut self, data: &[u8]) {
self.ctx.as_mut().unwrap().update(data);
}
fn finish(&mut self) -> Result<&[u8]> {
let tag = self.ctx.take().unwrap().sign();
Ok(hmac::Tag::as_ref(self.out.insert(tag)))
}
}
#[derive(Debug)]
pub(crate) struct RsaKeyPair(signature::RsaKeyPair);
impl RsaKeyPair {
pub(crate) fn from_pem(encoded: &[u8]) -> Result<Self, AwsLcError> {
use rustls_pki_types::PrivateKeyDer;
use rustls_pki_types::pem::PemObject;
match PrivateKeyDer::from_pem_slice(encoded) {
Ok(PrivateKeyDer::Pkcs8(key)) => Self::from_pkcs8(key.secret_pkcs8_der()),
Ok(PrivateKeyDer::Pkcs1(key)) => Self::from_der(key.secret_pkcs1_der()),
Ok(_) => Err(AwsLcError::MissingKey),
Err(source) => Err(AwsLcError::ReadPem { source }),
}
}
pub(crate) fn from_pkcs8(key: &[u8]) -> Result<Self, AwsLcError> {
Ok(Self(signature::RsaKeyPair::from_pkcs8(key)?))
}
pub(crate) fn from_der(key: &[u8]) -> Result<Self, AwsLcError> {
Ok(Self(signature::RsaKeyPair::from_der(key)?))
}
}
impl Signer for RsaKeyPair {
fn sign(&self, string_to_sign: &[u8]) -> Result<Vec<u8>> {
let mut signature = vec![0; self.0.public_modulus_len()];
self.0
.sign(
&signature::RSA_PKCS1_SHA256,
&rand::SystemRandom::new(),
string_to_sign,
&mut signature,
)
.map_err(|source| AwsLcError::Sign { source })?;
Ok(signature)
}
}
}