diff --git a/Cargo.toml b/Cargo.toml index ffa8beb..2185f9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,5 @@ version = "0.1.0" serde = { version = "^1", features = ["derive"], default-features = false } serde_cbor = { package = "serde_cbor_2", git = "https://github.com/kanidm/cbor", rev = "ab3296b35322aa5caa79a4ad9ce00a49c000864e" } zerotier-common-utils = { git = "https://github.com/zerotier/common-utils.git", branch = "main" } -zerotier-crypto-glue = { git = "https://github.com/zerotier/crypto-glue.git", branch = "main" } +zerotier-crypto-glue = { git = "https://github.com/zerotier/crypto-glue.git", branch = "xoshiro" } +zeroize = { version = "1.6.0", features = ["zeroize_derive"], default-features = false } diff --git a/src/base62.rs b/src/base62.rs index 02d30c6..c84c21b 100644 --- a/src/base62.rs +++ b/src/base62.rs @@ -43,13 +43,15 @@ pub fn decode_11to8(s: &[u8]) -> Result { #[cfg(test)] mod tests { + use zerotier_crypto_glue::random::{rand_core::RngCore, SecureRandom}; + use super::*; #[test] fn base62_encode_decode() { let mut tmp = String::with_capacity(16); for _ in 0..10000 { - let r = zerotier_crypto_glue::random::next_u64_secure(); + let r = SecureRandom.next_u64(); tmp.clear(); encode_8to11(r, &mut tmp); assert_eq!(decode_11to8(tmp.as_bytes()).unwrap(), r); diff --git a/src/p384.rs b/src/p384.rs index 970e6fc..c2fc9a5 100644 --- a/src/p384.rs +++ b/src/p384.rs @@ -13,7 +13,7 @@ use std::str::FromStr; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use zerotier_common_utils::arrayvec::ArrayVec; +use zeroize::{Zeroize, ZeroizeOnDrop}; use zerotier_common_utils::base64; use zerotier_common_utils::blob::Blob; use zerotier_common_utils::error::InvalidParameterError; @@ -24,6 +24,8 @@ use zerotier_crypto_glue::p384::*; use crate::{base24, base62}; use crate::{ADDRESS_ERR, IDENTITY_ERR}; +const IDENTITY_DOMAIN: &[u8] = b"zerotier_p384_identity"; + // Implementation note: the addresses use u64 arrays that are actually treated as flat byte // array memory arenas in order to optimize for fast lookup when these are used as map keys. // This reduces the number of instructions required to perform equality comparisons and @@ -396,15 +398,16 @@ pub struct Identity { impl Identity { fn locally_validate(&self) -> bool { - let mut sign_tmp = ArrayVec::::new(); - sign_tmp.push_slice(self.address.as_bytes()); - sign_tmp.push_slice(&self.timestamp.to_be_bytes()); - sign_tmp.push_slice(self.ecdh.as_bytes()); - sign_tmp.push_slice(self.ecdsa.as_bytes()); self.address.is_valid() - && self - .master_signing_key - .verify(sign_tmp.as_bytes(), &self.master_signature) + && self.master_signing_key.verify_all( + IDENTITY_DOMAIN, + &[ + &self.timestamp.to_be_bytes(), + self.ecdh.as_bytes(), + self.ecdsa.as_bytes(), + ], + &self.master_signature, + ) } /// Returns true if this identity should replace the other. @@ -523,7 +526,11 @@ impl crate::Identity for Identity { #[inline(always)] fn verify_signature(&self, data: &[u8], signature: &[u8]) -> bool { - self.ecdsa.verify(data, signature) + if let Ok(sig) = signature.try_into() { + self.ecdsa.verify_raw(data, sig) + } else { + false + } } } @@ -553,16 +560,22 @@ impl Clone for IdentitySecret { } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] struct IdentitySecretSerialized { + #[zeroize(skip)] a: Address, + #[zeroize(skip)] pm: Blob, sm: Option>, + #[zeroize(skip)] ts: u64, + #[zeroize(skip)] p0: Blob, s0: Blob, + #[zeroize(skip)] p1: Blob, s1: Blob, + #[zeroize(skip)] ms: Blob, } @@ -571,21 +584,24 @@ impl Serialize for IdentitySecret { where S: Serializer, { - IdentitySecretSerialized { + let mut tmp = IdentitySecretSerialized { a: self.public.address, pm: (*self.public.master_signing_key.as_bytes()).into(), - sm: self - .master_signing_key - .as_ref() - .map(|sk| (*sk.secret_key_bytes().as_bytes()).into()), + sm: None, ts: self.public.timestamp, p0: (*self.public.ecdh.as_bytes()).into(), - s0: (*self.ecdh.secret_key_bytes().as_bytes()).into(), + s0: Blob::default(), p1: (*self.public.ecdsa.as_bytes()).into(), - s1: (*self.ecdsa.secret_key_bytes().as_bytes()).into(), + s1: Blob::default(), ms: self.public.master_signature.into(), + }; + self.ecdh.secret_key_bytes(&mut tmp.s0); + self.ecdsa.secret_key_bytes(&mut tmp.s1); + if let Some(ecdsa) = self.master_signing_key.as_ref() { + tmp.sm = Some(Blob::default()); + ecdsa.secret_key_bytes(tmp.sm.as_mut().unwrap()); } - .serialize(serializer) + tmp.serialize(serializer) } } @@ -651,11 +667,6 @@ impl crate::IdentitySecret for IdentitySecret { let ecdh = P384KeyPair::generate(); let ecdsa = P384KeyPair::generate(); - let mut sign_tmp = ArrayVec::::new(); - sign_tmp.push_slice(address.as_bytes()); - sign_tmp.push_slice(×tamp.to_be_bytes()); - sign_tmp.push_slice(ecdh.public_key_bytes()); - sign_tmp.push_slice(ecdsa.public_key_bytes()); Self { public: Identity { address, @@ -663,7 +674,14 @@ impl crate::IdentitySecret for IdentitySecret { timestamp, ecdh: ecdh.to_public_key(), ecdsa: ecdsa.to_public_key(), - master_signature: master_signing_key.sign(sign_tmp.as_bytes()), + master_signature: master_signing_key.sign_all( + IDENTITY_DOMAIN, + &[ + ×tamp.to_be_bytes(), + ecdh.public_key_bytes(), + ecdsa.public_key_bytes(), + ], + ), }, master_signing_key: Some(master_signing_key), ecdh, @@ -678,7 +696,7 @@ impl crate::IdentitySecret for IdentitySecret { #[inline(always)] fn sign(&self, data: &[u8]) -> Self::Signature { - self.ecdsa.sign(data) + self.ecdsa.sign_raw(data) } } diff --git a/src/x25519.rs b/src/x25519.rs index a9fd517..660135d 100644 --- a/src/x25519.rs +++ b/src/x25519.rs @@ -14,6 +14,7 @@ use std::str::FromStr; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use zeroize::{Zeroize, ZeroizeOnDrop}; use zerotier_common_utils::blob::Blob; use zerotier_common_utils::error::InvalidParameterError; use zerotier_common_utils::hex; @@ -305,8 +306,11 @@ impl ToString for IdentitySecret { tmp.push_str(hex::to_string(&self.public.ecdh).as_str()); tmp.push_str(hex::to_string(&self.public.eddsa).as_str()); tmp.push(':'); - tmp.push_str(hex::to_string(self.ecdh.secret_bytes().as_bytes()).as_str()); - tmp.push_str(hex::to_string(self.eddsa.secret_bytes().as_bytes()).as_str()); + let mut buf = [0u8; ED25519_SECRET_KEY_SIZE]; + self.ecdh.secret_bytes(&mut buf); + tmp.push_str(hex::to_string(&buf).as_str()); + self.eddsa.secret_bytes(&mut buf); + tmp.push_str(hex::to_string(&buf).as_str()); tmp } } @@ -324,10 +328,16 @@ impl FromStr for IdentitySecret { if secret_bytes.len() != C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE { return Err(IDENTITY_ERR); } - let ecdh = X25519KeyPair::from_bytes(&public.ecdh, &secret_bytes.as_slice()[..C25519_SECRET_KEY_SIZE]) - .ok_or(IDENTITY_ERR)?; - let eddsa = Ed25519KeyPair::from_bytes(&public.eddsa, &secret_bytes.as_slice()[C25519_SECRET_KEY_SIZE..]) - .ok_or(IDENTITY_ERR)?; + let ecdh = X25519KeyPair::from_bytes( + &public.ecdh, + &secret_bytes.as_slice()[..C25519_SECRET_KEY_SIZE].try_into().unwrap(), + ) + .ok_or(IDENTITY_ERR)?; + let eddsa = Ed25519KeyPair::from_bytes( + &public.eddsa, + &secret_bytes.as_slice()[C25519_SECRET_KEY_SIZE..].try_into().unwrap(), + ) + .ok_or(IDENTITY_ERR)?; return Ok(Self { public, ecdh, eddsa }); } } @@ -385,11 +395,14 @@ impl crate::IdentitySecret for IdentitySecret { } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)] struct IdentitySecretSerialized { + #[zeroize(skip)] a: Address, + #[zeroize(skip)] p0: Blob, s0: Blob, + #[zeroize(skip)] p1: Blob, s1: Blob, } @@ -399,14 +412,16 @@ impl Serialize for IdentitySecret { where S: Serializer, { - IdentitySecretSerialized { + let mut tmp = IdentitySecretSerialized { a: self.public.address, p0: self.public.ecdh.into(), - s0: (*self.ecdh.secret_bytes().as_bytes()).into(), + s0: Blob::default(), p1: self.public.eddsa.into(), - s1: (*self.eddsa.secret_bytes().as_bytes()).into(), - } - .serialize(serializer) + s1: Blob::default(), + }; + self.ecdh.secret_bytes(&mut tmp.s0); + self.eddsa.secret_bytes(&mut tmp.s1); + tmp.serialize(serializer) } }