encryption checkpoint

This commit is contained in:
John Smith 2023-05-24 00:05:27 +01:00
parent fd7257e9bf
commit 5760096fcb
8 changed files with 150 additions and 30 deletions

View File

@ -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
}
}
}; };
} }

View File

@ -97,27 +97,27 @@ pub trait CryptoSystem {
// NoAuth Encrypt/Decrypt // NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth( fn crypt_in_place_no_auth(
&self, &self,
body: &mut Vec<u8>, body: &mut [u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
); );
fn crypt_b2b_no_auth( fn crypt_b2b_no_auth(
&self, &self,
in_buf: &[u8], in_buf: &[u8],
out_buf: &mut [u8], out_buf: &mut [u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
); );
fn crypt_no_auth_aligned_8( fn crypt_no_auth_aligned_8(
&self, &self,
body: &[u8], body: &[u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
) -> Vec<u8>; ) -> Vec<u8>;
fn crypt_no_auth_unaligned( fn crypt_no_auth_unaligned(
&self, &self,
body: &[u8], body: &[u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
) -> Vec<u8>; ) -> Vec<u8>;
} }

View File

@ -183,8 +183,11 @@ impl Envelope {
let dh_secret = vcrypto.cached_dh(&self.sender_id, node_id_secret)?; let dh_secret = vcrypto.cached_dh(&self.sender_id, node_id_secret)?;
// Decrypt message without authentication // Decrypt message without authentication
let body = let body = vcrypto.crypt_no_auth_aligned_8(
vcrypto.crypt_no_auth_aligned_8(&data[0x6A..data.len() - 64], &self.nonce, &dh_secret); &data[0x6A..data.len() - 64],
&self.nonce.bytes,
&dh_secret,
);
Ok(body) Ok(body)
} }
@ -227,7 +230,7 @@ impl Envelope {
data[0x4A..0x6A].copy_from_slice(&self.recipient_id.bytes); data[0x4A..0x6A].copy_from_slice(&self.recipient_id.bytes);
// Encrypt and authenticate message // 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 // Write body
if !encrypted_body.is_empty() { if !encrypted_body.is_empty() {

View File

@ -139,6 +139,9 @@ impl Crypto {
trace!("Crypto::init"); trace!("Crypto::init");
let table_store = self.unlocked_inner.table_store.clone(); 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 // Init node id from config
if let Err(e) = self if let Err(e) = self
.unlocked_inner .unlocked_inner

View File

@ -82,8 +82,7 @@ impl CryptoSystem for CryptoSystemNONE {
// Generation // Generation
fn random_bytes(&self, len: u32) -> Vec<u8> { fn random_bytes(&self, len: u32) -> Vec<u8> {
let mut bytes = Vec::<u8>::with_capacity(len as usize); let mut bytes = unsafe { unaligned_u8_vec_uninit(len as usize) };
bytes.resize(len as usize, 0u8);
random_bytes(bytes.as_mut()); random_bytes(bytes.as_mut());
bytes bytes
} }
@ -322,12 +321,7 @@ impl CryptoSystem for CryptoSystemNONE {
} }
// NoAuth Encrypt/Decrypt // NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth( fn crypt_in_place_no_auth(&self, body: &mut [u8], nonce: &Nonce, shared_secret: &SharedSecret) {
&self,
body: &mut Vec<u8>,
nonce: &Nonce,
shared_secret: &SharedSecret,
) {
let mut blob = nonce.bytes.to_vec(); let mut blob = nonce.bytes.to_vec();
blob.extend_from_slice(&[0u8; 8]); blob.extend_from_slice(&[0u8; 8]);
let blob = do_xor_32(&blob, &shared_secret.bytes); let blob = do_xor_32(&blob, &shared_secret.bytes);

View File

@ -76,8 +76,7 @@ impl CryptoSystem for CryptoSystemVLD0 {
// Generation // Generation
fn random_bytes(&self, len: u32) -> Vec<u8> { fn random_bytes(&self, len: u32) -> Vec<u8> {
let mut bytes = Vec::<u8>::with_capacity(len as usize); let mut bytes = unsafe { unaligned_u8_vec_uninit(len as usize) };
bytes.resize(len as usize, 0u8);
random_bytes(bytes.as_mut()); random_bytes(bytes.as_mut());
bytes bytes
} }
@ -318,11 +317,11 @@ impl CryptoSystem for CryptoSystemVLD0 {
// NoAuth Encrypt/Decrypt // NoAuth Encrypt/Decrypt
fn crypt_in_place_no_auth( fn crypt_in_place_no_auth(
&self, &self,
body: &mut Vec<u8>, body: &mut [u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, 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); cipher.apply_keystream(body);
} }
@ -330,17 +329,17 @@ impl CryptoSystem for CryptoSystemVLD0 {
&self, &self,
in_buf: &[u8], in_buf: &[u8],
out_buf: &mut [u8], out_buf: &mut [u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, 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(); cipher.apply_keystream_b2b(in_buf, out_buf).unwrap();
} }
fn crypt_no_auth_aligned_8( fn crypt_no_auth_aligned_8(
&self, &self,
in_buf: &[u8], in_buf: &[u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
) -> Vec<u8> { ) -> Vec<u8> {
let mut out_buf = unsafe { aligned_8_u8_vec_uninit(in_buf.len()) }; 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( fn crypt_no_auth_unaligned(
&self, &self,
in_buf: &[u8], in_buf: &[u8],
nonce: &Nonce, nonce: &[u8; NONCE_LENGTH],
shared_secret: &SharedSecret, shared_secret: &SharedSecret,
) -> Vec<u8> { ) -> Vec<u8> {
let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) }; let mut out_buf = unsafe { unaligned_u8_vec_uninit(in_buf.len()) };

View File

@ -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 { pub struct TableDBUnlockedInner {
table: String, table: String,
table_store: TableStore, table_store: TableStore,
crypto: Crypto,
database: Database, database: Database,
encryption_key: Option<TypedSharedSecret>, // Encryption and decryption key will be the same unless configured for an in-place migration
encrypt_info: Option<CryptInfo>,
decrypt_info: Option<CryptInfo>,
} }
impl fmt::Debug for TableDBUnlockedInner { impl fmt::Debug for TableDBUnlockedInner {
@ -38,15 +53,22 @@ impl TableDB {
pub(super) fn new( pub(super) fn new(
table: String, table: String,
table_store: TableStore, table_store: TableStore,
crypto: Crypto,
database: Database, database: Database,
encryption_key: Option<TypedSharedSecret>, encryption_key: Option<TypedSharedSecret>,
decryption_key: Option<TypedSharedSecret>,
) -> Self { ) -> 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 { Self {
unlocked_inner: Arc::new(TableDBUnlockedInner { unlocked_inner: Arc::new(TableDBUnlockedInner {
table, table,
table_store, table_store,
crypto,
database, database,
encryption_key, encrypt_info,
decrypt_info,
}), }),
} }
} }
@ -67,8 +89,46 @@ impl TableDB {
db.num_columns().map_err(VeilidAPIError::from) db.num_columns().map_err(VeilidAPIError::from)
} }
fn maybe_encrypt(&self, data: &[u8]) -> Vec<u8> {
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<Vec<u8>> {
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 /// Get the list of keys in a column of the TableDB
pub async fn get_keys(&self, col: u32) -> VeilidAPIResult<Vec<Box<[u8]>>> { pub async fn get_keys(&self, col: u32) -> VeilidAPIResult<Vec<Vec<u8>>> {
let db = self.unlocked_inner.database.clone(); let db = self.unlocked_inner.database.clone();
let mut out: Vec<Box<[u8]>> = Vec::new(); let mut out: Vec<Box<[u8]>> = Vec::new();
db.iter(col, None, |kv| { db.iter(col, None, |kv| {

View File

@ -6,6 +6,7 @@ struct TableStoreInner {
encryption_key: Option<TypedSharedSecret>, encryption_key: Option<TypedSharedSecret>,
all_table_names: HashMap<String, String>, all_table_names: HashMap<String, String>,
all_tables_db: Option<Database>, all_tables_db: Option<Database>,
crypto: Option<Crypto>,
} }
/// Veilid Table Storage /// Veilid Table Storage
@ -26,6 +27,7 @@ impl TableStore {
encryption_key: None, encryption_key: None,
all_table_names: HashMap::new(), all_table_names: HashMap::new(),
all_tables_db: None, all_tables_db: None,
crypto: None,
} }
} }
pub(crate) fn new(config: VeilidConfig, protected_store: ProtectedStore) -> Self { 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 // Flush internal control state
async fn flush(&self) { async fn flush(&self) {
let (all_table_names_value, all_tables_db) = { let (all_table_names_value, all_tables_db) = {
@ -54,7 +61,7 @@ impl TableStore {
if let Err(e) = all_tables_db.write(dbt).await { if let Err(e) = all_tables_db.write(dbt).await {
error!("failed to write all tables db: {}", e); error!("failed to write all tables db: {}", e);
} }
} xxx must from_rkyv the all_table_names }
// Internal naming support // Internal naming support
// Adds rename capability and ensures names of tables are totally unique and valid // 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<()> { pub(crate) async fn init(&self) -> EyreResult<()> {
let _async_guard = self.async_lock.lock().await; let _async_guard = self.async_lock.lock().await;
let encryption_key: Option<TypedSharedSecret> = self // Get device encryption key from protected store
let mut encryption_key: Option<TypedSharedSecret> = self
.protected_store .protected_store
.load_user_secret_rkyv("device_encryption_key") .load_user_secret_rkyv("device_encryption_key")
.await?; .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 let all_tables_db = self
.table_store_driver .table_store_driver
.open("__veilid_all_tables", 1) .open("__veilid_all_tables", 1)
.await .await
.wrap_err("failed to create all tables table")?; .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::<HashMap<String, String>>(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(); let mut inner = self.inner.lock();
@ -190,6 +231,9 @@ impl TableStore {
pub(crate) async fn terminate(&self) { pub(crate) async fn terminate(&self) {
let _async_guard = self.async_lock.lock().await; let _async_guard = self.async_lock.lock().await;
self.flush().await;
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
if !inner.opened.is_empty() { if !inner.opened.is_empty() {
panic!( panic!(
@ -198,6 +242,7 @@ impl TableStore {
); );
} }
inner.all_tables_db = None; inner.all_tables_db = None;
inner.all_table_names.clear();
inner.encryption_key = None; inner.encryption_key = None;
} }
@ -251,8 +296,10 @@ impl TableStore {
let table_db = TableDB::new( let table_db = TableDB::new(
table_name.clone(), table_name.clone(),
self.clone(), self.clone(),
inner.crypto.as_ref().unwrap().clone(),
db, db,
inner.encryption_key.clone(), inner.encryption_key.clone(),
inner.encryption_key.clone(),
); );
// Keep track of opened DBs // Keep track of opened DBs