diff --git a/Cargo.lock b/Cargo.lock index ea76bc256..c46833c0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,7 +451,6 @@ dependencies = [ "protobuf 2.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "ra-client 0.5.0", - "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "secp256k1zkp 0.13.0 (git+https://github.com/crypto-com/rust-secp256k1-zkp.git?rev=f8759809f6e3fed793b37166f7cd91c57cdb2eab)", "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", @@ -478,14 +477,17 @@ dependencies = [ "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fixed 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mls 0.5.0", "parity-scale-codec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ra-client 0.5.0", "secp256k1zkp 0.13.0 (git+https://github.com/crypto-com/rust-secp256k1-zkp.git?rev=f8759809f6e3fed793b37166f7cd91c57cdb2eab)", "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "sgx_tstd 1.1.2 (git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.2)", "sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "test-common 0.5.0", "thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -616,6 +618,7 @@ dependencies = [ "secstr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "test-common 0.5.0", "tiny-bip39 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -719,6 +722,7 @@ dependencies = [ "secp256k1zkp 0.13.0 (git+https://github.com/crypto-com/rust-secp256k1-zkp.git?rev=f8759809f6e3fed793b37166f7cd91c57cdb2eab)", "secstr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "tendermint 0.12.0-rc0 (git+https://github.com/crypto-com/tendermint-rs.git?rev=e8d350960726b242fdaa67d293d71ba8cfdb8024)", + "test-common 0.5.0", ] [[package]] @@ -756,6 +760,7 @@ dependencies = [ "secstr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "test-common 0.5.0", "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1069,6 +1074,7 @@ dependencies = [ "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "test-common 0.5.0", ] [[package]] @@ -2858,7 +2864,7 @@ version = "0.5.0" dependencies = [ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "der-parser 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ra-common 0.5.0", "rustls 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/chain-abci/Cargo.toml b/chain-abci/Cargo.toml index 24a72e0d1..d9ca29f57 100644 --- a/chain-abci/Cargo.toml +++ b/chain-abci/Cargo.toml @@ -37,7 +37,6 @@ structopt = "0.3" secp256k1zkp = { git = "https://github.com/crypto-com/rust-secp256k1-zkp.git", rev = "f8759809f6e3fed793b37166f7cd91c57cdb2eab", features = ["recovery", "endomorphism"] } parity-scale-codec = { features = ["derive"], version = "1.3" } thiserror = "1.0" -rustls = { version = "0.17", features = ["dangerous_configuration"] } [target.'cfg(target_os = "linux")'.dependencies] enclave-u-common = { path = "../chain-tx-enclave/enclave-u-common" } diff --git a/chain-abci/src/app/app_init.rs b/chain-abci/src/app/app_init.rs index fb5502f5c..661c5a845 100644 --- a/chain-abci/src/app/app_init.rs +++ b/chain-abci/src/app/app_init.rs @@ -31,7 +31,6 @@ use chain_storage::buffer::{ }; use chain_storage::jellyfish::{compute_staking_root, sum_staking_coins, StakingGetter, Version}; use chain_storage::{Storage, StoredChainState}; -use ra_client::EnclaveCertVerifier; /// ABCI app state snapshot #[derive(Serialize, Deserialize, Clone, Encode, Decode)] @@ -55,6 +54,8 @@ pub struct ChainNodeState { pub staking_version: Version, /// Record the sum of all the coins in UTxO set pub utxo_coins: Coin, + /// The biggest ISVSVN of keypackage so far + pub enclave_isv_svn: u16, /// The parts of states which involved in computing app_hash pub top_level: ChainState, @@ -98,6 +99,7 @@ impl ChainNodeState { max_evidence_age, staking_version: 0, utxo_coins: Coin::zero(), + enclave_isv_svn: 0, top_level: ChainState { account_root, rewards_pool, @@ -137,8 +139,6 @@ pub struct ChainNodeApp { pub rewards_pool_updated: bool, /// address of tx query enclave to supply to clients (if any) pub tx_query_address: Option, - /// Enclave certificate verifier - pub enclave_cert_verifier: EnclaveCertVerifier, /// consensus buffer of staking merkle trie storage pub staking_buffer: StakingBuffer, @@ -225,14 +225,14 @@ fn get_voting_power( } pub fn init_app_hash(conf: &InitConfig, genesis_time: Timespec) -> H256 { - let (accounts, rp, _nodes) = conf + let state = conf .validate_config_get_genesis(genesis_time) .expect("distribution validation error"); compute_app_hash( &MerkleTree::empty(), - &compute_staking_root(&accounts), - &rp, + &compute_staking_root(&state.accounts), + &state.rewards_pool, &NetworkParameters::Genesis(conf.network_params.clone()), ) } @@ -245,7 +245,6 @@ impl ChainNodeApp { chain_id: &str, storage: Storage, tx_query_address: Option, - enclave_cert_verifier: EnclaveCertVerifier, ) -> Self { let stored_genesis = storage.get_genesis_app_hash(); @@ -276,7 +275,6 @@ impl ChainNodeApp { tx_validator, rewards_pool_updated: false, tx_query_address, - enclave_cert_verifier, staking_buffer: HashMap::new(), mempool_staking_buffer: HashMap::new(), @@ -315,9 +313,6 @@ impl ChainNodeApp { let _ = start_zmq(_conn_str, chain_hex_id, storage.get_read_only()); } - let enclave_cert_verifier = EnclaveCertVerifier::new(Default::default()) - .expect("enclave cert verifier init failed"); - if let Some(data) = storage.get_last_app_state() { info!("last app state stored"); let mut last_state = @@ -365,7 +360,6 @@ impl ChainNodeApp { chain_id, storage, tx_query_address, - enclave_cert_verifier, ) } else { info!("no last app state stored"); @@ -390,7 +384,6 @@ impl ChainNodeApp { tx_validator, rewards_pool_updated: false, tx_query_address, - enclave_cert_verifier, staking_buffer: HashMap::new(), mempool_staking_buffer: HashMap::new(), @@ -426,7 +419,7 @@ impl ChainNodeApp { .get_seconds() .try_into() .expect("invalid genesis time"); - let (accounts, rp, nodes) = conf + let state = conf .validate_config_get_genesis(genesis_time) .expect("distribution validation error"); @@ -440,11 +433,11 @@ impl ChainNodeApp { } let network_params = NetworkParameters::Genesis(conf.network_params); - let new_account_root = self.storage.put_stakings(0, &accounts); + let new_account_root = self.storage.put_stakings(0, &state.accounts); let genesis_app_hash = compute_app_hash( &MerkleTree::empty(), &new_account_root, - &rp, + &state.rewards_pool, &network_params, ); @@ -454,19 +447,23 @@ impl ChainNodeApp { check_and_store_consensus_params( req.consensus_params.as_ref(), - &nodes, + &state.validators, &network_params, &mut self.storage, ); check_validators( - &nodes, + &state.validators, req.validators.clone().into_vec(), &conf.distribution, ) .expect("validators in genesis configuration are not consistent with app_state"); - let val_addresses = nodes.iter().map(|(addr, _)| *addr).collect::>(); + let val_addresses = state + .validators + .iter() + .map(|(addr, _)| *addr) + .collect::>(); let staking_table = StakingTable::from_genesis( &staking_getter!(self, 0), network_params.get_required_council_node_stake(), @@ -479,7 +476,7 @@ impl ChainNodeApp { genesis_time, max_evidence_age, new_account_root, - rp, + state.rewards_pool, network_params, staking_table, ); diff --git a/chain-abci/src/app/mod.rs b/chain-abci/src/app/mod.rs index f12281ca9..846b00526 100644 --- a/chain-abci/src/app/mod.rs +++ b/chain-abci/src/app/mod.rs @@ -404,9 +404,11 @@ fn generate_tx_staking_change_event(tx_action: TxAction) -> Option fee, .. } => Some(StakingEvent::Unbond(&unbond.0, unbond.1, unbonded_from, fee).into()), - TxPublicAction::NodeJoin(staking_address, council_node) => { - Some(StakingEvent::NodeJoin(&staking_address, council_node).into()) - } + TxPublicAction::NodeJoin { + address, + council_node, + .. + } => Some(StakingEvent::NodeJoin(&address, council_node).into()), TxPublicAction::Unjail(staking_address) => { Some(StakingEvent::Unjail(&staking_address).into()) } diff --git a/chain-abci/src/app/validate_tx.rs b/chain-abci/src/app/validate_tx.rs index 843bc400b..d4beae1f4 100644 --- a/chain-abci/src/app/validate_tx.rs +++ b/chain-abci/src/app/validate_tx.rs @@ -1,6 +1,8 @@ use super::{BufferType, ChainNodeApp, ChainNodeState}; use crate::enclave_bridge::EnclaveProxy; -use crate::storage::{process_public_tx, verify_enclave_tx, TxAction, TxEnclaveAction}; +use crate::storage::{ + process_public_tx, verify_enclave_tx, TxAction, TxEnclaveAction, TxPublicAction, +}; use crate::tx_error::TxError; use abci::*; use chain_core::tx::data::TxId; @@ -118,11 +120,18 @@ impl ChainNodeApp { let action = process_public_tx( &mut staking_store!(self, state.staking_version, buffer_type), &mut state.staking_table, - &self.enclave_cert_verifier, + state.enclave_isv_svn, &extra_info, &tx, )?; + match action { + TxPublicAction::NodeJoin { isv_svn, .. } => { + state.enclave_isv_svn = isv_svn; + } + _ => {} + }; + TxAction::Public(action) } }; diff --git a/chain-abci/src/staking/mod.rs b/chain-abci/src/staking/mod.rs index a8d11bcc0..f81cecbcc 100644 --- a/chain-abci/src/staking/mod.rs +++ b/chain-abci/src/staking/mod.rs @@ -16,14 +16,13 @@ mod tests { use chain_core::init::config::SlashRatio; use chain_core::init::params::NetworkParameters; use chain_core::state::account::{ - ConfidentialInit, CouncilNode, PunishmentKind, StakedState, StakedStateAddress, UnbondTx, - UnjailTx, Validator, + PunishmentKind, StakedState, StakedStateAddress, UnbondTx, UnjailTx, Validator, }; use chain_core::state::tendermint::{BlockHeight, TendermintValidatorPubKey}; use chain_core::state::validator::NodeJoinRequestTx; use chain_core::tx::fee::Fee; use chain_storage::buffer::{Get, GetStaking, MemStore, StoreStaking}; - use test_common::chain_env::get_init_network_params; + use test_common::chain_env::{get_init_network_params, mock_council_node}; use super::*; use crate::app::BeginBlockInfo; @@ -58,11 +57,8 @@ mod tests { fn new_validator(seed: &[u8; 32], bonded: Coin) -> StakedState { let mut staking = StakedState::default(staking_address(seed)); staking.bonded = bonded; - staking.validator = Some(Validator::new(CouncilNode::new( + staking.validator = Some(Validator::new(mock_council_node( TendermintValidatorPubKey::Ed25519(seed.clone()), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, ))); staking } @@ -120,16 +116,9 @@ mod tests { nonce, address: addr4, attributes: Default::default(), - node_meta: CouncilNode::new( - val_pk4.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + node_meta: mock_council_node(val_pk4.clone()), }; - table - .node_join(&mut store, 10, 0, &Default::default(), &node_join) - .unwrap(); + table.node_join(&mut store, 10, 0, 0, &node_join).unwrap(); assert_eq!(table.end_block(&store, 3), vec![]); // node-join increase nonce by one assert_eq!(store.get(&addr4).unwrap().nonce, nonce + 1); @@ -273,15 +262,10 @@ mod tests { nonce, address: addr1, attributes: Default::default(), - node_meta: CouncilNode::new( - val_pk_new, - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + node_meta: mock_council_node(val_pk_new), }; assert!(matches!( - table.node_join(&mut store, 3, 0, &Default::default(), &node_join), + table.node_join(&mut store, 3, 0, 0, &node_join), Err(PublicTxError::NodeJoin(NodeJoinError::IsJailed)) )); // failed execution don't increase nonce @@ -337,15 +321,10 @@ mod tests { nonce: staking.nonce + 1, address: addr, attributes: Default::default(), - node_meta: CouncilNode::new( - val_pk_new.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + node_meta: mock_council_node(val_pk_new.clone()), }; // change to new validator key - let result = table.node_join(store, 1, 1, &Default::default(), &node_join); + let result = table.node_join(store, 1, 1, 0, &node_join); if result.is_ok() { let staking = store.get(&addr).unwrap(); assert_eq!( @@ -353,7 +332,7 @@ mod tests { vec![(val_pk_new, staking.bonded.into())] ); } - result + result.map(|_| ()) } #[test] @@ -397,16 +376,11 @@ mod tests { nonce: 0, address: addr_new, attributes: Default::default(), - node_meta: CouncilNode::new( - val_pk1, - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + node_meta: mock_council_node(val_pk1), }; // can't join with used key assert!(matches!( - table.node_join(&mut store, 1, 0, &Default::default(), &node_join), + table.node_join(&mut store, 1, 0, 0, &node_join), Err(PublicTxError::NodeJoin( NodeJoinError::DuplicateValidatorAddress )) @@ -479,12 +453,7 @@ mod tests { nonce, address: addr1, attributes: Default::default(), - node_meta: CouncilNode::new( - val_pk1.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + node_meta: mock_council_node(val_pk1.clone()), }; let mut init_params = get_init_network_params(Coin::zero()); @@ -586,7 +555,7 @@ mod tests { let slashed = (bonded_slashed + unbonded_slashed).unwrap(); table.deposit(&mut store, &addr1, slashed).unwrap(); table - .node_join(&mut store, 8, 0, &Default::default(), &node_join_tx(0)) + .node_join(&mut store, 8, 0, 0, &node_join_tx(0)) .unwrap(); assert_eq!( table.end_block(&mut store, 3), @@ -638,7 +607,7 @@ mod tests { .deposit(&mut store, &addr1, Coin::new(11_0000_0000).unwrap()) .unwrap(); table - .node_join(&mut store, 11, 0, &Default::default(), &node_join_tx(2)) + .node_join(&mut store, 11, 0, 0, &node_join_tx(2)) .unwrap(); assert_eq!( table.end_block(&mut store, 3), @@ -783,16 +752,9 @@ mod tests { nonce: 1, address: addr2, attributes: Default::default(), - node_meta: CouncilNode::new( - val_pk_new.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + node_meta: mock_council_node(val_pk_new.clone()), }; - table - .node_join(&mut store, 2, 0, &Default::default(), &tx) - .unwrap(); + table.node_join(&mut store, 2, 0, 0, &tx).unwrap(); assert_eq!( table.end_block(&mut store, 3), vec![(val_pk_new.clone(), Coin::new(12_0000_0000).unwrap().into())] diff --git a/chain-abci/src/staking/tx.rs b/chain-abci/src/staking/tx.rs index a3064718f..61c14013f 100644 --- a/chain-abci/src/staking/tx.rs +++ b/chain-abci/src/staking/tx.rs @@ -1,4 +1,4 @@ -use rustls::internal::msgs::codec::Codec; +use log::warn; use chain_core::common::Timespec; use chain_core::init::coin::Coin; @@ -7,8 +7,8 @@ use chain_core::state::tendermint::{BlockHeight, TendermintValidatorAddress}; use chain_core::state::validator::NodeJoinRequestTx; use chain_core::tx::fee::Fee; use chain_storage::buffer::StoreStaking; -use mls::KeyPackage; -use ra_client::EnclaveCertVerifier; +use mls::{Codec, KeyPackage}; +use ra_client::ENCLAVE_CERT_VERIFIER; use super::table::{set_staking, StakingTable}; use crate::tx_error::{ @@ -24,9 +24,9 @@ impl StakingTable { heap: &mut impl StoreStaking, block_time: Timespec, max_evidence_age: Timespec, - ra_verifier: &EnclaveCertVerifier, + mut recent_isv_svn: u16, tx: &NodeJoinRequestTx, - ) -> Result<(), PublicTxError> { + ) -> Result { let mut staking = self.get_or_default(heap, &tx.address); if tx.nonce != staking.nonce { return Err(PublicTxError::IncorrectNonce); @@ -35,13 +35,14 @@ impl StakingTable { return Err(NodeJoinError::BondedNotEnough.into()); } - // FIXME verify keypackage - if let Ok(keypackage) = KeyPackage::read_bytes(&tx.node_meta.confidential_init.keypackage) - .ok_or(NodeJoinError::KeyPackageDecodeError) - { - let _ = keypackage - .verify(ra_verifier, block_time) - .map_err(NodeJoinError::KeyPackageVerifyError); + let keypackage = KeyPackage::read_bytes(&tx.node_meta.confidential_init.keypackage) + .ok_or(NodeJoinError::KeyPackageDecodeError)?; + let info = keypackage + .verify(&ENCLAVE_CERT_VERIFIER, block_time) + .map_err(NodeJoinError::KeyPackageVerifyError)?; + if info.quote.report_body.isv_svn > recent_isv_svn { + warn!("more recent version of enclave"); + recent_isv_svn = info.quote.report_body.isv_svn; } let val_addr = TendermintValidatorAddress::from(&tx.node_meta.consensus_pubkey); @@ -94,9 +95,11 @@ impl StakingTable { } staking.inc_nonce(); set_staking(heap, staking, self.minimal_required_staking); + #[cfg(debug_assertions)] self.check_invariants(heap); - Ok(()) + + Ok(recent_isv_svn) } /// Handle `UnjailTx` diff --git a/chain-abci/src/storage/mod.rs b/chain-abci/src/storage/mod.rs index e62522efc..aa4fe90ba 100644 --- a/chain-abci/src/storage/mod.rs +++ b/chain-abci/src/storage/mod.rs @@ -10,7 +10,6 @@ use chain_core::tx::{TransactionId, TxEnclaveAux, TxObfuscated, TxPublicAux}; use chain_storage::buffer::{GetKV, GetStaking, StoreStaking}; use chain_tx_validation::{verify_unjailed, witness::verify_tx_recover_address, ChainInfo, Error}; use enclave_protocol::{IntraEnclaveRequest, IntraEnclaveResponseOk, SealedLog}; -use ra_client::EnclaveCertVerifier; pub enum TxAction { Enclave(TxEnclaveAction), @@ -117,7 +116,12 @@ pub enum TxPublicAction { unbond: (StakedStateAddress, Coin), unbonded_from: Timespec, }, - NodeJoin(StakedStateAddress, CouncilNode), + NodeJoin { + address: StakedStateAddress, + council_node: CouncilNode, + /// most recent isv_svn + isv_svn: u16, + }, Unjail(StakedStateAddress), } @@ -129,8 +133,12 @@ impl TxPublicAction { unbonded_from, } } - fn node_join(staking_address: StakedStateAddress, council_node: CouncilNode) -> Self { - Self::NodeJoin(staking_address, council_node) + fn node_join(address: StakedStateAddress, council_node: CouncilNode, isv_svn: u16) -> Self { + Self::NodeJoin { + address, + council_node, + isv_svn, + } } fn unjail(staking_address: StakedStateAddress) -> Self { Self::Unjail(staking_address) @@ -139,7 +147,7 @@ impl TxPublicAction { pub fn fee(&self) -> Fee { match self { Self::Unbond { fee, .. } => *fee, - Self::NodeJoin(_, _) => Fee::new(Coin::zero()), + Self::NodeJoin { .. } => Fee::new(Coin::zero()), Self::Unjail(_) => Fee::new(Coin::zero()), } } @@ -147,7 +155,7 @@ impl TxPublicAction { pub fn staking_address(&self) -> Option { match self { Self::Unbond { unbond, .. } => Some(unbond.0), - Self::NodeJoin(staking_address, _) => Some(*staking_address), + Self::NodeJoin { address, .. } => Some(*address), Self::Unjail(staking_address) => Some(*staking_address), } } @@ -287,7 +295,7 @@ fn check_staking_attributes( pub fn process_public_tx( staking_store: &mut impl StoreStaking, staking_table: &mut StakingTable, - enclave_cert_verifier: &EnclaveCertVerifier, + enclave_isv_svn: u16, chain_info: &ChainInfo, txaux: &TxPublicAux, ) -> Result { @@ -331,15 +339,19 @@ pub fn process_public_tx( if address != maintx.address { return Err(PublicTxError::StakingWitnessNotMatch); } - staking_table.node_join( + let isv_svn = staking_table.node_join( staking_store, chain_info.block_time, chain_info.max_evidence_age, - enclave_cert_verifier, + enclave_isv_svn, maintx, )?; - Ok(TxPublicAction::node_join(address, maintx.node_meta.clone())) + Ok(TxPublicAction::node_join( + address, + maintx.node_meta.clone(), + isv_svn, + )) } } } diff --git a/chain-abci/tests/abci_app.rs b/chain-abci/tests/abci_app.rs index 9a6a40b2c..f519a404a 100644 --- a/chain-abci/tests/abci_app.rs +++ b/chain-abci/tests/abci_app.rs @@ -14,9 +14,8 @@ use chain_core::init::config::{ JailingParameters, RewardsParameters, SlashRatio, SlashingParameters, }; use chain_core::state::account::{ - ConfidentialInit, CouncilNode, DepositBondTx, StakedState, StakedStateAddress, - StakedStateDestination, StakedStateOpAttributes, StakedStateOpWitness, UnbondTx, - WithdrawUnbondedTx, + DepositBondTx, StakedState, StakedStateAddress, StakedStateDestination, + StakedStateOpAttributes, StakedStateOpWitness, UnbondTx, WithdrawUnbondedTx, }; use chain_core::state::tendermint::{ BlockHeight, TendermintValidatorAddress, TendermintValidatorPubKey, TendermintVotePower, @@ -57,7 +56,7 @@ use std::convert::TryFrom; use std::convert::TryInto; use std::str::FromStr; use std::sync::Arc; -use test_common::chain_env::ChainEnv; +use test_common::chain_env::{mock_confidential_init, mock_council_node, ChainEnv}; const TEST_CHAIN_ID: &str = "test-00"; const EXAMPLE_HASH: &str = "F5E8DFBF717082D6E9508E1A5A5C9B8EAC04A39F69C40262CB733C920DA10962"; @@ -219,6 +218,7 @@ fn get_dummy_app_state(app_hash: H256) -> ChainNodeState { staking_table: StakingTable::default(), staking_version: 0, utxo_coins: Coin::zero(), + enclave_isv_svn: 0, top_level: ChainState { account_root: [0u8; 32], rewards_pool: RewardsPoolState::new(0, params.get_rewards_monetary_expansion_tau()), @@ -271,9 +271,7 @@ fn init_chain_for(address: RedeemAddress) -> ChainNodeApp { "test".to_owned(), None, pub_key.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + mock_confidential_init(), ); let validator = ValidatorUpdate { pub_key: Some(PubKey { @@ -289,14 +287,14 @@ fn init_chain_for(address: RedeemAddress) -> ChainNodeApp { let c = InitConfig::new(distribution, params, nodes); let t = ::protobuf::well_known_types::Timestamp::new(); let result = c.validate_config_get_genesis(t.get_seconds().try_into().unwrap()); - if let Ok((accounts, rp, _nodes)) = result { + if let Ok(genesis_state) = result { let tx_tree = MerkleTree::empty(); let mut storage = Storage::new_db(db.clone()); - let new_account_root = storage.put_stakings(0, &accounts); + let new_account_root = storage.put_stakings(0, &genesis_state.accounts); let genesis_app_hash = compute_app_hash( &tx_tree, &new_account_root, - &rp, + &genesis_state.rewards_pool, &get_dummy_network_params(), ); @@ -993,12 +991,7 @@ fn all_valid_tx_types_should_commit() { 1, addr.into(), StakedStateOpAttributes::new(0), - CouncilNode::new( - TendermintValidatorPubKey::Ed25519([2u8; 32]), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + mock_council_node(TendermintValidatorPubKey::Ed25519([2u8; 32])), ); let secp = Secp256k1::new(); let witness = StakedStateOpWitness::new(get_ecdsa_witness(&secp, &tx.id(), &secret_key)); diff --git a/chain-abci/tests/tx_validation.rs b/chain-abci/tests/tx_validation.rs index 93900144e..74984c56c 100644 --- a/chain-abci/tests/tx_validation.rs +++ b/chain-abci/tests/tx_validation.rs @@ -17,8 +17,7 @@ use chain_core::state::account::StakedState; use chain_core::state::account::StakedStateAddress; use chain_core::state::account::StakedStateOpAttributes; use chain_core::state::account::{ - ConfidentialInit, DepositBondTx, StakedStateOpWitness, UnbondTx, UnjailTx, Validator, - WithdrawUnbondedTx, + DepositBondTx, StakedStateOpWitness, UnbondTx, UnjailTx, Validator, WithdrawUnbondedTx, }; use chain_core::state::tendermint::BlockHeight; use chain_core::state::tendermint::TendermintValidatorPubKey; @@ -53,6 +52,7 @@ use secp256k1::{key::PublicKey, key::SecretKey, key::XOnlyPublicKey, Message, Se use std::fmt::Debug; use std::mem; use std::sync::Arc; +use test_common::chain_env::{mock_confidential_init, mock_council_node}; fn verify_enclave_tx( tx_validator: &mut T, @@ -82,8 +82,7 @@ fn verify_public_tx( let mut buffer = HashMap::new(); let mut store = StakingBufferStore::new(StakingGetter::new(storage, version), &mut buffer); - let tx_action = - process_public_tx(&mut store, &mut tbl, &Default::default(), extra_info, txaux)?; + let tx_action = process_public_tx(&mut store, &mut tbl, 0, extra_info, txaux)?; let fee = tx_action.fee(); let maddress = tx_action.staking_address(); @@ -1152,12 +1151,7 @@ fn prepare_jailed_accounts() -> ( 0, addr.into(), Some(Validator { - council_node: CouncilNode::new( - TendermintValidatorPubKey::Ed25519([0xcd; 32]), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, - ), + council_node: mock_council_node(TendermintValidatorPubKey::Ed25519([0xcd; 32])), jailed_until: Some(100), inactive_time: Some(0), inactive_block: Some(BlockHeight::genesis()), @@ -1346,9 +1340,7 @@ fn prepare_nodejoin_transaction( name: "test".to_string(), security_contact: None, consensus_pubkey: TendermintValidatorPubKey::Ed25519([1u8; 32]), - confidential_init: ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + confidential_init: mock_confidential_init(), }, }; let witness = get_account_op_witness(secp, &tx.id(), &secret_key); @@ -1378,11 +1370,8 @@ fn prepare_valid_nodejoin_tx( 0, addr.into(), if validator { - Some(Validator::new(CouncilNode::new( + Some(Validator::new(mock_council_node( TendermintValidatorPubKey::Ed25519([1u8; 32]), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, ))) } else { None diff --git a/chain-core/Cargo.toml b/chain-core/Cargo.toml index 43632fca6..53b97bed9 100644 --- a/chain-core/Cargo.toml +++ b/chain-core/Cargo.toml @@ -7,12 +7,13 @@ readme = "../README.md" edition = "2018" [features] -default = ["serde", "bech32", "hex", "base64", "secp256k1zkp/serde", "secp256k1zkp/std"] +default = ["serde", "bech32", "hex", "base64", "secp256k1zkp/serde", "secp256k1zkp/std", "mls", "ra-client"] edp = ["secp256k1zkp/edp"] mesalock_sgx = ["sgx_tstd", "secp256k1zkp/sgx"] - [dependencies] +mls = { path = "../chain-tx-enclave-next/mls", optional = true } +ra-client = { path = "../chain-tx-enclave-next/enclave-ra/ra-client", optional = true} digest = { version = "0.8", default-features = false} tiny-keccak = { version = "2.0", features = ["keccak"] } sha2 = { version = "0.8", default-features = false } @@ -32,3 +33,4 @@ thiserror = { version = "1.0", default-features = false } quickcheck = "0.9" serde_json = "1.0" fixed = "0.5.7" +test-common = { path = "../test-common" } diff --git a/chain-core/src/init/config.rs b/chain-core/src/init/config.rs index afb2b196c..7d137c54b 100644 --- a/chain-core/src/init/config.rs +++ b/chain-core/src/init/config.rs @@ -2,85 +2,61 @@ use crate::common::Timespec; use crate::init::address::RedeemAddress; use crate::init::coin::{sum_coins, Coin, CoinError}; pub use crate::init::params::*; -use crate::init::MAX_COIN; use crate::state::account::{ ConfidentialInit, CouncilNode, StakedState, StakedStateAddress, StakedStateDestination, ValidatorName, ValidatorSecurityContact, }; -use crate::state::tendermint::{TendermintValidatorPubKey, TendermintVotePower}; +use crate::state::tendermint::TendermintValidatorPubKey; use crate::state::RewardsPoolState; +use mls::{keypackage, Codec, KeyPackage}; +use ra_client::ENCLAVE_CERT_VERIFIER; #[cfg(not(feature = "mesalock_sgx"))] use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashSet}; -use std::fmt; /// problems with initial config -#[derive(Debug)] +#[derive(thiserror::Error, Debug)] pub enum DistributionError { /// coin arithmetic problems - DistributionCoinError(CoinError), + #[error("coin error: {0}")] + DistributionCoinError(#[from] CoinError), /// lower than the expected supply + #[error("The total sum of allocated amounts ({0}) does not match the expected total supply")] DoesNotMatchMaxSupply(Coin), /// council node uses non-specified address + #[error("Address not found in the distribution ({0})")] AddressNotInDistribution(RedeemAddress), /// address should have that amount assigned + #[error("Address ({0}) does not have the expected amount ({1}) or is not an externally owned account")] DoesNotMatchRequiredAmount(RedeemAddress, Coin), /// problems with encoded consensus pubkeys + #[error("Invalid validator key")] InvalidValidatorKey, /// duplicate consensus pubkeys + #[error("Duplicate validator key")] DuplicateValidatorKey, /// associated state not in distribution + #[error("Invalid validator account")] InvalidValidatorAccount, /// at least one validator needs to be specified + #[error("No validators / council nodes specified")] NoValidators, /// voting power too large or otherwise invalid - InvalidVotingPower, + #[error("Invalid minimal required staking: {0}")] + InvalidMinimalStake(CoinError), /// problems with reward configuration /// TODO: embed the error type? + #[error("Invalid rewards parameters: {0}")] InvalidRewardsParamter(&'static str), /// Invalid punishment configuration parameter + #[error("Invalid punishment parameters")] InvalidPunishmentParamter, -} - -impl fmt::Display for DistributionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - DistributionError::DistributionCoinError(c) => c.fmt(f), - DistributionError::DoesNotMatchMaxSupply(c) => write!( - f, - "The total sum of allocated amounts ({}) does not match the expected total supply ({})", - c, - MAX_COIN - ), - DistributionError::AddressNotInDistribution(a) => { - write!(f, "Address not found in the distribution ({})", a) - }, - DistributionError::DoesNotMatchRequiredAmount(a, c) => { - write!(f, "Address ({}) does not have the expected amount ({}) or is not an externally owned account", a, c) - }, - DistributionError::InvalidValidatorKey => { - write!(f, "Invalid validator key") - }, - DistributionError::InvalidValidatorAccount => { - write!(f, "Invalid validator account") - }, - DistributionError::DuplicateValidatorKey => { - write!(f, "Duplicate validator key") - }, - DistributionError::NoValidators => { - write!(f, "No validators / council nodes specified") - }, - DistributionError::InvalidVotingPower => { - write!(f, "Invalid voting power") - }, - DistributionError::InvalidRewardsParamter(err) => { - write!(f, "Invalid rewards parameters: {}", err) - } - DistributionError::InvalidPunishmentParamter => { - write!(f, "Invalid punishment parameters") - } - } - } + /// keypackage decode error + #[error("key package decode failed")] + KeyPackageDecodeError, + /// keypackage verify error + #[error("invalid key package: {0}")] + KeyPackageVerifyError(#[from] keypackage::Error), } /// Initial configuration ("app_state" in genesis.json of Tendermint config) @@ -105,16 +81,17 @@ pub struct InitConfig { >, } -/// the initial state at genesis: -/// - initial states -/// - initial rewards pool -/// - initial tendermint validators -/// TODO: some tdbe state? -pub type GenesisState = ( - Vec, - RewardsPoolState, - Vec<(StakedStateAddress, CouncilNode)>, -); +/// the initial state at genesis +pub struct GenesisState { + /// initial states + pub accounts: Vec, + /// initial rewards pool + pub rewards_pool: RewardsPoolState, + /// initial tendermint validators + pub validators: Vec<(StakedStateAddress, CouncilNode)>, + /// enclave ISVSVN in genesis keypackage + pub isv_svn: u16, +} impl InitConfig { /// creates a new config (mainly for testing / tools) @@ -209,39 +186,34 @@ impl InitConfig { return Err(DistributionError::DuplicateValidatorKey); } - let validators: Result, DistributionError> = self + let validators = self .council_nodes .keys() .map(|address| { let council_node = self.get_council_node(address)?; Ok((StakedStateAddress::BasicRedeem(*address), council_node)) }) - .collect(); + .collect::, DistributionError>>()?; + + let isv_svn = validators + .iter() + .map(|v| verify_keypackage(genesis_time, &v.1.confidential_init.keypackage)) + .collect::, _>>()? + .into_iter() + .max() + .unwrap_or(0); + Coin::new( u64::from(self.network_params.required_council_node_stake) * self.council_nodes.len() as u64, ) - .map(TendermintVotePower::from) // sanity check - .map_err(|_| DistributionError::InvalidVotingPower)?; + .map_err(DistributionError::InvalidMinimalStake)?; // check the total amount - match sum_coins(self.distribution.iter().map(|(_, (_, amount))| *amount)) { - Ok(s) => { - let sum_result = s + self.network_params.rewards_config.monetary_expansion_cap; - match sum_result { - Ok(sum) => { - if sum != Coin::max() { - return Err(DistributionError::DoesNotMatchMaxSupply(sum)); - } - } - Err(e) => { - return Err(DistributionError::DistributionCoinError(e)); - } - } - } - Err(e) => { - return Err(DistributionError::DistributionCoinError(e)); - } + let sum = sum_coins(self.distribution.iter().map(|(_, (_, amount))| *amount))?; + let sum = (sum + self.network_params.rewards_config.monetary_expansion_cap)?; + if sum != Coin::max() { + return Err(DistributionError::DoesNotMatchMaxSupply(sum)); } let accounts = self.get_account(genesis_time); @@ -253,6 +225,20 @@ impl InitConfig { genesis_time, self.network_params.rewards_config.monetary_expansion_tau, ); - Ok((accounts, rewards_pool, validators?)) + Ok(GenesisState { + accounts, + rewards_pool, + validators, + isv_svn, + }) } } + +fn verify_keypackage(genesis_time: Timespec, keypackage: &[u8]) -> Result { + let keypackage = + KeyPackage::read_bytes(keypackage).ok_or(DistributionError::KeyPackageDecodeError)?; + let info = keypackage + .verify(&ENCLAVE_CERT_VERIFIER, genesis_time) + .map_err(DistributionError::KeyPackageVerifyError)?; + Ok(info.quote.report_body.isv_svn) +} diff --git a/chain-core/tests/verify_config.rs b/chain-core/tests/verify_config.rs index 3fefa8103..9a3c69814 100644 --- a/chain-core/tests/verify_config.rs +++ b/chain-core/tests/verify_config.rs @@ -4,12 +4,13 @@ use chain_core::init::config::{ InitConfig, InitNetworkParameters, JailingParameters, RewardsParameters, SlashRatio, SlashingParameters, }; -use chain_core::state::account::{ConfidentialInit, StakedStateDestination}; +use chain_core::state::account::StakedStateDestination; use chain_core::state::tendermint::TendermintValidatorPubKey; use chain_core::tx::fee::{LinearFee, Milli}; use serde::Deserialize; use std::collections::BTreeMap; use std::str::FromStr; +use test_common::chain_env::mock_confidential_init; #[derive(Deserialize)] pub struct Distribution { @@ -40,7 +41,7 @@ fn test_verify_test_example_snapshot() { "no-name".to_owned(), None, node_pubkey, - ConfidentialInit { keypackage: vec![] }, + mock_confidential_init(), ), ); @@ -81,12 +82,10 @@ fn test_verify_test_example_snapshot() { }; let config = InitConfig::new(dist.clone(), params.clone(), nodes.clone()); - let result = config.validate_config_get_genesis(0); - assert!(result.is_ok()); + config.validate_config_get_genesis(0).unwrap(); // add 1 into rewards_pool params.rewards_config.monetary_expansion_cap = Coin::new(951_6484_5705_9733_7035).unwrap(); let config = InitConfig::new(dist, params, nodes); - let result = config.validate_config_get_genesis(0); - assert!(result.is_err()); + assert!(config.validate_config_get_genesis(0).is_err()); } diff --git a/chain-tx-enclave-next/enclave-ra/ra-client/Cargo.toml b/chain-tx-enclave-next/enclave-ra/ra-client/Cargo.toml index db9e5ca9a..113b3d967 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-client/Cargo.toml +++ b/chain-tx-enclave-next/enclave-ra/ra-client/Cargo.toml @@ -10,11 +10,11 @@ edition = "2018" [dependencies] chrono = "0.4" der-parser = "3.0" -log = "0.4" rustls = { version = "0.17", features = ["dangerous_configuration"] } serde_json = "1.0" thiserror = "1.0" webpki = "0.21" x509-parser = "0.7" +lazy_static = "1.4" ra-common = { path = "../ra-common" } diff --git a/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs b/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs index 8a483acce..697f8da6c 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs +++ b/chain-tx-enclave-next/enclave-ra/ra-client/src/config.rs @@ -19,7 +19,8 @@ impl<'a> EnclaveCertVerifierConfig<'a> { pub fn new() -> Self { Self { signing_ca_cert_pem: IAS_CERT.into(), - valid_enclave_quote_statuses: vec!["OK".into()].into(), + // FIXME remove SW_HARDENING_NEEDED after https://github.com/rust-lang/llvm-project/pull/58 + valid_enclave_quote_statuses: vec!["OK".into(), "SW_HARDENING_NEEDED".into()].into(), report_validity_secs: 86400, // FIXME construct enclave_info from env var or config file enclave_info: None, diff --git a/chain-tx-enclave-next/enclave-ra/ra-client/src/lib.rs b/chain-tx-enclave-next/enclave-ra/ra-client/src/lib.rs index d09ef5247..a997d8e1a 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-client/src/lib.rs +++ b/chain-tx-enclave-next/enclave-ra/ra-client/src/lib.rs @@ -23,5 +23,7 @@ mod verifier; pub use self::{ config::{EnclaveCertVerifierConfig, EnclaveInfo}, - verifier::{EnclaveCertVerifier, EnclaveCertVerifierError}, + verifier::{ + CertVerifyResult, EnclaveCertVerifier, EnclaveCertVerifierError, ENCLAVE_CERT_VERIFIER, + }, }; diff --git a/chain-tx-enclave-next/enclave-ra/ra-client/src/verifier.rs b/chain-tx-enclave-next/enclave-ra/ra-client/src/verifier.rs index df18deeaa..5d4511b1a 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-client/src/verifier.rs +++ b/chain-tx-enclave-next/enclave-ra/ra-client/src/verifier.rs @@ -2,8 +2,10 @@ use std::{collections::HashSet, sync::Arc, time::SystemTime}; use chrono::{DateTime, Duration, Utc}; use der_parser::oid::Oid; +use lazy_static::lazy_static; use ra_common::{ - AttestationReport, AttestationReportBody, EnclaveQuoteStatus, OID_EXTENSION_ATTESTATION_REPORT, + AttestationReport, AttestationReportBody, EnclaveQuoteStatus, Quote, + OID_EXTENSION_ATTESTATION_REPORT, }; use rustls::{ internal::pemfile::certs, Certificate, ClientCertVerified, ClientCertVerifier, ClientConfig, @@ -21,6 +23,10 @@ use crate::{EnclaveCertVerifierConfig, EnclaveInfo}; static SUPPORTED_SIG_ALGS: &[&SignatureAlgorithm] = &[&ECDSA_P256_SHA256]; +lazy_static! { + pub static ref ENCLAVE_CERT_VERIFIER: EnclaveCertVerifier = EnclaveCertVerifier::default(); +} + pub struct EnclaveCertVerifier { root_cert_store: RootCertStore, valid_enclave_quote_statuses: HashSet, @@ -65,7 +71,7 @@ impl EnclaveCertVerifier { &self, certificate: &[u8], now: DateTime, - ) -> Result, EnclaveCertVerifierError> { + ) -> Result { let (_, certificate) = parse_x509_der(certificate) .map_err(|_| EnclaveCertVerifierError::CertificateParsingError)?; @@ -75,33 +81,30 @@ impl EnclaveCertVerifier { } = certificate.tbs_certificate.validity; let now_sec = now.timestamp(); if now_sec < not_before.to_timespec().sec { - return Err(EnclaveCertVerifierError::CertificateExpired); + return Err(EnclaveCertVerifierError::CertificateNotBegin); } if now_sec >= not_after.to_timespec().sec { return Err(EnclaveCertVerifierError::CertificateExpired); } let attestation_report_oid = Oid::from(OID_EXTENSION_ATTESTATION_REPORT); - let mut attestation_report_received = false; - let public_key = certificate .tbs_certificate .subject_pki .subject_public_key .data; - for extension in certificate.tbs_certificate.extensions { - if extension.oid == attestation_report_oid { - attestation_report_received = true; - self.verify_attestation_report(extension.value, public_key, now)?; - } - } - - if attestation_report_received { - Ok(public_key.to_vec()) - } else { - Err(EnclaveCertVerifierError::MissingAttestationReport) - } + let extension = certificate + .tbs_certificate + .extensions + .iter() + .find(|ext| ext.oid == attestation_report_oid) + .ok_or(EnclaveCertVerifierError::MissingAttestationReport)?; + let quote = self.verify_attestation_report(extension.value, public_key, now)?; + Ok(CertVerifyResult { + public_key: public_key.to_vec(), + quote, + }) } /// Verifies attestation report @@ -110,9 +113,7 @@ impl EnclaveCertVerifier { attestation_report: &[u8], public_key: &[u8], now: DateTime, - ) -> Result<(), EnclaveCertVerifierError> { - log::info!("Verifying attestation report"); - + ) -> Result { let trust_anchors: Vec = self .root_cert_store .roots @@ -144,10 +145,7 @@ impl EnclaveCertVerifier { )?; } - self.verify_attestation_report_body(&attestation_report.body, public_key, now)?; - - log::info!("Attestation report is valid!"); - Ok(()) + self.verify_attestation_report_body(&attestation_report.body, public_key, now) } fn verify_attestation_report_body( @@ -155,7 +153,7 @@ impl EnclaveCertVerifier { attestation_report_body: &[u8], public_key: &[u8], now: DateTime, - ) -> Result<(), EnclaveCertVerifierError> { + ) -> Result { let attestation_report_body: AttestationReportBody = serde_json::from_slice(attestation_report_body)?; @@ -206,7 +204,7 @@ impl EnclaveCertVerifier { } } - Ok(()) + Ok(quote) } /// Converts enclave certificate verifier into client config expected by `rustls` @@ -268,6 +266,8 @@ impl ClientCertVerifier for EnclaveCertVerifier { pub enum EnclaveCertVerifierError { #[error("Enclave certificate expired")] CertificateExpired, + #[error("Enclave certificate not begin yet")] + CertificateNotBegin, #[error("Failed to parse server certificate")] CertificateParsingError, #[error("Unable to parse date time: {0}")] @@ -301,3 +301,11 @@ impl From for TLSError { TLSError::General(e.to_string()) } } + +/// Extracted information after success verify attestation certificate +pub struct CertVerifyResult { + /// the returned public key is in uncompressed raw format (65 bytes) + pub public_key: Vec, + /// the quote + pub quote: Quote, +} diff --git a/chain-tx-enclave-next/enclave-ra/ra-enclave/src/lib.rs b/chain-tx-enclave-next/enclave-ra/ra-enclave/src/lib.rs index a9e6492eb..c579369d2 100644 --- a/chain-tx-enclave-next/enclave-ra/ra-enclave/src/lib.rs +++ b/chain-tx-enclave-next/enclave-ra/ra-enclave/src/lib.rs @@ -30,4 +30,4 @@ mod context; pub use self::{certificate::Certificate, config::EnclaveRaConfig}; #[cfg(target_env = "sgx")] -pub use {EnclaveRaContext, EnclaveRaContextError}; +pub use context::{EnclaveRaContext, EnclaveRaContextError}; diff --git a/chain-tx-enclave-next/mls/src/keypackage.rs b/chain-tx-enclave-next/mls/src/keypackage.rs index 4a3d26897..81d623b81 100644 --- a/chain-tx-enclave-next/mls/src/keypackage.rs +++ b/chain-tx-enclave-next/mls/src/keypackage.rs @@ -1,7 +1,9 @@ //! Key package of mls protocol (draft-ietf-mls-protocol.md#key-packages) +#[cfg(target_env = "sgx")] +use std::convert::TryInto; use std::time::{Duration, UNIX_EPOCH}; -use ra_client::{EnclaveCertVerifier, EnclaveCertVerifierError}; +use ra_client::{CertVerifyResult, EnclaveCertVerifier, EnclaveCertVerifierError}; #[cfg(target_env = "sgx")] use ra_enclave::{Certificate, EnclaveRaContext, EnclaveRaContextError}; use rustls::internal::msgs::codec::{self, Codec, Reader}; @@ -18,7 +20,7 @@ pub type Timespec = u64; pub type CipherSuite = u16; pub const PROTOCOL_VERSION_MLS10: ProtocolVersion = 0; -pub const DEFAULT_LIFE_TIME: Timespec = 30 * 24 * 3600; +pub const DEFAULT_LIFE_TIME: Timespec = 90 * 24 * 3600; // certificate has 90 days valid duration pub const MLS10_128_DHKEMP256_AES128GCM_SHA256_P256: CipherSuite = 2; pub const CREDENTIAL_TYPE_X509: u8 = 1; @@ -79,7 +81,11 @@ impl KeyPackagePayload { } /// Verify key package payload - pub fn verify(&self, ra_verifier: &EnclaveCertVerifier, now: Timespec) -> Result<(), Error> { + pub fn verify( + &self, + ra_verifier: &EnclaveCertVerifier, + now: Timespec, + ) -> Result { if self.cipher_suite != MLS10_128_DHKEMP256_AES128GCM_SHA256_P256 { return Err(Error::UnsupportedCipherSuite(self.cipher_suite)); } @@ -108,13 +114,13 @@ impl KeyPackagePayload { let x509 = self.credential.x509().ok_or(Error::InvalidCredential)?; - let cert_pubkey = ra_verifier + let info = ra_verifier .verify_cert(x509, (UNIX_EPOCH + Duration::from_secs(now)).into()) .map_err(Error::CertificateVerifyError)?; - if cert_pubkey.as_slice() != self.init_key.as_ref() { + if info.public_key.as_slice() != self.init_key.as_ref() { return Err(Error::InitKeyDontMatch); } - Ok(()) + Ok(info) } } @@ -140,12 +146,17 @@ impl Codec for KeyPackage { impl KeyPackage { /// Verify key package and signature - pub fn verify(&self, ra_verifier: &EnclaveCertVerifier, now: Timespec) -> Result<(), Error> { - self.payload.verify(ra_verifier, now)?; + pub fn verify( + &self, + ra_verifier: &EnclaveCertVerifier, + now: Timespec, + ) -> Result { + let info = self.payload.verify(ra_verifier, now)?; self.payload .init_key .verify_signature(&self.payload.get_encoding(), &self.signature) - .map_err(Error::SignatureVerifyError) + .map_err(Error::SignatureVerifyError)?; + Ok(info) } } @@ -173,8 +184,11 @@ impl OwnedKeyPackage { let extensions = vec![ ext::SupportedVersionsExt(vec![PROTOCOL_VERSION_MLS10]).entry(), ext::SupportedCipherSuitesExt(vec![MLS10_128_DHKEMP256_AES128GCM_SHA256_P256]).entry(), - ext::LifeTimeExt::new(not_before.to_timespec().sec, not_after.to_timespec().sec) - .entry(), + ext::LifeTimeExt::new( + not_before.to_timespec().sec.try_into().unwrap(), + not_after.to_timespec().sec.try_into().unwrap(), + ) + .entry(), ]; let private_key = PrivateKey::from_pkcs8(&private_key.0).expect("invalid private key"); diff --git a/chain-tx-enclave-next/mls/src/lib.rs b/chain-tx-enclave-next/mls/src/lib.rs index 6d0c279e8..30270fe12 100644 --- a/chain-tx-enclave-next/mls/src/lib.rs +++ b/chain-tx-enclave-next/mls/src/lib.rs @@ -9,3 +9,4 @@ pub mod tree; pub mod utils; pub use keypackage::{KeyPackage, OwnedKeyPackage}; +pub use rustls::internal::msgs::codec::{self, Codec, Reader}; diff --git a/chain-tx-enclave-next/mls/tests/test_vectors/keypackage.bin b/chain-tx-enclave-next/mls/tests/test_vectors/keypackage.bin index ca1572ad4..02a00891b 100644 Binary files a/chain-tx-enclave-next/mls/tests/test_vectors/keypackage.bin and b/chain-tx-enclave-next/mls/tests/test_vectors/keypackage.bin differ diff --git a/chain-tx-enclave-next/mls/tests/verify_test_vectors.rs b/chain-tx-enclave-next/mls/tests/verify_test_vectors.rs index 9ef76beca..f8f39c3e4 100644 --- a/chain-tx-enclave-next/mls/tests/verify_test_vectors.rs +++ b/chain-tx-enclave-next/mls/tests/verify_test_vectors.rs @@ -4,16 +4,15 @@ use mls::{ keypackage::{Error, DEFAULT_LIFE_TIME}, KeyPackage, }; -use ra_client::{EnclaveCertVerifier, EnclaveCertVerifierConfig}; +use ra_client::ENCLAVE_CERT_VERIFIER; #[test] fn verify_keypackage_test_vector_mock() { // keypackage_mock.bin is generated by golang implementation static VECTOR: &[u8] = include_bytes!("test_vectors/keypackage_mock.bin"); let kp = ::read_bytes(VECTOR).expect("decode"); - let verifier = EnclaveCertVerifier::new(Default::default()).unwrap(); assert!(matches!( - kp.verify(&verifier, 0), + kp.verify(&ENCLAVE_CERT_VERIFIER, 0), Err(Error::InvalidCredential) )); } @@ -23,17 +22,11 @@ fn verify_keypackage_test_vector() { static VECTOR: &[u8] = include_bytes!("test_vectors/keypackage.bin"); let kp = ::read_bytes(VECTOR).expect("decode"); - let verifier = EnclaveCertVerifier::new(EnclaveCertVerifierConfig { - // FIXME GROUP_OUT_OF_DATE is not valid status - valid_enclave_quote_statuses: vec!["OK".into(), "GROUP_OUT_OF_DATE".into()].into(), - ..Default::default() - }) - .unwrap(); - let now = 1590042554; + let now = 1590490084; let expire = now + DEFAULT_LIFE_TIME; - kp.verify(&verifier, now).unwrap(); + kp.verify(&ENCLAVE_CERT_VERIFIER, now).unwrap(); assert!(matches!( - kp.verify(&verifier, expire + 1), + kp.verify(&ENCLAVE_CERT_VERIFIER, expire + 1), Err(Error::NotAfter(_)) )); } diff --git a/client-cli/Cargo.toml b/client-cli/Cargo.toml index 96e914b92..66b233d2f 100644 --- a/client-cli/Cargo.toml +++ b/client-cli/Cargo.toml @@ -15,6 +15,7 @@ chain-core = { path = "../chain-core"} client-common = { path = "../client-common" } client-core = { path = "../client-core" } client-network = { path = "../client-network" } +test-common = { path = "../test-common" } rand = "0.7" once_cell = "1.4" structopt = "0.3" diff --git a/client-cli/src/command/transaction_command.rs b/client-cli/src/command/transaction_command.rs index e740a6c5c..46b7b7191 100644 --- a/client-cli/src/command/transaction_command.rs +++ b/client-cli/src/command/transaction_command.rs @@ -8,9 +8,7 @@ use std::str::FromStr; use chain_core::common::{Timespec, HASH_SIZE_256}; use chain_core::init::coin::Coin; use chain_core::init::network::get_network_id; -use chain_core::state::account::{ - ConfidentialInit, CouncilNode, StakedStateAddress, StakedStateOpAttributes, -}; +use chain_core::state::account::{CouncilNode, StakedStateAddress, StakedStateOpAttributes}; use chain_core::state::tendermint::TendermintValidatorPubKey; use chain_core::tx::data::access::{TxAccess, TxAccessPolicy}; use chain_core::tx::data::address::ExtendedAddr; @@ -23,6 +21,7 @@ use client_core::transaction_builder::SignedTransferTransaction; use client_core::types::{BalanceChange, TransactionPending}; use client_core::WalletClient; use client_network::NetworkOpsClient; +use test_common::chain_env::mock_confidential_init; use chrono::{DateTime, Local, NaiveDateTime, Utc}; use cli_table::format::{CellFormat, Color, Justify}; @@ -1038,8 +1037,7 @@ fn ask_node_metadata() -> Result { name, security_contact: None, consensus_pubkey: TendermintValidatorPubKey::Ed25519(pubkey_bytes), - confidential_init: ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + // FIXME real keypackage + confidential_init: mock_confidential_init(), }) } diff --git a/client-network/Cargo.toml b/client-network/Cargo.toml index 292633f34..83d7becf6 100644 --- a/client-network/Cargo.toml +++ b/client-network/Cargo.toml @@ -22,3 +22,4 @@ tendermint = { git = "https://github.com/crypto-com/tendermint-rs.git", default- [dev-dependencies] secp256k1zkp = { git = "https://github.com/crypto-com/rust-secp256k1-zkp.git", rev = "f8759809f6e3fed793b37166f7cd91c57cdb2eab", features = ["serde", "zeroize", "rand", "recovery", "endomorphism"] } +test-common = { path = "../test-common" } diff --git a/client-network/src/network_ops/default_network_ops_client.rs b/client-network/src/network_ops/default_network_ops_client.rs index 558f7dce8..f7f24c3fc 100644 --- a/client-network/src/network_ops/default_network_ops_client.rs +++ b/client-network/src/network_ops/default_network_ops_client.rs @@ -542,9 +542,7 @@ mod tests { use chain_core::init::address::RedeemAddress; use chain_core::init::coin::CoinError; - use chain_core::state::account::{ - ConfidentialInit, StakedState, StakedStateOpAttributes, Validator, - }; + use chain_core::state::account::{StakedState, StakedStateOpAttributes, Validator}; use chain_core::state::tendermint::BlockHeight; use chain_core::state::tendermint::TendermintValidatorPubKey; use chain_core::state::ChainState; @@ -563,6 +561,7 @@ mod tests { use client_core::signer::WalletSignerManager; use client_core::types::WalletKind; use client_core::wallet::DefaultWalletClient; + use test_common::chain_env::mock_confidential_init; #[derive(Debug, Clone)] struct MockTransactionCipher; @@ -680,9 +679,7 @@ mod tests { Some(Validator { council_node: CouncilNode::new( TendermintValidatorPubKey::Ed25519([0xcd; 32]), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + mock_confidential_init(), ), jailed_until: Some(100), inactive_time: Some(0), @@ -1180,9 +1177,7 @@ mod tests { name: "test".to_owned(), security_contact: None, consensus_pubkey: TendermintValidatorPubKey::Ed25519(validator_pubkey), - confidential_init: ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + confidential_init: mock_confidential_init(), }; let transaction = network_ops_client diff --git a/client-rpc/Cargo.toml b/client-rpc/Cargo.toml index 28cf121c1..8733dac52 100644 --- a/client-rpc/Cargo.toml +++ b/client-rpc/Cargo.toml @@ -9,6 +9,7 @@ chain-core = { path = "../chain-core" } client-common = { path = "../client-common" } client-core = { path = "../client-core" } client-network = { path= "../client-network"} +test-common = { path = "../test-common" } base64 = "0.11" jsonrpc-core = "14.1" diff --git a/client-rpc/src/rpc/staking_rpc.rs b/client-rpc/src/rpc/staking_rpc.rs index 875752aaa..266a3c8b5 100644 --- a/client-rpc/src/rpc/staking_rpc.rs +++ b/client-rpc/src/rpc/staking_rpc.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::str::FromStr; use jsonrpc_core::Result; @@ -6,7 +7,7 @@ use jsonrpc_derive::rpc; use crate::{rpc_error_from_string, to_rpc_error}; use chain_core::init::coin::Coin; use chain_core::state::account::{ - ConfidentialInit, CouncilNode, StakedState, StakedStateAddress, StakedStateOpAttributes, + CouncilNode, StakedState, StakedStateAddress, StakedStateOpAttributes, }; use chain_core::state::tendermint::TendermintValidatorPubKey; use chain_core::tx::data::access::{TxAccess, TxAccessPolicy}; @@ -18,7 +19,7 @@ use client_common::{Error, ErrorKind, PublicKey, Result as CommonResult, ResultE use client_core::wallet::WalletRequest; use client_core::{MultiSigWalletClient, WalletClient}; use client_network::NetworkOpsClient; -use std::collections::BTreeSet; +use test_common::chain_env::mock_confidential_init; #[rpc(server)] pub trait StakingRpc: Send + Sync { @@ -461,8 +462,7 @@ fn get_node_metadata(validator_name: &str, validator_pubkey: &str) -> Result, ) -> Result> { let to_address = StakedStateAddress::from_str(&to_address_user).chain(|| { ( @@ -111,9 +113,7 @@ fn create_encoded_signed_join( security_contact: Some(validator_contact.to_string()), // 32 bytes consensus_pubkey: pubkey, - confidential_init: ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + confidential_init: ConfidentialInit { keypackage }, }; let transaction: NodeJoinRequestTx = NodeJoinRequestTx { nonce, @@ -149,6 +149,8 @@ pub unsafe extern "C" fn cro_join( validator_name_user: *const c_char, validator_contact_user: *const c_char, validator_pubkey_user: *const c_char, + keypackage: *const u8, + keypackage_len: usize, output: *mut u8, output_length: *mut u32, ) -> CroResult { @@ -166,6 +168,7 @@ pub unsafe extern "C" fn cro_join( &validator_name, &validator_contact, &validator_pubkey, + slice::from_raw_parts(keypackage, keypackage_len).to_vec(), ) { Ok(encoded) => { ptr::copy_nonoverlapping(encoded.as_ptr(), output, encoded.len()); diff --git a/dev-utils/Cargo.toml b/dev-utils/Cargo.toml index c7b2f25ef..1855eb2dd 100644 --- a/dev-utils/Cargo.toml +++ b/dev-utils/Cargo.toml @@ -11,6 +11,7 @@ client-core = { path = "../client-core" } client-network = { path = "../client-network" } chain-core = { path = "../chain-core/" } chain-abci = { path = "../chain-abci/" } +test-common = { path = "../test-common" } structopt = "0.3" hex = "0.4" serde_json = "1.0" diff --git a/dev-utils/src/commands/init_command.rs b/dev-utils/src/commands/init_command.rs index 00df1e74b..c6c505f6d 100644 --- a/dev-utils/src/commands/init_command.rs +++ b/dev-utils/src/commands/init_command.rs @@ -9,15 +9,13 @@ use secstr::SecUtf8; use serde_json::json; use chain_core::init::{address::RedeemAddress, coin::Coin, config::InitConfig}; -use chain_core::state::{ - account::ConfidentialInit, - tendermint::{TendermintValidator, TendermintValidatorPubKey}, -}; +use chain_core::state::tendermint::{TendermintValidator, TendermintValidatorPubKey}; use client_common::storage::SledStorage; use client_common::tendermint::types::Time; use client_common::{Error, ErrorKind, Result, ResultExt}; use client_core::types::WalletKind; use client_core::wallet::{DefaultWalletClient, WalletClient}; +use test_common::chain_env::mock_confidential_init; use super::genesis_command::generate_genesis; use super::genesis_dev_config::GenesisDevConfig; @@ -190,9 +188,8 @@ impl InitCommand { "dev test".to_owned(), None, pubkey, - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + // FIXME real keypackage + mock_confidential_init(), ), ); Ok(()) diff --git a/dev-utils/src/commands/test_vector_command.rs b/dev-utils/src/commands/test_vector_command.rs index ac15be12e..6bd1a173f 100644 --- a/dev-utils/src/commands/test_vector_command.rs +++ b/dev-utils/src/commands/test_vector_command.rs @@ -6,8 +6,8 @@ use chain_core::init::address::{CroAddress, RedeemAddress}; use chain_core::init::coin::Coin; use chain_core::init::network::Network; use chain_core::state::account::{ - ConfidentialInit, CouncilNode, DepositBondTx, StakedStateAddress, StakedStateOpAttributes, - StakedStateOpWitness, UnbondTx, WithdrawUnbondedTx, + CouncilNode, DepositBondTx, StakedStateAddress, StakedStateOpAttributes, StakedStateOpWitness, + UnbondTx, WithdrawUnbondedTx, }; use chain_core::state::tendermint::TendermintValidatorPubKey; use chain_core::state::validator::NodeJoinRequestTx; @@ -27,6 +27,7 @@ use client_core::service::{HDAccountType, HdKey}; use client_core::HDSeed; use secp256k1::Secp256k1; use secp256k1::{key::XOnlyPublicKey, SecretKey}; +use test_common::chain_env::mock_confidential_init; #[derive(Debug)] pub struct TestVectorCommand { @@ -321,9 +322,8 @@ impl VectorFactory { "example".to_string(), Some("security@example.com".to_string()), tendermint_validator_pubkey.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + // real keypackage + mock_confidential_init(), ), ); let txid = tx.id(); diff --git a/integration-tests/bot/chainbinding.py b/integration-tests/bot/chainbinding.py index 375b93915..2505b2a58 100644 --- a/integration-tests/bot/chainbinding.py +++ b/integration-tests/bot/chainbinding.py @@ -57,7 +57,7 @@ def __del__(self): dll.cro_destroy_jsonrpc(self._p) def call(self, req): - rsp = ctypes.create_string_buffer(10240) + rsp = ctypes.create_string_buffer(102400) retcode = dll.cro_run_jsonrpc(self._p, req.encode(), rsp, len(rsp), None) assert retcode == 0, rsp.value return rsp.value diff --git a/test-common/src/block_generator.rs b/test-common/src/block_generator.rs index 54d783034..4317ca673 100644 --- a/test-common/src/block_generator.rs +++ b/test-common/src/block_generator.rs @@ -27,9 +27,7 @@ use chain_core::init::config::NetworkParameters; use chain_core::init::{ address::RedeemAddress, coin::Coin, config::InitConfig, network::Network, params, }; -use chain_core::state::account::{ - ConfidentialInit, CouncilNode, StakedStateAddress, StakedStateDestination, -}; +use chain_core::state::account::{CouncilNode, StakedStateAddress, StakedStateDestination}; use chain_core::state::tendermint::{ TendermintValidatorAddress, TendermintValidatorPubKey, TendermintVotePower, }; @@ -46,6 +44,8 @@ use tendermint::block::BlockIDFlag::BlockIDFlagCommit; use tendermint::block::{CommitSig, CommitSigs}; use tendermint::hash::Algorithm; +use crate::chain_env::mock_confidential_init; + lazy_static! { static ref DEFAULT_NODES: Vec = vec![Node::new( 0, @@ -96,9 +96,7 @@ impl Node { name: self.name.clone(), security_contact: Some(format!("{}@example.com", self.name)), consensus_pubkey: self.tendermint_pub_key(), - confidential_init: ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + confidential_init: mock_confidential_init(), } } @@ -258,9 +256,7 @@ impl TestnetSpec { node.name.clone(), None, node.tendermint_pub_key(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + mock_confidential_init(), ), ); } @@ -285,15 +281,15 @@ impl TestnetSpec { .duration_since(Time::unix_epoch()) .expect("invalid genesis time") .as_secs(); - let (accounts, rewards_pool, nodes) = config + let genesis_state = config .validate_config_get_genesis(genesis_seconds) .expect("distribution validation error"); - let account_root = put_stakings(&mut store, 0, accounts.iter()).unwrap(); + let account_root = put_stakings(&mut store, 0, genesis_state.accounts.iter()).unwrap(); let network_params = NetworkParameters::Genesis(config.network_params.clone()); let app_hash = compute_app_hash( &MerkleTree::empty(), &account_root, - &rewards_pool, + &genesis_state.rewards_pool, &network_params, ); @@ -334,7 +330,11 @@ impl TestnetSpec { &StakingGetter::new(&store, 0), network_params.get_required_council_node_stake(), network_params.get_max_validators(), - &nodes.iter().map(|(addr, _)| *addr).collect::>(), + &genesis_state + .validators + .iter() + .map(|(addr, _)| *addr) + .collect::>(), ); let state = ChainNodeState::genesis( @@ -342,7 +342,7 @@ impl TestnetSpec { genesis_seconds, self.max_evidence_age, account_root, - rewards_pool, + genesis_state.rewards_pool, network_params, staking_table, ); diff --git a/test-common/src/chain_env.rs b/test-common/src/chain_env.rs index d5c7f3107..76a456a2e 100644 --- a/test-common/src/chain_env.rs +++ b/test-common/src/chain_env.rs @@ -49,6 +49,8 @@ lazy_static! { .map(|s| TendermintValidatorPubKey::from_base64(*s).unwrap()) .collect(); } +const KEYPACKAGE_VECTOR: &[u8] = + include_bytes!("../../chain-tx-enclave-next/mls/tests/test_vectors/keypackage.bin"); pub fn get_account( account_address: &StakedStateAddress, @@ -109,6 +111,21 @@ pub fn get_init_network_params(expansion_cap: Coin) -> InitNetworkParameters { } } +pub fn mock_council_node(consensus_pubkey: TendermintValidatorPubKey) -> CouncilNode { + CouncilNode::new_with_details( + "no-name".to_string(), + None, + consensus_pubkey, + mock_confidential_init(), + ) +} + +pub fn mock_confidential_init() -> ConfidentialInit { + ConfidentialInit { + keypackage: KEYPACKAGE_VECTOR.to_vec(), + } +} + pub fn get_nodes( addresses: &[Account], ) -> BTreeMap< @@ -129,9 +146,7 @@ pub fn get_nodes( acct.name.clone(), None, acct.validator_pub_key.clone(), - ConfidentialInit { - keypackage: b"FIXME".to_vec(), - }, + mock_confidential_init(), ), ) }) @@ -228,15 +243,15 @@ impl ChainEnv { ); let timestamp = Timestamp::new(); - let (states, rewards_pool_state, council_nodes) = init_config + let genesis_state = init_config .validate_config_get_genesis(timestamp.get_seconds().try_into().unwrap()) .expect("Error while validating distribution"); - let new_account_root = storage.put_stakings(0, &states); + let new_account_root = storage.put_stakings(0, &genesis_state.accounts); let genesis_app_hash = compute_app_hash( &MerkleTree::empty(), &new_account_root, - &rewards_pool_state, + &genesis_state.rewards_pool, &NetworkParameters::Genesis(init_network_params), ); ( @@ -247,7 +262,7 @@ impl ChainEnv { timestamp, init_config, max_evidence_age: 172_800, - council_nodes, + council_nodes: genesis_state.validators, accounts, }, storage,