removing dev branch, many changes

This commit is contained in:
John Smith
2023-05-29 19:24:57 +00:00
parent 1430f3f656
commit 0a890c8707
250 changed files with 18084 additions and 8040 deletions

View File

@@ -7,22 +7,26 @@ use core::hash::Hash;
use data_encoding::BASE64URL_NOPAD;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
//////////////////////////////////////////////////////////////////////
/// Length of a public key in bytes
/// Length of a crypto key in bytes
#[allow(dead_code)]
pub const PUBLIC_KEY_LENGTH: usize = 32;
/// Length of a public key in bytes after encoding to base64url
pub const CRYPTO_KEY_LENGTH: usize = 32;
/// Length of a crypto key in bytes after encoding to base64url
#[allow(dead_code)]
pub const PUBLIC_KEY_LENGTH_ENCODED: usize = 43;
pub const CRYPTO_KEY_LENGTH_ENCODED: usize = 43;
/// Length of a crypto key in bytes
#[allow(dead_code)]
pub const PUBLIC_KEY_LENGTH: usize = CRYPTO_KEY_LENGTH;
/// Length of a crypto key in bytes after encoding to base64url
#[allow(dead_code)]
pub const PUBLIC_KEY_LENGTH_ENCODED: usize = CRYPTO_KEY_LENGTH_ENCODED;
/// Length of a secret key in bytes
#[allow(dead_code)]
pub const SECRET_KEY_LENGTH: usize = 32;
pub const SECRET_KEY_LENGTH: usize = CRYPTO_KEY_LENGTH;
/// Length of a secret key in bytes after encoding to base64url
#[allow(dead_code)]
pub const SECRET_KEY_LENGTH_ENCODED: usize = 43;
pub const SECRET_KEY_LENGTH_ENCODED: usize = CRYPTO_KEY_LENGTH_ENCODED;
/// Length of a signature in bytes
#[allow(dead_code)]
pub const SIGNATURE_LENGTH: usize = 64;
@@ -37,16 +41,22 @@ pub const NONCE_LENGTH: usize = 24;
pub const NONCE_LENGTH_ENCODED: usize = 32;
/// Length of a shared secret in bytes
#[allow(dead_code)]
pub const SHARED_SECRET_LENGTH: usize = 32;
pub const SHARED_SECRET_LENGTH: usize = CRYPTO_KEY_LENGTH;
/// Length of a shared secret in bytes after encoding to base64url
#[allow(dead_code)]
pub const SHARED_SECRET_LENGTH_ENCODED: usize = 43;
pub const SHARED_SECRET_LENGTH_ENCODED: usize = CRYPTO_KEY_LENGTH_ENCODED;
/// Length of a route id in bytes
#[allow(dead_code)]
pub const ROUTE_ID_LENGTH: usize = 32;
pub const ROUTE_ID_LENGTH: usize = CRYPTO_KEY_LENGTH;
/// Length of a route id in bytes afer encoding to base64url
#[allow(dead_code)]
pub const ROUTE_ID_LENGTH_ENCODED: usize = 43;
pub const ROUTE_ID_LENGTH_ENCODED: usize = CRYPTO_KEY_LENGTH_ENCODED;
/// Length of a hash digest in bytes
#[allow(dead_code)]
pub const HASH_DIGEST_LENGTH: usize = CRYPTO_KEY_LENGTH;
/// Length of a hash digest in bytes after encoding to base64url
#[allow(dead_code)]
pub const HASH_DIGEST_LENGTH_ENCODED: usize = CRYPTO_KEY_LENGTH_ENCODED;
//////////////////////////////////////////////////////////////////////
@@ -56,11 +66,11 @@ where
{
fn encode(&self) -> String;
fn encoded_len() -> usize;
fn try_decode<S: AsRef<str>>(input: S) -> Result<Self, VeilidAPIError> {
fn try_decode<S: AsRef<str>>(input: S) -> VeilidAPIResult<Self> {
let b = input.as_ref().as_bytes();
Self::try_decode_bytes(b)
}
fn try_decode_bytes(b: &[u8]) -> Result<Self, VeilidAPIError>;
fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self>;
}
//////////////////////////////////////////////////////////////////////
@@ -120,18 +130,6 @@ macro_rules! byte_array_type {
Self { bytes }
}
pub fn try_from_vec(v: Vec<u8>) -> Result<Self, VeilidAPIError> {
let vl = v.len();
Ok(Self {
bytes: v.try_into().map_err(|_| {
VeilidAPIError::generic(format!(
"Expected a Vec of length {} but it was {}",
$size, vl
))
})?,
})
}
pub fn bit(&self, index: usize) -> bool {
assert!(index < ($size * 8));
let bi = index / 8;
@@ -182,7 +180,7 @@ macro_rules! byte_array_type {
fn encoded_len() -> usize {
$encoded_size
}
fn try_decode_bytes(b: &[u8]) -> Result<Self, VeilidAPIError> {
fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self> {
let mut bytes = [0u8; $size];
let res = BASE64URL_NOPAD.decode_len(b.len());
match res {
@@ -244,23 +242,47 @@ macro_rules! byte_array_type {
Self::try_decode(value)
}
}
impl TryFrom<&[u8]> for $name {
type Error = VeilidAPIError;
fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
let vl = v.len();
Ok(Self {
bytes: v.try_into().map_err(|_| {
VeilidAPIError::generic(format!(
"Expected a slice of length {} but it was {}",
$size, vl
))
})?,
})
}
}
impl core::ops::Deref for $name {
type Target = [u8; $size];
fn deref(&self) -> &Self::Target {
&self.bytes
}
}
impl core::ops::DerefMut for $name {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.bytes
}
}
};
}
/////////////////////////////////////////
byte_array_type!(PublicKey, PUBLIC_KEY_LENGTH, PUBLIC_KEY_LENGTH_ENCODED);
byte_array_type!(SecretKey, SECRET_KEY_LENGTH, SECRET_KEY_LENGTH_ENCODED);
byte_array_type!(CryptoKey, CRYPTO_KEY_LENGTH, CRYPTO_KEY_LENGTH_ENCODED);
pub type PublicKey = CryptoKey;
pub type SecretKey = CryptoKey;
pub type HashDigest = CryptoKey;
pub type SharedSecret = CryptoKey;
pub type RouteId = CryptoKey;
pub type CryptoKeyDistance = CryptoKey;
byte_array_type!(Signature, SIGNATURE_LENGTH, SIGNATURE_LENGTH_ENCODED);
byte_array_type!(
PublicKeyDistance,
PUBLIC_KEY_LENGTH,
PUBLIC_KEY_LENGTH_ENCODED
);
byte_array_type!(Nonce, NONCE_LENGTH, NONCE_LENGTH_ENCODED);
byte_array_type!(
SharedSecret,
SHARED_SECRET_LENGTH,
SHARED_SECRET_LENGTH_ENCODED
);
byte_array_type!(RouteId, ROUTE_ID_LENGTH, ROUTE_ID_LENGTH_ENCODED);

View File

@@ -6,52 +6,36 @@ pub trait CryptoSystem {
fn crypto(&self) -> Crypto;
// Cached Operations
fn cached_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError>;
fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret>;
// Generation
fn random_bytes(&self, len: u32) -> Vec<u8>;
fn default_salt_length(&self) -> u32;
fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<String>;
fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult<bool>;
fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret>;
fn random_nonce(&self) -> Nonce;
fn random_shared_secret(&self) -> SharedSecret;
fn compute_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError>;
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret>;
fn generate_keypair(&self) -> KeyPair;
fn generate_hash(&self, data: &[u8]) -> PublicKey;
fn generate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
) -> Result<PublicKey, VeilidAPIError>;
fn generate_hash(&self, data: &[u8]) -> HashDigest;
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<HashDigest>;
// Validation
fn validate_keypair(&self, dht_key: &PublicKey, dht_key_secret: &SecretKey) -> bool;
fn validate_hash(&self, data: &[u8], dht_key: &PublicKey) -> bool;
fn validate_keypair(&self, key: &PublicKey, secret: &SecretKey) -> bool;
fn validate_hash(&self, data: &[u8], hash: &HashDigest) -> bool;
fn validate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
key: &PublicKey,
) -> Result<bool, VeilidAPIError>;
hash: &HashDigest,
) -> VeilidAPIResult<bool>;
// Distance Metric
fn distance(&self, key1: &PublicKey, key2: &PublicKey) -> PublicKeyDistance;
fn distance(&self, key1: &CryptoKey, key2: &CryptoKey) -> CryptoKeyDistance;
// Authentication
fn sign(
&self,
key: &PublicKey,
secret: &SecretKey,
data: &[u8],
) -> Result<Signature, VeilidAPIError>;
fn verify(
&self,
key: &PublicKey,
data: &[u8],
signature: &Signature,
) -> Result<(), VeilidAPIError>;
fn sign(&self, key: &PublicKey, secret: &SecretKey, data: &[u8]) -> VeilidAPIResult<Signature>;
fn verify(&self, key: &PublicKey, data: &[u8], signature: &Signature) -> VeilidAPIResult<()>;
// AEAD Encrypt/Decrypt
fn aead_overhead(&self) -> usize;
@@ -61,53 +45,53 @@ pub trait CryptoSystem {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError>;
) -> VeilidAPIResult<()>;
fn decrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError>;
) -> VeilidAPIResult<Vec<u8>>;
fn encrypt_in_place_aead(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError>;
) -> VeilidAPIResult<()>;
fn encrypt_aead(
&self,
body: &[u8],
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError>;
) -> VeilidAPIResult<Vec<u8>>;
// NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
body: &mut [u8],
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
);
fn crypt_b2b_no_auth(
&self,
in_buf: &[u8],
out_buf: &mut [u8],
nonce: &Nonce,
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
);
fn crypt_no_auth_aligned_8(
&self,
body: &[u8],
nonce: &Nonce,
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
) -> Vec<u8>;
fn crypt_no_auth_unaligned(
&self,
body: &[u8],
nonce: &Nonce,
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
) -> Vec<u8>;
}

View File

@@ -66,7 +66,7 @@ impl Envelope {
}
}
pub fn from_signed_data(crypto: Crypto, data: &[u8]) -> Result<Envelope, VeilidAPIError> {
pub fn from_signed_data(crypto: Crypto, data: &[u8]) -> VeilidAPIResult<Envelope> {
// Ensure we are at least the length of the envelope
// Silent drop here, as we use zero length packets as part of the protocol for hole punching
if data.len() < MIN_ENVELOPE_SIZE {
@@ -175,7 +175,7 @@ impl Envelope {
crypto: Crypto,
data: &[u8],
node_id_secret: &SecretKey,
) -> Result<Vec<u8>, VeilidAPIError> {
) -> VeilidAPIResult<Vec<u8>> {
// Get DH secret
let vcrypto = crypto
.get(self.crypto_kind)
@@ -183,8 +183,11 @@ impl Envelope {
let dh_secret = vcrypto.cached_dh(&self.sender_id, node_id_secret)?;
// Decrypt message without authentication
let body =
vcrypto.crypt_no_auth_aligned_8(&data[0x6A..data.len() - 64], &self.nonce, &dh_secret);
let body = vcrypto.crypt_no_auth_aligned_8(
&data[0x6A..data.len() - 64],
&self.nonce.bytes,
&dh_secret,
);
Ok(body)
}
@@ -194,7 +197,7 @@ impl Envelope {
crypto: Crypto,
body: &[u8],
node_id_secret: &SecretKey,
) -> Result<Vec<u8>, VeilidAPIError> {
) -> VeilidAPIResult<Vec<u8>> {
// Ensure body isn't too long
let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE;
if envelope_size > MAX_ENVELOPE_SIZE {
@@ -227,7 +230,7 @@ impl Envelope {
data[0x4A..0x6A].copy_from_slice(&self.recipient_id.bytes);
// Encrypt and authenticate message
let encrypted_body = vcrypto.crypt_no_auth_unaligned(body, &self.nonce, &dh_secret);
let encrypted_body = vcrypto.crypt_no_auth_unaligned(body, &self.nonce.bytes, &dh_secret);
// Write body
if !encrypted_body.is_empty() {

View File

@@ -4,7 +4,6 @@ mod dh_cache;
mod envelope;
mod receipt;
mod types;
mod value;
pub mod crypto_system;
#[cfg(feature = "enable-crypto-none")]
@@ -20,7 +19,6 @@ pub use dh_cache::*;
pub use envelope::*;
pub use receipt::*;
pub use types::*;
pub use value::*;
#[cfg(feature = "enable-crypto-none")]
pub use none::*;
@@ -84,7 +82,6 @@ struct CryptoInner {
struct CryptoUnlockedInner {
config: VeilidConfig,
table_store: TableStore,
protected_store: ProtectedStore,
}
/// Crypto factory implementation
@@ -106,16 +103,11 @@ impl Crypto {
}
}
pub fn new(
config: VeilidConfig,
table_store: TableStore,
protected_store: ProtectedStore,
) -> Self {
pub fn new(config: VeilidConfig, table_store: TableStore) -> Self {
let out = Self {
unlocked_inner: Arc::new(CryptoUnlockedInner {
config,
table_store,
protected_store,
}),
inner: Arc::new(Mutex::new(Self::new_inner())),
};
@@ -140,12 +132,11 @@ impl Crypto {
pub async fn init(&self) -> EyreResult<()> {
trace!("Crypto::init");
let table_store = self.unlocked_inner.table_store.clone();
// Init node id from config
if let Err(e) = self
.unlocked_inner
.config
.init_node_ids(self.clone(), self.unlocked_inner.protected_store.clone())
.init_node_ids(self.clone(), table_store.clone())
.await
{
return Err(e).wrap_err("init node id failed");
@@ -171,13 +162,16 @@ impl Crypto {
};
// load caches if they are valid for this node id
let mut db = table_store.open("crypto_caches", 1).await?;
let caches_valid = match db.load(0, b"cache_validity_key")? {
let mut db = table_store
.open("crypto_caches", 1)
.await
.wrap_err("failed to open crypto_caches")?;
let caches_valid = match db.load(0, b"cache_validity_key").await? {
Some(v) => v == cache_validity_key,
None => false,
};
if caches_valid {
if let Some(b) = db.load(0, b"dh_cache")? {
if let Some(b) = db.load(0, b"dh_cache").await? {
let mut inner = self.inner.lock();
bytes_to_cache(&b, &mut inner.dh_cache);
}
@@ -263,7 +257,7 @@ impl Crypto {
node_ids: &[TypedKey],
data: &[u8],
typed_signatures: &[TypedSignature],
) -> Result<TypedKeySet, VeilidAPIError> {
) -> VeilidAPIResult<TypedKeySet> {
let mut out = TypedKeySet::with_capacity(node_ids.len());
for sig in typed_signatures {
for nid in node_ids {
@@ -286,7 +280,7 @@ impl Crypto {
data: &[u8],
typed_key_pairs: &[TypedKeyPair],
transform: F,
) -> Result<Vec<R>, VeilidAPIError>
) -> VeilidAPIResult<Vec<R>>
where
F: Fn(&TypedKeyPair, Signature) -> R,
{
@@ -302,7 +296,7 @@ impl Crypto {
/// Generate keypair
/// Does not require startup/init
pub fn generate_keypair(crypto_kind: CryptoKind) -> Result<TypedKeyPair, VeilidAPIError> {
pub fn generate_keypair(crypto_kind: CryptoKind) -> VeilidAPIResult<TypedKeyPair> {
#[cfg(feature = "enable-crypto-vld0")]
if crypto_kind == CRYPTO_KIND_VLD0 {
let kp = vld0_generate_keypair();
@@ -323,7 +317,7 @@ impl Crypto {
vcrypto: &T,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
) -> VeilidAPIResult<SharedSecret> {
Ok(
match self.inner.lock().dh_cache.entry(
DHCacheKey {

View File

@@ -1,7 +1,8 @@
use super::*;
use argon2::password_hash::Salt;
use data_encoding::BASE64URL_NOPAD;
use digest::Digest;
use rand::RngCore;
const AEAD_OVERHEAD: usize = PUBLIC_KEY_LENGTH;
pub const CRYPTO_KIND_NONE: CryptoKind = FourCC([b'N', b'O', b'N', b'E']);
@@ -70,16 +71,49 @@ impl CryptoSystem for CryptoSystemNONE {
}
// Cached Operations
fn cached_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
self.crypto
.cached_dh_internal::<CryptoSystemNONE>(self, key, secret)
}
// Generation
fn random_bytes(&self, len: u32) -> Vec<u8> {
let mut bytes = unsafe { unaligned_u8_vec_uninit(len as usize) };
random_bytes(bytes.as_mut());
bytes
}
fn default_salt_length(&self) -> u32 {
4
}
fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<String> {
if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
apibail_generic!("invalid salt length");
}
Ok(format!(
"{}:{}",
BASE64URL_NOPAD.encode(salt),
BASE64URL_NOPAD.encode(password)
))
}
fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult<bool> {
let Some((salt, _)) = password_hash.split_once(":") else {
apibail_generic!("invalid format");
};
let Ok(salt) = BASE64URL_NOPAD.decode(salt.as_bytes()) else {
apibail_generic!("invalid salt");
};
return Ok(&self.hash_password(password, &salt)? == password_hash);
}
fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret> {
if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
apibail_generic!("invalid salt length");
}
Ok(SharedSecret::new(
*blake3::hash(self.hash_password(password, salt)?.as_bytes()).as_bytes(),
))
}
fn random_nonce(&self) -> Nonce {
let mut nonce = [0u8; NONCE_LENGTH];
random_bytes(&mut nonce).unwrap();
@@ -90,11 +124,7 @@ impl CryptoSystem for CryptoSystemNONE {
random_bytes(&mut s).unwrap();
SharedSecret::new(s)
}
fn compute_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
let s = do_xor_32(&key.bytes, &secret.bytes);
Ok(SharedSecret::new(s))
}
@@ -104,10 +134,7 @@ impl CryptoSystem for CryptoSystemNONE {
fn generate_hash(&self, data: &[u8]) -> PublicKey {
PublicKey::new(*blake3::hash(data).as_bytes())
}
fn generate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
) -> Result<PublicKey, VeilidAPIError> {
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<PublicKey> {
let mut hasher = blake3::Hasher::new();
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
Ok(PublicKey::new(*hasher.finalize().as_bytes()))
@@ -132,21 +159,21 @@ impl CryptoSystem for CryptoSystemNONE {
&self,
reader: &mut dyn std::io::Read,
dht_key: &PublicKey,
) -> Result<bool, VeilidAPIError> {
) -> VeilidAPIResult<bool> {
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: &PublicKey, key2: &PublicKey) -> PublicKeyDistance {
fn distance(&self, key1: &PublicKey, key2: &PublicKey) -> CryptoKeyDistance {
let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
for (n, byte) in bytes.iter_mut().enumerate() {
*byte = key1.bytes[n] ^ key2.bytes[n];
}
PublicKeyDistance::new(bytes)
CryptoKeyDistance::new(bytes)
}
// Authentication
@@ -155,7 +182,7 @@ impl CryptoSystem for CryptoSystemNONE {
dht_key: &PublicKey,
dht_key_secret: &SecretKey,
data: &[u8],
) -> Result<Signature, VeilidAPIError> {
) -> VeilidAPIResult<Signature> {
if !is_bytes_eq_32(&do_xor_32(&dht_key.bytes, &dht_key_secret.bytes), 0xFFu8) {
return Err(VeilidAPIError::parse_error(
"Keypair is invalid",
@@ -178,7 +205,7 @@ impl CryptoSystem for CryptoSystemNONE {
dht_key: &PublicKey,
data: &[u8],
signature: &Signature,
) -> Result<(), VeilidAPIError> {
) -> VeilidAPIResult<()> {
let mut dig = Blake3Digest512::new();
dig.update(data);
let sig = dig.finalize();
@@ -215,7 +242,7 @@ impl CryptoSystem for CryptoSystemNONE {
nonce: &Nonce,
shared_secret: &SharedSecret,
_associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
) -> VeilidAPIResult<()> {
let mut blob = nonce.bytes.to_vec();
blob.extend_from_slice(&[0u8; 8]);
let blob = do_xor_32(&blob, &shared_secret.bytes);
@@ -237,7 +264,7 @@ impl CryptoSystem for CryptoSystemNONE {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
) -> VeilidAPIResult<Vec<u8>> {
let mut out = body.to_vec();
self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
@@ -251,7 +278,7 @@ impl CryptoSystem for CryptoSystemNONE {
nonce: &Nonce,
shared_secret: &SharedSecret,
_associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
) -> VeilidAPIResult<()> {
let mut blob = nonce.bytes.to_vec();
blob.extend_from_slice(&[0u8; 8]);
let blob = do_xor_32(&blob, &shared_secret.bytes);
@@ -266,7 +293,7 @@ impl CryptoSystem for CryptoSystemNONE {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
) -> VeilidAPIResult<Vec<u8>> {
let mut out = body.to_vec();
self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
@@ -275,12 +302,7 @@ impl CryptoSystem for CryptoSystemNONE {
}
// NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
) {
fn crypt_in_place_no_auth(&self, body: &mut [u8], nonce: &Nonce, shared_secret: &SharedSecret) {
let mut blob = nonce.bytes.to_vec();
blob.extend_from_slice(&[0u8; 8]);
let blob = do_xor_32(&blob, &shared_secret.bytes);

View File

@@ -49,7 +49,7 @@ impl Receipt {
nonce: Nonce,
sender_id: PublicKey,
extra_data: D,
) -> Result<Self, VeilidAPIError> {
) -> VeilidAPIResult<Self> {
assert!(VALID_ENVELOPE_VERSIONS.contains(&version));
assert!(VALID_CRYPTO_KINDS.contains(&crypto_kind));
@@ -68,7 +68,7 @@ impl Receipt {
})
}
pub fn from_signed_data(crypto: Crypto, data: &[u8]) -> Result<Receipt, VeilidAPIError> {
pub fn from_signed_data(crypto: Crypto, data: &[u8]) -> VeilidAPIResult<Receipt> {
// Ensure we are at least the length of the envelope
if data.len() < MIN_RECEIPT_SIZE {
apibail_parse_error!("receipt too small", data.len());
@@ -153,11 +153,7 @@ impl Receipt {
})
}
pub fn to_signed_data(
&self,
crypto: Crypto,
secret: &SecretKey,
) -> Result<Vec<u8>, VeilidAPIError> {
pub fn to_signed_data(&self, crypto: Crypto, secret: &SecretKey) -> VeilidAPIResult<Vec<u8>> {
// Ensure extra data isn't too long
let receipt_size: usize = self.extra_data.len() + MIN_RECEIPT_SIZE;
if receipt_size > MAX_RECEIPT_SIZE {

View File

@@ -162,6 +162,66 @@ pub async fn test_dh(vcrypto: CryptoSystemVersion) {
trace!("cached_dh: {:?}", r5);
}
pub async fn test_generation(vcrypto: CryptoSystemVersion) {
let b1 = vcrypto.random_bytes(32);
let b2 = vcrypto.random_bytes(32);
assert_ne!(b1, b2);
assert_eq!(b1.len(), 32);
assert_eq!(b2.len(), 32);
let b3 = vcrypto.random_bytes(0);
let b4 = vcrypto.random_bytes(0);
assert_eq!(b3, b4);
assert_eq!(b3.len(), 0);
assert_ne!(vcrypto.default_salt_length(), 0);
let pstr1 = vcrypto.hash_password(b"abc123", b"qwerasdf").unwrap();
let pstr2 = vcrypto.hash_password(b"abc123", b"qwerasdf").unwrap();
assert_eq!(pstr1, pstr2);
let pstr3 = vcrypto.hash_password(b"abc123", b"qwerasdg").unwrap();
assert_ne!(pstr1, pstr3);
let pstr4 = vcrypto.hash_password(b"abc124", b"qwerasdf").unwrap();
assert_ne!(pstr1, pstr4);
let pstr5 = vcrypto.hash_password(b"abc124", b"qwerasdg").unwrap();
assert_ne!(pstr3, pstr5);
vcrypto
.hash_password(b"abc123", b"qwe")
.expect_err("should reject short salt");
vcrypto
.hash_password(
b"abc123",
b"qwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerz",
)
.expect_err("should reject long salt");
assert!(vcrypto.verify_password(b"abc123", &pstr1).unwrap());
assert!(vcrypto.verify_password(b"abc123", &pstr2).unwrap());
assert!(vcrypto.verify_password(b"abc123", &pstr3).unwrap());
assert!(!vcrypto.verify_password(b"abc123", &pstr4).unwrap());
assert!(!vcrypto.verify_password(b"abc123", &pstr5).unwrap());
let ss1 = vcrypto.derive_shared_secret(b"abc123", b"qwerasdf");
let ss2 = vcrypto.derive_shared_secret(b"abc123", b"qwerasdf");
assert_eq!(ss1, ss2);
let ss3 = vcrypto.derive_shared_secret(b"abc123", b"qwerasdg");
assert_ne!(ss1, ss3);
let ss4 = vcrypto.derive_shared_secret(b"abc124", b"qwerasdf");
assert_ne!(ss1, ss4);
let ss5 = vcrypto.derive_shared_secret(b"abc124", b"qwerasdg");
assert_ne!(ss3, ss5);
vcrypto
.derive_shared_secret(b"abc123", b"qwe")
.expect_err("should reject short salt");
vcrypto
.derive_shared_secret(
b"abc123",
b"qwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerz",
)
.expect_err("should reject long salt");
}
pub async fn test_all() {
let api = crypto_tests_startup().await;
let crypto = api.crypto().unwrap();
@@ -171,7 +231,8 @@ pub async fn test_all() {
let vcrypto = crypto.get(v).unwrap();
test_aead(vcrypto.clone()).await;
test_no_auth(vcrypto.clone()).await;
test_dh(vcrypto).await;
test_dh(vcrypto.clone()).await;
test_generation(vcrypto).await;
}
crypto_tests_shutdown(api.clone()).await;

View File

@@ -225,6 +225,38 @@ pub async fn test_encode_decode(vcrypto: CryptoSystemVersion) {
assert!(f2.is_err());
}
pub async fn test_typed_convert(vcrypto: CryptoSystemVersion) {
let tks1 = format!(
"{}:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",
vcrypto.kind().to_string()
);
let tk1 = TypedKey::from_str(&tks1).expect("failed");
let tks1x = tk1.to_string();
assert_eq!(tks1, tks1x);
let tks2 = format!(
"{}:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzd",
vcrypto.kind().to_string()
);
let _tk2 = TypedKey::from_str(&tks2).expect_err("succeeded when it shouldnt have");
let tks3 = format!("XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",);
let tk3 = TypedKey::from_str(&tks3).expect("failed");
let tks3x = tk3.to_string();
assert_eq!(tks3, tks3x);
let tks4 = format!("XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzd",);
let _tk4 = TypedKey::from_str(&tks4).expect_err("succeeded when it shouldnt have");
let tks5 = format!("XXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",);
let _tk5 = TypedKey::from_str(&tks5).expect_err("succeeded when it shouldnt have");
let tks6 = format!("7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",);
let tk6 = TypedKey::from_str(&tks6).expect("failed");
let tks6x = tk6.to_string();
assert!(tks6x.ends_with(&tks6));
}
async fn test_hash(vcrypto: CryptoSystemVersion) {
let mut s = BTreeSet::<PublicKey>::new();
@@ -333,6 +365,7 @@ pub async fn test_all() {
test_sign_and_verify(vcrypto.clone()).await;
test_key_conversions(vcrypto.clone()).await;
test_encode_decode(vcrypto.clone()).await;
test_typed_convert(vcrypto.clone()).await;
test_hash(vcrypto.clone()).await;
test_operations(vcrypto).await;
}

View File

@@ -127,12 +127,17 @@ where
type Err = VeilidAPIError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let b = s.as_bytes();
if b.len() != (5 + K::encoded_len()) || b[4..5] != b":"[..] {
apibail_parse_error!("invalid typed key", s);
if b.len() == (5 + K::encoded_len()) && b[4..5] == b":"[..] {
let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert");
let value = K::try_decode_bytes(&b[5..])?;
Ok(Self { kind, value })
} else if b.len() == K::encoded_len() {
let kind = best_crypto_kind();
let value = K::try_decode_bytes(b)?;
Ok(Self { kind, value })
} else {
apibail_generic!("invalid cryptotyped format");
}
let kind: CryptoKind = b[0..4].try_into().expect("should not fail to convert");
let value = K::try_decode_bytes(&b[5..])?;
Ok(Self { kind, value })
}
}
impl<'de, K> Deserialize<'de> for CryptoTyped<K>

View File

@@ -141,9 +141,9 @@ where
}
false
}
pub fn contains_key(&self, key: &K) -> bool {
pub fn contains_value(&self, value: &K) -> bool {
for tk in &self.items {
if tk.value == *key {
if tk.value == *value {
return true;
}
}
@@ -282,6 +282,28 @@ where
tks
}
}
impl<K> From<&[CryptoTyped<K>]> for CryptoTypedSet<K>
where
K: Clone
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
+ RkyvArchive
+ Encodable,
<K as RkyvArchive>::Archived: Hash + PartialEq + Eq,
{
fn from(x: &[CryptoTyped<K>]) -> Self {
let mut tks = CryptoTypedSet::<K>::with_capacity(x.len());
tks.add_all(x);
tks
}
}
impl<K> Into<Vec<CryptoTyped<K>>> for CryptoTypedSet<K>
where
K: Clone

View File

@@ -39,7 +39,7 @@ impl Encodable for KeyPair {
fn encoded_len() -> usize {
PublicKey::encoded_len() + 1 + SecretKey::encoded_len()
}
fn try_decode_bytes(b: &[u8]) -> Result<Self, VeilidAPIError> {
fn try_decode_bytes(b: &[u8]) -> VeilidAPIResult<Self> {
if b.len() != Self::encoded_len() {
apibail_parse_error!("input has wrong encoded length", format!("len={}", b.len()));
}
@@ -56,9 +56,7 @@ impl fmt::Display for KeyPair {
impl fmt::Debug for KeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
write!(f, "{}", self.encode())?;
write!(f, ")")
write!(f, "KeyPair({})", self.encode())
}
}

View File

@@ -5,8 +5,6 @@ use core::convert::TryInto;
use core::fmt;
use core::hash::Hash;
use rkyv::{Archive as RkyvArchive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
/// Cryptography version fourcc code
pub type CryptoKind = FourCC;
@@ -55,5 +53,10 @@ pub type TypedKey = CryptoTyped<PublicKey>;
pub type TypedSecret = CryptoTyped<SecretKey>;
pub type TypedKeyPair = CryptoTyped<KeyPair>;
pub type TypedSignature = CryptoTyped<Signature>;
pub type TypedSharedSecret = CryptoTyped<SharedSecret>;
pub type TypedKeySet = CryptoTypedSet<PublicKey>;
pub type TypedSecretSet = CryptoTypedSet<SecretKey>;
pub type TypedKeyPairSet = CryptoTypedSet<KeyPair>;
pub type TypedSignatureSet = CryptoTypedSet<Signature>;
pub type TypedSharedSecretSet = CryptoTypedSet<SharedSecret>;

View File

@@ -1,5 +1,9 @@
use super::*;
use argon2::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, Salt, SaltString},
Argon2,
};
use chacha20::cipher::{KeyIvInit, StreamCipher};
use chacha20::XChaCha20;
use chacha20poly1305 as ch;
@@ -13,7 +17,7 @@ use x25519_dalek as xd;
const AEAD_OVERHEAD: usize = 16;
pub const CRYPTO_KIND_VLD0: CryptoKind = FourCC([b'V', b'L', b'D', b'0']);
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIError> {
fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> VeilidAPIResult<xd::PublicKey> {
let bytes = key.to_bytes();
let compressed = cd::edwards::CompressedEdwardsY(bytes);
let point = compressed
@@ -22,7 +26,7 @@ fn ed25519_to_x25519_pk(key: &ed::PublicKey) -> Result<xd::PublicKey, VeilidAPIE
let mp = point.to_montgomery();
Ok(xd::PublicKey::from(mp.to_bytes()))
}
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> Result<xd::StaticSecret, VeilidAPIError> {
fn ed25519_to_x25519_sk(key: &ed::SecretKey) -> VeilidAPIResult<xd::StaticSecret> {
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)?;
@@ -61,31 +65,71 @@ impl CryptoSystem for CryptoSystemVLD0 {
}
// Cached Operations
fn cached_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
fn cached_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
self.crypto
.cached_dh_internal::<CryptoSystemVLD0>(self, key, secret)
}
// Generation
fn random_bytes(&self, len: u32) -> Vec<u8> {
let mut bytes = unsafe { unaligned_u8_vec_uninit(len as usize) };
random_bytes(bytes.as_mut());
bytes
}
fn default_salt_length(&self) -> u32 {
16
}
fn hash_password(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<String> {
if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
apibail_generic!("invalid salt length");
}
// Hash password to PHC string ($argon2id$v=19$...)
let salt = SaltString::encode_b64(salt).map_err(VeilidAPIError::generic)?;
// Argon2 with default params (Argon2id v19)
let argon2 = Argon2::default();
let password_hash = argon2
.hash_password(password, &salt)
.map_err(VeilidAPIError::generic)?
.to_string();
Ok(password_hash)
}
fn verify_password(&self, password: &[u8], password_hash: &str) -> VeilidAPIResult<bool> {
let parsed_hash = PasswordHash::new(password_hash).map_err(VeilidAPIError::generic)?;
// Argon2 with default params (Argon2id v19)
let argon2 = Argon2::default();
Ok(argon2.verify_password(password, &parsed_hash).is_ok())
}
fn derive_shared_secret(&self, password: &[u8], salt: &[u8]) -> VeilidAPIResult<SharedSecret> {
if salt.len() < Salt::MIN_LENGTH || salt.len() > Salt::MAX_LENGTH {
apibail_generic!("invalid salt length");
}
// Argon2 with default params (Argon2id v19)
let argon2 = Argon2::default();
let mut output_key_material = [0u8; SHARED_SECRET_LENGTH];
argon2
.hash_password_into(password, salt, &mut output_key_material)
.map_err(VeilidAPIError::generic)?;
Ok(SharedSecret::new(output_key_material))
}
fn random_nonce(&self) -> Nonce {
let mut nonce = [0u8; NONCE_LENGTH];
random_bytes(&mut nonce).unwrap();
random_bytes(&mut nonce);
Nonce::new(nonce)
}
fn random_shared_secret(&self) -> SharedSecret {
let mut s = [0u8; SHARED_SECRET_LENGTH];
random_bytes(&mut s).unwrap();
random_bytes(&mut s);
SharedSecret::new(s)
}
fn compute_dh(
&self,
key: &PublicKey,
secret: &SecretKey,
) -> Result<SharedSecret, VeilidAPIError> {
fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult<SharedSecret> {
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)?;
@@ -98,10 +142,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
fn generate_hash(&self, data: &[u8]) -> PublicKey {
PublicKey::new(*blake3::hash(data).as_bytes())
}
fn generate_hash_reader(
&self,
reader: &mut dyn std::io::Read,
) -> Result<PublicKey, VeilidAPIError> {
fn generate_hash_reader(&self, reader: &mut dyn std::io::Read) -> VeilidAPIResult<PublicKey> {
let mut hasher = blake3::Hasher::new();
std::io::copy(reader, &mut hasher).map_err(VeilidAPIError::generic)?;
Ok(PublicKey::new(*hasher.finalize().as_bytes()))
@@ -127,21 +168,21 @@ impl CryptoSystem for CryptoSystemVLD0 {
&self,
reader: &mut dyn std::io::Read,
dht_key: &PublicKey,
) -> Result<bool, VeilidAPIError> {
) -> VeilidAPIResult<bool> {
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: &PublicKey, key2: &PublicKey) -> PublicKeyDistance {
fn distance(&self, key1: &PublicKey, key2: &PublicKey) -> CryptoKeyDistance {
let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
for (n, byte) in bytes.iter_mut().enumerate() {
*byte = key1.bytes[n] ^ key2.bytes[n];
}
PublicKeyDistance::new(bytes)
CryptoKeyDistance::new(bytes)
}
// Authentication
@@ -150,7 +191,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
dht_key: &PublicKey,
dht_key_secret: &SecretKey,
data: &[u8],
) -> Result<Signature, VeilidAPIError> {
) -> VeilidAPIResult<Signature> {
let mut kpb: [u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH] =
[0u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH];
@@ -177,7 +218,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
dht_key: &PublicKey,
data: &[u8],
signature: &Signature,
) -> Result<(), VeilidAPIError> {
) -> VeilidAPIResult<()> {
let pk = ed::PublicKey::from_bytes(&dht_key.bytes)
.map_err(|e| VeilidAPIError::parse_error("Public key is invalid", e))?;
let sig = ed::Signature::from_bytes(&signature.bytes)
@@ -201,7 +242,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
) -> VeilidAPIResult<()> {
let key = ch::Key::from(shared_secret.bytes);
let xnonce = ch::XNonce::from(nonce.bytes);
let aead = ch::XChaCha20Poly1305::new(&key);
@@ -216,7 +257,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
) -> VeilidAPIResult<Vec<u8>> {
let mut out = body.to_vec();
self.decrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
@@ -230,7 +271,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<(), VeilidAPIError> {
) -> VeilidAPIResult<()> {
let key = ch::Key::from(shared_secret.bytes);
let xnonce = ch::XNonce::from(nonce.bytes);
let aead = ch::XChaCha20Poly1305::new(&key);
@@ -246,7 +287,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
nonce: &Nonce,
shared_secret: &SharedSecret,
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>, VeilidAPIError> {
) -> VeilidAPIResult<Vec<u8>> {
let mut out = body.to_vec();
self.encrypt_in_place_aead(&mut out, nonce, shared_secret, associated_data)
.map_err(map_to_string)
@@ -257,11 +298,11 @@ impl CryptoSystem for CryptoSystemVLD0 {
// NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth(
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
body: &mut [u8],
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
) {
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), &nonce.bytes.into());
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), nonce.into());
cipher.apply_keystream(body);
}
@@ -269,17 +310,17 @@ impl CryptoSystem for CryptoSystemVLD0 {
&self,
in_buf: &[u8],
out_buf: &mut [u8],
nonce: &Nonce,
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
) {
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), &nonce.bytes.into());
let mut cipher = XChaCha20::new(&shared_secret.bytes.into(), nonce.into());
cipher.apply_keystream_b2b(in_buf, out_buf).unwrap();
}
fn crypt_no_auth_aligned_8(
&self,
in_buf: &[u8],
nonce: &Nonce,
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
) -> Vec<u8> {
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) };
@@ -290,7 +331,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
fn crypt_no_auth_unaligned(
&self,
in_buf: &[u8],
nonce: &Nonce,
nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret,
) -> Vec<u8> {
let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) };