use argon2::Argon2;
use chacha20poly1305::{
aead::{Aead, Payload},
AeadCore, ChaCha20Poly1305, Key, KeyInit, Nonce,
};
use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
use rand_core::OsRng;
use snafu::Snafu;
use crate::{
codec::{CodecError, Decodable, Encodable, Format, WritesEncodable},
sized_byte_array,
stream::Writes,
types::binary::hex_from_bytes,
};
use super::Coda;
sized_byte_array!(
HashBytes,
32
);
sized_byte_array!(
PrivateKeyBytes,
32
);
sized_byte_array!(
PublicKeyBytes,
32
);
sized_byte_array!(
SignatureBytes,
64
);
#[derive(Default)]
pub struct CryptoHasher {
hasher: blake3::Hasher,
}
impl CryptoHasher {
pub fn write(&mut self, bytes: &[u8]) {
self.hasher.update(bytes);
}
pub fn finalize(self) -> HashBytes {
HashBytes::from(*self.hasher.finalize().as_bytes())
}
pub fn finalize_into_bytes(self, bytes: &mut HashBytes) {
bytes.0 = *self.hasher.finalize().as_bytes();
}
}
impl Writes for CryptoHasher {
fn write(&mut self, buf: &[u8]) -> Result<usize, crate::stream::StreamError> {
self.hasher.update(buf);
Ok(buf.len())
}
fn write_all(&mut self, buf: &[u8]) -> Result<(), crate::stream::StreamError> {
self.hasher.update(buf);
Ok(())
}
}
pub struct CryptoKeys {
signer: CryptoSigner,
verifier: CryptoVerifier,
}
impl CryptoKeys {
pub fn generate() -> Self {
let mut rng = OsRng;
let signer = SigningKey::generate(&mut rng);
let verifier = signer.verifying_key();
CryptoKeys {
signer: CryptoSigner {
private_key: signer,
},
verifier: CryptoVerifier {
public_key: verifier,
},
}
}
pub fn from_private(private_key: PrivateKeyBytes) -> Result<Self, CryptoError> {
let signer = SigningKey::from_bytes(&private_key.0);
let verifier = signer.verifying_key();
Ok(CryptoKeys {
signer: CryptoSigner {
private_key: signer,
},
verifier: CryptoVerifier {
public_key: verifier,
},
})
}
pub fn into_private(self) -> PrivateKeyBytes {
let mut bytes = PrivateKeyBytes::default();
let private_key = &self.signer.private_key.to_keypair_bytes()[0..PrivateKeyBytes::SIZE];
bytes.copy_from_slice(private_key);
bytes
}
}
pub struct CryptoSigner {
private_key: SigningKey,
}
#[derive(Copy, Clone, Debug)]
pub struct CryptoVerifier {
public_key: VerifyingKey,
}
impl TryFrom<&PublicKeyBytes> for CryptoVerifier {
type Error = CryptoError;
fn try_from(public_key: &PublicKeyBytes) -> Result<Self, Self::Error> {
let public_key =
VerifyingKey::from_bytes(&public_key.0).map_err(|_| CryptoError::InvalidPublicKey {
pub_key: *public_key,
})?;
Ok(CryptoVerifier { public_key })
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct CryptoCert {
pub public_key: PublicKeyBytes,
pub signature: SignatureBytes,
}
impl CryptoCert {
pub fn sign(
&mut self,
signer: &impl CryptoSigns,
data: &[&[u8]],
) -> core::result::Result<(), CryptoError> {
self.public_key = signer.public_key_bytes();
self.signature = signer.sign(data)?;
Ok(())
}
pub fn verify(&self, data: &[&[u8]]) -> core::result::Result<(), CryptoError> {
let key = CryptoVerifier::try_from(&self.public_key)?;
key.verify(data, &self.signature)
}
}
impl Eq for CryptoCert {}
impl PartialEq for CryptoCert {
fn eq(&self, other: &Self) -> bool {
self.public_key == other.public_key && self.signature == other.signature
}
}
impl Ord for CryptoCert {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.signature.cmp(&other.signature)
}
}
impl PartialOrd for CryptoCert {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl core::hash::Hash for CryptoCert {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.public_key.hash(state);
self.signature.hash(state);
}
}
impl Encodable for CryptoCert {
const FORMAT: Format = PublicKeyBytes::FORMAT.with(SignatureBytes::FORMAT);
fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
writer.write_data(&self.public_key)?;
writer.write_data(&self.signature)?;
Ok(())
}
}
impl Decodable for CryptoCert {
fn decode(
&mut self,
reader: &mut (impl crate::codec::ReadsDecodable + ?Sized),
header: Option<crate::codec::DataHeader>,
) -> Result<(), CodecError> {
Self::ensure_no_header(header)?;
reader.read_data_into(&mut self.public_key)?;
reader.read_data_into(&mut self.signature)?;
Ok(())
}
}
pub trait HasCryptoHash {
fn crypto_hash_into(&self, hasher: &mut CryptoHasher);
fn crypto_hasher(&self) -> CryptoHasher {
let mut hasher = CryptoHasher::default();
self.crypto_hash_into(&mut hasher);
hasher
}
}
impl HasCryptoHash for CryptoCert {
fn crypto_hash_into(&self, hasher: &mut CryptoHasher) {
hasher.write(&self.signature);
}
}
impl HasCryptoHash for Coda {
fn crypto_hash_into(&self, hasher: &mut CryptoHasher) {
let _ = self.encode(hasher);
}
}
pub trait HasCryptoPublicKey {
fn public_key_bytes(&self) -> PublicKeyBytes;
}
impl HasCryptoPublicKey for CryptoKeys {
fn public_key_bytes(&self) -> PublicKeyBytes {
self.verifier.public_key_bytes()
}
}
impl HasCryptoPublicKey for CryptoSigner {
fn public_key_bytes(&self) -> PublicKeyBytes {
(*self.private_key.verifying_key().as_bytes()).into()
}
}
impl HasCryptoPublicKey for CryptoVerifier {
fn public_key_bytes(&self) -> PublicKeyBytes {
(*self.public_key.as_bytes()).into()
}
}
pub trait CryptoSigns: HasCryptoPublicKey {
fn sign(&self, message: &[&[u8]]) -> Result<SignatureBytes, CryptoError>;
}
impl CryptoSigns for CryptoKeys {
fn sign(&self, message: &[&[u8]]) -> Result<SignatureBytes, CryptoError> {
self.signer.sign(message)
}
}
impl CryptoSigns for CryptoSigner {
fn sign(&self, message: &[&[u8]]) -> Result<SignatureBytes, CryptoError> {
let signature = self
.private_key
.try_sign(message.concat().as_slice())
.expect("signing failure");
Ok(signature.to_bytes().into())
}
}
pub trait CryptoVerifies: HasCryptoPublicKey {
fn verify(&self, message: &[&[u8]], signature: &SignatureBytes) -> Result<(), CryptoError>;
}
impl CryptoVerifies for CryptoKeys {
fn verify(&self, message: &[&[u8]], signature: &SignatureBytes) -> Result<(), CryptoError> {
self.verifier.verify(message, signature)
}
}
impl CryptoVerifies for CryptoVerifier {
fn verify(&self, message: &[&[u8]], signature: &SignatureBytes) -> Result<(), CryptoError> {
let message = message.concat();
let message = message.as_slice();
let sig = Signature::from_bytes(&signature.0);
self.public_key
.verify_strict(message, &sig)
.map_err(|_| CryptoError::InvalidSignature {
signature: *signature,
})
}
}
#[derive(Default)]
pub struct EncryptedData {
nonce: [u8; 12],
data: alloc::vec::Vec<u8>,
}
impl EncryptedData {
pub fn new(key: &[u8], data: &[u8]) -> Result<Self, CryptoError> {
let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
let mut derived_key = [0u8; 32];
Argon2::default().hash_password_into(key, nonce.as_ref(), &mut derived_key)?;
let cipher = ChaCha20Poly1305::new(&Key::from(derived_key));
let encrypted = cipher.encrypt(
&nonce,
Payload {
msg: data,
aad: &nonce,
},
)?;
Ok(Self {
nonce: nonce.into(),
data: encrypted,
})
}
pub fn decrypt(&self, key: &[u8]) -> Result<alloc::vec::Vec<u8>, CryptoError> {
let mut derived_key = [0u8; 32];
Argon2::default().hash_password_into(key, self.nonce.as_ref(), &mut derived_key)?;
let cipher = ChaCha20Poly1305::new(&Key::from(derived_key));
let decrypted = cipher.decrypt(
Nonce::from_slice(&self.nonce),
Payload {
msg: &self.data,
aad: &self.nonce,
},
)?;
Ok(decrypted)
}
pub fn to_hex(&self) -> alloc::string::String {
alloc::format!(
"{}-{}",
hex_from_bytes(&self.nonce),
hex_from_bytes(&self.data)
)
}
pub fn from_hex(hex: &str) -> Result<Self, CryptoError> {
let (nonce, key) = hex.split_once('-').ok_or(CryptoError::Malformed)?;
let nonce = super::binary::bytes_from_hex(nonce).map_err(|_| CryptoError::Malformed)?;
let key = super::binary::bytes_from_hex(key).map_err(|_| CryptoError::Malformed)?;
Ok(EncryptedData {
nonce: nonce.try_into().map_err(|_| CryptoError::Malformed)?,
data: key,
})
}
}
impl Encodable for EncryptedData {
const FORMAT: Format = Format::data(0)
.with(<[u8; 12]>::FORMAT)
.with(alloc::vec::Vec::<u8>::FORMAT);
fn encode(&self, writer: &mut (impl WritesEncodable + ?Sized)) -> Result<(), CodecError> {
writer.write_data(&self.nonce)?;
writer.write_data(&self.data)?;
Ok(())
}
}
impl Decodable for EncryptedData {
fn decode(
&mut self,
reader: &mut (impl crate::codec::ReadsDecodable + ?Sized),
header: Option<crate::codec::DataHeader>,
) -> Result<(), CodecError> {
Self::ensure_header(header, &[0])?;
reader.read_data_into(&mut self.nonce)?;
reader.read_data_into(&mut self.data)?;
Ok(())
}
}
#[derive(Debug, Snafu, Clone)]
pub enum CryptoError {
#[snafu(display("the private key could not be loaded as an Ed25519 private key"))]
InvalidPrivateKey,
#[snafu(display("{pub_key} could not be loaded as an Ed25519 public key"))]
InvalidPublicKey { pub_key: PublicKeyBytes },
#[snafu(display("{signature} was not a valid Ed25519 signature for the provided message"))]
InvalidSignature { signature: SignatureBytes },
#[snafu(display("deriving a cryptographic key failed: {message}"))]
KeyDerivationFailure { message: alloc::string::String },
#[snafu(display("encrypting or decrypting data failed: {message}"))]
CipherFailure { message: alloc::string::String },
#[snafu(display("the provided input was malformed or corrupt"))]
Malformed,
}
impl From<argon2::Error> for CryptoError {
fn from(value: argon2::Error) -> Self {
Self::KeyDerivationFailure {
message: <argon2::Error as crate::alloc::string::ToString>::to_string(&value),
}
}
}
impl From<chacha20poly1305::Error> for CryptoError {
fn from(value: chacha20poly1305::Error) -> Self {
Self::CipherFailure {
message: <chacha20poly1305::Error as crate::alloc::string::ToString>::to_string(&value),
}
}
}
#[cfg(test)]
mod tests {
use crate::codec::ReadsDecodable;
use super::*;
#[test]
fn encrypted_data() {
let key = b"cupc4k3s";
let message = b"i'm so secret.";
let mut encrypted = EncryptedData::new(key, message).unwrap();
let decrypted = encrypted.decrypt(key).unwrap();
assert_eq!(message, decrypted.as_slice());
let mut encrypted_too = EncryptedData::new(key, message).unwrap();
assert_ne!(encrypted_too.data, encrypted.data);
assert_ne!(encrypted_too.nonce, encrypted.nonce);
encrypted.nonce.fill(0u8);
assert!(encrypted.decrypt(key).is_err());
encrypted_too.data.fill(0u8);
assert!(encrypted_too.decrypt(key).is_err());
}
#[test]
fn encrypted_data_codas_codec() {
let key = b"p4nc4k3s";
let message = b"i'm pretty secret.";
let encrypted = EncryptedData::new(key, message).unwrap();
let mut encoded = vec![];
encoded.write_data(&encrypted).unwrap();
let decoded: EncryptedData = encoded.as_slice().read_data().unwrap();
let decrypted = decoded.decrypt(key).unwrap();
assert_eq!(message, decrypted.as_slice());
}
#[test]
fn encrypted_data_hex_codec() {
let key = b"p4nc4k3s";
let message = b"i'm pretty secret.";
let encrypted = EncryptedData::new(key, message).unwrap();
let encoded = encrypted.to_hex();
let mut bytes = vec![];
bytes.write_data(&encrypted).unwrap();
eprintln!("raw hex: {encoded}");
eprintln!("\n\ncoda hx: {}", hex_from_bytes(&bytes));
let decoded = EncryptedData::from_hex(&encoded).unwrap();
assert_eq!(encrypted.nonce, decoded.nonce);
assert_eq!(encrypted.data, decoded.data);
}
}