use std::fmt;
use std::sync::Arc;
use aes_gcm::aead::{AeadCore, AeadInOut, KeyInit};
use ed25519_dalek::pkcs8::DecodePrivateKey as _;
use p256::ecdsa::signature::hazmat::PrehashVerifier;
use rustls::crypto::cipher::{
AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, MessageDecrypter, MessageEncrypter,
Nonce, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, Tls13AeadAlgorithm,
UnsupportedOperationError, make_tls13_aad,
};
use rustls::crypto::{
ActiveKeyExchange, CryptoProvider, GetRandomFailed, KeyProvider, SecureRandom, SharedSecret,
SupportedKxGroup, WebPkiSupportedAlgorithms, hash, hmac as rustls_hmac, tls13,
};
use rustls::pki_types::{
AlgorithmIdentifier, InvalidSignature, PrivateKeyDer, SignatureVerificationAlgorithm, alg_id,
};
use rustls::sign::{Signer, SigningKey};
use rustls::{
CipherSuite, CipherSuiteCommon, ConnectionTrafficSecrets, ContentType, Error, NamedGroup,
ProtocolVersion, SignatureAlgorithm, SignatureScheme, SupportedCipherSuite, Tls13CipherSuite,
};
use sha2::Digest as _;
pub fn provider() -> CryptoProvider {
CryptoProvider {
cipher_suites: vec![
TLS13_AES_128_GCM_SHA256,
TLS13_AES_256_GCM_SHA384,
TLS13_CHACHA20_POLY1305_SHA256,
],
kx_groups: vec![&X25519, &SECP256R1, &SECP384R1],
signature_verification_algorithms: SUPPORTED_SIG_ALGS,
secure_random: &Random,
key_provider: &Keys,
}
}
#[derive(Debug)]
struct Random;
impl SecureRandom for Random {
fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> {
getrandom::fill(buf).map_err(|_| GetRandomFailed)
}
}
fn random<const N: usize>() -> Result<[u8; N], Error> {
let mut bytes = [0; N];
getrandom::fill(&mut bytes).map_err(|_| Error::FailedToGetRandomBytes)?;
Ok(bytes)
}
struct Sha<D>(std::marker::PhantomData<fn() -> D>);
static SHA256: Sha<sha2::Sha256> = Sha(std::marker::PhantomData);
static SHA384: Sha<sha2::Sha384> = Sha(std::marker::PhantomData);
trait ShaAlgorithm: sha2::Digest + Clone + Send + Sync + 'static {
const ALGORITHM: rustls::crypto::hash::HashAlgorithm;
}
impl ShaAlgorithm for sha2::Sha256 {
const ALGORITHM: hash::HashAlgorithm = hash::HashAlgorithm::SHA256;
}
impl ShaAlgorithm for sha2::Sha384 {
const ALGORITHM: hash::HashAlgorithm = hash::HashAlgorithm::SHA384;
}
impl<D: ShaAlgorithm> hash::Hash for Sha<D> {
fn start(&self) -> Box<dyn hash::Context> {
Box::new(ShaContext(D::new()))
}
fn hash(&self, data: &[u8]) -> hash::Output {
hash::Output::new(&D::digest(data))
}
fn output_len(&self) -> usize {
<D as sha2::Digest>::output_size()
}
fn algorithm(&self) -> hash::HashAlgorithm {
D::ALGORITHM
}
}
struct ShaContext<D>(D);
impl<D: ShaAlgorithm> hash::Context for ShaContext<D> {
fn fork_finish(&self) -> hash::Output {
hash::Output::new(&self.0.clone().finalize())
}
fn fork(&self) -> Box<dyn hash::Context> {
Box::new(ShaContext(self.0.clone()))
}
fn finish(self: Box<Self>) -> hash::Output {
hash::Output::new(&self.0.finalize())
}
fn update(&mut self, data: &[u8]) {
self.0.update(data);
}
}
struct Hmac<D>(std::marker::PhantomData<fn() -> D>);
static HMAC_SHA256: Hmac<sha2::Sha256> = Hmac(std::marker::PhantomData);
static HMAC_SHA384: Hmac<sha2::Sha384> = Hmac(std::marker::PhantomData);
impl<D> rustls_hmac::Hmac for Hmac<D>
where
D: ShaAlgorithm + hmac::EagerHash,
hmac::Hmac<D>: KeyInit + hmac::Mac + Clone + Send + Sync,
{
fn with_key(&self, key: &[u8]) -> Box<dyn rustls_hmac::Key> {
Box::new(HmacKey(
<hmac::Hmac<D> as KeyInit>::new_from_slice(key).expect("HMAC accepts any key length"),
))
}
fn hash_output_len(&self) -> usize {
<D as sha2::Digest>::output_size()
}
}
struct HmacKey<M>(M);
impl<M: hmac::Mac + Clone + Send + Sync> rustls_hmac::Key for HmacKey<M> {
fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> rustls_hmac::Tag {
let mut mac = self.0.clone();
mac.update(first);
for part in middle {
mac.update(part);
}
mac.update(last);
rustls_hmac::Tag::new(&mac.finalize().into_bytes())
}
fn tag_len(&self) -> usize {
<M as hmac::digest::OutputSizeUser>::output_size()
}
}
pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite =
SupportedCipherSuite::Tls13(&Tls13CipherSuite {
common: CipherSuiteCommon {
suite: CipherSuite::TLS13_AES_128_GCM_SHA256,
hash_provider: &SHA256,
confidentiality_limit: 1 << 24,
},
hkdf_provider: &tls13::HkdfUsingHmac(&HMAC_SHA256),
aead_alg: &Aead::<aes_gcm::Aes128Gcm>(std::marker::PhantomData),
quic: None,
});
pub static TLS13_AES_256_GCM_SHA384: SupportedCipherSuite =
SupportedCipherSuite::Tls13(&Tls13CipherSuite {
common: CipherSuiteCommon {
suite: CipherSuite::TLS13_AES_256_GCM_SHA384,
hash_provider: &SHA384,
confidentiality_limit: 1 << 24,
},
hkdf_provider: &tls13::HkdfUsingHmac(&HMAC_SHA384),
aead_alg: &Aead::<aes_gcm::Aes256Gcm>(std::marker::PhantomData),
quic: None,
});
pub static TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite =
SupportedCipherSuite::Tls13(&Tls13CipherSuite {
common: CipherSuiteCommon {
suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
hash_provider: &SHA256,
confidentiality_limit: u64::MAX,
},
hkdf_provider: &tls13::HkdfUsingHmac(&HMAC_SHA256),
aead_alg: &Aead::<chacha20poly1305::ChaCha20Poly1305>(std::marker::PhantomData),
quic: None,
});
struct Aead<C>(std::marker::PhantomData<fn() -> C>);
trait Cipher: AeadInOut + KeyInit + Send + Sync + 'static {
const KEY_LEN: usize;
fn traffic_secrets(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets;
}
impl Cipher for aes_gcm::Aes128Gcm {
const KEY_LEN: usize = 16;
fn traffic_secrets(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets {
ConnectionTrafficSecrets::Aes128Gcm { key, iv }
}
}
impl Cipher for aes_gcm::Aes256Gcm {
const KEY_LEN: usize = 32;
fn traffic_secrets(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets {
ConnectionTrafficSecrets::Aes256Gcm { key, iv }
}
}
impl Cipher for chacha20poly1305::ChaCha20Poly1305 {
const KEY_LEN: usize = 32;
fn traffic_secrets(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets {
ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }
}
}
const TAG_LEN: usize = 16;
impl<C: Cipher> Tls13AeadAlgorithm for Aead<C> {
fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> {
Box::new(Crypter {
cipher: C::new_from_slice(key.as_ref()).expect("key of the suite's length"),
iv,
})
}
fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> {
Box::new(Crypter {
cipher: C::new_from_slice(key.as_ref()).expect("key of the suite's length"),
iv,
})
}
fn key_len(&self) -> usize {
C::KEY_LEN
}
fn extract_keys(
&self,
key: AeadKey,
iv: Iv,
) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
Ok(C::traffic_secrets(key, iv))
}
}
struct Crypter<C> {
cipher: C,
iv: Iv,
}
impl<C: Cipher> Crypter<C> {
fn nonce(&self, seq: u64) -> aes_gcm::aead::Nonce<C> {
let nonce = Nonce::new(&self.iv, seq).0;
aes_gcm::aead::Nonce::<C>::try_from(&nonce[..]).expect("12-byte nonces")
}
}
impl<C: Cipher> MessageEncrypter for Crypter<C> {
fn encrypt(
&mut self,
msg: OutboundPlainMessage<'_>,
seq: u64,
) -> Result<OutboundOpaqueMessage, Error> {
let total_len = self.encrypted_payload_len(msg.payload.len());
let mut payload = PrefixedPayload::with_capacity(total_len);
payload.extend_from_chunks(&msg.payload);
payload.extend_from_slice(&msg.typ.to_array());
let tag = self
.cipher
.encrypt_inout_detached(
&self.nonce(seq),
&make_tls13_aad(total_len),
payload.as_mut().into(),
)
.map_err(|_| Error::EncryptError)?;
debug_assert_eq!(tag.len(), TAG_LEN);
payload.extend_from_slice(&tag);
Ok(OutboundOpaqueMessage::new(
ContentType::ApplicationData,
ProtocolVersion::TLSv1_2,
payload,
))
}
fn encrypted_payload_len(&self, payload_len: usize) -> usize {
payload_len + 1 + TAG_LEN
}
}
impl<C: Cipher> MessageDecrypter for Crypter<C> {
fn decrypt<'a>(
&mut self,
mut msg: InboundOpaqueMessage<'a>,
seq: u64,
) -> Result<InboundPlainMessage<'a>, Error> {
let payload = &mut msg.payload;
let Some(plain_len) = payload.len().checked_sub(TAG_LEN) else {
return Err(Error::DecryptError);
};
let nonce = self.nonce(seq);
let aad = make_tls13_aad(payload.len());
let (text, tag) = payload.split_at_mut(plain_len);
let tag = aes_gcm::aead::Tag::<C>::try_from(&*tag).map_err(|_| Error::DecryptError)?;
self.cipher
.decrypt_inout_detached(&nonce, &aad, text.into(), &tag)
.map_err(|_| Error::DecryptError)?;
payload.truncate(plain_len);
msg.into_tls13_unpadded_message()
}
}
const _: () = {
use aes_gcm::aead::array::typenum::Unsigned;
macro_rules! check {
($($cipher:ty),*) => {$(
assert!(<<$cipher as AeadCore>::NonceSize as Unsigned>::USIZE == 12);
assert!(<<$cipher as AeadCore>::TagSize as Unsigned>::USIZE == TAG_LEN);
)*};
}
check!(
aes_gcm::Aes128Gcm,
aes_gcm::Aes256Gcm,
chacha20poly1305::ChaCha20Poly1305
);
};
#[derive(Debug)]
struct X25519Group;
static X25519: X25519Group = X25519Group;
impl SupportedKxGroup for X25519Group {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
let secret = x25519_dalek::StaticSecret::from(random::<32>()?);
let public = x25519_dalek::PublicKey::from(&secret);
Ok(Box::new(X25519Exchange { secret, public }))
}
fn name(&self) -> NamedGroup {
NamedGroup::X25519
}
}
struct X25519Exchange {
secret: x25519_dalek::StaticSecret,
public: x25519_dalek::PublicKey,
}
impl ActiveKeyExchange for X25519Exchange {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
let peer: [u8; 32] = peer_pub_key
.try_into()
.map_err(|_| Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?;
let shared = self
.secret
.diffie_hellman(&x25519_dalek::PublicKey::from(peer));
if !shared.was_contributory() {
return Err(rustls::PeerMisbehaved::InvalidKeyShare.into());
}
Ok(SharedSecret::from(&shared.as_bytes()[..]))
}
fn pub_key(&self) -> &[u8] {
self.public.as_bytes()
}
fn group(&self) -> NamedGroup {
NamedGroup::X25519
}
}
#[derive(Debug)]
struct Secp256r1Group;
static SECP256R1: Secp256r1Group = Secp256r1Group;
impl SupportedKxGroup for Secp256r1Group {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
let secret = loop {
if let Ok(secret) = p256::SecretKey::from_slice(&random::<32>()?) {
break secret;
}
};
let public = secret.public_key().to_sec1_bytes().into_vec();
Ok(Box::new(Secp256r1Exchange { secret, public }))
}
fn name(&self) -> NamedGroup {
NamedGroup::secp256r1
}
}
struct Secp256r1Exchange {
secret: p256::SecretKey,
public: Vec<u8>,
}
impl ActiveKeyExchange for Secp256r1Exchange {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
if peer_pub_key.first() != Some(&4) {
return Err(rustls::PeerMisbehaved::InvalidKeyShare.into());
}
let peer = p256::PublicKey::from_sec1_bytes(peer_pub_key)
.map_err(|_| Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?;
let shared = self.secret.diffie_hellman(&peer);
Ok(SharedSecret::from(&shared.raw_secret_bytes()[..]))
}
fn pub_key(&self) -> &[u8] {
&self.public
}
fn group(&self) -> NamedGroup {
NamedGroup::secp256r1
}
}
#[derive(Debug)]
struct Secp384r1Group;
static SECP384R1: Secp384r1Group = Secp384r1Group;
impl SupportedKxGroup for Secp384r1Group {
fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
let secret = loop {
if let Ok(secret) = p384::SecretKey::from_slice(&random::<48>()?) {
break secret;
}
};
let public = secret.public_key().to_sec1_bytes().into_vec();
Ok(Box::new(Secp384r1Exchange { secret, public }))
}
fn name(&self) -> NamedGroup {
NamedGroup::secp384r1
}
}
struct Secp384r1Exchange {
secret: p384::SecretKey,
public: Vec<u8>,
}
impl ActiveKeyExchange for Secp384r1Exchange {
fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
if peer_pub_key.first() != Some(&4) {
return Err(rustls::PeerMisbehaved::InvalidKeyShare.into());
}
let peer = p384::PublicKey::from_sec1_bytes(peer_pub_key)
.map_err(|_| Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?;
let shared = self.secret.diffie_hellman(&peer);
Ok(SharedSecret::from(&shared.raw_secret_bytes()[..]))
}
fn pub_key(&self) -> &[u8] {
&self.public
}
fn group(&self) -> NamedGroup {
NamedGroup::secp384r1
}
}
#[derive(Debug)]
struct Keys;
impl KeyProvider for Keys {
fn load_private_key(
&self,
key_der: PrivateKeyDer<'static>,
) -> Result<Arc<dyn SigningKey>, Error> {
let unsupported = |what: &str| {
Error::General(format!(
"{what}; furnace's pure-Rust TLS takes ECDSA P-256 / P-384 or Ed25519 keys"
))
};
let key: Arc<dyn SigningKey> = match &key_der {
PrivateKeyDer::Pkcs8(der) => {
let der = der.secret_pkcs8_der();
if let Ok(key) = p256::ecdsa::SigningKey::from_pkcs8_der(der) {
Arc::new(EcdsaKey::P256(key))
} else if let Ok(key) = p384::ecdsa::SigningKey::from_pkcs8_der(der) {
Arc::new(EcdsaKey::P384(key))
} else if let Ok(key) = ed25519_dalek::SigningKey::from_pkcs8_der(der) {
Arc::new(Ed25519Key(key))
} else {
return Err(unsupported("unsupported PKCS#8 key (RSA?)"));
}
}
PrivateKeyDer::Sec1(der) => {
let der = der.secret_sec1_der();
if let Ok(key) = p256::SecretKey::from_sec1_der(der) {
Arc::new(EcdsaKey::P256(key.into()))
} else if let Ok(key) = p384::SecretKey::from_sec1_der(der) {
Arc::new(EcdsaKey::P384(key.into()))
} else {
return Err(unsupported("unsupported EC key curve"));
}
}
PrivateKeyDer::Pkcs1(_) => return Err(unsupported("RSA keys are not supported")),
_ => return Err(unsupported("unsupported key format")),
};
Ok(key)
}
}
enum EcdsaKey {
P256(p256::ecdsa::SigningKey),
P384(p384::ecdsa::SigningKey),
}
impl fmt::Debug for EcdsaKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::P256(_) => "EcdsaKey(P-256)",
Self::P384(_) => "EcdsaKey(P-384)",
})
}
}
impl EcdsaKey {
fn scheme(&self) -> SignatureScheme {
match self {
Self::P256(_) => SignatureScheme::ECDSA_NISTP256_SHA256,
Self::P384(_) => SignatureScheme::ECDSA_NISTP384_SHA384,
}
}
}
impl SigningKey for EcdsaKey {
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
offered.contains(&self.scheme()).then(|| {
Box::new(EcdsaSigner(match self {
Self::P256(key) => Self::P256(key.clone()),
Self::P384(key) => Self::P384(key.clone()),
})) as Box<dyn Signer>
})
}
fn algorithm(&self) -> SignatureAlgorithm {
SignatureAlgorithm::ECDSA
}
}
#[derive(Debug)]
struct EcdsaSigner(EcdsaKey);
impl Signer for EcdsaSigner {
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
use p256::ecdsa::signature::Signer as _;
let der = match &self.0 {
EcdsaKey::P256(key) => {
let signature: p256::ecdsa::Signature = key
.try_sign(message)
.map_err(|e| Error::General(e.to_string()))?;
signature.to_der().to_bytes().into_vec()
}
EcdsaKey::P384(key) => {
let signature: p384::ecdsa::Signature = key
.try_sign(message)
.map_err(|e| Error::General(e.to_string()))?;
signature.to_der().to_bytes().into_vec()
}
};
Ok(der)
}
fn scheme(&self) -> SignatureScheme {
self.0.scheme()
}
}
struct Ed25519Key(ed25519_dalek::SigningKey);
impl fmt::Debug for Ed25519Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Ed25519Key")
}
}
impl SigningKey for Ed25519Key {
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
offered
.contains(&SignatureScheme::ED25519)
.then(|| Box::new(Ed25519Key(self.0.clone())) as Box<dyn Signer>)
}
fn algorithm(&self) -> SignatureAlgorithm {
SignatureAlgorithm::ED25519
}
}
impl Signer for Ed25519Key {
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
use ed25519_dalek::Signer as _;
Ok(self.0.sign(message).to_bytes().to_vec())
}
fn scheme(&self) -> SignatureScheme {
SignatureScheme::ED25519
}
}
#[derive(Debug)]
struct EcdsaVerify {
curve: Curve,
hash: hash::HashAlgorithm,
}
#[derive(Debug, Clone, Copy)]
enum Curve {
P256,
P384,
}
static ECDSA_P256_SHA256: EcdsaVerify = EcdsaVerify {
curve: Curve::P256,
hash: hash::HashAlgorithm::SHA256,
};
static ECDSA_P256_SHA384: EcdsaVerify = EcdsaVerify {
curve: Curve::P256,
hash: hash::HashAlgorithm::SHA384,
};
static ECDSA_P384_SHA256: EcdsaVerify = EcdsaVerify {
curve: Curve::P384,
hash: hash::HashAlgorithm::SHA256,
};
static ECDSA_P384_SHA384: EcdsaVerify = EcdsaVerify {
curve: Curve::P384,
hash: hash::HashAlgorithm::SHA384,
};
impl SignatureVerificationAlgorithm for EcdsaVerify {
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignature> {
let digest = match self.hash {
hash::HashAlgorithm::SHA256 => sha2::Sha256::digest(message).to_vec(),
hash::HashAlgorithm::SHA384 => sha2::Sha384::digest(message).to_vec(),
_ => return Err(InvalidSignature),
};
match self.curve {
Curve::P256 => {
let key = p256::ecdsa::VerifyingKey::from_sec1_bytes(public_key)
.map_err(|_| InvalidSignature)?;
let signature =
p256::ecdsa::Signature::from_der(signature).map_err(|_| InvalidSignature)?;
key.verify_prehash(&digest, &signature)
.map_err(|_| InvalidSignature)
}
Curve::P384 => {
let key = p384::ecdsa::VerifyingKey::from_sec1_bytes(public_key)
.map_err(|_| InvalidSignature)?;
let signature =
p384::ecdsa::Signature::from_der(signature).map_err(|_| InvalidSignature)?;
key.verify_prehash(&digest, &signature)
.map_err(|_| InvalidSignature)
}
}
}
fn public_key_alg_id(&self) -> AlgorithmIdentifier {
match self.curve {
Curve::P256 => alg_id::ECDSA_P256,
Curve::P384 => alg_id::ECDSA_P384,
}
}
fn signature_alg_id(&self) -> AlgorithmIdentifier {
match self.hash {
hash::HashAlgorithm::SHA384 => alg_id::ECDSA_SHA384,
_ => alg_id::ECDSA_SHA256,
}
}
}
#[derive(Debug)]
struct Ed25519Verify;
static ED25519: Ed25519Verify = Ed25519Verify;
impl SignatureVerificationAlgorithm for Ed25519Verify {
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignature> {
let key: [u8; 32] = public_key.try_into().map_err(|_| InvalidSignature)?;
let key = ed25519_dalek::VerifyingKey::from_bytes(&key).map_err(|_| InvalidSignature)?;
let signature =
ed25519_dalek::Signature::from_slice(signature).map_err(|_| InvalidSignature)?;
key.verify_strict(message, &signature)
.map_err(|_| InvalidSignature)
}
fn public_key_alg_id(&self) -> AlgorithmIdentifier {
alg_id::ED25519
}
fn signature_alg_id(&self) -> AlgorithmIdentifier {
alg_id::ED25519
}
}
static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
all: &[
&ECDSA_P256_SHA256,
&ECDSA_P256_SHA384,
&ECDSA_P384_SHA256,
&ECDSA_P384_SHA384,
&ED25519,
],
mapping: &[
(
SignatureScheme::ECDSA_NISTP384_SHA384,
&[&ECDSA_P384_SHA384, &ECDSA_P256_SHA384],
),
(
SignatureScheme::ECDSA_NISTP256_SHA256,
&[&ECDSA_P256_SHA256, &ECDSA_P384_SHA256],
),
(SignatureScheme::ED25519, &[&ED25519]),
],
};