removing dev branch, many changes
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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()) };
|
||||
|
||||
Reference in New Issue
Block a user