#![allow(clippy::disallowed_methods)]
use async_trait::async_trait;
use crate::provider::{CryptoError, CryptoProvider, ED25519_PUBLIC_KEY_LEN, SecureSeed};
use ring::rand::SystemRandom;
use ring::signature::{ED25519, Ed25519KeyPair, KeyPair, UnparsedPublicKey};
pub struct RingCryptoProvider;
impl RingCryptoProvider {
pub fn p256_generate() -> Result<(SecureSeed, Vec<u8>), CryptoError> {
use p256::ecdsa::SigningKey;
use p256::elliptic_curve::rand_core::OsRng;
let signing_key = SigningKey::random(&mut OsRng);
let verifying_key = p256::ecdsa::VerifyingKey::from(&signing_key);
let compressed = verifying_key.to_encoded_point(true);
let pubkey_bytes = compressed.as_bytes().to_vec();
let scalar_bytes = signing_key.to_bytes();
let mut seed = [0u8; 32];
seed.copy_from_slice(&scalar_bytes);
Ok((SecureSeed::new(seed), pubkey_bytes))
}
pub fn p256_sign(seed: &[u8; 32], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
use p256::ecdsa::{SigningKey, signature::Signer};
let signing_key = SigningKey::from_slice(seed)
.map_err(|e| CryptoError::InvalidPrivateKey(format!("P-256: {e}")))?;
let signature: p256::ecdsa::Signature = signing_key.sign(message);
Ok(signature.to_bytes().to_vec())
}
pub fn ed25519_verify(
pubkey: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), CryptoError> {
let pk = ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, pubkey);
pk.verify(message, signature)
.map_err(|_| CryptoError::InvalidSignature)
}
pub fn ed25519_sign(seed: &[u8; 32], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
let kp = Ed25519KeyPair::from_seed_unchecked(seed)
.map_err(|e| CryptoError::InvalidPrivateKey(format!("Ed25519: {e}")))?;
Ok(kp.sign(message).as_ref().to_vec())
}
pub fn ed25519_public_key(seed: &[u8; 32]) -> Result<[u8; 32], CryptoError> {
let kp = Ed25519KeyPair::from_seed_unchecked(seed)
.map_err(|e| CryptoError::OperationFailed(format!("Ed25519 pubkey: {e}")))?;
kp.public_key()
.as_ref()
.try_into()
.map_err(|_| CryptoError::OperationFailed("Ed25519 public key not 32 bytes".into()))
}
pub fn p256_verify(pubkey: &[u8], message: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
use p256::ecdsa::{Signature, VerifyingKey, signature::Verifier};
let vk = VerifyingKey::from_sec1_bytes(pubkey)
.map_err(|e| CryptoError::OperationFailed(format!("P-256 key parse: {e}")))?;
let sig = Signature::from_slice(signature)
.map_err(|e| CryptoError::OperationFailed(format!("P-256 sig parse: {e}")))?;
vk.verify(message, &sig)
.map_err(|_| CryptoError::InvalidSignature)
}
pub fn p256_public_key_from_seed(seed: &[u8; 32]) -> Result<Vec<u8>, CryptoError> {
use p256::ecdsa::SigningKey;
let signing_key = SigningKey::from_slice(seed)
.map_err(|e| CryptoError::InvalidPrivateKey(format!("P-256: {e}")))?;
let verifying_key = p256::ecdsa::VerifyingKey::from(&signing_key);
let compressed = verifying_key.to_encoded_point(true);
Ok(compressed.as_bytes().to_vec())
}
}
#[async_trait]
impl CryptoProvider for RingCryptoProvider {
async fn verify_p256(
&self,
pubkey: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), CryptoError> {
Self::p256_verify(pubkey, message, signature)
}
async fn verify_ed25519(
&self,
pubkey: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), CryptoError> {
if pubkey.len() != ED25519_PUBLIC_KEY_LEN {
return Err(CryptoError::InvalidKeyLength {
expected: ED25519_PUBLIC_KEY_LEN,
actual: pubkey.len(),
});
}
let pubkey = pubkey.to_vec();
let message = message.to_vec();
let signature = signature.to_vec();
tokio::task::spawn_blocking(move || {
let peer_public_key = UnparsedPublicKey::new(&ED25519, &pubkey);
peer_public_key
.verify(&message, &signature)
.map_err(|_| CryptoError::InvalidSignature)
})
.await
.map_err(|_| CryptoError::OperationFailed("Verification task panicked".into()))?
}
async fn sign_ed25519(
&self,
seed: &SecureSeed,
message: &[u8],
) -> Result<Vec<u8>, CryptoError> {
let seed_bytes = *seed.as_bytes();
let message = message.to_vec();
tokio::task::spawn_blocking(move || {
let keypair = Ed25519KeyPair::from_seed_unchecked(&seed_bytes)
.map_err(|e| CryptoError::InvalidPrivateKey(format!("{e}")))?;
Ok(keypair.sign(&message).as_ref().to_vec())
})
.await
.map_err(|_| CryptoError::OperationFailed("Signing task panicked".into()))?
}
async fn generate_ed25519_keypair(&self) -> Result<(SecureSeed, [u8; 32]), CryptoError> {
tokio::task::spawn_blocking(move || {
let rng = SystemRandom::new();
let pkcs8_doc = Ed25519KeyPair::generate_pkcs8(&rng)
.map_err(|_| CryptoError::OperationFailed("Key generation failed".into()))?;
let keypair = Ed25519KeyPair::from_pkcs8(pkcs8_doc.as_ref())
.map_err(|e| CryptoError::OperationFailed(format!("Parse generated key: {e}")))?;
let public_key: [u8; 32] = keypair
.public_key()
.as_ref()
.try_into()
.map_err(|_| CryptoError::OperationFailed("Public key not 32 bytes".into()))?;
let pkcs8_bytes = pkcs8_doc.as_ref();
let seed: [u8; 32] = pkcs8_bytes[16..48]
.try_into()
.map_err(|_| CryptoError::OperationFailed("Seed extraction failed".into()))?;
Ok((SecureSeed::new(seed), public_key))
})
.await
.map_err(|_| CryptoError::OperationFailed("Keygen task panicked".into()))?
}
async fn ed25519_public_key_from_seed(
&self,
seed: &SecureSeed,
) -> Result<[u8; 32], CryptoError> {
let seed_bytes = *seed.as_bytes();
tokio::task::spawn_blocking(move || {
let keypair = Ed25519KeyPair::from_seed_unchecked(&seed_bytes)
.map_err(|e| CryptoError::InvalidPrivateKey(format!("{e}")))?;
keypair
.public_key()
.as_ref()
.try_into()
.map_err(|_| CryptoError::OperationFailed("Public key not 32 bytes".into()))
})
.await
.map_err(|_| CryptoError::OperationFailed("Public key extraction panicked".into()))?
}
async fn sign_p256(&self, seed: &SecureSeed, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
Self::p256_sign(seed.as_bytes(), message)
}
async fn generate_p256_keypair(&self) -> Result<(SecureSeed, Vec<u8>), CryptoError> {
Self::p256_generate()
}
async fn p256_public_key_from_seed(&self, seed: &SecureSeed) -> Result<Vec<u8>, CryptoError> {
Self::p256_public_key_from_seed(seed.as_bytes())
}
async fn aead_encrypt(
&self,
key: &[u8; 32],
nonce: &[u8; 12],
aad: &[u8],
plaintext: &[u8],
) -> Result<Vec<u8>, CryptoError> {
use chacha20poly1305::{
ChaCha20Poly1305, Nonce,
aead::{Aead, KeyInit, Payload},
};
let cipher = ChaCha20Poly1305::new(key.into());
let nonce = Nonce::from_slice(nonce);
cipher
.encrypt(
nonce,
Payload {
msg: plaintext,
aad,
},
)
.map_err(|e| CryptoError::OperationFailed(format!("AEAD encrypt: {e}")))
}
async fn aead_decrypt(
&self,
key: &[u8; 32],
nonce: &[u8; 12],
aad: &[u8],
ciphertext: &[u8],
) -> Result<Vec<u8>, CryptoError> {
use chacha20poly1305::{
ChaCha20Poly1305, Nonce,
aead::{Aead, KeyInit, Payload},
};
let cipher = ChaCha20Poly1305::new(key.into());
let nonce = Nonce::from_slice(nonce);
cipher
.decrypt(
nonce,
Payload {
msg: ciphertext,
aad,
},
)
.map_err(|_| CryptoError::InvalidSignature)
}
async fn hkdf_sha256_expand(
&self,
ikm: &[u8],
salt: &[u8],
info: &[u8],
out_len: usize,
) -> Result<Vec<u8>, CryptoError> {
use hkdf::Hkdf;
use sha2::Sha256;
if out_len > 255 * 32 {
return Err(CryptoError::OperationFailed(
"HKDF-SHA256 output length exceeds 255 * 32 = 8160 bytes".into(),
));
}
let hk = Hkdf::<Sha256>::new(if salt.is_empty() { None } else { Some(salt) }, ikm);
let mut out = vec![0u8; out_len];
hk.expand(info, &mut out)
.map_err(|e| CryptoError::OperationFailed(format!("HKDF-SHA256 expand: {e}")))?;
Ok(out)
}
async fn hkdf_sha384_expand(
&self,
ikm: &[u8],
salt: &[u8],
info: &[u8],
out_len: usize,
) -> Result<Vec<u8>, CryptoError> {
use hkdf::Hkdf;
use sha2::Sha384;
if out_len > 255 * 48 {
return Err(CryptoError::OperationFailed(
"HKDF-SHA384 output length exceeds 255 * 48 = 12240 bytes".into(),
));
}
let hk = Hkdf::<Sha384>::new(if salt.is_empty() { None } else { Some(salt) }, ikm);
let mut out = vec![0u8; out_len];
hk.expand(info, &mut out)
.map_err(|e| CryptoError::OperationFailed(format!("HKDF-SHA384 expand: {e}")))?;
Ok(out)
}
async fn hmac_sha256_compute(&self, key: &[u8], msg: &[u8]) -> Result<[u8; 32], CryptoError> {
use hmac::{Hmac, Mac};
use sha2::Sha256;
let mut mac = <Hmac<Sha256> as Mac>::new_from_slice(key)
.map_err(|e| CryptoError::OperationFailed(format!("HMAC-SHA256 key: {e}")))?;
mac.update(msg);
let tag = mac.finalize().into_bytes();
let out: [u8; 32] = tag.into();
Ok(out)
}
async fn hmac_sha256_verify(
&self,
key: &[u8],
msg: &[u8],
tag: &[u8],
) -> Result<(), CryptoError> {
use hmac::{Hmac, Mac};
use sha2::Sha256;
let mut mac = <Hmac<Sha256> as Mac>::new_from_slice(key)
.map_err(|e| CryptoError::OperationFailed(format!("HMAC-SHA256 key: {e}")))?;
mac.update(msg);
mac.verify_slice(tag)
.map_err(|_| CryptoError::InvalidSignature)
}
async fn hmac_sha384_compute(&self, key: &[u8], msg: &[u8]) -> Result<[u8; 48], CryptoError> {
use hmac::{Hmac, Mac};
use sha2::Sha384;
let mut mac = <Hmac<Sha384> as Mac>::new_from_slice(key)
.map_err(|e| CryptoError::OperationFailed(format!("HMAC-SHA384 key: {e}")))?;
mac.update(msg);
let tag = mac.finalize().into_bytes();
let out: [u8; 48] = tag.into();
Ok(out)
}
async fn hmac_sha384_verify(
&self,
key: &[u8],
msg: &[u8],
tag: &[u8],
) -> Result<(), CryptoError> {
use hmac::{Hmac, Mac};
use sha2::Sha384;
let mut mac = <Hmac<Sha384> as Mac>::new_from_slice(key)
.map_err(|e| CryptoError::OperationFailed(format!("HMAC-SHA384 key: {e}")))?;
mac.update(msg);
mac.verify_slice(tag)
.map_err(|_| CryptoError::InvalidSignature)
}
}