checkpoint
This commit is contained in:
parent
8b026f4756
commit
766872ac26
390
Cargo.lock
generated
390
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -55,8 +55,8 @@ curve25519-dalek = { package = "curve25519-dalek-ng", version = "^4", default_fe
|
||||
# ed25519-dalek needs rand 0.7 until it updates itself
|
||||
rand = "0.7"
|
||||
# curve25519-dalek-ng is stuck on digest 0.9.0
|
||||
blake3 = { version = "1.1.0", default_features = false }
|
||||
digest = "0.9.0"
|
||||
blake3 = { version = "1.1.0" }
|
||||
rtnetlink = { version = "^0", default-features = false, optional = true }
|
||||
async-std-resolver = { version = "^0", optional = true }
|
||||
trust-dns-resolver = { version = "^0", optional = true }
|
||||
|
@ -5,34 +5,38 @@ use crate::routing_table::VersionRange;
|
||||
use crate::*;
|
||||
use core::convert::TryInto;
|
||||
|
||||
// #[repr(C, packed)]
|
||||
// struct EnvelopeHeader {
|
||||
// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
|
||||
// version: u8, // 0x04: 0 = EnvelopeV0
|
||||
// min_version: u8, // 0x05: 0 = EnvelopeV0
|
||||
// max_version: u8, // 0x06: 0 = EnvelopeV0
|
||||
// reserved: u8, // 0x07: Reserved for future use
|
||||
// }
|
||||
|
||||
// #[repr(C, packed)]
|
||||
// struct EnvelopeV0 {
|
||||
// // Size is 106 bytes.
|
||||
// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
|
||||
// version: u8, // 0x04: 0 = EnvelopeV0
|
||||
// min_version: u8, // 0x05: 0 = EnvelopeV0
|
||||
// max_version: u8, // 0x06: 0 = EnvelopeV0
|
||||
// reserved: u8, // 0x07: Reserved for future use
|
||||
// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4.
|
||||
// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped.
|
||||
// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for x25519
|
||||
// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the Ed25519 public key of the sender (must be verified with find_node if this is a new node_id/address combination)
|
||||
// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the Ed25519 public key of the recipient (must be the receiving node, or a relay lease holder)
|
||||
// // 0x6A: message is appended (operations)
|
||||
// // encrypted by XChaCha20Poly1305(nonce,x25519(recipient_id, sender_secret_key))
|
||||
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire envelope including header is appended to the packet
|
||||
// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature.
|
||||
// }
|
||||
/// Envelopes are versioned along with crypto versions
|
||||
///
|
||||
/// These are the formats for the on-the-wire serialization performed by this module
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct EnvelopeHeader {
|
||||
/// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
/// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
|
||||
/// version: u8, // 0x04: 0 = EnvelopeV0
|
||||
/// min_version: u8, // 0x05: 0 = EnvelopeV0
|
||||
/// max_version: u8, // 0x06: 0 = EnvelopeV0
|
||||
/// reserved: u8, // 0x07: Reserved for future use
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct EnvelopeV0 {
|
||||
/// // Size is 106 bytes.
|
||||
/// magic: [u8; 4], // 0x00: 0x56 0x4C 0x49 0x44 ("VLID")
|
||||
/// version: u8, // 0x04: 0 = EnvelopeV0
|
||||
/// min_version: u8, // 0x05: 0 = EnvelopeV0
|
||||
/// max_version: u8, // 0x06: 0 = EnvelopeV0
|
||||
/// reserved: u8, // 0x07: Reserved for future use
|
||||
/// size: u16, // 0x08: Total size of the envelope including the encrypted operations message. Maximum size is 65,507 bytes, which is the data size limit for a single UDP message on IPv4.
|
||||
/// timestamp: u64, // 0x0A: Duration since UNIX_EPOCH in microseconds when this message is sent. Messages older than 10 seconds are dropped.
|
||||
/// nonce: [u8; 24], // 0x12: Random nonce for replay protection and for x25519
|
||||
/// sender_id: [u8; 32], // 0x2A: Node ID of the message source, which is the Ed25519 public key of the sender (must be verified with find_node if this is a new node_id/address combination)
|
||||
/// recipient_id: [u8; 32], // 0x4A: Node ID of the intended recipient, which is the Ed25519 public key of the recipient (must be the receiving node, or a relay lease holder)
|
||||
/// // 0x6A: message is appended (operations)
|
||||
/// // encrypted by XChaCha20Poly1305(nonce,x25519(recipient_id, sender_secret_key))
|
||||
/// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire envelope including header is appended to the packet
|
||||
/// // entire header needs to be included in message digest, relays are not allowed to modify the envelope without invalidating the signature.
|
||||
/// }
|
||||
|
||||
pub const MAX_ENVELOPE_SIZE: usize = 65507;
|
||||
pub const MIN_ENVELOPE_SIZE: usize = 0x6A + 0x40; // Header + Signature
|
||||
@ -186,10 +190,12 @@ impl Envelope {
|
||||
node_id_secret: &DHTKeySecret,
|
||||
) -> Result<Vec<u8>, VeilidAPIError> {
|
||||
// Get DH secret
|
||||
let dh_secret = crypto.cached_dh(&self.sender_id, node_id_secret)?;
|
||||
let vcrypto = crypto.get(self.version)?;
|
||||
let dh_secret = vcrypto.cached_dh(&self.sender_id, node_id_secret)?;
|
||||
|
||||
// Decrypt message without authentication
|
||||
let body = Crypto::crypt_no_auth(&data[0x6A..data.len() - 64], &self.nonce, &dh_secret);
|
||||
let body =
|
||||
vcrypto.crypt_no_auth_aligned_8(&data[0x6A..data.len() - 64], &self.nonce, &dh_secret);
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
@ -227,10 +233,11 @@ impl Envelope {
|
||||
data[0x4A..0x6A].copy_from_slice(&self.recipient_id.bytes);
|
||||
|
||||
// Generate dh secret
|
||||
let dh_secret = crypto.cached_dh(&self.recipient_id, node_id_secret)?;
|
||||
let vcrypto = crypto.get(self.version)?;
|
||||
let dh_secret = vcrypto.cached_dh(&self.recipient_id, node_id_secret)?;
|
||||
|
||||
// Encrypt and authenticate message
|
||||
let encrypted_body = Crypto::crypt_no_auth(body, &self.nonce, &dh_secret);
|
||||
let encrypted_body = vcrypto.crypt_no_auth_aligned_8(body, &self.nonce, &dh_secret);
|
||||
|
||||
// Write body
|
||||
if !encrypted_body.is_empty() {
|
||||
|
@ -6,10 +6,7 @@ use core::fmt;
|
||||
use core::hash::Hash;
|
||||
|
||||
use data_encoding::BASE64URL_NOPAD;
|
||||
use digest::generic_array::typenum::U64;
|
||||
use digest::{Digest, Output};
|
||||
use ed25519_dalek::{Keypair, PublicKey, Signature};
|
||||
use generic_array::GenericArray;
|
||||
|
||||
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -225,165 +222,9 @@ macro_rules! byte_array_type {
|
||||
};
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
|
||||
byte_array_type!(DHTKey, DHT_KEY_LENGTH);
|
||||
byte_array_type!(DHTKeySecret, DHT_KEY_SECRET_LENGTH);
|
||||
byte_array_type!(DHTSignature, DHT_SIGNATURE_LENGTH);
|
||||
byte_array_type!(DHTKeyDistance, DHT_KEY_LENGTH);
|
||||
|
||||
/////////////////////////////////////////
|
||||
|
||||
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<Self> {
|
||||
let mut b = [0u8; 64];
|
||||
self.dig.finalize_xof().fill(&mut b);
|
||||
let mut out = GenericArray::<u8, U64>::default();
|
||||
for n in 0..64 {
|
||||
out[n] = b[n];
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn finalize_reset(&mut self) -> Output<Self> {
|
||||
let mut b = [0u8; 64];
|
||||
self.dig.finalize_xof().fill(&mut b);
|
||||
let mut out = GenericArray::<u8, U64>::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<Self> {
|
||||
let mut dig = blake3::Hasher::new();
|
||||
dig.update(data);
|
||||
let mut b = [0u8; 64];
|
||||
dig.finalize_xof().fill(&mut b);
|
||||
let mut out = GenericArray::<u8, U64>::default();
|
||||
for n in 0..64 {
|
||||
out[n] = b[n];
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
|
||||
pub fn generate_secret() -> (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)
|
||||
}
|
||||
|
||||
pub fn sign(
|
||||
dht_key: &DHTKey,
|
||||
dht_key_secret: &DHTKeySecret,
|
||||
data: &[u8],
|
||||
) -> Result<DHTSignature, VeilidAPIError> {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn verify(
|
||||
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(())
|
||||
}
|
||||
|
||||
pub fn generate_hash(data: &[u8]) -> DHTKey {
|
||||
DHTKey::new(*blake3::hash(data).as_bytes())
|
||||
}
|
||||
|
||||
pub fn validate_hash(data: &[u8], dht_key: &DHTKey) -> bool {
|
||||
let bytes = *blake3::hash(data).as_bytes();
|
||||
|
||||
bytes == dht_key.bytes
|
||||
}
|
||||
|
||||
pub fn validate_key(dht_key: &DHTKey, dht_key_secret: &DHTKeySecret) -> bool {
|
||||
let data = vec![0u8; 512];
|
||||
let sig = match sign(dht_key, dht_key_secret, &data) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
verify(dht_key, &data, &sig).is_ok()
|
||||
}
|
||||
|
||||
pub fn distance(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)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn sort_closest_fn(key: DHTKey) -> impl FnMut(&DHTKey, &DHTKey) -> std::cmp::Ordering {
|
||||
move |k1, k2| distance(k1, &key).cmp(&distance(k2, &key))
|
||||
}
|
||||
|
@ -3,35 +3,32 @@ mod key;
|
||||
mod receipt;
|
||||
mod value;
|
||||
|
||||
pub mod crypto_system;
|
||||
pub mod tests;
|
||||
pub mod v0;
|
||||
|
||||
pub use crypto_system::*;
|
||||
pub use envelope::*;
|
||||
pub use key::*;
|
||||
pub use receipt::*;
|
||||
pub use value::*;
|
||||
|
||||
pub const MIN_CRYPTO_VERSION: u8 = 0u8;
|
||||
pub const MAX_CRYPTO_VERSION: u8 = 0u8;
|
||||
pub type CryptoVersion = u8;
|
||||
pub const MIN_CRYPTO_VERSION: CryptoVersion = 0u8;
|
||||
pub const MAX_CRYPTO_VERSION: CryptoVersion = 0u8;
|
||||
|
||||
use crate::*;
|
||||
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 ed25519_dalek as ed;
|
||||
use hashlink::linked_hash_map::Entry;
|
||||
use hashlink::LruCache;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use x25519_dalek as xd;
|
||||
|
||||
pub type SharedSecret = [u8; 32];
|
||||
pub type Nonce = [u8; 24];
|
||||
|
||||
const DH_CACHE_SIZE: usize = 1024;
|
||||
pub const AEAD_OVERHEAD: usize = 16;
|
||||
pub type CryptoSystemVersion = Arc<dyn CryptoSystem + Send + Sync>;
|
||||
|
||||
const DH_CACHE_SIZE: usize = 4096;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
struct DHCacheKey {
|
||||
@ -79,6 +76,7 @@ struct CryptoInner {
|
||||
node_id_secret: DHTKeySecret,
|
||||
dh_cache: DHCache,
|
||||
flush_future: Option<SendPinBoxFuture<()>>,
|
||||
crypto_v0: Option<Arc<dyn CryptoSystem + Send + Sync>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -95,14 +93,19 @@ impl Crypto {
|
||||
node_id_secret: Default::default(),
|
||||
dh_cache: DHCache::new(DH_CACHE_SIZE),
|
||||
flush_future: None,
|
||||
crypto_v0: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(config: VeilidConfig, table_store: TableStore) -> Self {
|
||||
Self {
|
||||
let out = Self {
|
||||
config,
|
||||
inner: Arc::new(Mutex::new(Self::new_inner(table_store))),
|
||||
}
|
||||
};
|
||||
|
||||
out.inner.lock().crypto_v0 = Some(Arc::new(v0::CryptoV0System::new(out.clone())));
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub async fn init(&self) -> EyreResult<()> {
|
||||
@ -180,24 +183,24 @@ impl Crypto {
|
||||
};
|
||||
}
|
||||
|
||||
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIError> {
|
||||
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<xd::StaticSecret, VeilidAPIError> {
|
||||
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))
|
||||
// Factory
|
||||
fn get(&self, version: CryptoVersion) -> Result<CryptoSystemVersion, VeilidAPIError> {
|
||||
let inner = self.inner.lock();
|
||||
match version {
|
||||
0u8 => Ok(inner.crypto_v0.clone().unwrap()),
|
||||
_ => Err(VeilidAPIError::InvalidArgument {
|
||||
context: "Unsupported crypto version".to_owned(),
|
||||
argument: "version".to_owned(),
|
||||
value: format!("{}", version),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cached_dh(
|
||||
// Internal utilities
|
||||
|
||||
fn cached_dh_internal<T: CryptoSystem>(
|
||||
&self,
|
||||
vcrypto: &T,
|
||||
key: &DHTKey,
|
||||
secret: &DHTKeySecret,
|
||||
) -> Result<SharedSecret, VeilidAPIError> {
|
||||
@ -208,111 +211,11 @@ impl Crypto {
|
||||
}) {
|
||||
Entry::Occupied(e) => e.get().shared_secret,
|
||||
Entry::Vacant(e) => {
|
||||
let shared_secret = Self::compute_dh(key, secret)?;
|
||||
let shared_secret = vcrypto.compute_dh(key, secret)?;
|
||||
e.insert(DHCacheValue { shared_secret });
|
||||
shared_secret
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
///////////
|
||||
// These are safe to use regardless of initialization status
|
||||
|
||||
pub fn compute_dh(key: &DHTKey, secret: &DHTKeySecret) -> Result<SharedSecret, VeilidAPIError> {
|
||||
let pk_ed = ed::PublicKey::from_bytes(&key.bytes).map_err(VeilidAPIError::internal)?;
|
||||
let pk_xd = Self::ed25519_to_x25519_pk(&pk_ed)?;
|
||||
let sk_ed = ed::SecretKey::from_bytes(&secret.bytes).map_err(VeilidAPIError::internal)?;
|
||||
let sk_xd = Self::ed25519_to_x25519_sk(&sk_ed)?;
|
||||
Ok(sk_xd.diffie_hellman(&pk_xd).to_bytes())
|
||||
}
|
||||
|
||||
pub fn get_random_nonce() -> Nonce {
|
||||
let mut nonce = [0u8; 24];
|
||||
random_bytes(&mut nonce).unwrap();
|
||||
nonce
|
||||
}
|
||||
|
||||
pub fn get_random_secret() -> SharedSecret {
|
||||
let mut s = [0u8; 32];
|
||||
random_bytes(&mut s).unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
pub fn decrypt_in_place_aead(
|
||||
body: &mut Vec<u8>,
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn decrypt_aead(
|
||||
body: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> Result<Vec<u8>, 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)
|
||||
}
|
||||
|
||||
pub fn encrypt_in_place_aead(
|
||||
body: &mut Vec<u8>,
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn encrypt_aead(
|
||||
body: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
associated_data: Option<&[u8]>,
|
||||
) -> Result<Vec<u8>, 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)
|
||||
}
|
||||
|
||||
pub fn crypt_in_place_no_auth(body: &mut Vec<u8>, nonce: &Nonce, shared_secret: &SharedSecret) {
|
||||
let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into());
|
||||
cipher.apply_keystream(body);
|
||||
}
|
||||
|
||||
pub fn crypt_b2b_no_auth(
|
||||
in_buf: &[u8],
|
||||
nonce: &Nonce,
|
||||
shared_secret: &SharedSecret,
|
||||
) -> Vec<u8> {
|
||||
let mut cipher = XChaCha20::new(shared_secret.into(), nonce.into());
|
||||
// Allocate uninitialized memory, aligned to 8 byte boundary because capnp is faster this way
|
||||
// and the Vec returned here will be used to hold decrypted rpc messages
|
||||
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
|
||||
cipher.apply_keystream_b2b(in_buf, &mut out_buf).unwrap();
|
||||
out_buf
|
||||
}
|
||||
|
||||
pub fn crypt_no_auth(body: &[u8], nonce: &Nonce, shared_secret: &SharedSecret) -> Vec<u8> {
|
||||
Self::crypt_b2b_no_auth(body, nonce, shared_secret)
|
||||
}
|
||||
}
|
||||
|
@ -5,26 +5,30 @@ use crate::*;
|
||||
use core::convert::TryInto;
|
||||
use data_encoding::BASE64URL_NOPAD;
|
||||
|
||||
// #[repr(C, packed)]
|
||||
// struct ReceiptHeader {
|
||||
// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
|
||||
// version: u8, // 0x04: 0 = ReceiptV0
|
||||
// reserved: u8, // 0x05: Reserved for future use
|
||||
// }
|
||||
|
||||
// #[repr(C, packed)]
|
||||
// struct ReceiptV0 {
|
||||
// // Size is 106 bytes.
|
||||
// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
|
||||
// version: u8, // 0x04: 0 = ReceiptV0
|
||||
// reserved: u8, // 0x05: Reserved for future use
|
||||
// size: u16, // 0x06: Total size of the receipt including the extra data and the signature. Maximum size is 1152 bytes.
|
||||
// nonce: [u8; 24], // 0x08: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required.
|
||||
// sender_id: [u8; 32], // 0x20: Node ID of the message source, which is the Ed25519 public key of the sender
|
||||
// extra_data: [u8; ??], // 0x40: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1024 bytes)
|
||||
// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire receipt including header and extra data is appended to the packet
|
||||
// }
|
||||
/// Out-of-band receipts are versioned along with crypto versions
|
||||
///
|
||||
/// These are the formats for the on-the-wire serialization performed by this module
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct ReceiptHeader {
|
||||
/// // Size is at least 8 bytes. Depending on the version specified, the size may vary and should be case to the appropriate struct
|
||||
/// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
|
||||
/// version: u8, // 0x04: 0 = ReceiptV0
|
||||
/// reserved: u8, // 0x05: Reserved for future use
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C, packed)]
|
||||
/// struct ReceiptV0 {
|
||||
/// // Size is 106 bytes.
|
||||
/// magic: [u8; 4], // 0x00: 0x52 0x43 0x50 0x54 ("RCPT")
|
||||
/// version: u8, // 0x04: 0 = ReceiptV0
|
||||
/// reserved: u8, // 0x05: Reserved for future use
|
||||
/// size: u16, // 0x06: Total size of the receipt including the extra data and the signature. Maximum size is 1152 bytes.
|
||||
/// nonce: [u8; 24], // 0x08: Randomly chosen bytes that represent a unique receipt. Could be used to encrypt the extra data, but it's not required.
|
||||
/// sender_id: [u8; 32], // 0x20: Node ID of the message source, which is the Ed25519 public key of the sender
|
||||
/// extra_data: [u8; ??], // 0x40: Extra data is appended (arbitrary extra data, not encrypted by receipt itself, maximum size is 1024 bytes)
|
||||
/// signature: [u8; 64], // 0x?? (end-0x40): Ed25519 signature of the entire receipt including header and extra data is appended to the packet
|
||||
/// }
|
||||
|
||||
pub const MAX_RECEIPT_SIZE: usize = 1152;
|
||||
pub const MAX_EXTRA_DATA_SIZE: usize = 1024;
|
||||
|
@ -3,3 +3,19 @@ pub mod test_dht_key;
|
||||
pub mod test_envelope_receipt;
|
||||
|
||||
use super::*;
|
||||
use crate::tests::common::test_veilid_config::*;
|
||||
|
||||
async fn crypto_tests_startup() -> VeilidAPI {
|
||||
trace!("crypto_tests: starting");
|
||||
let (update_callback, config_callback) = setup_veilid_core();
|
||||
let api = api_startup(update_callback, config_callback)
|
||||
.await
|
||||
.expect("startup failed");
|
||||
api
|
||||
}
|
||||
|
||||
async fn crypto_tests_shutdown(api: VeilidAPI) {
|
||||
trace!("crypto_tests: shutting down");
|
||||
api.shutdown().await;
|
||||
trace!("crypto_tests: finished");
|
||||
}
|
||||
|
@ -1,37 +1,21 @@
|
||||
use super::*;
|
||||
use crate::tests::common::test_veilid_config::*;
|
||||
|
||||
static LOREM_IPSUM:&[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
|
||||
|
||||
async fn startup() -> VeilidAPI {
|
||||
trace!("test_table_store: starting");
|
||||
let (update_callback, config_callback) = setup_veilid_core();
|
||||
let api = api_startup(update_callback, config_callback)
|
||||
.await
|
||||
.expect("startup failed");
|
||||
api
|
||||
}
|
||||
|
||||
async fn shutdown(api: VeilidAPI) {
|
||||
trace!("test_table_store: shutting down");
|
||||
api.shutdown().await;
|
||||
trace!("test_table_store: finished");
|
||||
}
|
||||
|
||||
pub async fn test_aead() {
|
||||
pub async fn test_aead(vcrypto: CryptoSystemVersion) {
|
||||
trace!("test_aead");
|
||||
|
||||
let n1 = Crypto::get_random_nonce();
|
||||
let n1 = vcrypto.random_nonce();
|
||||
let n2 = loop {
|
||||
let n = Crypto::get_random_nonce();
|
||||
let n = vcrypto.random_nonce();
|
||||
if n != n1 {
|
||||
break n;
|
||||
}
|
||||
};
|
||||
|
||||
let ss1 = Crypto::get_random_secret();
|
||||
let ss1 = vcrypto.random_shared_secret();
|
||||
let ss2 = loop {
|
||||
let ss = Crypto::get_random_secret();
|
||||
let ss = vcrypto.random_shared_secret();
|
||||
if ss != ss1 {
|
||||
break ss;
|
||||
}
|
||||
@ -41,67 +25,77 @@ pub async fn test_aead() {
|
||||
let body2 = body.clone();
|
||||
let size_before_encrypt = body.len();
|
||||
assert!(
|
||||
Crypto::encrypt_in_place_aead(&mut body, &n1, &ss1, None).is_ok(),
|
||||
vcrypto
|
||||
.encrypt_in_place_aead(&mut body, &n1, &ss1, None)
|
||||
.is_ok(),
|
||||
"encrypt should succeed"
|
||||
);
|
||||
let size_after_encrypt = body.len();
|
||||
assert!(
|
||||
size_after_encrypt - size_before_encrypt == AEAD_OVERHEAD,
|
||||
size_after_encrypt - size_before_encrypt == vcrypto.aead_overhead(),
|
||||
"overhead should match"
|
||||
);
|
||||
let mut body3 = body.clone();
|
||||
let mut body4 = body.clone();
|
||||
let mut body5 = body.clone();
|
||||
assert!(
|
||||
Crypto::decrypt_in_place_aead(&mut body, &n1, &ss1, None).is_ok(),
|
||||
vcrypto
|
||||
.decrypt_in_place_aead(&mut body, &n1, &ss1, None)
|
||||
.is_ok(),
|
||||
"decrypt should succeed"
|
||||
);
|
||||
assert_eq!(body, body2, "results should be the same");
|
||||
|
||||
assert!(
|
||||
Crypto::decrypt_in_place_aead(&mut body3, &n2, &ss1, None).is_err(),
|
||||
vcrypto
|
||||
.decrypt_in_place_aead(&mut body3, &n2, &ss1, None)
|
||||
.is_err(),
|
||||
"decrypt with wrong nonce should fail"
|
||||
);
|
||||
assert_ne!(body3, body, "failure changes data");
|
||||
|
||||
assert!(
|
||||
Crypto::decrypt_in_place_aead(&mut body4, &n1, &ss2, None).is_err(),
|
||||
vcrypto
|
||||
.decrypt_in_place_aead(&mut body4, &n1, &ss2, None)
|
||||
.is_err(),
|
||||
"decrypt with wrong secret should fail"
|
||||
);
|
||||
assert_ne!(body4, body, "failure changes data");
|
||||
|
||||
assert!(
|
||||
Crypto::decrypt_in_place_aead(&mut body5, &n1, &ss2, Some(b"foobar")).is_err(),
|
||||
vcrypto
|
||||
.decrypt_in_place_aead(&mut body5, &n1, &ss2, Some(b"foobar"))
|
||||
.is_err(),
|
||||
"decrypt with wrong associated data should fail"
|
||||
);
|
||||
assert_ne!(body5, body, "failure changes data");
|
||||
|
||||
assert!(
|
||||
Crypto::decrypt_aead(LOREM_IPSUM, &n1, &ss1, None).is_err(),
|
||||
vcrypto.decrypt_aead(LOREM_IPSUM, &n1, &ss1, None).is_err(),
|
||||
"should fail authentication"
|
||||
);
|
||||
|
||||
let body5 = Crypto::encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
|
||||
let body6 = Crypto::decrypt_aead(&body5, &n1, &ss1, None).unwrap();
|
||||
let body7 = Crypto::encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
|
||||
let body5 = vcrypto.encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
|
||||
let body6 = vcrypto.decrypt_aead(&body5, &n1, &ss1, None).unwrap();
|
||||
let body7 = vcrypto.encrypt_aead(LOREM_IPSUM, &n1, &ss1, None).unwrap();
|
||||
assert_eq!(body6, LOREM_IPSUM);
|
||||
assert_eq!(body5, body7);
|
||||
}
|
||||
|
||||
pub async fn test_no_auth() {
|
||||
pub async fn test_no_auth(vcrypto: CryptoSystemVersion) {
|
||||
trace!("test_no_auth");
|
||||
|
||||
let n1 = Crypto::get_random_nonce();
|
||||
let n1 = vcrypto.random_nonce();
|
||||
let n2 = loop {
|
||||
let n = Crypto::get_random_nonce();
|
||||
let n = vcrypto.random_nonce();
|
||||
if n != n1 {
|
||||
break n;
|
||||
}
|
||||
};
|
||||
|
||||
let ss1 = Crypto::get_random_secret();
|
||||
let ss1 = vcrypto.random_shared_secret();
|
||||
let ss2 = loop {
|
||||
let ss = Crypto::get_random_secret();
|
||||
let ss = vcrypto.random_shared_secret();
|
||||
if ss != ss1 {
|
||||
break ss;
|
||||
}
|
||||
@ -110,7 +104,7 @@ pub async fn test_no_auth() {
|
||||
let mut body = LOREM_IPSUM.to_vec();
|
||||
let body2 = body.clone();
|
||||
let size_before_encrypt = body.len();
|
||||
Crypto::crypt_in_place_no_auth(&mut body, &n1, &ss1);
|
||||
vcrypto.crypt_in_place_no_auth(&mut body, &n1, &ss1);
|
||||
|
||||
let size_after_encrypt = body.len();
|
||||
assert_eq!(
|
||||
@ -120,41 +114,47 @@ pub async fn test_no_auth() {
|
||||
let mut body3 = body.clone();
|
||||
let mut body4 = body.clone();
|
||||
|
||||
Crypto::crypt_in_place_no_auth(&mut body, &n1, &ss1);
|
||||
vcrypto.crypt_in_place_no_auth(&mut body, &n1, &ss1);
|
||||
assert_eq!(body, body2, "result after decrypt should be the same");
|
||||
|
||||
Crypto::crypt_in_place_no_auth(&mut body3, &n2, &ss1);
|
||||
vcrypto.crypt_in_place_no_auth(&mut body3, &n2, &ss1);
|
||||
assert_ne!(body3, body, "decrypt should not be equal with wrong nonce");
|
||||
|
||||
Crypto::crypt_in_place_no_auth(&mut body4, &n1, &ss2);
|
||||
vcrypto.crypt_in_place_no_auth(&mut body4, &n1, &ss2);
|
||||
assert_ne!(body4, body, "decrypt should not be equal with wrong secret");
|
||||
|
||||
let body5 = Crypto::crypt_no_auth(LOREM_IPSUM, &n1, &ss1);
|
||||
let body6 = Crypto::crypt_no_auth(&body5, &n1, &ss1);
|
||||
let body7 = Crypto::crypt_no_auth(LOREM_IPSUM, &n1, &ss1);
|
||||
let body5 = vcrypto.crypt_no_auth_unaligned(LOREM_IPSUM, &n1, &ss1);
|
||||
let body6 = vcrypto.crypt_no_auth_unaligned(&body5, &n1, &ss1);
|
||||
let body7 = vcrypto.crypt_no_auth_unaligned(LOREM_IPSUM, &n1, &ss1);
|
||||
assert_eq!(body6, LOREM_IPSUM);
|
||||
assert_eq!(body5, body7);
|
||||
|
||||
let body5 = vcrypto.crypt_no_auth_aligned_8(LOREM_IPSUM, &n1, &ss1);
|
||||
let body6 = vcrypto.crypt_no_auth_aligned_8(&body5, &n1, &ss1);
|
||||
let body7 = vcrypto.crypt_no_auth_aligned_8(LOREM_IPSUM, &n1, &ss1);
|
||||
assert_eq!(body6, LOREM_IPSUM);
|
||||
assert_eq!(body5, body7);
|
||||
}
|
||||
|
||||
pub async fn test_dh(crypto: Crypto) {
|
||||
pub async fn test_dh(vcrypto: CryptoSystemVersion) {
|
||||
trace!("test_dh");
|
||||
let (dht_key, dht_key_secret) = key::generate_secret();
|
||||
let (dht_key2, dht_key_secret2) = key::generate_secret();
|
||||
let (dht_key, dht_key_secret) = vcrypto.generate_keypair();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair();
|
||||
|
||||
let r1 = Crypto::compute_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r2 = Crypto::compute_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
let r3 = Crypto::compute_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r4 = Crypto::compute_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
let r1 = vcrypto.compute_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r2 = vcrypto.compute_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
let r3 = vcrypto.compute_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r4 = vcrypto.compute_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r3, r4);
|
||||
assert_eq!(r2, r3);
|
||||
trace!("dh: {:?}", r1);
|
||||
|
||||
// test cache
|
||||
let r5 = crypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r6 = crypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
let r7 = crypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r8 = crypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
let r5 = vcrypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r6 = vcrypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
let r7 = vcrypto.cached_dh(&dht_key, &dht_key_secret2).unwrap();
|
||||
let r8 = vcrypto.cached_dh(&dht_key2, &dht_key_secret).unwrap();
|
||||
assert_eq!(r1, r5);
|
||||
assert_eq!(r2, r6);
|
||||
assert_eq!(r3, r7);
|
||||
@ -163,11 +163,17 @@ pub async fn test_dh(crypto: Crypto) {
|
||||
}
|
||||
|
||||
pub async fn test_all() {
|
||||
let api = startup().await;
|
||||
let api = crypto_tests_startup().await;
|
||||
let crypto = api.crypto().unwrap();
|
||||
test_aead().await;
|
||||
test_no_auth().await;
|
||||
test_dh(crypto).await;
|
||||
shutdown(api.clone()).await;
|
||||
|
||||
// Test versions
|
||||
for v in MIN_CRYPTO_VERSION..=MAX_CRYPTO_VERSION {
|
||||
let vcrypto = crypto.get(v).unwrap();
|
||||
test_aead(vcrypto.clone()).await;
|
||||
test_no_auth(vcrypto.clone()).await;
|
||||
test_dh(vcrypto).await;
|
||||
}
|
||||
|
||||
crypto_tests_shutdown(api.clone()).await;
|
||||
assert!(api.is_shutdown());
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ static CHEEZBURGER: &str = "I can has cheezburger";
|
||||
static EMPTY_KEY: [u8; key::DHT_KEY_LENGTH] = [0u8; key::DHT_KEY_LENGTH];
|
||||
static EMPTY_KEY_SECRET: [u8; key::DHT_KEY_SECRET_LENGTH] = [0u8; key::DHT_KEY_SECRET_LENGTH];
|
||||
|
||||
pub async fn test_generate_secret() {
|
||||
pub async fn test_generate_secret(vcrypto: CryptoSystemVersion) {
|
||||
// Verify keys generate
|
||||
let (dht_key, dht_key_secret) = key::generate_secret();
|
||||
let (dht_key2, dht_key_secret2) = key::generate_secret();
|
||||
let (dht_key, dht_key_secret) = vcrypto.generate_keypair();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair();
|
||||
|
||||
// Verify byte patterns are different between public and secret
|
||||
assert_ne!(dht_key.bytes, dht_key_secret.bytes);
|
||||
@ -22,29 +22,45 @@ pub async fn test_generate_secret() {
|
||||
assert_ne!(dht_key_secret, dht_key_secret2);
|
||||
}
|
||||
|
||||
pub async fn test_sign_and_verify() {
|
||||
pub async fn test_sign_and_verify(vcrypto: CryptoSystemVersion) {
|
||||
// Make two keys
|
||||
let (dht_key, dht_key_secret) = key::generate_secret();
|
||||
let (dht_key2, dht_key_secret2) = key::generate_secret();
|
||||
let (dht_key, dht_key_secret) = vcrypto.generate_keypair();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair();
|
||||
// Sign the same message twice
|
||||
let dht_sig = key::sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let dht_sig = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
trace!("dht_sig: {:?}", dht_sig);
|
||||
let dht_sig_b = key::sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let dht_sig_b = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
// Sign a second message
|
||||
let dht_sig_c = key::sign(&dht_key, &dht_key_secret, CHEEZBURGER.as_bytes()).unwrap();
|
||||
let dht_sig_c = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, CHEEZBURGER.as_bytes())
|
||||
.unwrap();
|
||||
trace!("dht_sig_c: {:?}", dht_sig_c);
|
||||
// Verify they are the same signature
|
||||
assert_eq!(dht_sig, dht_sig_b);
|
||||
// Sign the same message with a different key
|
||||
let dht_sig2 = key::sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let dht_sig2 = vcrypto
|
||||
.sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
// Verify a different key gives a different signature
|
||||
assert_ne!(dht_sig2, dht_sig_b);
|
||||
|
||||
// Try using the wrong secret to sign
|
||||
let a1 = key::sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let a2 = key::sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let b1 = key::sign(&dht_key, &dht_key_secret2, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let b2 = key::sign(&dht_key2, &dht_key_secret, LOREM_IPSUM.as_bytes()).unwrap();
|
||||
let a1 = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
let a2 = vcrypto
|
||||
.sign(&dht_key2, &dht_key_secret2, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
let b1 = vcrypto
|
||||
.sign(&dht_key, &dht_key_secret2, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
let b2 = vcrypto
|
||||
.sign(&dht_key2, &dht_key_secret, LOREM_IPSUM.as_bytes())
|
||||
.unwrap();
|
||||
assert_ne!(a1, b1);
|
||||
assert_ne!(a2, b2);
|
||||
assert_ne!(a1, b2);
|
||||
@ -54,36 +70,54 @@ pub async fn test_sign_and_verify() {
|
||||
assert_ne!(a1, b2);
|
||||
assert_ne!(b1, a2);
|
||||
|
||||
assert_eq!(key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &a1), Ok(()));
|
||||
assert_eq!(key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a2), Ok(()));
|
||||
assert!(key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &b1).is_err());
|
||||
assert!(key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &b2).is_err());
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &a1),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &a2),
|
||||
Ok(())
|
||||
);
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key, LOREM_IPSUM.as_bytes(), &b1)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &b2)
|
||||
.is_err());
|
||||
|
||||
// Try verifications that should work
|
||||
assert_eq!(
|
||||
key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig),
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig_b),
|
||||
vcrypto.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig_b),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig2),
|
||||
vcrypto.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig2),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
key::verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig_c),
|
||||
vcrypto.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig_c),
|
||||
Ok(())
|
||||
);
|
||||
// Try verifications that shouldn't work
|
||||
assert!(key::verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig).is_err());
|
||||
assert!(key::verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig2).is_err());
|
||||
assert!(key::verify(&dht_key2, CHEEZBURGER.as_bytes(), &dht_sig_c).is_err());
|
||||
assert!(key::verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig).is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key2, LOREM_IPSUM.as_bytes(), &dht_sig)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key, LOREM_IPSUM.as_bytes(), &dht_sig2)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key2, CHEEZBURGER.as_bytes(), &dht_sig_c)
|
||||
.is_err());
|
||||
assert!(vcrypto
|
||||
.verify(&dht_key, CHEEZBURGER.as_bytes(), &dht_sig)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
pub async fn test_key_conversions() {
|
||||
pub async fn test_key_conversions(vcrypto: CryptoSystemVersion) {
|
||||
// Test default key
|
||||
let (dht_key, dht_key_secret) = (key::DHTKey::default(), key::DHTKeySecret::default());
|
||||
assert_eq!(dht_key.bytes, EMPTY_KEY);
|
||||
@ -99,10 +133,10 @@ pub async fn test_key_conversions() {
|
||||
assert_eq!(dht_key_secret_string, dht_key_string);
|
||||
|
||||
// Make different keys
|
||||
let (dht_key2, dht_key_secret2) = key::generate_secret();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair();
|
||||
trace!("dht_key2: {:?}", dht_key2);
|
||||
trace!("dht_key_secret2: {:?}", dht_key_secret2);
|
||||
let (dht_key3, _dht_key_secret3) = key::generate_secret();
|
||||
let (dht_key3, _dht_key_secret3) = vcrypto.generate_keypair();
|
||||
trace!("dht_key3: {:?}", dht_key3);
|
||||
trace!("_dht_key_secret3: {:?}", _dht_key_secret3);
|
||||
|
||||
@ -154,7 +188,7 @@ pub async fn test_key_conversions() {
|
||||
.is_err());
|
||||
}
|
||||
|
||||
pub async fn test_encode_decode() {
|
||||
pub async fn test_encode_decode(vcrypto: CryptoSystemVersion) {
|
||||
let dht_key = key::DHTKey::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
|
||||
let dht_key_secret =
|
||||
key::DHTKeySecret::try_decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap();
|
||||
@ -163,7 +197,7 @@ pub async fn test_encode_decode() {
|
||||
assert_eq!(dht_key, dht_key_b);
|
||||
assert_eq!(dht_key_secret, dht_key_secret_b);
|
||||
|
||||
let (dht_key2, dht_key_secret2) = key::generate_secret();
|
||||
let (dht_key2, dht_key_secret2) = vcrypto.generate_keypair();
|
||||
|
||||
let e1 = dht_key.encode();
|
||||
trace!("e1: {:?}", e1);
|
||||
@ -198,15 +232,15 @@ pub async fn test_encode_decode() {
|
||||
assert!(f2.is_err());
|
||||
}
|
||||
|
||||
async fn test_hash() {
|
||||
async fn test_hash(vcrypto: CryptoSystemVersion) {
|
||||
let mut s = BTreeSet::<key::DHTKey>::new();
|
||||
|
||||
let k1 = key::generate_hash("abc".as_bytes());
|
||||
let k2 = key::generate_hash("abcd".as_bytes());
|
||||
let k3 = key::generate_hash("".as_bytes());
|
||||
let k4 = key::generate_hash(" ".as_bytes());
|
||||
let k5 = key::generate_hash(LOREM_IPSUM.as_bytes());
|
||||
let k6 = key::generate_hash(CHEEZBURGER.as_bytes());
|
||||
let k1 = vcrypto.generate_hash("abc".as_bytes());
|
||||
let k2 = vcrypto.generate_hash("abcd".as_bytes());
|
||||
let k3 = vcrypto.generate_hash("".as_bytes());
|
||||
let k4 = vcrypto.generate_hash(" ".as_bytes());
|
||||
let k5 = vcrypto.generate_hash(LOREM_IPSUM.as_bytes());
|
||||
let k6 = vcrypto.generate_hash(CHEEZBURGER.as_bytes());
|
||||
|
||||
s.insert(k1);
|
||||
s.insert(k2);
|
||||
@ -216,12 +250,12 @@ async fn test_hash() {
|
||||
s.insert(k6);
|
||||
assert_eq!(s.len(), 6);
|
||||
|
||||
let v1 = key::generate_hash("abc".as_bytes());
|
||||
let v2 = key::generate_hash("abcd".as_bytes());
|
||||
let v3 = key::generate_hash("".as_bytes());
|
||||
let v4 = key::generate_hash(" ".as_bytes());
|
||||
let v5 = key::generate_hash(LOREM_IPSUM.as_bytes());
|
||||
let v6 = key::generate_hash(CHEEZBURGER.as_bytes());
|
||||
let v1 = vcrypto.generate_hash("abc".as_bytes());
|
||||
let v2 = vcrypto.generate_hash("abcd".as_bytes());
|
||||
let v3 = vcrypto.generate_hash("".as_bytes());
|
||||
let v4 = vcrypto.generate_hash(" ".as_bytes());
|
||||
let v5 = vcrypto.generate_hash(LOREM_IPSUM.as_bytes());
|
||||
let v6 = vcrypto.generate_hash(CHEEZBURGER.as_bytes());
|
||||
|
||||
assert_eq!(k1, v1);
|
||||
assert_eq!(k2, v2);
|
||||
@ -230,24 +264,24 @@ async fn test_hash() {
|
||||
assert_eq!(k5, v5);
|
||||
assert_eq!(k6, v6);
|
||||
|
||||
key::validate_hash("abc".as_bytes(), &v1);
|
||||
key::validate_hash("abcd".as_bytes(), &v2);
|
||||
key::validate_hash("".as_bytes(), &v3);
|
||||
key::validate_hash(" ".as_bytes(), &v4);
|
||||
key::validate_hash(LOREM_IPSUM.as_bytes(), &v5);
|
||||
key::validate_hash(CHEEZBURGER.as_bytes(), &v6);
|
||||
vcrypto.validate_hash("abc".as_bytes(), &v1);
|
||||
vcrypto.validate_hash("abcd".as_bytes(), &v2);
|
||||
vcrypto.validate_hash("".as_bytes(), &v3);
|
||||
vcrypto.validate_hash(" ".as_bytes(), &v4);
|
||||
vcrypto.validate_hash(LOREM_IPSUM.as_bytes(), &v5);
|
||||
vcrypto.validate_hash(CHEEZBURGER.as_bytes(), &v6);
|
||||
}
|
||||
|
||||
async fn test_operations() {
|
||||
let k1 = key::generate_hash(LOREM_IPSUM.as_bytes());
|
||||
let k2 = key::generate_hash(CHEEZBURGER.as_bytes());
|
||||
let k3 = key::generate_hash("abc".as_bytes());
|
||||
async fn test_operations(vcrypto: CryptoSystemVersion) {
|
||||
let k1 = vcrypto.generate_hash(LOREM_IPSUM.as_bytes());
|
||||
let k2 = vcrypto.generate_hash(CHEEZBURGER.as_bytes());
|
||||
let k3 = vcrypto.generate_hash("abc".as_bytes());
|
||||
|
||||
// Get distance
|
||||
let d1 = key::distance(&k1, &k2);
|
||||
let d2 = key::distance(&k2, &k1);
|
||||
let d3 = key::distance(&k1, &k3);
|
||||
let d4 = key::distance(&k2, &k3);
|
||||
let d1 = vcrypto.distance(&k1, &k2);
|
||||
let d2 = vcrypto.distance(&k2, &k1);
|
||||
let d3 = vcrypto.distance(&k1, &k3);
|
||||
let d4 = vcrypto.distance(&k2, &k3);
|
||||
|
||||
trace!("d1={:?}", d1);
|
||||
trace!("d2={:?}", d2);
|
||||
@ -295,10 +329,21 @@ async fn test_operations() {
|
||||
}
|
||||
|
||||
pub async fn test_all() {
|
||||
test_generate_secret().await;
|
||||
test_sign_and_verify().await;
|
||||
test_key_conversions().await;
|
||||
test_encode_decode().await;
|
||||
test_hash().await;
|
||||
test_operations().await;
|
||||
let api = crypto_tests_startup().await;
|
||||
let crypto = api.crypto().unwrap();
|
||||
|
||||
// Test versions
|
||||
for v in MIN_CRYPTO_VERSION..=MAX_CRYPTO_VERSION {
|
||||
let vcrypto = crypto.get(v).unwrap();
|
||||
|
||||
test_generate_secret(vcrypto.clone()).await;
|
||||
test_sign_and_verify(vcrypto.clone()).await;
|
||||
test_key_conversions(vcrypto.clone()).await;
|
||||
test_encode_decode(vcrypto.clone()).await;
|
||||
test_hash(vcrypto.clone()).await;
|
||||
test_operations(vcrypto).await;
|
||||
}
|
||||
|
||||
crypto_tests_shutdown(api.clone()).await;
|
||||
assert!(api.is_shutdown());
|
||||
}
|
||||
|
@ -1,29 +1,21 @@
|
||||
use super::*;
|
||||
use crate::tests::common::test_veilid_config::*;
|
||||
|
||||
pub async fn test_envelope_round_trip() {
|
||||
pub async fn test_envelope_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
info!("--- test envelope round trip ---");
|
||||
let (update_callback, config_callback) = setup_veilid_core();
|
||||
let api = api_startup(update_callback, config_callback)
|
||||
.await
|
||||
.expect("startup failed");
|
||||
|
||||
// Get crypto
|
||||
let crypto = api.crypto().unwrap();
|
||||
|
||||
// Create envelope
|
||||
let ts = Timestamp::from(0x12345678ABCDEF69u64);
|
||||
let nonce = Crypto::get_random_nonce();
|
||||
let (sender_id, sender_secret) = generate_secret();
|
||||
let (recipient_id, recipient_secret) = generate_secret();
|
||||
let envelope = Envelope::new(0, ts, nonce, sender_id, recipient_id);
|
||||
let nonce = vcrypto.random_nonce();
|
||||
let (sender_id, sender_secret) = vcrypto.generate_keypair();
|
||||
let (recipient_id, recipient_secret) = vcrypto.generate_keypair();
|
||||
let envelope = Envelope::new(vcrypto.version(), ts, nonce, sender_id, recipient_id);
|
||||
|
||||
// Create arbitrary body
|
||||
let body = b"This is an arbitrary body";
|
||||
|
||||
// Serialize to bytes
|
||||
let enc_data = envelope
|
||||
.to_encrypted_data(crypto.clone(), body, &sender_secret)
|
||||
.to_encrypted_data(vcrypto.crypto(), body, &sender_secret)
|
||||
.expect("failed to encrypt data");
|
||||
|
||||
// Deserialize from bytes
|
||||
@ -31,7 +23,7 @@ pub async fn test_envelope_round_trip() {
|
||||
Envelope::from_signed_data(&enc_data).expect("failed to deserialize envelope from data");
|
||||
|
||||
let body2 = envelope2
|
||||
.decrypt_body(crypto.clone(), &enc_data, &recipient_secret)
|
||||
.decrypt_body(vcrypto.crypto(), &enc_data, &recipient_secret)
|
||||
.expect("failed to decrypt envelope body");
|
||||
|
||||
// Compare envelope and body
|
||||
@ -52,18 +44,16 @@ pub async fn test_envelope_round_trip() {
|
||||
Envelope::from_signed_data(&mod_enc_data2).is_err(),
|
||||
"should have failed to decode envelope with modified data"
|
||||
);
|
||||
|
||||
api.shutdown().await;
|
||||
}
|
||||
|
||||
pub async fn test_receipt_round_trip() {
|
||||
pub async fn test_receipt_round_trip(vcrypto: CryptoSystemVersion) {
|
||||
info!("--- test receipt round trip ---");
|
||||
// Create arbitrary body
|
||||
let body = b"This is an arbitrary body";
|
||||
|
||||
// Create receipt
|
||||
let nonce = Crypto::get_random_nonce();
|
||||
let (sender_id, sender_secret) = generate_secret();
|
||||
let nonce = vcrypto.random_nonce();
|
||||
let (sender_id, sender_secret) = vcrypto.generate_keypair();
|
||||
let receipt = Receipt::try_new(0, nonce, sender_id, body).expect("should not fail");
|
||||
|
||||
// Serialize to bytes
|
||||
@ -85,6 +75,17 @@ pub async fn test_receipt_round_trip() {
|
||||
}
|
||||
|
||||
pub async fn test_all() {
|
||||
test_envelope_round_trip().await;
|
||||
test_receipt_round_trip().await;
|
||||
let api = crypto_tests_startup().await;
|
||||
let crypto = api.crypto().unwrap();
|
||||
|
||||
// Test versions
|
||||
for v in MIN_CRYPTO_VERSION..=MAX_CRYPTO_VERSION {
|
||||
let vcrypto = crypto.get(v).unwrap();
|
||||
|
||||
test_envelope_round_trip(vcrypto.clone()).await;
|
||||
test_receipt_round_trip(vcrypto).await;
|
||||
}
|
||||
|
||||
crypto_tests_shutdown(api.clone()).await;
|
||||
assert!(api.is_shutdown());
|
||||
}
|
||||
|
@ -299,6 +299,14 @@ pub unsafe fn aligned_8_u8_vec_uninit(n_bytes: usize) -> Vec<u8> {
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn unaligned_u8_vec_uninit(n_bytes: usize) -> Vec<u8> {
|
||||
let mut unaligned: Vec<u8> = Vec::with_capacity(n_bytes);
|
||||
let ptr = unaligned.as_mut_ptr();
|
||||
mem::forget(unaligned);
|
||||
|
||||
Vec::from_raw_parts(ptr as *mut u8, n_bytes, n_bytes)
|
||||
}
|
||||
|
||||
pub fn debug_backtrace() -> String {
|
||||
let bt = backtrace::Backtrace::new();
|
||||
format!("{:?}", bt)
|
||||
|
Loading…
Reference in New Issue
Block a user