Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Database encryption key backup / recovery feature for entropy-tss #1249

Open
wants to merge 43 commits into
base: peg/non-persistant-tss-keys
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
af51f2d
Add key provider module
ameba23 Jan 14, 2025
a6a9838
KVDB - replace password with 32 byte key
ameba23 Jan 14, 2025
55e4847
KVDB - rm password.rs
ameba23 Jan 14, 2025
d9c0a3c
Rm commented code
ameba23 Jan 14, 2025
10ab4bd
Add key provider logic
ameba23 Jan 15, 2025
06fe850
Improve key provider and add test
ameba23 Jan 16, 2025
d5922da
Kvdb stores key for backup
ameba23 Jan 16, 2025
374c98f
Actually back up keyshares in production
ameba23 Jan 16, 2025
974cdc8
Doccomments
ameba23 Jan 17, 2025
9043c95
Store backed-up keys in memory not kvdb
ameba23 Jan 17, 2025
b06a721
Encrypt keys when sending to be backed up
ameba23 Jan 17, 2025
39f3f68
Error handling, quote handling
ameba23 Jan 17, 2025
7a6b4b7
Fix test
ameba23 Jan 17, 2025
88ac607
Use tss_account id from signed message, not one provided by user
ameba23 Jan 20, 2025
e2558a8
Copy quote validation logic into entropy-tss and verify quotes when r…
ameba23 Jan 20, 2025
cb7dd4f
Error handling, tidy
ameba23 Jan 20, 2025
aacb5fc
Check measurement values when verifying quote
ameba23 Jan 20, 2025
b8ecee8
Merge peg/non-persistant-tss-keys
ameba23 Jan 20, 2025
76c9443
Handle tdx-quote errors
ameba23 Jan 20, 2025
184c2dd
Use known encryption keys for test validators
ameba23 Jan 21, 2025
6aa01fd
Rename to backup provider, quote nonce getting api
ameba23 Jan 21, 2025
74b3927
Clippy
ameba23 Jan 21, 2025
1d10dab
Require node is ready in http route handlers
ameba23 Jan 21, 2025
5911e08
Mv quote measurement checking fn to attestation module
ameba23 Jan 21, 2025
e73ad94
Error handling
ameba23 Jan 21, 2025
7005f44
Changelog
ameba23 Jan 21, 2025
dcdaa67
Changelog
ameba23 Jan 21, 2025
054bcbc
Doccomments following review
ameba23 Jan 22, 2025
b33d261
Choose backup provider randomly, not from TSS id
ameba23 Jan 22, 2025
8c513cc
Refactor duplicated quote verifying fn
ameba23 Jan 22, 2025
d1ecf5b
Taplo
ameba23 Jan 22, 2025
05f5f3c
Update test-cli
ameba23 Jan 22, 2025
1de72b6
Fix for building entropy-shared for wasm
ameba23 Jan 22, 2025
9991f6d
Fix staking pallet mock
ameba23 Jan 22, 2025
c4c7e42
Fix staking pallet benchmarks
ameba23 Jan 22, 2025
57924d9
Fix attestation pallet tests
ameba23 Jan 22, 2025
0822704
Fix client tests
ameba23 Jan 22, 2025
9c68e94
Use sp_core::crypto::AccountId32 as key for hashmap to be more explicit
ameba23 Jan 23, 2025
57d22e2
Rename struct field following review
ameba23 Jan 23, 2025
18105ee
Minor edits from PR review
ameba23 Jan 23, 2025
ef46c51
Clippy
ameba23 Jan 23, 2025
a31ff0e
Flatten HTTP API structure following review
ameba23 Jan 24, 2025
f6146f4
Add an extra TSS state for connected to chain but not funded / fully …
ameba23 Jan 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ runtime
- Test CLI command to retrieve quote and change endpoint / TSS account in one command ([#1198](https://github.com/entropyxyz/entropy-core/pull/1198))
- On-chain unresponsiveness reporting [(#1215)](https://github.com/entropyxyz/entropy-core/pull/1215)
- Add cli options for adding validator [(#1242)](https://github.com/entropyxyz/entropy-core/pull/1242)
- Database encryption key backup / recovery feature for entropy-tss [(#1249)](https://github.com/entropyxyz/entropy-core/pull/1249)

### Changed
- Use correct key rotation endpoint in OCW ([#1104](https://github.com/entropyxyz/entropy-core/pull/1104))
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub use crate::{
errors::{ClientError, SubstrateError},
};
pub use entropy_protocol::{sign_and_encrypt::EncryptedSignedMessage, KeyParams};
pub use entropy_shared::{HashingAlgorithm, QuoteContext};
pub use entropy_shared::{attestation::QuoteContext, HashingAlgorithm};
use parity_scale_codec::Decode;
use rand::Rng;
use std::str::FromStr;
Expand Down
6 changes: 3 additions & 3 deletions crates/client/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
update_programs,
};

use entropy_shared::{QuoteContext, QuoteInputData};
use entropy_shared::attestation::{QuoteContext, QuoteInputData};
use entropy_testing_utils::{
constants::{TEST_PROGRAM_WASM_BYTECODE, TSS_ACCOUNTS, X25519_PUBLIC_KEYS},
helpers::{encode_verifying_key, spawn_tss_nodes_and_start_chain},
Expand Down Expand Up @@ -129,7 +129,7 @@ async fn test_change_threshold_accounts() {
let encoded_pck = encode_verifying_key(&pck.verifying_key()).unwrap().to_vec();

let quote = {
let input_data = entropy_shared::QuoteInputData::new(
let input_data = QuoteInputData::new(
tss_public_key,
*x25519_public_key.as_bytes(),
nonce,
Expand Down Expand Up @@ -368,7 +368,7 @@ async fn test_set_session_key_and_declare_validate() {
let encoded_pck = encode_verifying_key(&pck.verifying_key()).unwrap().to_vec();

let quote = {
let input_data = entropy_shared::QuoteInputData::new(
let input_data = QuoteInputData::new(
tss_public_key,
*x25519_public_key.as_bytes(),
nonce,
Expand Down
2 changes: 0 additions & 2 deletions crates/kvdb/src/encrypted_sled/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,3 @@
//! Constants for [encrypted_sled](crate::encrypted_sled)
pub(super) const PASSWORD_VERIFICATION_KEY: &str = "verification_key";
pub(super) const PASSWORD_VERIFICATION_VALUE: &str = "verification_value";
pub(super) const PASSWORD_SALT_KEY: &[u8] = b"password_salt_key";
pub(super) const UNSAFE_PASSWORD: &str = "entropy_unsafe_password";
37 changes: 2 additions & 35 deletions crates/kvdb/src/encrypted_sled/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
//! to be inserted, forming a [EncryptedRecord]:<encrypted value, nonce>. The nonce is later
//! used to decrypt and retrieve the originally inserted value.

use std::convert::TryInto;

use chacha20poly1305::{
self,
aead::{AeadInPlace, NewAead},
Expand All @@ -32,7 +30,6 @@ use zeroize::Zeroize;

use super::{
constants::*,
password::{Password, PasswordSalt},
record::EncryptedRecord,
result::{EncryptedDbError::*, EncryptedDbResult},
};
Expand All @@ -48,26 +45,13 @@ impl EncryptedDb {
/// Creates an XChaCha20 stream cipher from a password-based-key-derivation-function and
/// verifies that the password is valid.
/// See [super::Password] for more info on pdkdf.
pub fn open<P>(db_name: P, password: Password) -> EncryptedDbResult<Self>
pub fn open<P>(db_name: P, mut key: [u8; 32]) -> EncryptedDbResult<Self>
where
P: AsRef<std::path::Path>,
{
let kv = sled::open(db_name).map_err(CorruptedKv)?;

let password_salt: PasswordSalt = if kv.was_recovered() {
// existing kv: get the existing password salt
kv.get(PASSWORD_SALT_KEY)?.ok_or(MissingPasswordSalt)?.try_into()?
} else {
// new kv: choose a new password salt and store it
let mut password_salt = [0u8; 32];
rand::thread_rng().fill_bytes(&mut password_salt);
kv.insert(PASSWORD_SALT_KEY, &password_salt)?;
password_salt.into()
};

// zeroize key since we are no longer using it after creating cipher
let mut key = Self::chacha20poly1305_kdf(password, password_salt)?;
let cipher = XChaCha20Poly1305::new(&key);
let cipher = XChaCha20Poly1305::new(&key.into());
key.zeroize();

let encrypted_db = EncryptedDb { kv, cipher };
Expand All @@ -84,23 +68,6 @@ impl EncryptedDb {
Ok(encrypted_db)
}

fn chacha20poly1305_kdf(
password: Password,
salt: PasswordSalt,
) -> EncryptedDbResult<chacha20poly1305::Key> {
let mut output = chacha20poly1305::Key::default();

// default params: log_n = 15, r = 8, p = 1
scrypt::scrypt(
password.as_ref(),
salt.as_ref(),
&scrypt::Params::default(),
output.as_mut_slice(),
)?;

Ok(output)
}

/// get a new random nonce to use for value encryption using [rand::thread_rng]
fn generate_nonce() -> chacha20poly1305::XNonce {
let mut bytes = chacha20poly1305::XNonce::default();
Expand Down
5 changes: 0 additions & 5 deletions crates/kvdb/src/encrypted_sled/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@

mod constants;
mod kv;
mod password;
mod record;
mod result;

// match the API of sled
pub use kv::EncryptedDb as Db;
pub use password::{Password, PasswordMethod, PasswordSalt};
pub use result::{EncryptedDbError as Error, EncryptedDbResult as Result};

#[cfg(test)]
mod tests;

#[cfg(test)]
pub use tests::get_test_password;
91 changes: 0 additions & 91 deletions crates/kvdb/src/encrypted_sled/password.rs

This file was deleted.

37 changes: 12 additions & 25 deletions crates/kvdb/src/encrypted_sled/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,17 @@

use serial_test::serial;

use super::{kv::EncryptedDb, Password};
use super::kv::EncryptedDb;
use crate::{clean_tests, encrypted_sled::Db, get_db_path};

fn setup_db(require_password: bool) -> Db {
let db = if !require_password {
EncryptedDb::open(get_db_path(true), get_test_password())
} else {
EncryptedDb::open(get_db_path(true), Password::from("super-secret password."))
};
assert!(db.is_ok());
db.unwrap()
fn setup_db(key: [u8; 32]) -> Db {
EncryptedDb::open(get_db_path(true), key).unwrap()
}

#[test]
#[serial]
fn test_encrypted_sled() {
let db = setup_db(false);
let db = setup_db([1; 32]);

// insert <key: value> -> returns None
let res = db.insert("key", "value").unwrap();
Expand Down Expand Up @@ -73,36 +67,33 @@ fn test_encrypted_sled() {

#[test]
#[serial]
fn test_use_existing_salt() {
let db = setup_db(false);
fn test_use_existing_key() {
let db = setup_db([1; 32]);
let db_path = get_db_path(true);
drop(db);
// open existing db
assert!(EncryptedDb::open(db_path, get_test_password()).is_ok());
assert!(EncryptedDb::open(db_path, [1; 32]).is_ok());
clean_tests();
}

#[test]
#[serial]
fn test_password() {
let db = setup_db(true);
fn test_key() {
let db = setup_db([1; 32]);
let db_path = get_db_path(true);

drop(db);

// try to open the kv store using a different password
let db = EncryptedDb::open(
db_path,
Password::from("super-secret password!"), // replace '.' with '!'
);
// try to open the kv store using a different key
let db = EncryptedDb::open(db_path, [2; 32]);
assert!(matches!(db, Err(super::result::EncryptedDbError::WrongPassword)));
clean_tests();
}

#[test]
#[serial]
fn test_large_input() {
let db = setup_db(false);
let db = setup_db([1; 32]);

let large_value = vec![0; 100000];
let res = db.insert("key", large_value.clone()).unwrap();
Expand All @@ -112,7 +103,3 @@ fn test_large_input() {
assert_eq!(res, Some(sled::IVec::from(large_value)));
clean_tests();
}

pub fn get_test_password() -> Password {
crate::encrypted_sled::PasswordMethod::NoPassword.execute().unwrap()
}
17 changes: 7 additions & 10 deletions crates/kvdb/src/kv_manager/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use super::{
KeyReservation, DEFAULT_KV_NAME, DEFAULT_KV_PATH,
},
};
use crate::encrypted_sled::{self, Password};
use crate::encrypted_sled;

#[derive(Clone)]
pub struct Kv<V> {
Expand All @@ -44,22 +44,22 @@ where
{
/// Creates a new kv service. Returns [InitErr] on failure.
/// the path of the kvstore is `root_path` + "/kvstore/" + `kv_name`
pub fn new(root_path: PathBuf, password: Password) -> KvResult<Self> {
pub fn new(root_path: PathBuf, key: [u8; 32]) -> KvResult<Self> {
let kv_path = root_path.join(DEFAULT_KV_PATH).join(DEFAULT_KV_NAME);
// use to_string_lossy() instead of to_str() to avoid handling Option<&str>
let kv_path = kv_path.to_string_lossy().to_string();
Self::with_db_name(kv_path, password)
Self::with_db_name(kv_path, key)
}

/// Creates a kvstore at `full_db_name` and spawns a new kv_manager. Returns [InitErr] on
/// failure. `full_db_name` is the name of the path of the kvstrore + its name
/// Example: ~/entropy/kvstore/database_1
pub fn with_db_name(full_db_name: String, password: Password) -> KvResult<Self> {
pub fn with_db_name(full_db_name: String, encryption_key: [u8; 32]) -> KvResult<Self> {
let (sender, rx) = mpsc::unbounded_channel();

// get kv store from db name before entering the kv_cmd_handler because
// it's more convenient to return an error from outside of a tokio::span
let kv = get_kv_store(&full_db_name, password)?;
let kv = get_kv_store(&full_db_name, encryption_key)?;

tokio::spawn(kv_cmd_handler(rx, kv));
Ok(Self { sender })
Expand Down Expand Up @@ -129,13 +129,10 @@ where
/// let my_db = get_kv_store(&"my_current_dir_db")?;
/// let my_db = get_kv_store(&"/tmp/my_tmp_bd")?;
#[tracing::instrument(skip_all, fields(db_name))]
pub fn get_kv_store(
db_name: &str,
password: Password,
) -> encrypted_sled::Result<encrypted_sled::Db> {
pub fn get_kv_store(db_name: &str, key: [u8; 32]) -> encrypted_sled::Result<encrypted_sled::Db> {
// create/open DB
tracing::debug!("Decrypting KV store");
let kv = encrypted_sled::Db::open(db_name, password)?;
let kv = encrypted_sled::Db::open(db_name, key)?;

// log whether the DB was newly created or not
if kv.was_recovered() {
Expand Down
Loading
Loading