encryption checkpoint
This commit is contained in:
parent
fd7257e9bf
commit
5760096fcb
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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()) };
|
||||||
|
@ -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| {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user