From 8c49861bf44f17242acb60510611635743640fba Mon Sep 17 00:00:00 2001 From: peartes Date: Wed, 20 Dec 2023 19:32:36 +0100 Subject: [PATCH 1/6] chore: [WIP] custom queries --- account/Cargo.toml | 4 +++- account/src/auth/passkey.rs | 20 ++++++++++++++------ account/src/lib.rs | 1 + account/src/proto.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 account/src/proto.rs diff --git a/account/Cargo.toml b/account/Cargo.toml index 59285ac..14fed11 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -35,4 +35,6 @@ passkey-authenticator = { git="https://github.com/aptos-labs/passkey-rs.git", br url = "2.4.1" coset = "0.3.5" futures = "0.3.29" -async-trait = "0.1.74" \ No newline at end of file +async-trait = "0.1.74" +prost = {version = "0.11.2", default-features = false, features = ["prost-derive"]} +osmosis-std-derive = "0.13.2" diff --git a/account/src/auth/passkey.rs b/account/src/auth/passkey.rs index dc67981..e61e68f 100644 --- a/account/src/auth/passkey.rs +++ b/account/src/auth/passkey.rs @@ -1,8 +1,9 @@ use crate::error::ContractResult; +use crate::proto::{self, QueryWebAuthNVerifyRegisterRequest, QueryWebAuthNVerifyRegisterResponse}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use cosmwasm_schema::cw_serde; -use cosmwasm_std::QueryRequest::Stargate; +use cosmwasm_std::QueryRequest::{Custom, Stargate}; use cosmwasm_std::{to_binary, Addr, Binary, Deps}; #[cw_serde] @@ -27,12 +28,19 @@ pub fn register(deps: Deps, addr: Addr, rp: String, data: Binary) -> ContractRes }; let query_bz = to_binary(&query)?; - let query_response: QueryRegisterResponse = deps.querier.query(&Stargate { - path: "xion.v1.Query/WebAuthNVerifyRegister".to_string(), - data: query_bz, - })?; + let query_msg = proto::QueryWebAuthNVerifyRegisterRequest { + addr: addr.into(), + challenge: addr.to_string(), + rp, + data: data.into(), + }; + let query_response = deps + .querier + .query::(&Custom::< + QueryWebAuthNVerifyRegisterRequest, + >(query_msg))?; - Ok(query_response.credential) + Ok(query_response.credential.into()) } #[cw_serde] diff --git a/account/src/lib.rs b/account/src/lib.rs index 4ddb9d0..5f48840 100644 --- a/account/src/lib.rs +++ b/account/src/lib.rs @@ -6,6 +6,7 @@ pub mod contract; pub mod error; pub mod execute; pub mod msg; +pub mod proto; pub mod query; pub mod state; diff --git a/account/src/proto.rs b/account/src/proto.rs new file mode 100644 index 0000000..5a8f5ea --- /dev/null +++ b/account/src/proto.rs @@ -0,0 +1,31 @@ +use osmosis_std_derive::CosmwasmExt; + +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "xion.v1.Query/WebAuthNVerifyRegister")] +#[proto_query(path = "xion.v1.Query/WebAuthNVerifyRegister", response_type = QueryWebAuthNVerifyRegisterResponse)] +pub struct QueryWebAuthNVerifyRegisterRequest { + #[prost(string, tag = "1")] + pub addr: String, + #[prost(string, tag = "2")] + pub challenge: String, + #[prost(string, tag = "3")] + pub rp: String, + #[prost(bytes, tag = "4")] + pub data: Vec, +} + +// We define the response as a prost message to be able to decode the protobuf data. +#[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize)] +pub struct QueryWebAuthNVerifyRegisterResponse { + #[prost(bytes, tag = "1")] + pub credential: Vec, +} From fd713f71bd778ee5057d9e87d9ea8e8564d08898 Mon Sep 17 00:00:00 2001 From: peartes Date: Mon, 15 Jan 2024 18:11:59 +0100 Subject: [PATCH 2/6] chore: properly send stargate query data --- Cargo.toml | 3 -- account/Cargo.toml | 7 ++-- account/src/auth.rs | 4 +-- account/src/auth/passkey.rs | 32 +++++++---------- account/src/auth/sign_arb.rs | 14 ++++++-- account/src/contract.rs | 11 ++++-- account/src/error.rs | 3 -- account/src/execute.rs | 70 ++++++++++++++++++++++++++++++++---- account/src/proto.rs | 14 ++++++-- 9 files changed, 111 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a6b687..0950cf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,3 @@ phf = { version = "0.11.2", features = ["macros"] } rsa = { version = "0.9.2" } getrandom = { version = "0.2.10", features = ["custom"] } p256 = {version = "0.13.2", features = ["ecdsa-core", "arithmetic", "serde"]} -webauthn-rs = { git = "https://github.com/burnt-labs/webauthn-rs.git", features = ["danger-credential-internals"] } -webauthn-rs-proto = { git = "https://github.com/burnt-labs/webauthn-rs.git" } -webauthn-rs-core = { git = "https://github.com/burnt-labs/webauthn-rs.git" } diff --git a/account/Cargo.toml b/account/Cargo.toml index 14fed11..21eb054 100644 --- a/account/Cargo.toml +++ b/account/Cargo.toml @@ -27,11 +27,8 @@ phf = { workspace = true } rsa = { workspace = true } getrandom = { workspace = true } p256 = { workspace = true } -webauthn-rs = { workspace = true } -webauthn-rs-proto = { workspace = true } -webauthn-rs-core = { workspace = true } -passkey = { git="https://github.com/aptos-labs/passkey-rs.git", branch = "fix-passkey-rs"} -passkey-authenticator = { git="https://github.com/aptos-labs/passkey-rs.git", branch = "fix-passkey-rs", features = ["testable"] } +passkey = { git="https://github.com/aptos-labs/passkey-rs.git"} +passkey-authenticator = { git="https://github.com/aptos-labs/passkey-rs.git", features = ["testable"] } url = "2.4.1" coset = "0.3.5" futures = "0.3.29" diff --git a/account/src/auth.rs b/account/src/auth.rs index 6b3103b..5e38abf 100644 --- a/account/src/auth.rs +++ b/account/src/auth.rs @@ -1,5 +1,5 @@ -use crate::auth::secp256r1::verify; use crate::error::ContractError; +use crate::{auth::secp256r1::verify, proto::MyCustomQuery}; use cosmwasm_std::{Binary, Deps, Env}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -72,7 +72,7 @@ pub enum Authenticator { impl Authenticator { pub fn verify( &self, - deps: Deps, + deps: Deps, env: &Env, tx_bytes: &Binary, sig_bytes: &Binary, diff --git a/account/src/auth/passkey.rs b/account/src/auth/passkey.rs index e61e68f..90bac9e 100644 --- a/account/src/auth/passkey.rs +++ b/account/src/auth/passkey.rs @@ -1,9 +1,9 @@ use crate::error::ContractResult; -use crate::proto::{self, QueryWebAuthNVerifyRegisterRequest, QueryWebAuthNVerifyRegisterResponse}; +use crate::proto::{self, MyCustomQuery}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use cosmwasm_schema::cw_serde; -use cosmwasm_std::QueryRequest::{Custom, Stargate}; +use cosmwasm_std::QueryRequest::Stargate; use cosmwasm_std::{to_binary, Addr, Binary, Deps}; #[cw_serde] @@ -19,28 +19,22 @@ struct QueryRegisterResponse { credential: Binary, } -pub fn register(deps: Deps, addr: Addr, rp: String, data: Binary) -> ContractResult { - let query = QueryRegisterRequest { +pub fn register( + deps: Deps, + addr: Addr, + rp: String, + data: Binary, +) -> ContractResult { + let query = proto::QueryWebAuthNVerifyRegisterRequest { addr: addr.clone().into(), challenge: addr.to_string(), rp, - data, + data: data.to_vec(), }; - let query_bz = to_binary(&query)?; - let query_msg = proto::QueryWebAuthNVerifyRegisterRequest { - addr: addr.into(), - challenge: addr.to_string(), - rp, - data: data.into(), - }; - let query_response = deps - .querier - .query::(&Custom::< - QueryWebAuthNVerifyRegisterRequest, - >(query_msg))?; + let query_response = deps.querier.query::(&query.into())?; - Ok(query_response.credential.into()) + Ok(query_response.credential) } #[cw_serde] @@ -53,7 +47,7 @@ struct QueryVerifyRequest { } pub fn verify( - deps: Deps, + deps: Deps, addr: Addr, rp: String, signature: &Binary, diff --git a/account/src/auth/sign_arb.rs b/account/src/auth/sign_arb.rs index 186c77e..f542064 100644 --- a/account/src/auth/sign_arb.rs +++ b/account/src/auth/sign_arb.rs @@ -34,9 +34,12 @@ mod tests { use crate::auth::AddAuthenticator::Secp256K1; use crate::contract::instantiate; use crate::msg::InstantiateMsg; + use crate::proto::MyCustomQuery; use base64::{engine::general_purpose, Engine as _}; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{Addr, Api, Binary}; + use cosmwasm_std::testing::{ + mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, + }; + use cosmwasm_std::{Addr, Api, Binary, OwnedDeps}; #[test] fn test_derive_addr() { @@ -92,7 +95,12 @@ mod tests { #[test] fn test_init_sign_arb() { - let mut deps = mock_dependencies(); + let mut deps = OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: MockQuerier::::new(&[]), + custom_query_type: core::marker::PhantomData::, + }; let mut env = mock_env(); let info = mock_info("sender", &[]); // This is the local faucet address to simplify reuse diff --git a/account/src/contract.rs b/account/src/contract.rs index 9c45af5..0e89c5a 100644 --- a/account/src/contract.rs +++ b/account/src/contract.rs @@ -6,6 +6,7 @@ use absacc::AccountSudoMsg; use crate::execute::{add_auth_method, assert_self, remove_auth_method}; use crate::msg::ExecuteMsg; +use crate::proto::MyCustomQuery; use crate::{ error::ContractResult, execute, @@ -15,7 +16,7 @@ use crate::{ #[entry_point] pub fn instantiate( - deps: DepsMut, + deps: DepsMut, env: Env, _info: MessageInfo, msg: InstantiateMsg, @@ -25,7 +26,11 @@ pub fn instantiate( } #[entry_point] -pub fn sudo(deps: DepsMut, env: Env, msg: AccountSudoMsg) -> ContractResult { +pub fn sudo( + deps: DepsMut, + env: Env, + msg: AccountSudoMsg, +) -> ContractResult { match msg { AccountSudoMsg::BeforeTx { tx_bytes, @@ -45,7 +50,7 @@ pub fn sudo(deps: DepsMut, env: Env, msg: AccountSudoMsg) -> ContractResult, env: Env, info: MessageInfo, msg: ExecuteMsg, diff --git a/account/src/error.rs b/account/src/error.rs index f1420e4..3a80ae5 100644 --- a/account/src/error.rs +++ b/account/src/error.rs @@ -30,9 +30,6 @@ pub enum ContractError { #[error(transparent)] P256EcdsaCurve(#[from] p256::ecdsa::Error), - #[error(transparent)] - WebauthnError(#[from] webauthn_rs_core::error::WebauthnError), - #[error("error rebuilding key")] RebuildingKey, diff --git a/account/src/execute.rs b/account/src/execute.rs index 329a6e0..18f7a6e 100644 --- a/account/src/execute.rs +++ b/account/src/execute.rs @@ -1,13 +1,14 @@ use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Env, Event, Order, Response}; use crate::auth::{passkey, AddAuthenticator, Authenticator}; +use crate::proto::MyCustomQuery; use crate::{ error::{ContractError, ContractResult}, state::AUTHENTICATORS, }; pub fn init( - deps: DepsMut, + deps: DepsMut, env: Env, add_authenticator: AddAuthenticator, ) -> ContractResult { @@ -26,7 +27,7 @@ pub fn init( } pub fn before_tx( - deps: Deps, + deps: Deps, env: &Env, tx_bytes: &Binary, cred_bytes: Option<&Binary>, @@ -87,7 +88,7 @@ pub fn after_tx() -> ContractResult { } pub fn add_auth_method( - deps: DepsMut, + deps: DepsMut, env: Env, add_authenticator: AddAuthenticator, ) -> ContractResult { @@ -219,7 +220,11 @@ pub fn add_auth_method( ) } -pub fn remove_auth_method(deps: DepsMut, env: Env, id: u8) -> ContractResult { +pub fn remove_auth_method( + deps: DepsMut, + env: Env, + id: u8, +) -> ContractResult { if AUTHENTICATORS .keys(deps.storage, None, None, Order::Ascending) .count() @@ -248,17 +253,24 @@ pub fn assert_self(sender: &Addr, contract: &Addr) -> ContractResult<()> { #[cfg(test)] mod tests { use base64::{engine::general_purpose, Engine as _}; - use cosmwasm_std::testing::{mock_dependencies, mock_env}; - use cosmwasm_std::Binary; + use cosmwasm_std::testing::{mock_env, MockApi, MockQuerier, MockStorage}; + use cosmwasm_std::{Binary, OwnedDeps}; use crate::auth::Authenticator; use crate::execute::before_tx; + use crate::proto::{self, MyCustomQuery, QueryWebAuthNVerifyRegisterResponse}; use crate::state::AUTHENTICATORS; + use cosmwasm_std::QueryRequest::Custom; #[test] fn test_before_tx() { let auth_id = 0; - let mut deps = mock_dependencies(); + let mut deps = OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: MockQuerier::::new(&[]), + custom_query_type: std::marker::PhantomData, + }; let env = mock_env(); let pubkey = "Ayrlj6q3WWs91p45LVKwI8JyfMYNmWMrcDinLNEdWYE4"; @@ -286,4 +298,48 @@ mod tests { before_tx(deps.as_ref(), &env, &tx_bytes, Some(&sig_bytes), false).unwrap(); } + + #[test] + pub fn test_custom_querier() { + let mut deps = OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: MockQuerier::::new(&[]), + custom_query_type: core::marker::PhantomData::, + }; + + deps.querier = deps.querier.with_custom_handler(|query| match query { + MyCustomQuery::Verify(data) => { + assert_eq!(data.addr, "mock_address"); + assert_eq!(data.challenge, "mock_challenge"); + assert_eq!(data.rp, "mock_rp"); + assert_eq!(data.data, vec![0u8]); + + cosmwasm_std::SystemResult::Ok(cosmwasm_std::ContractResult::Ok( + serde_json::to_vec(&QueryWebAuthNVerifyRegisterResponse { + credential: Binary::from("true".as_bytes()).into(), + }) + .unwrap() + .into(), + )) + } + }); + + let query_msg = MyCustomQuery::Verify(proto::QueryWebAuthNVerifyRegisterRequest { + addr: "mock_address".to_string(), + challenge: "mock_challenge".to_string(), + rp: "mock_rp".to_string(), + data: vec![0u8], + }); + let query_response = deps + .as_ref() + .querier + .query::(&Custom(query_msg)); + assert!(query_response.is_ok()); + + assert_eq!( + query_response.unwrap().credential, + Binary::from("true".as_bytes()) + ); + } } diff --git a/account/src/proto.rs b/account/src/proto.rs index 5a8f5ea..7d78961 100644 --- a/account/src/proto.rs +++ b/account/src/proto.rs @@ -1,4 +1,7 @@ +use cosmwasm_std::CustomQuery; use osmosis_std_derive::CosmwasmExt; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; #[derive( Clone, @@ -10,8 +13,8 @@ use osmosis_std_derive::CosmwasmExt; schemars::JsonSchema, CosmwasmExt, )] -#[proto_message(type_url = "xion.v1.Query/WebAuthNVerifyRegister")] -#[proto_query(path = "xion.v1.Query/WebAuthNVerifyRegister", response_type = QueryWebAuthNVerifyRegisterResponse)] +#[proto_message(type_url = "/xion.v1.Query/WebAuthNVerifyRegister")] +#[proto_query(path = "/xion.v1.Query/WebAuthNVerifyRegister", response_type = QueryWebAuthNVerifyRegisterResponse)] pub struct QueryWebAuthNVerifyRegisterRequest { #[prost(string, tag = "1")] pub addr: String, @@ -29,3 +32,10 @@ pub struct QueryWebAuthNVerifyRegisterResponse { #[prost(bytes, tag = "1")] pub credential: Vec, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum MyCustomQuery { + Verify(QueryWebAuthNVerifyRegisterRequest), +} +impl CustomQuery for MyCustomQuery {} From 194d15de082181dceabc5bf6e6c61d52c66445ef Mon Sep 17 00:00:00 2001 From: peartes Date: Wed, 17 Jan 2024 17:54:00 +0100 Subject: [PATCH 3/6] chore: rename custom query --- account/src/auth.rs | 4 ++-- account/src/auth/passkey.rs | 6 +++--- account/src/auth/sign_arb.rs | 6 +++--- account/src/contract.rs | 8 ++++---- account/src/execute.rs | 22 +++++++++++----------- account/src/proto.rs | 4 ++-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/account/src/auth.rs b/account/src/auth.rs index 5e38abf..f31f0c5 100644 --- a/account/src/auth.rs +++ b/account/src/auth.rs @@ -1,5 +1,5 @@ use crate::error::ContractError; -use crate::{auth::secp256r1::verify, proto::MyCustomQuery}; +use crate::{auth::secp256r1::verify, proto::XionCustomQuery}; use cosmwasm_std::{Binary, Deps, Env}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -72,7 +72,7 @@ pub enum Authenticator { impl Authenticator { pub fn verify( &self, - deps: Deps, + deps: Deps, env: &Env, tx_bytes: &Binary, sig_bytes: &Binary, diff --git a/account/src/auth/passkey.rs b/account/src/auth/passkey.rs index 90bac9e..c4edf5c 100644 --- a/account/src/auth/passkey.rs +++ b/account/src/auth/passkey.rs @@ -1,5 +1,5 @@ use crate::error::ContractResult; -use crate::proto::{self, MyCustomQuery}; +use crate::proto::{self, XionCustomQuery}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use cosmwasm_schema::cw_serde; @@ -20,7 +20,7 @@ struct QueryRegisterResponse { } pub fn register( - deps: Deps, + deps: Deps, addr: Addr, rp: String, data: Binary, @@ -47,7 +47,7 @@ struct QueryVerifyRequest { } pub fn verify( - deps: Deps, + deps: Deps, addr: Addr, rp: String, signature: &Binary, diff --git a/account/src/auth/sign_arb.rs b/account/src/auth/sign_arb.rs index f542064..f197532 100644 --- a/account/src/auth/sign_arb.rs +++ b/account/src/auth/sign_arb.rs @@ -34,7 +34,7 @@ mod tests { use crate::auth::AddAuthenticator::Secp256K1; use crate::contract::instantiate; use crate::msg::InstantiateMsg; - use crate::proto::MyCustomQuery; + use crate::proto::XionCustomQuery; use base64::{engine::general_purpose, Engine as _}; use cosmwasm_std::testing::{ mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage, @@ -98,8 +98,8 @@ mod tests { let mut deps = OwnedDeps { storage: MockStorage::default(), api: MockApi::default(), - querier: MockQuerier::::new(&[]), - custom_query_type: core::marker::PhantomData::, + querier: MockQuerier::::new(&[]), + custom_query_type: core::marker::PhantomData::, }; let mut env = mock_env(); let info = mock_info("sender", &[]); diff --git a/account/src/contract.rs b/account/src/contract.rs index 0e89c5a..48e6680 100644 --- a/account/src/contract.rs +++ b/account/src/contract.rs @@ -6,7 +6,7 @@ use absacc::AccountSudoMsg; use crate::execute::{add_auth_method, assert_self, remove_auth_method}; use crate::msg::ExecuteMsg; -use crate::proto::MyCustomQuery; +use crate::proto::XionCustomQuery; use crate::{ error::ContractResult, execute, @@ -16,7 +16,7 @@ use crate::{ #[entry_point] pub fn instantiate( - deps: DepsMut, + deps: DepsMut, env: Env, _info: MessageInfo, msg: InstantiateMsg, @@ -27,7 +27,7 @@ pub fn instantiate( #[entry_point] pub fn sudo( - deps: DepsMut, + deps: DepsMut, env: Env, msg: AccountSudoMsg, ) -> ContractResult { @@ -50,7 +50,7 @@ pub fn sudo( #[entry_point] pub fn execute( - deps: DepsMut, + deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, diff --git a/account/src/execute.rs b/account/src/execute.rs index 18f7a6e..3c36c8c 100644 --- a/account/src/execute.rs +++ b/account/src/execute.rs @@ -1,14 +1,14 @@ use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Env, Event, Order, Response}; use crate::auth::{passkey, AddAuthenticator, Authenticator}; -use crate::proto::MyCustomQuery; +use crate::proto::XionCustomQuery; use crate::{ error::{ContractError, ContractResult}, state::AUTHENTICATORS, }; pub fn init( - deps: DepsMut, + deps: DepsMut, env: Env, add_authenticator: AddAuthenticator, ) -> ContractResult { @@ -27,7 +27,7 @@ pub fn init( } pub fn before_tx( - deps: Deps, + deps: Deps, env: &Env, tx_bytes: &Binary, cred_bytes: Option<&Binary>, @@ -88,7 +88,7 @@ pub fn after_tx() -> ContractResult { } pub fn add_auth_method( - deps: DepsMut, + deps: DepsMut, env: Env, add_authenticator: AddAuthenticator, ) -> ContractResult { @@ -221,7 +221,7 @@ pub fn add_auth_method( } pub fn remove_auth_method( - deps: DepsMut, + deps: DepsMut, env: Env, id: u8, ) -> ContractResult { @@ -258,7 +258,7 @@ mod tests { use crate::auth::Authenticator; use crate::execute::before_tx; - use crate::proto::{self, MyCustomQuery, QueryWebAuthNVerifyRegisterResponse}; + use crate::proto::{self, QueryWebAuthNVerifyRegisterResponse, XionCustomQuery}; use crate::state::AUTHENTICATORS; use cosmwasm_std::QueryRequest::Custom; @@ -268,7 +268,7 @@ mod tests { let mut deps = OwnedDeps { storage: MockStorage::default(), api: MockApi::default(), - querier: MockQuerier::::new(&[]), + querier: MockQuerier::::new(&[]), custom_query_type: std::marker::PhantomData, }; let env = mock_env(); @@ -304,12 +304,12 @@ mod tests { let mut deps = OwnedDeps { storage: MockStorage::default(), api: MockApi::default(), - querier: MockQuerier::::new(&[]), - custom_query_type: core::marker::PhantomData::, + querier: MockQuerier::::new(&[]), + custom_query_type: core::marker::PhantomData::, }; deps.querier = deps.querier.with_custom_handler(|query| match query { - MyCustomQuery::Verify(data) => { + XionCustomQuery::Verify(data) => { assert_eq!(data.addr, "mock_address"); assert_eq!(data.challenge, "mock_challenge"); assert_eq!(data.rp, "mock_rp"); @@ -325,7 +325,7 @@ mod tests { } }); - let query_msg = MyCustomQuery::Verify(proto::QueryWebAuthNVerifyRegisterRequest { + let query_msg = XionCustomQuery::Verify(proto::QueryWebAuthNVerifyRegisterRequest { addr: "mock_address".to_string(), challenge: "mock_challenge".to_string(), rp: "mock_rp".to_string(), diff --git a/account/src/proto.rs b/account/src/proto.rs index 7d78961..271a154 100644 --- a/account/src/proto.rs +++ b/account/src/proto.rs @@ -35,7 +35,7 @@ pub struct QueryWebAuthNVerifyRegisterResponse { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] -pub enum MyCustomQuery { +pub enum XionCustomQuery { Verify(QueryWebAuthNVerifyRegisterRequest), } -impl CustomQuery for MyCustomQuery {} +impl CustomQuery for XionCustomQuery {} From 91ec6f9a45bf252fe5843db031c35eeb5dc48050 Mon Sep 17 00:00:00 2001 From: peartes Date: Tue, 30 Jan 2024 19:03:24 +0100 Subject: [PATCH 4/6] chore: add in verify signature proto struct --- account/src/auth/passkey.rs | 137 ++---------------------------------- account/src/contract.rs | 7 +- account/src/execute.rs | 65 ++++++++++------- account/src/proto.rs | 29 ++++++++ 4 files changed, 81 insertions(+), 157 deletions(-) diff --git a/account/src/auth/passkey.rs b/account/src/auth/passkey.rs index c4edf5c..a4fbaee 100644 --- a/account/src/auth/passkey.rs +++ b/account/src/auth/passkey.rs @@ -3,8 +3,7 @@ use crate::proto::{self, XionCustomQuery}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use cosmwasm_schema::cw_serde; -use cosmwasm_std::QueryRequest::Stargate; -use cosmwasm_std::{to_binary, Addr, Binary, Deps}; +use cosmwasm_std::{Addr, Binary, Deps}; #[cw_serde] struct QueryRegisterRequest { @@ -27,7 +26,7 @@ pub fn register( ) -> ContractResult { let query = proto::QueryWebAuthNVerifyRegisterRequest { addr: addr.clone().into(), - challenge: addr.to_string(), + challenge: Binary::from(addr.as_bytes()).to_base64(), rp, data: data.to_vec(), }; @@ -54,139 +53,17 @@ pub fn verify( tx_hash: Vec, credential: &Binary, ) -> ContractResult { - let challenge = URL_SAFE_NO_PAD.encode(tx_hash); + let challenge = Binary::from(tx_hash).to_base64(); - let query = QueryVerifyRequest { + let query = proto::QueryWebAuthNVerifyAuthenticateRequest { addr: addr.into(), challenge, rp, - credential: credential.clone(), - data: signature.clone(), + credential: credential.clone().into(), + data: signature.clone().into(), }; - let query_bz = to_binary(&query)?; - deps.querier.query(&Stargate { - path: "xion.v1.Query/WebAuthNVerifyAuthenticate".to_string(), - data: query_bz, - })?; + deps.querier.query::(&query.into())?; Ok(true) } - -// use crate::error::{ContractError, ContractResult}; -// use cosmwasm_std::{from_binary, Binary}; -// use webauthn_rs::prelude::{Passkey, PasskeyAuthentication, PasskeyRegistration, Url}; -// // use webauthn_rs::prelude::*; -// use crate::error::ContractError::InvalidToken; -// use webauthn_rs::WebauthnBuilder; -// use webauthn_rs_core::interface::{AuthenticationState, RegistrationState}; -// use webauthn_rs_proto::{COSEAlgorithm, PublicKeyCredential, UserVerificationPolicy}; -// -// -// -// pub fn register(url: String, cred: &Binary, challenge: Vec) -> ContractResult { -// let rp_origin = match Url::parse(&url) { -// Ok(u) => u, -// Err(_) => return Err(ContractError::URLParse { url }), -// }; -// -// let reg = from_binary(cred)?; -// -// let rp_id = rp_origin.domain().ok_or(ContractError::URLParse { url })?; -// let builder = WebauthnBuilder::new(rp_id, &rp_origin)?; -// let webauthn = builder.build()?; -// -// let registration_state = RegistrationState { -// policy: UserVerificationPolicy::Preferred, -// exclude_credentials: vec![], -// challenge: challenge.into(), -// credential_algorithms: vec![COSEAlgorithm::ES256], -// require_resident_key: false, -// authenticator_attachment: None, -// extensions: Default::default(), -// experimental_allow_passkeys: true, -// }; -// -// let passkey = webauthn.finish_passkey_registration( -// ®, -// &PasskeyRegistration { -// rs: registration_state, -// }, -// )?; -// -// Ok(passkey) -// } -// -// pub fn verify( -// url: String, -// passkey_bytes: &Binary, -// cred: &Binary, -// tx_bytes: Vec, -// ) -> ContractResult<()> { -// let rp_origin = match Url::parse(&url) { -// Ok(u) => u, -// Err(_err) => return Err(ContractError::URLParse { url }), -// }; -// -// let rp_id = rp_origin.domain().ok_or(ContractError::URLParse { url })?; -// let builder = WebauthnBuilder::new(rp_id, &rp_origin).expect("Invalid configuration"); -// let webauthn = builder.build().expect("Invalid configuration"); -// -// let passkey: Passkey = from_binary(passkey_bytes)?; -// -// let authentication_state = AuthenticationState { -// credentials: vec![passkey.into()], -// policy: UserVerificationPolicy::Preferred, -// challenge: tx_bytes.into(), -// appid: None, -// allow_backup_eligible_upgrade: false, -// }; -// -// let public_key_credential: PublicKeyCredential = from_binary(cred)?; -// -// webauthn.finish_passkey_authentication( -// &public_key_credential, -// &PasskeyAuthentication { -// ast: authentication_state, -// }, -// )?; -// -// Ok(()) -// } -// -// #[cfg(test)] -// mod tests { -// use crate::auth::passkey::{register, verify}; -// use cosmwasm_std::to_binary; -// use webauthn_rs::prelude::*; -// -// #[test] -// fn test_passkey_example() { -// let challenge = "test-challenge"; -// -// let rp_origin = -// Url::parse("https://xion-dapp-example-git-feat-faceid-burntfinance.vercel.app") -// .expect("Invalid URL"); -// let register_credential: RegisterPublicKeyCredential = serde_json::from_str(r#"{"type":"public-key","id":"6BnpSHlIXwOndHhxfPw4l3SylupnZIvTVP9Vp_aK34w","rawId":"6BnpSHlIXwOndHhxfPw4l3SylupnZIvTVP9Vp_aK34w","authenticatorAttachment":"platform","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiZEdWemRDMWphR0ZzYkdWdVoyVSIsIm9yaWdpbiI6Imh0dHBzOi8veGlvbi1kYXBwLWV4YW1wbGUtZ2l0LWZlYXQtZmFjZWlkLWJ1cm50ZmluYW5jZS52ZXJjZWwuYXBwIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ","attestationObject":"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViksGMBiDcEppiMfxQ10TPCe2-FaKrLeTkvpzxczngTMw1BAAAAAK3OAAI1vMYKZIsLJfHwVQMAIOgZ6Uh5SF8Dp3R4cXz8OJd0spbqZ2SL01T_Vaf2it-MpQECAyYgASFYINnBKEMfG6wkb9W1grSXgNAQ8lx6H7j6EcMyTSbZ91-XIlggdk2OOxV_bISxCsqFac6ZE8-gEurV4xQd7kFFYdfMqtE","transports":["internal"]},"clientExtensionResults":{}}"#).unwrap(); -// -// let reg_bytes = to_binary(®ister_credential).unwrap(); -// let passkey = register( -// rp_origin.to_string(), -// ®_bytes, -// challenge.as_bytes().to_vec(), -// ) -// .unwrap(); -// let passkey_bytes = to_binary(&passkey).unwrap(); -// -// let authenticate_credential: PublicKeyCredential = serde_json::from_str(r#"{"type":"public-key","id":"6BnpSHlIXwOndHhxfPw4l3SylupnZIvTVP9Vp_aK34w","rawId":"6BnpSHlIXwOndHhxfPw4l3SylupnZIvTVP9Vp_aK34w","authenticatorAttachment":"platform","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZEdWemRDMWphR0ZzYkdWdVoyVSIsIm9yaWdpbiI6Imh0dHBzOi8veGlvbi1kYXBwLWV4YW1wbGUtZ2l0LWZlYXQtZmFjZWlkLWJ1cm50ZmluYW5jZS52ZXJjZWwuYXBwIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ","authenticatorData":"sGMBiDcEppiMfxQ10TPCe2-FaKrLeTkvpzxczngTMw0BAAAAAA","signature":"MEQCIF1Fm_XjFV5FjBRYXNN1WcDm0V4xbPn3sQ85gC34_FGmAiBzLYGsat3HwDcn4jh50gTW4mgGcmYqkvT2g1bfdFxElA","userHandle":null},"clientExtensionResults":{}}"#).unwrap(); -// let authenticate_credential_bytes = to_binary(&authenticate_credential).unwrap(); -// -// verify( -// rp_origin.to_string(), -// &passkey_bytes, -// &authenticate_credential_bytes, -// challenge.as_bytes().to_vec(), -// ) -// .unwrap(); -// } -// } diff --git a/account/src/contract.rs b/account/src/contract.rs index 48e6680..2957d0e 100644 --- a/account/src/contract.rs +++ b/account/src/contract.rs @@ -22,7 +22,7 @@ pub fn instantiate( msg: InstantiateMsg, ) -> ContractResult { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - execute::init(deps, env, msg.authenticator) + execute::init(deps, env, &mut msg.authenticator.clone()) } #[entry_point] @@ -56,11 +56,12 @@ pub fn execute( msg: ExecuteMsg, ) -> ContractResult { assert_self(&info.sender, &env.contract.address)?; - match msg { + let mut owned_msg = msg.clone(); + match &mut owned_msg { ExecuteMsg::AddAuthMethod { add_authenticator } => { add_auth_method(deps, env, add_authenticator) } - ExecuteMsg::RemoveAuthMethod { id } => remove_auth_method(deps, env, id), + ExecuteMsg::RemoveAuthMethod { id } => remove_auth_method(deps, env, *id), } } diff --git a/account/src/execute.rs b/account/src/execute.rs index 3c36c8c..ef62604 100644 --- a/account/src/execute.rs +++ b/account/src/execute.rs @@ -1,3 +1,5 @@ +use std::borrow::BorrowMut; + use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Env, Event, Order, Response}; use crate::auth::{passkey, AddAuthenticator, Authenticator}; @@ -10,9 +12,9 @@ use crate::{ pub fn init( deps: DepsMut, env: Env, - add_authenticator: AddAuthenticator, + add_authenticator: &mut AddAuthenticator, ) -> ContractResult { - add_auth_method(deps, env.clone(), add_authenticator.clone())?; + add_auth_method(deps, env.clone(), add_authenticator)?; Ok( Response::new().add_event(Event::new("create_abstract_account").add_attributes(vec![ @@ -90,27 +92,27 @@ pub fn after_tx() -> ContractResult { pub fn add_auth_method( deps: DepsMut, env: Env, - add_authenticator: AddAuthenticator, + add_authenticator: &mut AddAuthenticator, ) -> ContractResult { - match add_authenticator.clone() { + match add_authenticator.borrow_mut() { AddAuthenticator::Secp256K1 { id, pubkey, signature, } => { let auth = Authenticator::Secp256K1 { - pubkey: pubkey.clone(), + pubkey: (*pubkey).clone(), }; if !auth.verify( deps.as_ref(), &env, &Binary::from(env.contract.address.as_bytes()), - &signature, + signature, )? { Err(ContractError::InvalidSignature) } else { - AUTHENTICATORS.save(deps.storage, id, &auth)?; + AUTHENTICATORS.save(deps.storage, *id, &auth)?; Ok(()) } } @@ -119,17 +121,19 @@ pub fn add_auth_method( pubkey, signature, } => { - let auth = Authenticator::Ed25519 { pubkey }; + let auth = Authenticator::Ed25519 { + pubkey: (*pubkey).clone(), + }; if !auth.verify( deps.as_ref(), &env, &Binary::from(env.contract.address.as_bytes()), - &signature, + signature, )? { Err(ContractError::InvalidSignature) } else { - AUTHENTICATORS.save(deps.storage, id, &auth)?; + AUTHENTICATORS.save(deps.storage, *id, &auth)?; Ok(()) } } @@ -138,17 +142,19 @@ pub fn add_auth_method( address, signature, } => { - let auth = Authenticator::EthWallet { address }; + let auth = Authenticator::EthWallet { + address: (*address).clone(), + }; if !auth.verify( deps.as_ref(), &env, &Binary::from(env.contract.address.as_bytes()), - &signature, + signature, )? { Err(ContractError::InvalidSignature) } else { - AUTHENTICATORS.save(deps.storage, id, &auth)?; + AUTHENTICATORS.save(deps.storage, *id, &auth)?; Ok(()) } } @@ -158,17 +164,20 @@ pub fn add_auth_method( sub, token, } => { - let auth = Authenticator::Jwt { aud, sub }; + let auth = Authenticator::Jwt { + aud: (*aud).clone(), + sub: (*sub).clone(), + }; if !auth.verify( deps.as_ref(), &env, &Binary::from(env.contract.address.as_bytes()), - &token, + token, )? { Err(ContractError::InvalidSignature) } else { - AUTHENTICATORS.save(deps.storage, id, &auth)?; + AUTHENTICATORS.save(deps.storage, *id, &auth)?; Ok(()) } } @@ -177,17 +186,19 @@ pub fn add_auth_method( pubkey, signature, } => { - let auth = Authenticator::Secp256R1 { pubkey }; + let auth = Authenticator::Secp256R1 { + pubkey: (*pubkey).clone(), + }; if !auth.verify( deps.as_ref(), &env, &Binary::from(env.contract.address.as_bytes()), - &signature, + signature, )? { Err(ContractError::InvalidSignature) } else { - AUTHENTICATORS.save(deps.storage, id, &auth)?; + AUTHENTICATORS.save(deps.storage, *id, &auth)?; Ok(()) } } @@ -199,13 +210,18 @@ pub fn add_auth_method( let passkey = passkey::register( deps.as_ref(), env.contract.address.clone(), - url.clone(), - credential, + (*url).clone(), + (*credential).clone(), )?; - let auth = Authenticator::Passkey { url, passkey }; - AUTHENTICATORS.save(deps.storage, id, &auth)?; - + let auth = Authenticator::Passkey { + url: (*url).clone(), + passkey: passkey.clone(), + }; + AUTHENTICATORS.save(deps.storage, *id, &auth)?; + // we replace the sent credential with the passkey for indexers and other + // observers to see + *(credential) = passkey; Ok(()) } }?; @@ -323,6 +339,7 @@ mod tests { .into(), )) } + XionCustomQuery::Authenticate(_) => todo!(), }); let query_msg = XionCustomQuery::Verify(proto::QueryWebAuthNVerifyRegisterRequest { diff --git a/account/src/proto.rs b/account/src/proto.rs index 271a154..d3dfb46 100644 --- a/account/src/proto.rs +++ b/account/src/proto.rs @@ -26,6 +26,31 @@ pub struct QueryWebAuthNVerifyRegisterRequest { pub data: Vec, } +#[derive( + Clone, + PartialEq, + Eq, + ::prost::Message, + serde::Serialize, + serde::Deserialize, + schemars::JsonSchema, + CosmwasmExt, +)] +#[proto_message(type_url = "/xion.v1.Query/WebAuthNVerifyAuthenticate")] +#[proto_query(path = "/xion.v1.Query/WebAuthNVerifyAuthenticate", response_type = QueryWebAuthNVerifyAuthenticateResponse)] +pub struct QueryWebAuthNVerifyAuthenticateRequest { + #[prost(string, tag = "1")] + pub addr: String, + #[prost(string, tag = "2")] + pub challenge: String, + #[prost(string, tag = "3")] + pub rp: String, + #[prost(bytes, tag = "4")] + pub credential: Vec, + #[prost(bytes, tag = "5")] + pub data: Vec, +} + // We define the response as a prost message to be able to decode the protobuf data. #[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize)] pub struct QueryWebAuthNVerifyRegisterResponse { @@ -33,9 +58,13 @@ pub struct QueryWebAuthNVerifyRegisterResponse { pub credential: Vec, } +#[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize)] +pub struct QueryWebAuthNVerifyAuthenticateResponse {} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum XionCustomQuery { Verify(QueryWebAuthNVerifyRegisterRequest), + Authenticate(QueryWebAuthNVerifyAuthenticateRequest), } impl CustomQuery for XionCustomQuery {} From b83902895b3f6e096cb067d23866a5ce5305a697 Mon Sep 17 00:00:00 2001 From: peartes Date: Thu, 1 Feb 2024 14:36:48 +0100 Subject: [PATCH 5/6] chore: properly serialize challenge buffer --- account/src/auth/passkey.rs | 11 ++++++++--- account/src/contract.rs | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/account/src/auth/passkey.rs b/account/src/auth/passkey.rs index a4fbaee..2a9cd40 100644 --- a/account/src/auth/passkey.rs +++ b/account/src/auth/passkey.rs @@ -1,6 +1,6 @@ use crate::error::ContractResult; use crate::proto::{self, XionCustomQuery}; -use base64::engine::general_purpose::URL_SAFE_NO_PAD; +use base64::engine::general_purpose::{self}; use base64::Engine; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Binary, Deps}; @@ -18,6 +18,9 @@ struct QueryRegisterResponse { credential: Binary, } +#[cw_serde] +struct QueryAuthenticateResponse {} + pub fn register( deps: Deps, addr: Addr, @@ -53,7 +56,8 @@ pub fn verify( tx_hash: Vec, credential: &Binary, ) -> ContractResult { - let challenge = Binary::from(tx_hash).to_base64(); + let challenge = + general_purpose::URL_SAFE_NO_PAD.encode(general_purpose::STANDARD.encode(tx_hash)); let query = proto::QueryWebAuthNVerifyAuthenticateRequest { addr: addr.into(), @@ -63,7 +67,8 @@ pub fn verify( data: signature.clone().into(), }; - deps.querier.query::(&query.into())?; + deps.querier + .query::(&query.into())?; Ok(true) } diff --git a/account/src/contract.rs b/account/src/contract.rs index 2957d0e..1e20dbe 100644 --- a/account/src/contract.rs +++ b/account/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ use absacc::AccountSudoMsg; -use crate::execute::{add_auth_method, assert_self, remove_auth_method}; +use crate::execute::{add_auth_method, remove_auth_method}; use crate::msg::ExecuteMsg; use crate::proto::XionCustomQuery; use crate::{ @@ -55,7 +55,7 @@ pub fn execute( info: MessageInfo, msg: ExecuteMsg, ) -> ContractResult { - assert_self(&info.sender, &env.contract.address)?; + // assert_self(&info.sender, &env.contract.address)?; let mut owned_msg = msg.clone(); match &mut owned_msg { ExecuteMsg::AddAuthMethod { add_authenticator } => { From 5aa58efbc6b689022fcc5710831f3a73f91f9584 Mon Sep 17 00:00:00 2001 From: peartes Date: Thu, 1 Feb 2024 15:27:00 +0100 Subject: [PATCH 6/6] chore: remove debug pass --- account/src/contract.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/account/src/contract.rs b/account/src/contract.rs index 1e20dbe..2957d0e 100644 --- a/account/src/contract.rs +++ b/account/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ use absacc::AccountSudoMsg; -use crate::execute::{add_auth_method, remove_auth_method}; +use crate::execute::{add_auth_method, assert_self, remove_auth_method}; use crate::msg::ExecuteMsg; use crate::proto::XionCustomQuery; use crate::{ @@ -55,7 +55,7 @@ pub fn execute( info: MessageInfo, msg: ExecuteMsg, ) -> ContractResult { - // assert_self(&info.sender, &env.contract.address)?; + assert_self(&info.sender, &env.contract.address)?; let mut owned_msg = msg.clone(); match &mut owned_msg { ExecuteMsg::AddAuthMethod { add_authenticator } => {