From 48e18ba4db398802b522ade90c3adde2fc832cfd Mon Sep 17 00:00:00 2001 From: driemworks Date: Thu, 21 Nov 2024 10:11:32 -0600 Subject: [PATCH] feat: upgrade to latest timelock library, add more docs, cleanup --- Cargo.lock | 42 +++++++++++++------------- core/Cargo.toml | 8 ++--- core/README.md | 75 ++++++++++++++++++++++++++++++++++++++++++++-- core/src/murmur.rs | 31 +++++++++++++------ core/src/types.rs | 2 +- lib/README.md | 2 +- lib/src/lib.rs | 14 +++------ 7 files changed, 125 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9ceef2..cf9d5a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2533,7 +2533,7 @@ dependencies = [ "rand_core", "serde", "sha3", - "tle", + "timelock", "totp-rs", "w3f-bls", "zeroize", @@ -4880,24 +4880,9 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tle" -version = "0.1.0" -source = "git+https://github.com/ideal-lab5/tle.git?branch=feat/tle-monorepo#1a50d569cdda608df56d4951b7141e43cc067cef" +name = "timelock" +version = "0.0.1" +source = "git+https://github.com/ideal-lab5/tle.git#6420b2231f0fb22d05fb0783d74d8394330d0ae1" dependencies = [ "aes-gcm", "ark-bls12-377", @@ -4922,6 +4907,21 @@ dependencies = [ "w3f-bls", ] +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.40.0" @@ -5274,9 +5274,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "w3f-bls" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7335e4c132c28cc43caef6adb339789e599e39adbe78da0c4d547fad48cbc331" +checksum = "9c5da5fa2c6afa2c9158eaa7cd9aee249765eb32b5fb0c63ad8b9e79336a47ec" dependencies = [ "ark-bls12-377", "ark-bls12-381", diff --git a/core/Cargo.toml b/core/Cargo.toml index 8e43997..e57271c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,14 +21,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] totp-rs = { version = "5.5.1", default-features = false, optional = true } codec = { package = "parity-scale-codec", version = "3.6.12", features = ["derive"], default-features = false } -tle = { package = "tle", default-features = false, git = "https://github.com/ideal-lab5/tle.git", branch = "feat/tle-monorepo"} +timelock = { git = "https://github.com/ideal-lab5/tle.git", default-features = false } ckb-merkle-mountain-range = { version = "0.5.2", default-features = false } sha3 = { version = "0.10.8", default-features = false } serde = { version = "1.0.188", features = ["alloc", "derive"], default-features = false } ark-bls12-377 = { version = "0.4.0", default-features = false } ark-std = { version = "0.4.0", default-features = false } ark-serialize = { version = "0.4.0", default-features = false } -w3f-bls = { version = "=0.1.3", default-features = false } +w3f-bls = { version = "0.1.4", default-features = false } zeroize = { version = "1.8.1", default-features = false } ark-ec = { version = "0.4", default-features = false } ark-ff = { version = "0.4", default-features = false } @@ -51,10 +51,10 @@ std = [ "dleq_vrf/std", "w3f-bls/std", "serde/std", + "ckb-merkle-mountain-range/std", "codec/std", - "tle/std", + "timelock/std", "sha3/std", - "ckb-merkle-mountain-range/std", "zeroize/std", ] no_std = [] diff --git a/core/README.md b/core/README.md index b058f88..3a2373e 100644 --- a/core/README.md +++ b/core/README.md @@ -1,6 +1,77 @@ # Murmur Core -This library contains the core implementation of the murmur protocol. This implementation can support both BLS12-377 and BLS12-381, but is left curve-agnostic, only expecting that the beacon is produced by an ETF-PFG instance. +This library contains the core implementation of the murmur protocol. This implementation can support both BLS12-377 and BLS12-381, but is left curve-agnostic. This crate can support the randomness beacon produced by the [Ideal Network](https://idealabs.network) as well as [Drand](https://drand.love)'s Quicknet. In general, this library is intended to work with a blockchain whose runtime includes the corresponding [Murmur Pallet](https://github.com/ideal-lab5/idn-sdk/tree/main/pallets/murmur). More specifcially, it is intended to run against the [Ideal Network](https://idealabs.network). For examples of usage against a real network, refer to the [CLI](../lib/src/bin/murmur/main.rs). + +## Usage + +### Creation and Execution + +#### Create a Murmur Store + +``` rust +use ark_serialize::CanonicalDeserialize; +use ark_std::rand::SeedableRng; +use rand_chacha::ChaCha20Rng; +use rand_core::OsRng; +use w3f_bls::{DoublePublicKeyScheme, KeypairVT, TinyBLS377}; + +// This simulates the production of a randomness beacon public key +// In practice, this would be fetched from the beacon (e.g. as a hex string) and must be deseraialized +let keypair = KeypairVT::::generate(&mut rng); +let double_public: DoublePublicKey = + DoublePublicKey(keypair.into_public_key_in_signature_group().0, keypair.public.0); + +// The 'lifetime' of the Murmur wallet for the given session +let block_schedule: &[BlockNumber] = + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + +// This is your 'secret' seed, a short password used while constructing OTP codes +let seed = vec![1, 2, 3]; + +// The nonce functions similarly to nonce's for standard accounts, except instead of updating on a +// "per transaction" basis, it only updates on a "per session" basis +// +let nonce = 0; + +let murmur_store = MurmurStore::::new::< + DummyIdBuilder, + OsRng, + ChaCha20Rng, +>(seed.clone(), block_schedule.to_vec(), nonce, double_public, &mut rng) +.unwrap(); +``` +#### Update a Murmur Store + +Updating a Murmur store is done by calling the same new function as above and using the 'next' nonce in the + +``` rust +// update the nonce +let nonce = 1; + +let murmur_store = MurmurStore::::new::< + DummyIdBuilder, + OsRng, + ChaCha20Rng, +>(seed.clone(), block_schedule.to_vec(), nonce, double_public, &mut rng) +.unwrap(); +``` + +#### Prepare Execution Parameters + +``` rust +``` + +### Verification + +#### Verify Updates + +``` rust +``` + +#### Verify Execution Parameters + +``` rust +``` ## Build @@ -33,8 +104,6 @@ cargo test --features "client" ## Future Work/Notes - **OTPAuth Feature**: There is an 'otpauth' feature that can be enabled on the totp lib. It allows for the inclusion of an issuer and account_name. We can investigate usage of this in the future. [TOTP Library Reference](https://github.com/constantoine/totp-rs/blob/da78569b0c233adbce126dbe0c35452340fd3929/src/lib.rs#L160) -- **Wallet Update logic**: Each murmur wallet is ephemeral, since any MMR must be limited in size. We can use a zkp to prove knowledge of the seed in order to allow the wallet owner to update the wallet by providing a new MMR root. - ## Contributing Contributions are welcome! Please open an issue or submit a pull request. diff --git a/core/src/murmur.rs b/core/src/murmur.rs index 97cecc0..4069111 100644 --- a/core/src/murmur.rs +++ b/core/src/murmur.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -//! The murmur protocol implementation +//! The Murmur protocol implementation use alloc::{collections::BTreeMap, vec, vec::Vec}; #[cfg(feature = "client")] @@ -40,12 +40,12 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use dleq_vrf::{EcVrfVerifier, PublicKey, SecretKey}; use sha3::Digest; -use tle::{ +use timelock::{ ibe::fullident::Identity, stream_ciphers::{AESGCMStreamCipherProvider, AESOutput, StreamCipherProvider}, tlock::*, }; -use w3f_bls::{DoublePublicKey, EngineBLS}; +use w3f_bls::DoublePublicKey; /// The base Murmur protocol label pub const MURMUR_PROTO: &[u8] = b"Murmur://"; @@ -63,6 +63,7 @@ pub enum Error { ExecuteError, /// An error occurred when creating a murmur wallet MMRError, + /// Some data in the murmur store is corrupted InconsistentStore, /// No leaf could be identified in the MMR at the specified position NoLeafFound, @@ -92,7 +93,8 @@ pub trait ProtocolEngine { /// The supported protocols #[derive(Clone, serde::Serialize, serde::Deserialize, Encode, Decode)] pub enum ProtocolId { - /// small signatures, SignatureGroup = G1 (48 bytes), PublicKeyGroup = G2 (96 bytes) + /// A curve config with small signatures and large pubkyes + /// SignatureGroup = G1 (48 bytes), PublicKeyGroup = G2 (96 bytes) TinyBLS377, } @@ -127,12 +129,22 @@ pub struct MurmurStore { #[cfg(feature = "client")] impl MurmurStore

{ - /// Create a new Murmur store + /// Create a new Murmur store. + /// + /// This function allows for two separate RNGs to be specified. In general, the first RNG type R + /// should be created externally from this function and passed as an argument. + /// In practice, this should probably be the OsRng or something similar. + /// The second type of RNG, S, must be seedable from [u8;32]. This RNG is instantiated within + /// the function, where we seed a new RNG each time we encrypt a new OTP code using timelock + /// encryption. /// /// * `seed`: An any-length seed (i.e. password) /// * `block_schedule`: The blocks for which OTP codes will be generated - /// * `ephemeral_msk`: Any 32 bytes + /// * `nonce`: A value representing the 'number of times' the Murmur wallet has been created or updated. + /// Should be monotonically increasing with each subsequent call. /// * `round_public_key`: The IDN beacon's public key + /// * `rng`: An instance of an CPRNG of type `R` + pub fn new, R, S>( mut seed: Vec, block_schedule: Vec, @@ -168,6 +180,7 @@ impl MurmurStore

{ let mut witness: [u8; 32] = transcript.clone().witness(rng).read_byte_array(); let totp = BOTPGenerator::new(witness.to_vec()).map_err(|_| Error::InvalidSeed)?; + // drop secret data witness.zeroize(); challenge.zeroize(); secret_key.zeroize(); @@ -403,8 +416,8 @@ pub mod verifier { /// Verify the correctness of execution parameters by checking that the Merkle proof, `Proof`, /// and hash `H` are valid. The function outputs true if both conditions are true: /// - /// 1. Proof.Verify(root, [(pos, Leaf(ciphertext))]) - /// 2. H == Sha256(otp || aux_data) + /// 1. Proof.Verify(root, [(pos, Leaf(ciphertext))]) + /// 2. H == Sha256(otp || aux_data) //// /// It outputs false otherwise. /// @@ -489,7 +502,7 @@ mod tests { pub struct DummyIdBuilder; impl IdentityBuilder for DummyIdBuilder { fn build_identity(at: BlockNumber) -> Identity { - Identity::new(&[at as u8]) + Identity::new(b"", vec![vec![at as u8]]) } } diff --git a/core/src/types.rs b/core/src/types.rs index abdb059..ff76d62 100644 --- a/core/src/types.rs +++ b/core/src/types.rs @@ -19,7 +19,7 @@ use ckb_merkle_mountain_range::{Merge, Result as MMRResult}; use codec::{Decode, Encode}; use sha3::Digest; -pub use tle::ibe::fullident::Identity; +pub use timelock::ibe::fullident::Identity; /// The type to represent a block number pub type BlockNumber = u32; diff --git a/lib/README.md b/lib/README.md index d7312b4..7c53341 100644 --- a/lib/README.md +++ b/lib/README.md @@ -32,7 +32,7 @@ To generate a wallet valid for the next 1000 blocks, use: To send a balance transfer, use: ```shell -./target/debug/murmur execute --name test --seed my_secret_key --to CuqfkE3QieYPAWPpwiygDufmyrKecDcVCF7PN1psaLEn8yr --amount 100_000_000 +./target/debug/murmur execute --name test --seed my_secret_key --to CuqfkE3QieYPAWPpwiygDufmyrKecDcVCF7PN1psaLEn8yr --amount 100000000 ``` ## Test diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 2d15679..87fedf2 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -116,17 +116,14 @@ pub fn prepare_execute( #[cfg(test)] mod tests { use super::*; - use rand_core::{OsRng, SeedableRng}; #[test] pub fn it_can_create_an_mmr_store_and_call_data() { let seed = b"seed".to_vec(); let block_schedule = vec![1, 2, 3, 4, 5, 6, 7]; let double_public_bytes = murmur_test_utils::get_dummy_beacon_pubkey(); - let mut rng = ChaCha20Rng::from_rng(&mut OsRng).unwrap(); let mmr_store = - create(seed.clone(), 0, block_schedule.clone(), double_public_bytes.clone(), &mut rng) - .unwrap(); + create(seed.clone(), 0, block_schedule.clone(), double_public_bytes.clone()).unwrap(); assert_eq!(mmr_store.root.0.len(), 32); assert_eq!(mmr_store.metadata.keys().len(), 7); @@ -137,9 +134,7 @@ mod tests { let seed = b"seed".to_vec(); let block_schedule = vec![1, 2, 3, 4, 5, 6, 7]; let double_public_bytes = murmur_test_utils::get_dummy_beacon_pubkey(); - let mut rng = ChaCha20Rng::from_rng(&mut OsRng).unwrap(); - let mmr_store = - create(seed.clone(), 0, block_schedule, double_public_bytes, &mut rng).unwrap(); + let mmr_store = create(seed.clone(), 0, block_schedule, double_public_bytes).unwrap(); let bob = subxt_signer::sr25519::dev::bob().public_key(); let balance_transfer_call = @@ -152,11 +147,10 @@ mod tests { let when = 1; - let proxy_data = - prepare_execute(seed.clone(), when, mmr_store.clone(), &balance_transfer_call).unwrap(); + let proxy_data = prepare_execute(seed, when, mmr_store, &balance_transfer_call).unwrap(); assert_eq!(proxy_data.position, 0); assert_eq!(proxy_data.hash.len(), 32); - assert_eq!(proxy_data.ciphertext.len(), 266); + assert_eq!(proxy_data.ciphertext.len(), 250); } }