Skip to content

Commit

Permalink
Merge pull-request #491
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-kearney committed Nov 15, 2024
2 parents 37da3ac + 5b344df commit 12d4113
Show file tree
Hide file tree
Showing 29 changed files with 864 additions and 346 deletions.
224 changes: 175 additions & 49 deletions src/Cargo.lock

Large diffs are not rendered by default.

206 changes: 176 additions & 30 deletions src/init/Cargo.lock

Large diffs are not rendered by default.

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
041d1f67e987e6f4c742c1f35c07f1184e950e534e12db50ff468688471aee48def99ebf3fbbbd2e72040627fa6c948adef6caaa8ceb862a5eb655eb6b674bbf21042a5e726d5e85f865599c26949fc672d2660c89824a6d9cecc8a7e8679dd89e56e3f506b35bfaddfb2c0a5f5aad256040160269c0f3cff72737348b97cb44dfe9
Binary file not shown.
1 change: 1 addition & 0 deletions src/integration/fixtures/preprod/evm-parser/quorum_key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0448c07c3724c9a4fa9b002f12cf326136433625b27f35da8d992a4f4b5688627fbf3705da7042d1bb8c74b719d62f89c889bb45876efcc7faced3f28f5e54969804545f5da84824570e3710dc88c0f1303db91e4b7ff0ee17fe41c66c954d8bcddc4f74910117620565061c7e84a05d13b681e75c90d7ddc0b369a7000cef85367c
Binary file not shown.
1 change: 1 addition & 0 deletions src/integration/fixtures/preprod/notarizer/quorum_key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
04bff189bf2177cca7618f09acef5129064d43956d334bedf61cd117c996e5c17f66937dc07f0f18c7d47d2af937e1bd56bd10f71e513dc8d1d218cc39fdeaae3304c849c5e8bb24962b8339cf566ce6853faa754cffa3886919defde706d875d27903808b53677813d27dd1bc985c1d38d21181568adb2ae3c8a7789e9e37e6577f
1 change: 1 addition & 0 deletions src/integration/fixtures/preprod/old_dev.secret.keep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
79d88e2069d49dd24452b39cf17eb07581eb5350aa55e918cede5e4e7f171c70
Binary file not shown.
1 change: 1 addition & 0 deletions src/integration/fixtures/preprod/signer/quorum_key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
048e92f6cdcc0b375505980a298d9b79201db1f08b1f135360d2864af1a67186ec0dbeb570d396a456226b0844be93dbc0180abbf7e2e4c9cfde8d5da4e3f8a49004f3422b8afbe425d6ece77b8d2469954715a2ff273ab7ac89f1ed70e0a9325eaa1698b4351fd1b23734e65c0b6a86b62dd49d70b37c94606aac402cbd84353212
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
04603e5bd2e15714e162b5adad657cc0bd26b8d92a1c8d255b148911a834ac1be5c9a76720aac5ec80a49b6b26c48c4bcecf828f9986e1550c829250566ae14fa40445a0e4b7e2672556492c493b2cc0b9894bee5ca4ee37bae0b717930b0e970cd4922417c326c348a669863c4385dd0676cc8f53c8b6f3692e6c31481ac022735d
Binary file not shown.
1 change: 1 addition & 0 deletions src/integration/fixtures/preprod/ump/quorum_key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
04296d80de8593982b0a70a8cc03f4ae664433df5e091c5555d6f49311b4dba1f5c8073565f6b1f2a17ad274b0e7f50f826431f3d5e47ad1cb5e64ea893ce6409f04044ece64c3cea982c7859398ad7d79462d8e35db1173deac68fe173e9585b8acdd43c24902e4e965f6f45f4ba37ce15394e863d4a20c2094ddeea573c66d3554
2 changes: 2 additions & 0 deletions src/integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub const LOCAL_HOST: &str = "127.0.0.1";
pub const PCR3: &str = "78fce75db17cd4e0a3fb8dad3ad128ca5e77edbb2b2c7f75329dccd99aa5f6ef4fc1f1a452e315b9e98f9e312e6921e6";
/// QOS dist directory.
pub const QOS_DIST_DIR: &str = "./mock/dist";
/// Mock pcr3 pre-image.
pub const PCR3_PRE_IMAGE_PATH: &str = "./mock/namespaces/pcr3-preimage.txt";

const MSG: &str = "msg";

Expand Down
9 changes: 5 additions & 4 deletions src/integration/tests/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use std::{

use borsh::de::BorshDeserialize;
use integration::{
LOCAL_HOST, PIVOT_OK2_PATH, PIVOT_OK2_SUCCESS_FILE, QOS_DIST_DIR,
LOCAL_HOST, PCR3_PRE_IMAGE_PATH, PIVOT_OK2_PATH, PIVOT_OK2_SUCCESS_FILE,
QOS_DIST_DIR,
};
use qos_core::protocol::{
services::{
Expand Down Expand Up @@ -81,7 +82,7 @@ async fn standard_boot_e2e() {
"--qos-release-dir",
QOS_DIST_DIR,
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--manifest-path",
&cli_manifest_path,
"--pivot-args",
Expand Down Expand Up @@ -157,7 +158,7 @@ async fn standard_boot_e2e() {
"--manifest-approvals-dir",
&*boot_dir,
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--pivot-hash-path",
PIVOT_HASH_PATH,
"--qos-release-dir",
Expand Down Expand Up @@ -361,7 +362,7 @@ async fn standard_boot_e2e() {
"--manifest-envelope-path",
&manifest_envelope_path,
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--manifest-set-dir",
"./mock/keys/manifest-set",
"--alias",
Expand Down
9 changes: 6 additions & 3 deletions src/integration/tests/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
};

use borsh::de::BorshDeserialize;
use integration::{LOCAL_HOST, QOS_DIST_DIR};
use integration::{LOCAL_HOST, PCR3_PRE_IMAGE_PATH, QOS_DIST_DIR};
use qos_core::protocol::services::genesis::GenesisOutput;
use qos_crypto::{sha_512, shamir::shares_reconstruct};
use qos_nsm::nitro::unsafe_attestation_doc_from_der;
Expand Down Expand Up @@ -153,7 +153,7 @@ async fn genesis_e2e() {
"--qos-release-dir",
QOS_DIST_DIR,
"--pcr3-preimage-path",
"./mock/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--dr-key-path",
DR_KEY_PUBLIC_PATH,
"--unsafe-skip-attestation"
Expand Down Expand Up @@ -199,7 +199,10 @@ async fn genesis_e2e() {
// Try recovering from a random permutation
decrypted_shares.shuffle(&mut thread_rng());
let master_secret: [u8; qos_p256::MASTER_SEED_LEN] =
shares_reconstruct(&decrypted_shares[0..threshold]).try_into().unwrap();
shares_reconstruct(&decrypted_shares[0..threshold])
.unwrap()
.try_into()
.unwrap();
let reconstructed = P256Pair::from_master_seed(&master_secret).unwrap();
assert!(
reconstructed.public_key()
Expand Down
12 changes: 7 additions & 5 deletions src/integration/tests/key.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::{fs, process::Command};

use integration::{LOCAL_HOST, PIVOT_LOOP_PATH, QOS_DIST_DIR};
use integration::{
LOCAL_HOST, PCR3_PRE_IMAGE_PATH, PIVOT_LOOP_PATH, QOS_DIST_DIR,
};
use qos_crypto::sha_256;
use qos_p256::{P256Pair, P256Public};
use qos_test_primitives::{ChildWrapper, PathWrapper};
Expand Down Expand Up @@ -158,7 +160,7 @@ fn generate_manifest_envelope() {
"--restart-policy",
"always",
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--pivot-hash-path",
PIVOT_HASH_PATH,
"--qos-release-dir",
Expand Down Expand Up @@ -196,7 +198,7 @@ fn generate_manifest_envelope() {
"--manifest-approvals-dir",
BOOT_DIR,
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--pivot-hash-path",
PIVOT_HASH_PATH,
"--qos-release-dir",
Expand Down Expand Up @@ -293,7 +295,7 @@ fn boot_old_enclave(old_host_port: u16) -> (ChildWrapper, ChildWrapper) {
"--host-ip",
LOCAL_HOST,
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--unsafe-skip-attestation",
])
.spawn()
Expand Down Expand Up @@ -343,7 +345,7 @@ fn boot_old_enclave(old_host_port: u16) -> (ChildWrapper, ChildWrapper) {
"--manifest-envelope-path",
MANIFEST_ENVELOPE_PATH,
"--pcr3-preimage-path",
"./mock/namespaces/pcr3-preimage.txt",
PCR3_PRE_IMAGE_PATH,
"--manifest-set-dir",
"./mock/keys/manifest-set",
"--alias",
Expand Down
169 changes: 169 additions & 0 deletions src/integration/tests/preprod_sharding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//! Utility script to reshard old dev shares into 2 pieces.
//! This lets us test "true" quorum settings instead of relying on a 1-out-of-1
//! quorum.
use std::{
fs,
path::Path,
process::Command,
time::{SystemTime, UNIX_EPOCH},
};

use qos_crypto::shamir::shares_generate;
use qos_p256::{
derive_secret, encrypt::P256EncryptPair, P256Pair, P256_ENCRYPT_DERIVE_PATH,
};

// Note: the dev secret can also be found in our keys repo
// (tkhq/keys:deployment/preprod/evm-parser/manifest-set/dev.secret)
// This secret is not security sensitive since it belongs to our dev/preprod
// environment I've also chosen to commit the old encrypted shares and quorum
// public keys because they're useful anchors for this tests. The quorum public
// keys and old dev shares for each enclaves are committed in
// ./fixtures/preprod/$ENCLAVE_NAME/
const OLD_DEV_SECRET_PATH: &str = "./fixtures/preprod/old_dev.secret.keep";

#[test]
fn preprod_reshard_ceremony() {
// Global setup: our test will write to a new folder in `/tmp`
let unix_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let tmp_dir =
format!("/tmp/preprod-reshard-{}", unix_timestamp.as_millis());
fs::create_dir_all(&tmp_dir).unwrap();

let tmp_path = |file: &str| -> String { format!("{}/{file}", tmp_dir) };

let dev_users_dir = tmp_path("dev-users");
fs::create_dir_all(dev_users_dir.clone()).unwrap();

let enclaves_dir = tmp_path("enclaves");
fs::create_dir_all(enclaves_dir.clone()).unwrap();

let user_dir = |user: &str| format!("{}/{}", dev_users_dir, user);
let enclave_dir = |enclave: &str| format!("{}/{}", enclaves_dir, enclave);
let get_key_paths =
|user: &str| (format!("{user}.secret"), format!("{user}.pub"));

let user1 = "1";
let (user1_private_path, user1_public_path) = get_key_paths(user1);

let user2 = "2";
let (user2_private_path, user2_public_path) = get_key_paths(user2);

// Generate user directories and keys
for (user, private, public) in [
(&user1, &user1_private_path, &user1_public_path),
(&user2, &user2_private_path, &user2_public_path),
] {
fs::create_dir_all(user_dir(user)).unwrap();

let master_seed_path = format!("{}/{}", user_dir(user), private);
let public_path = format!("{}/{}", user_dir(user), public);
assert!(Command::new("../target/debug/qos_client")
.args([
"generate-file-key",
"--master-seed-path",
&master_seed_path,
"--pub-path",
&public_path,
])
.spawn()
.unwrap()
.wait()
.unwrap()
.success());

// Assert both public and private key paths now exist
assert!(Path::new(&*user_dir(user)).join(public).is_file());
assert!(Path::new(&*user_dir(user)).join(private).is_file());
}

// Load previous dev secret (1/1 setting)
let dev_secret_utf8_bytes = fs::read(OLD_DEV_SECRET_PATH).unwrap();
let dev_secret_hex_bytes =
qos_hex::decode(std::str::from_utf8(&dev_secret_utf8_bytes).unwrap())
.unwrap();
let dev_key = P256Pair::from_master_seed(
&dev_secret_hex_bytes.clone().try_into().unwrap(),
)
.unwrap();

// For each of the enclaves...
for enclave_name in
["ump", "evm-parser", "notarizer", "signer", "tls-fetcher", "deploy-test"]
{
// Decrypt the old dev share and assert that the resulting quorum key
// has the right public key. Decrypted dev shares are _basically_ master
// seeds. They're just have a "01" prefix because it's the one and only
// "share" in a 1/1 SSS sharing.
let encrypted_old_dev_share = fs::read(format!(
"./fixtures/preprod/{}/old_dev.share.keep",
enclave_name
))
.unwrap();
let mut decrypted_dev_share =
dev_key.decrypt(&encrypted_old_dev_share).unwrap();
let removed_byte = decrypted_dev_share.remove(0);
assert_eq!(removed_byte, 1);

let pk = P256Pair::from_master_seed(
&decrypted_dev_share.clone().try_into().unwrap(),
)
.unwrap()
.public_key();
let expected_quorum_public_key = fs::read(format!(
"./fixtures/preprod/{}/quorum_key.pub",
enclave_name
))
.unwrap();
assert_eq!(
qos_hex::encode(&pk.to_bytes()),
std::str::from_utf8(&expected_quorum_public_key).unwrap()
);

// Now we have the proper quorum key, we're ready to shard it in two
// pieces, to our two new users.
let new_shares = shares_generate(&decrypted_dev_share, 2, 2).unwrap(); // (threshold, total)
assert_eq!(new_shares.len(), 2);

for (user, share) in
[(&user1, &new_shares[0]), (&user2, &new_shares[1])]
{
// Load the key pair for this user
let user_secret_path =
format!("{}/{}.secret", user_dir(user), user);
let user_key_pair =
P256Pair::from_hex_file(user_secret_path.clone()).unwrap();
// Encrypt the new share to it
let encrypted_share =
user_key_pair.public_key().encrypt(share).unwrap();

// And write the resulting file
fs::create_dir_all(enclave_dir(enclave_name)).unwrap();
let encrypted_share_path =
format!("{}/{}.share", enclave_dir(enclave_name), user);
fs::write(encrypted_share_path.clone(), encrypted_share).unwrap();

// Just to make sure: can the user decrypt the share we just created
// with their secret?
assert_can_decrypt(user_secret_path, encrypted_share_path);
}
}

println!("success, reshard complete. Outputs are in {}", tmp_dir);
}

// Helper function to assert a given user secret (1st arg) can decrypt a share
// (2nd arg)
fn assert_can_decrypt(user_secret_path: String, sharepath: String) {
let share = fs::read(sharepath).unwrap();
let master_seed_hex_bytes = fs::read(user_secret_path).unwrap();
let master_seed_utf8 = std::str::from_utf8(&master_seed_hex_bytes).unwrap();
let master_seed = qos_hex::decode(master_seed_utf8).unwrap();
let encryption_pair_secret = derive_secret(
&master_seed.try_into().unwrap(),
P256_ENCRYPT_DERIVE_PATH,
)
.unwrap();
let pair = P256EncryptPair::from_bytes(&encryption_pair_secret).unwrap();
assert!(pair.decrypt(&share).is_ok());
}
Loading

0 comments on commit 12d4113

Please sign in to comment.