Skip to content

Commit

Permalink
fix: add check for matching proof key (#251)
Browse files Browse the repository at this point in the history
  • Loading branch information
hu55a1n1 authored Oct 24, 2024
1 parent d37355e commit bc24db6
Show file tree
Hide file tree
Showing 19 changed files with 146 additions and 67 deletions.
8 changes: 5 additions & 3 deletions crates/cli/src/handler/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ async fn handshake(args: HandshakeRequest, config: Config) -> Result<String> {

info!("Running SessionCreate");

let res: serde_json::Value = RelayMessage::SessionCreate
.run_relay(config.enclave_rpc())
.await?;
let res: serde_json::Value = RelayMessage::SessionCreate {
contract: args.contract.clone(),
}
.run_relay(config.enclave_rpc())
.await?;

let output: WasmdTxResponse = serde_json::from_str(
cw_client
Expand Down
9 changes: 6 additions & 3 deletions crates/cli/src/handler/utils/relay.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use color_eyre::{eyre::eyre, Result};
use cosmrs::AccountId;
use quartz_common::proto::{
core_client::CoreClient, InstantiateRequest, SessionCreateRequest, SessionSetPubKeyRequest,
};
Expand All @@ -8,7 +9,7 @@ use serde_json::{json, Value as JsonValue};
#[derive(Debug)]
pub enum RelayMessage {
Instantiate { init_msg: JsonValue },
SessionCreate,
SessionCreate { contract: AccountId },
SessionSetPubKey { proof: ProofOutput },
}

Expand Down Expand Up @@ -37,8 +38,10 @@ impl RelayMessage {
init_msg["quartz"] = msg;
init_msg.to_string()
})?,
RelayMessage::SessionCreate => qc_client
.session_create(tonic::Request::new(SessionCreateRequest {}))
RelayMessage::SessionCreate { contract } => qc_client
.session_create(tonic::Request::new(SessionCreateRequest {
message: serde_json::to_string(&contract)?,
}))
.await
.map_err(|e| {
eyre!(
Expand Down
2 changes: 2 additions & 0 deletions crates/contracts/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum Error {
TcbInfoQueryError(String),
#[error("DCAP verification query error: {0}")]
DcapVerificationQueryError(String),
#[error("contract address mismatch")]
ContractAddrMismatch,
}

impl From<K256Error> for Error {
Expand Down
10 changes: 8 additions & 2 deletions crates/contracts/core/src/handler/execute/session_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ use crate::{
};

impl Handler for SessionCreate {
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
fn handle(self, deps: DepsMut<'_>, env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
// TODO(hu55a1n1): overwrite previous session?

let addr = deps.api.addr_validate(self.contract())?;
if addr != env.contract.address {
return Err(Error::ContractAddrMismatch);
}

SESSION
.save(deps.storage, &Session::create(self.into_nonce()))
.save(deps.storage, &Session::create(self.nonce()))
.map_err(Error::Std)?;

Ok(Response::new().add_attribute("action", "session_create"))
Expand Down
16 changes: 12 additions & 4 deletions crates/contracts/core/src/msg/execute/session_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,44 @@ use crate::{
#[derive(Clone, Debug, PartialEq)]
pub struct SessionCreate {
nonce: Nonce,
contract: String,
}

impl SessionCreate {
pub fn new(nonce: Nonce) -> Self {
Self { nonce }
pub fn new(nonce: Nonce, contract: String) -> Self {
Self { nonce, contract }
}

pub fn into_nonce(self) -> Nonce {
pub fn nonce(&self) -> Nonce {
self.nonce
}

pub fn contract(&self) -> &str {
self.contract.as_str()
}
}

#[cw_serde]
pub struct RawSessionCreate {
nonce: HexBinary,
contract: String,
}

impl TryFrom<RawSessionCreate> for SessionCreate {
type Error = StdError;

fn try_from(value: RawSessionCreate) -> Result<Self, Self::Error> {
let nonce = value.nonce.to_array()?;
Ok(Self { nonce })
let contract = value.contract;
Ok(Self { nonce, contract })
}
}

impl From<SessionCreate> for RawSessionCreate {
fn from(value: SessionCreate) -> Self {
Self {
nonce: value.nonce.into(),
contract: value.contract,
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions crates/contracts/core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ pub type Hash = [u8; 32];
pub type Height = u64;
pub type TrustThreshold = (u64, u64);

pub const CONFIG_KEY: &str = "quartz_config";
pub const SESSION_KEY: &str = "quartz_session";
pub const EPOCH_COUNTER_KEY: &str = "epoch_counter";
pub const CONFIG: Item<RawConfig> = Item::new(CONFIG_KEY);
pub const SESSION: Item<Session> = Item::new(SESSION_KEY);
pub const EPOCH_COUNTER: Item<Uint64> = Item::new(EPOCH_COUNTER_KEY);

#[derive(Clone, Debug, PartialEq)]
pub struct Config {
mr_enclave: MrEnclave,
Expand Down Expand Up @@ -247,7 +254,3 @@ impl Session {
self.nonce.to_array().expect("correct by construction")
}
}

pub const CONFIG: Item<RawConfig> = Item::new("quartz_config");
pub const SESSION: Item<Session> = Item::new("quartz_session");
pub const EPOCH_COUNTER: Item<Uint64> = Item::new("epoch_counter");
54 changes: 47 additions & 7 deletions crates/enclave/core/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
time::Duration,
};

use cosmrs::AccountId;
use futures_util::StreamExt;
use k256::ecdsa::SigningKey;
use quartz_contract_core::{
Expand All @@ -15,10 +16,11 @@ use quartz_contract_core::{
},
instantiate::CoreInstantiate,
},
state::{Config, LightClientOpts, Nonce, Session},
state::{Config, LightClientOpts, Nonce, Session, SESSION_KEY},
};
use quartz_cw_proof::proof::{
cw::{CwProof, RawCwProof},
key::CwAbciKey,
Proof,
};
use quartz_proto::quartz::{
Expand Down Expand Up @@ -106,13 +108,19 @@ impl QuartzServer {
pub fn new<A>(
config: Config,
sk: Arc<Mutex<Option<SigningKey>>>,
contract: Arc<Mutex<Option<AccountId>>>,
attestor: A,
ws_config: WsListenerConfig,
) -> Self
where
A: Attestor + Clone,
{
let core_service = CoreServer::new(CoreService::new(config, sk.clone(), attestor.clone()));
let core_service = CoreServer::new(CoreService::new(
config,
contract.clone(),
sk.clone(),
attestor.clone(),
));

Self {
router: Server::builder().add_service(core_service),
Expand Down Expand Up @@ -184,6 +192,7 @@ impl QuartzServer {
pub struct CoreService<A> {
config: Config,
nonce: Arc<Mutex<Nonce>>,
contract: Arc<Mutex<Option<AccountId>>>,
sk: Arc<Mutex<Option<SigningKey>>>,
attestor: A,
}
Expand All @@ -192,10 +201,16 @@ impl<A> CoreService<A>
where
A: Attestor,
{
pub fn new(config: Config, sk: Arc<Mutex<Option<SigningKey>>>, attestor: A) -> Self {
pub fn new(
config: Config,
contract: Arc<Mutex<Option<AccountId>>>,
sk: Arc<Mutex<Option<SigningKey>>>,
attestor: A,
) -> Self {
Self {
config,
nonce: Arc::new(Mutex::new([0u8; 32])),
contract,
sk,
attestor,
}
Expand Down Expand Up @@ -226,12 +241,19 @@ where

async fn session_create(
&self,
_request: Request<RawSessionCreateRequest>,
request: Request<RawSessionCreateRequest>,
) -> TonicResult<Response<RawSessionCreateResponse>> {
// FIXME(hu55a1n1) - disallow calling more than once
let deployed_contract: AccountId = serde_json::from_str(&request.into_inner().message)
.map_err(|e| Status::invalid_argument(e.to_string()))?;

let mut contract = self.contract.lock().unwrap();
*contract = Some(deployed_contract.clone());

let mut nonce = self.nonce.lock().unwrap();
*nonce = rand::thread_rng().gen::<Nonce>();
let msg = SessionCreate::new(*nonce);

let msg = SessionCreate::new(*nonce, deployed_contract.to_string());

let attestation = self
.attestor
Expand All @@ -253,8 +275,15 @@ where
serde_json::from_str(&request.into_inner().message)
.map_err(|e| Status::invalid_argument(e.to_string()))?;

let contract = self.contract.lock().unwrap().clone();

let (value, _msg) = proof
.verify(self.config.light_client_opts())
.verify(
self.config.light_client_opts(),
contract.expect("contract not set"),
SESSION_KEY.to_string(),
None,
)
.map_err(Status::failed_precondition)?;

let session: Session = serde_json::from_slice(&value).unwrap();
Expand Down Expand Up @@ -290,7 +319,13 @@ pub struct ProofOfPublication<M> {
}

impl<M> ProofOfPublication<M> {
pub fn verify(self, light_client_opts: &LightClientOpts) -> Result<(Vec<u8>, M), String> {
pub fn verify(
self,
light_client_opts: &LightClientOpts,
contract_address: AccountId,
storage_key: String,
storage_namespace: Option<String>,
) -> Result<(Vec<u8>, M), String> {
let config_trust_threshold = light_client_opts.trust_threshold();
let trust_threshold =
TrustThreshold::new(config_trust_threshold.0, config_trust_threshold.1).unwrap();
Expand Down Expand Up @@ -322,6 +357,11 @@ impl<M> ProofOfPublication<M> {
.and_then(|mut primary| primary.verify_to_height(target_height))
.map_err(|e| e.to_string())?;

let key = CwAbciKey::new(contract_address, storage_key, storage_namespace);
if key.into_vec() != self.merkle_proof.key() {
return Err("Merkle proof key mismatch".to_string());
}

let proof = CwProof::from(self.merkle_proof);
proof
.verify(
Expand Down
6 changes: 6 additions & 0 deletions crates/enclave/cw-proof/src/proof/cw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ pub struct RawCwProof {
proof: ProofOps,
}

impl RawCwProof {
pub fn key(&self) -> &[u8] {
self.key.as_ref()
}
}

impl From<RawCwProof> for CwProof {
fn from(RawCwProof { key, value, proof }: RawCwProof) -> Self {
Self {
Expand Down
4 changes: 4 additions & 0 deletions crates/enclave/cw-proof/src/proof/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ impl CwAbciKey {
}
}

pub fn into_vec(self) -> Vec<u8> {
self.into()
}

fn into_tuple(self) -> (AccountId, String, Option<String>) {
match self {
CwAbciKey::Item {
Expand Down
4 changes: 3 additions & 1 deletion crates/enclave/proto/proto/quartz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ message InstantiateResponse {
string message = 1;
}

message SessionCreateRequest {}
message SessionCreateRequest {
string message = 1;
}

message SessionCreateResponse {
string message = 1;
Expand Down
7 changes: 5 additions & 2 deletions crates/enclave/proto/src/prost/quartz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ pub struct InstantiateResponse {
#[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String,
}
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
pub struct SessionCreateRequest {}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SessionCreateRequest {
#[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SessionCreateResponse {
#[prost(string, tag = "1")]
Expand Down
10 changes: 0 additions & 10 deletions examples/transfers/contracts/Cargo.lock

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

3 changes: 3 additions & 0 deletions examples/transfers/contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ getrandom = { version = "0.2.15", features = ["js"] }
[dev-dependencies]
cw-multi-test = { version = "2.1.0", default-features = false }
serde_json = "1.0.122"

[patch.crates-io]
quartz-common = { path = "../../../crates/common" }
3 changes: 2 additions & 1 deletion examples/transfers/contracts/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use cw_storage_plus::{Item, Map};

use crate::msg::execute::Request;

pub const REQUESTS_KEY: &str = "requests";
pub const STATE: Item<HexBinary> = Item::new("state");
pub const REQUESTS: Item<Vec<Request>> = Item::new("requests");
pub const REQUESTS: Item<Vec<Request>> = Item::new(REQUESTS_KEY);
pub const DENOM: Item<String> = Item::new("donation_denom");
pub const BALANCES: Map<&str, HexBinary> = Map::new("balances");
Loading

0 comments on commit bc24db6

Please sign in to comment.