From 9d826b27dbebba5fa553ea70f39248dbe88b679c Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 29 Jan 2023 13:13:54 -0500 Subject: [PATCH] checkpoint --- veilid-core/src/crypto/crypto_system.rs | 113 +++++++ veilid-core/src/crypto/v0/blake3digest512.rs | 70 +++++ veilid-core/src/crypto/v0/mod.rs | 297 +++++++++++++++++++ 3 files changed, 480 insertions(+) create mode 100644 veilid-core/src/crypto/crypto_system.rs create mode 100644 veilid-core/src/crypto/v0/blake3digest512.rs create mode 100644 veilid-core/src/crypto/v0/mod.rs diff --git a/veilid-core/src/crypto/crypto_system.rs b/veilid-core/src/crypto/crypto_system.rs new file mode 100644 index 00000000..74ee6bcf --- /dev/null +++ b/veilid-core/src/crypto/crypto_system.rs @@ -0,0 +1,113 @@ +use super::*; + +pub trait CryptoSystem { + // Accessors + fn version(&self) -> CryptoVersion; + fn crypto(&self) -> Crypto; + + // Cached Operations + fn cached_dh( + &self, + key: &DHTKey, + secret: &DHTKeySecret, + ) -> Result; + + // Generation + fn random_nonce(&self) -> Nonce; + fn random_shared_secret(&self) -> SharedSecret; + fn compute_dh( + &self, + key: &DHTKey, + secret: &DHTKeySecret, + ) -> Result; + fn generate_keypair(&self) -> (DHTKey, DHTKeySecret); + fn generate_hash(&self, data: &[u8]) -> DHTKey; + fn generate_hash_reader( + &self, + reader: &mut dyn std::io::Read, + ) -> Result; + + // Validation + fn validate_keypair(&self, dht_key: &DHTKey, dht_key_secret: &DHTKeySecret) -> bool; + fn validate_hash(&self, data: &[u8], dht_key: &DHTKey) -> bool; + fn validate_hash_reader( + &self, + reader: &mut dyn std::io::Read, + dht_key: &DHTKey, + ) -> Result; + + // Distance Metric + fn distance(&self, key1: &DHTKey, key2: &DHTKey) -> DHTKeyDistance; + + // Authentication + fn sign( + &self, + dht_key: &DHTKey, + dht_key_secret: &DHTKeySecret, + data: &[u8], + ) -> Result; + fn verify( + &self, + dht_key: &DHTKey, + data: &[u8], + signature: &DHTSignature, + ) -> Result<(), VeilidAPIError>; + + // AEAD Encrypt/Decrypt + fn aead_overhead(&self) -> usize; + fn decrypt_in_place_aead( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result<(), VeilidAPIError>; + fn decrypt_aead( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result, VeilidAPIError>; + fn encrypt_in_place_aead( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result<(), VeilidAPIError>; + fn encrypt_aead( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result, VeilidAPIError>; + + // NoAuth Encrypt/Decrypt + fn crypt_in_place_no_auth( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + ); + fn crypt_b2b_no_auth( + &self, + in_buf: &[u8], + out_buf: &mut [u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ); + fn crypt_no_auth_aligned_8( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> Vec; + fn crypt_no_auth_unaligned( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> Vec; +} diff --git a/veilid-core/src/crypto/v0/blake3digest512.rs b/veilid-core/src/crypto/v0/blake3digest512.rs new file mode 100644 index 00000000..1db950af --- /dev/null +++ b/veilid-core/src/crypto/v0/blake3digest512.rs @@ -0,0 +1,70 @@ +use digest::generic_array::typenum::U64; +use digest::{Digest, Output}; +use generic_array::GenericArray; + +pub struct Blake3Digest512 { + dig: blake3::Hasher, +} + +impl Digest for Blake3Digest512 { + type OutputSize = U64; + + fn new() -> Self { + Self { + dig: blake3::Hasher::new(), + } + } + + fn update(&mut self, data: impl AsRef<[u8]>) { + self.dig.update(data.as_ref()); + } + + fn chain(mut self, data: impl AsRef<[u8]>) -> Self + where + Self: Sized, + { + self.update(data); + self + } + + fn finalize(self) -> Output { + let mut b = [0u8; 64]; + self.dig.finalize_xof().fill(&mut b); + let mut out = GenericArray::::default(); + for n in 0..64 { + out[n] = b[n]; + } + out + } + + fn finalize_reset(&mut self) -> Output { + let mut b = [0u8; 64]; + self.dig.finalize_xof().fill(&mut b); + let mut out = GenericArray::::default(); + for n in 0..64 { + out[n] = b[n]; + } + self.reset(); + out + } + + fn reset(&mut self) { + self.dig.reset(); + } + + fn output_size() -> usize { + 64 + } + + fn digest(data: &[u8]) -> Output { + let mut dig = blake3::Hasher::new(); + dig.update(data); + let mut b = [0u8; 64]; + dig.finalize_xof().fill(&mut b); + let mut out = GenericArray::::default(); + for n in 0..64 { + out[n] = b[n]; + } + out + } +} diff --git a/veilid-core/src/crypto/v0/mod.rs b/veilid-core/src/crypto/v0/mod.rs new file mode 100644 index 00000000..4094c8e3 --- /dev/null +++ b/veilid-core/src/crypto/v0/mod.rs @@ -0,0 +1,297 @@ +pub mod blake3digest512; +pub use blake3digest512::*; + +pub use super::*; + +use chacha20::cipher::{KeyIvInit, StreamCipher}; +use chacha20::XChaCha20; +use chacha20poly1305 as ch; +use chacha20poly1305::aead::{AeadInPlace, NewAead}; +use core::convert::TryInto; +use curve25519_dalek as cd; +use digest::Digest; +use ed25519_dalek as ed; + +use ed25519_dalek::{Keypair, PublicKey, Signature}; +use x25519_dalek as xd; + +const AEAD_OVERHEAD: usize = 16; + +fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result { + let bytes = key.to_bytes(); + let compressed = cd::edwards::CompressedEdwardsY(bytes); + let point = compressed + .decompress() + .ok_or_else(|| VeilidAPIError::internal("ed25519_to_x25519_pk failed"))?; + let mp = point.to_montgomery(); + Ok(xd::PublicKey::from(mp.to_bytes())) +} +fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result { + let exp = ed::ExpandedSecretKey::from(key); + let bytes: [u8; ed::EXPANDED_SECRET_KEY_LENGTH] = exp.to_bytes(); + let lowbytes: [u8; 32] = bytes[0..32].try_into().map_err(VeilidAPIError::internal)?; + Ok(xd::StaticSecret::from(lowbytes)) +} + +/// V1 CryptoSystem +#[derive(Clone)] +pub struct CryptoV0System { + crypto: Crypto, +} + +impl CryptoV0System { + pub fn new(crypto: Crypto) -> Self { + Self { crypto } + } +} + +impl CryptoSystem for CryptoV0System { + // Accessors + fn version(&self) -> CryptoVersion { + return 0u8; + } + + fn crypto(&self) -> Crypto { + return self.crypto.clone(); + } + + // Cached Operations + fn cached_dh( + &self, + key: &DHTKey, + secret: &DHTKeySecret, + ) -> Result { + self.crypto + .cached_dh_internal::(self, key, secret) + } + + // Generation + fn random_nonce(&self) -> Nonce { + let mut nonce = [0u8; 24]; + random_bytes(&mut nonce).unwrap(); + nonce + } + fn random_shared_secret(&self) -> SharedSecret { + let mut s = [0u8; 32]; + random_bytes(&mut s).unwrap(); + s + } + fn compute_dh( + &self, + key: &DHTKey, + secret: &DHTKeySecret, + ) -> Result { + let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?; + let pk_xd = ed25519_to_x25519_pk(&pk_ed)?; + let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?; + let sk_xd = ed25519_to_x25519_sk(&sk_ed)?; + Ok(sk_xd.diffie_hellman(&pk_xd).to_bytes()) + } + fn generate_keypair(&self) -> (DHTKey, DHTKeySecret) { + let mut csprng = VeilidRng {}; + let keypair = Keypair::generate(&mut csprng); + let dht_key = DHTKey::new(keypair.public.to_bytes()); + let dht_key_secret = DHTKeySecret::new(keypair.secret.to_bytes()); + + (dht_key, dht_key_secret) + } + fn generate_hash(&self, data: &[u8]) -> DHTKey { + DHTKey::new(*blake3::hash(data).as_bytes()) + } + fn generate_hash_reader( + &self, + reader: &mut dyn std::io::Read, + ) -> Result { + let mut hasher = blake3::Hasher::new(); + std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?; + Ok(DHTKey::new(*hasher.finalize().as_bytes())) + } + + // Validation + fn validate_keypair(&self, dht_key: &DHTKey, dht_key_secret: &DHTKeySecret) -> bool { + let data = vec![0u8; 512]; + let sig = match self.sign(dht_key, dht_key_secret, &data) { + Ok(s) => s, + Err(_) => { + return false; + } + }; + self.verify(dht_key, &data, &sig).is_ok() + } + fn validate_hash(&self, data: &[u8], dht_key: &DHTKey) -> bool { + let bytes = *blake3::hash(data).as_bytes(); + + bytes == dht_key.bytes + } + fn validate_hash_reader( + &self, + reader: &mut dyn std::io::Read, + dht_key: &DHTKey, + ) -> Result { + let mut hasher = blake3::Hasher::new(); + std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?; + let bytes = *hasher.finalize().as_bytes(); + Ok(bytes == dht_key.bytes) + } + // Distance Metric + fn distance(&self, key1: &DHTKey, key2: &DHTKey) -> DHTKeyDistance { + let mut bytes = [0u8; DHT_KEY_LENGTH]; + + for (n, byte) in bytes.iter_mut().enumerate() { + *byte = key1.bytes[n] ^ key2.bytes[n]; + } + + DHTKeyDistance::new(bytes) + } + + // Authentication + fn sign( + &self, + dht_key: &DHTKey, + dht_key_secret: &DHTKeySecret, + data: &[u8], + ) -> Result { + let mut kpb: [u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH] = + [0u8; DHT_KEY_SECRET_LENGTH + DHT_KEY_LENGTH]; + + kpb[..DHT_KEY_SECRET_LENGTH].copy_from_slice(&dht_key_secret.bytes); + kpb[DHT_KEY_SECRET_LENGTH..].copy_from_slice(&dht_key.bytes); + let keypair = Keypair::from_bytes(&kpb) + .map_err(|e| VeilidAPIError::parse_error("Keypair is invalid", e))?; + + let mut dig = Blake3Digest512::new(); + dig.update(data); + + let sig = keypair + .sign_prehashed(dig, None) + .map_err(VeilidAPIError::internal)?; + + let dht_sig = DHTSignature::new(sig.to_bytes()); + Ok(dht_sig) + } + fn verify( + &self, + dht_key: &DHTKey, + data: &[u8], + signature: &DHTSignature, + ) -> Result<(), VeilidAPIError> { + let pk = PublicKey::from_bytes(&dht_key.bytes) + .map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?; + let sig = Signature::from_bytes(&signature.bytes) + .map_err(|e| VeilidAPIError::parse_error("Signature is invalid", e))?; + + let mut dig = Blake3Digest512::new(); + dig.update(data); + + pk.verify_prehashed(dig, None, &sig) + .map_err(|e| VeilidAPIError::parse_error("Verification failed", e))?; + Ok(()) + } + + // AEAD Encrypt/Decrypt + fn aead_overhead(&self) -> usize { + AEAD_OVERHEAD + } + fn decrypt_in_place_aead( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result<(), VeilidAPIError> { + let key = ch::Key::from(*shared_secret); + let xnonce = ch::XNonce::from(*nonce); + let aead = ch::XChaCha20Poly1305::new(&key); + aead.decrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body) + .map_err(map_to_string) + .map_err(VeilidAPIError::generic) + } + + fn decrypt_aead( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result, VeilidAPIError> { + let mut out = body.to_vec(); + self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data) + .map_err(map_to_string) + .map_err(VeilidAPIError::generic)?; + Ok(out) + } + + fn encrypt_in_place_aead( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result<(), VeilidAPIError> { + let key = ch::Key::from(*shared_secret); + let xnonce = ch::XNonce::from(*nonce); + let aead = ch::XChaCha20Poly1305::new(&key); + + aead.encrypt_in_place(&xnonce, associated_data.unwrap_or(b""), body) + .map_err(map_to_string) + .map_err(VeilidAPIError::generic) + } + + fn encrypt_aead( + &self, + body: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + associated_data: Option<&[u8]>, + ) -> Result, VeilidAPIError> { + let mut out = body.to_vec(); + self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data) + .map_err(map_to_string) + .map_err(VeilidAPIError::generic)?; + Ok(out) + } + + // NoAuth Encrypt/Decrypt + fn crypt_in_place_no_auth( + &self, + body: &mut Vec, + nonce: &Nonce, + shared_secret: &SharedSecret, + ) { + let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into()); + cipher.apply_keystream(body); + } + + fn crypt_b2b_no_auth( + &self, + in_buf: &[u8], + out_buf: &mut [u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) { + let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into()); + cipher.apply_keystream_b2b(in_buf, &mut out_buf).unwrap(); + } + + fn crypt_no_auth_aligned_8( + &self, + in_buf: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> Vec { + let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) }; + self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret); + out_buf + } + + fn crypt_no_auth_unaligned( + &self, + in_buf: &[u8], + nonce: &Nonce, + shared_secret: &SharedSecret, + ) -> Vec { + let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) }; + self.crypt_b2b_no_auth(in_buf, &mut out_buf, nonce, shared_secret); + out_buf + } +}