diff --git a/veilid-core/src/crypto/byte_array_types.rs b/veilid-core/src/crypto/byte_array_types.rs index d17acb04..4e8018c8 100644 --- a/veilid-core/src/crypto/byte_array_types.rs +++ b/veilid-core/src/crypto/byte_array_types.rs @@ -256,6 +256,20 @@ macro_rules! byte_array_type { }) } } + + 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 + } + } }; } diff --git a/veilid-core/src/crypto/crypto_system.rs b/veilid-core/src/crypto/crypto_system.rs index d97a87bc..70967e04 100644 --- a/veilid-core/src/crypto/crypto_system.rs +++ b/veilid-core/src/crypto/crypto_system.rs @@ -97,27 +97,27 @@ pub trait CryptoSystem { // NoAuth Encrypt/Decrypt fn crypt_in_place_no_auth( &self, - body: &mut Vec, - 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; fn crypt_no_auth_unaligned( &self, body: &[u8], - nonce: &Nonce, + nonce: &[u8; NONCE_LENGTH], shared_secret: &SharedSecret, ) -> Vec; } diff --git a/veilid-core/src/crypto/envelope.rs b/veilid-core/src/crypto/envelope.rs index fe22f2fc..4a043588 100644 --- a/veilid-core/src/crypto/envelope.rs +++ b/veilid-core/src/crypto/envelope.rs @@ -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) } @@ -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() { diff --git a/veilid-core/src/crypto/mod.rs b/veilid-core/src/crypto/mod.rs index c2026237..816c9515 100644 --- a/veilid-core/src/crypto/mod.rs +++ b/veilid-core/src/crypto/mod.rs @@ -139,6 +139,9 @@ impl Crypto { trace!("Crypto::init"); let table_store = self.unlocked_inner.table_store.clone(); + // Set crypto for table store + table_store.set_crypto(self.clone()); + // Init node id from config if let Err(e) = self .unlocked_inner diff --git a/veilid-core/src/crypto/none/mod.rs b/veilid-core/src/crypto/none/mod.rs index 69ae1377..9b92783c 100644 --- a/veilid-core/src/crypto/none/mod.rs +++ b/veilid-core/src/crypto/none/mod.rs @@ -82,8 +82,7 @@ impl CryptoSystem for CryptoSystemNONE { // Generation fn random_bytes(&self, len: u32) -> Vec { - let mut bytes = Vec::::with_capacity(len as usize); - bytes.resize(len as usize, 0u8); + let mut bytes = unsafe { unaligned_u8_vec_uninit(len as usize) }; random_bytes(bytes.as_mut()); bytes } @@ -322,12 +321,7 @@ impl CryptoSystem for CryptoSystemNONE { } // NoAuth Encrypt/Decrypt - fn crypt_in_place_no_auth( - &self, - body: &mut Vec, - 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); diff --git a/veilid-core/src/crypto/vld0/mod.rs b/veilid-core/src/crypto/vld0/mod.rs index 41e6c640..9bd65567 100644 --- a/veilid-core/src/crypto/vld0/mod.rs +++ b/veilid-core/src/crypto/vld0/mod.rs @@ -76,8 +76,7 @@ impl CryptoSystem for CryptoSystemVLD0 { // Generation fn random_bytes(&self, len: u32) -> Vec { - let mut bytes = Vec::::with_capacity(len as usize); - bytes.resize(len as usize, 0u8); + let mut bytes = unsafe { unaligned_u8_vec_uninit(len as usize) }; random_bytes(bytes.as_mut()); bytes } @@ -318,11 +317,11 @@ impl CryptoSystem for CryptoSystemVLD0 { // NoAuth Encrypt/Decrypt fn crypt_in_place_no_auth( &self, - body: &mut Vec, - 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); } @@ -330,17 +329,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 { let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) }; @@ -351,7 +350,7 @@ impl CryptoSystem for CryptoSystemVLD0 { fn crypt_no_auth_unaligned( &self, in_buf: &[u8], - nonce: &Nonce, + nonce: &[u8; NONCE_LENGTH], shared_secret: &SharedSecret, ) -> Vec { let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) }; diff --git a/veilid-core/src/table_store/table_db.rs b/veilid-core/src/table_store/table_db.rs index cfa8cba3..d85383b2 100644 --- a/veilid-core/src/table_store/table_db.rs +++ b/veilid-core/src/table_store/table_db.rs @@ -10,11 +10,26 @@ cfg_if! { } } +struct CryptInfo { + vcrypto: CryptoSystemVersion, + key: SharedSecret, +} +impl CryptInfo { + pub fn new(crypto: Crypto, typed_key: TypedSharedSecret) -> Self { + let vcrypto = crypto.get(typed_key.kind).unwrap(); + let key = typed_key.value; + Self { vcrypto, key } + } +} + pub struct TableDBUnlockedInner { table: String, table_store: TableStore, + crypto: Crypto, database: Database, - encryption_key: Option, + // Encryption and decryption key will be the same unless configured for an in-place migration + encrypt_info: Option, + decrypt_info: Option, } impl fmt::Debug for TableDBUnlockedInner { @@ -38,15 +53,22 @@ impl TableDB { pub(super) fn new( table: String, table_store: TableStore, + crypto: Crypto, database: Database, encryption_key: Option, + decryption_key: Option, ) -> Self { + let encrypt_info = encryption_key.map(|ek| CryptInfo::new(crypto.clone(), ek)); + let decrypt_info = dcryption_key.map(|dk| CryptInfo::new(crypto.clone(), dk)); + Self { unlocked_inner: Arc::new(TableDBUnlockedInner { table, table_store, + crypto, database, - encryption_key, + encrypt_info, + decrypt_info, }), } } @@ -67,8 +89,46 @@ impl TableDB { db.num_columns().map_err(VeilidAPIError::from) } + fn maybe_encrypt(&self, data: &[u8]) -> Vec { + if let Some(ei) = &self.unlocked_inner.encrypt_info { + let mut out = unsafe { unaligned_u8_vec_uninit(NONCE_LENGTH + data.len()) }; + random_bytes(&mut out[0..NONCE_LENGTH]); + + ei.vcrypto.crypt_b2b_no_auth( + data, + &mut out[NONCE_LENGTH..], + &out[0..NONCE_LENGTH], + &ei.key, + ); + out + } else { + data.to_vec() + } + } + + fn maybe_decrypt(&self, data: &[u8]) -> VeilidAPIResult> { + if let Some(di) = &self.unlocked_inner.decrypt_info { + if data.len() <= NONCE_LENGTH { + return Err(VeilidAPIError::internal("data too short")); + } + xxxx make decrypt + let mut out = unsafe { unaligned_u8_vec_uninit(NONCE_LENGTH + data.len()) }; + random_bytes(&mut out[0..NONCE_LENGTH]); + + ei.vcrypto.crypt_b2b_no_auth( + data, + &mut out[NONCE_LENGTH..], + &out[0..NONCE_LENGTH], + &ei.key, + ); + out + } else { + Ok(data.to_vec()) + } + } + /// Get the list of keys in a column of the TableDB - pub async fn get_keys(&self, col: u32) -> VeilidAPIResult>> { + pub async fn get_keys(&self, col: u32) -> VeilidAPIResult>> { let db = self.unlocked_inner.database.clone(); let mut out: Vec> = Vec::new(); db.iter(col, None, |kv| { diff --git a/veilid-core/src/table_store/table_store.rs b/veilid-core/src/table_store/table_store.rs index d035bac8..b3a60080 100644 --- a/veilid-core/src/table_store/table_store.rs +++ b/veilid-core/src/table_store/table_store.rs @@ -6,6 +6,7 @@ struct TableStoreInner { encryption_key: Option, all_table_names: HashMap, all_tables_db: Option, + crypto: Option, } /// Veilid Table Storage @@ -26,6 +27,7 @@ impl TableStore { encryption_key: None, all_table_names: HashMap::new(), all_tables_db: None, + crypto: None, } } pub(crate) fn new(config: VeilidConfig, protected_store: ProtectedStore) -> Self { @@ -41,6 +43,11 @@ impl TableStore { } } + pub(crate) fn set_crypto(&self, crypto: Crypto) { + let mut inner = self.inner.lock(); + inner.crypto = Some(crypto); + } + // Flush internal control state async fn flush(&self) { let (all_table_names_value, all_tables_db) = { @@ -54,7 +61,7 @@ impl TableStore { if let Err(e) = all_tables_db.write(dbt).await { error!("failed to write all tables db: {}", e); } - } xxx must from_rkyv the all_table_names + } // Internal naming support // Adds rename capability and ensures names of tables are totally unique and valid @@ -159,16 +166,50 @@ impl TableStore { pub(crate) async fn init(&self) -> EyreResult<()> { let _async_guard = self.async_lock.lock().await; - let encryption_key: Option = self + // Get device encryption key from protected store + let mut encryption_key: Option = self .protected_store .load_user_secret_rkyv("device_encryption_key") .await?; + if let Some(encryption_key) = encryption_key { + // If encryption in current use is not the best encryption, then run table migration + let best_kind = best_crypto_kind(); + if encryption_key.kind != best_kind { + // XXX: Run migration. See issue #209 + } + } else { + // If we don't have an encryption key yet, then make one with the best cryptography and save it + let best_kind = best_crypto_kind(); + let mut shared_secret = SharedSecret::default(); + random_bytes(&mut shared_secret.bytes); + encryption_key = Some(TypedSharedSecret::new(best_kind, shared_secret)); + } + + // Deserialize all table names let all_tables_db = self .table_store_driver .open("__veilid_all_tables", 1) .await .wrap_err("failed to create all tables table")?; + match all_tables_db.get(0, b"all_table_names").await { + Ok(Some(v)) => match from_rkyv::>(v) { + Ok(all_table_names) => { + let mut inner = self.inner.lock(); + inner.all_table_names = all_table_names; + } + Err(e) => { + error!("could not deserialize __veilid_all_tables: {}", e); + } + }, + Ok(None) => { + // No table names yet, that's okay + trace!("__veilid_all_tables is empty"); + } + Err(e) => { + error!("could not get __veilid_all_tables: {}", e); + } + }; { let mut inner = self.inner.lock(); @@ -190,6 +231,9 @@ impl TableStore { pub(crate) async fn terminate(&self) { let _async_guard = self.async_lock.lock().await; + + self.flush().await; + let mut inner = self.inner.lock(); if !inner.opened.is_empty() { panic!( @@ -198,6 +242,7 @@ impl TableStore { ); } inner.all_tables_db = None; + inner.all_table_names.clear(); inner.encryption_key = None; } @@ -251,8 +296,10 @@ impl TableStore { let table_db = TableDB::new( table_name.clone(), self.clone(), + inner.crypto.as_ref().unwrap().clone(), db, inner.encryption_key.clone(), + inner.encryption_key.clone(), ); // Keep track of opened DBs