Skip to content

Commit

Permalink
Merge pull request #10 from burnt-labs/feat/secp256r1
Browse files Browse the repository at this point in the history
passkeys and secp256r1 authenticator
  • Loading branch information
ash-burnt authored Feb 14, 2024
2 parents 0e8b099 + 21e71cf commit 69d0115
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 75 deletions.
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ members = [
"account"
]

[build]
target = "wasm32-unknown-unknown"

[workspace.dependencies]
cosmos-sdk-proto = { version = "0.19", default-features = false }
cosmwasm-schema = "=1.4.1"
Expand All @@ -27,3 +24,7 @@ base64 = "0.21.4"
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" }
46 changes: 28 additions & 18 deletions account/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,31 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]

[dependencies]
absacc = { git = "https://github.com/larry0x/abstract-account.git" }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw2 = { workspace = true }
cw-storage-plus = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tiny-keccak = { workspace = true }
schemars = { workspace = true }
hex = { workspace = true }
ripemd = { workspace = true }
bech32 = { workspace = true }
base64 = { workspace = true }
phf = { workspace = true }
rsa = { workspace = true }
getrandom = { workspace = true }
absacc = { git = "https://github.com/larry0x/abstract-account.git" }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw2 = { workspace = true }
cw-storage-plus = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tiny-keccak = { workspace = true }
schemars = { workspace = true }
hex = { workspace = true }
ripemd = { workspace = true }
bech32 = { workspace = true }
base64 = { workspace = true }
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"] }
url = "2.4.1"
coset = "0.3.5"
futures = "0.3.29"
async-trait = "0.1.74"
59 changes: 53 additions & 6 deletions account/src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::auth::secp256r1::verify;
use crate::error::ContractError;
use cosmwasm_std::{Api, Binary, Env};
use cosmwasm_std::{Binary, Deps, Env};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

mod eth_crypto;
mod jwt;
pub mod passkey;
mod secp256r1;
mod sign_arb;
pub mod util;

Expand All @@ -31,6 +34,29 @@ pub enum AddAuthenticator {
sub: String,
token: Binary,
},
Secp256R1 {
id: u8,
pubkey: Binary,
signature: Binary,
},
Passkey {
id: u8,
url: String,
credential: Binary,
},
}

impl AddAuthenticator {
pub fn get_id(&self) -> u8 {
match self {
AddAuthenticator::Secp256K1 { id, .. } => *id,
AddAuthenticator::Ed25519 { id, .. } => *id,
AddAuthenticator::EthWallet { id, .. } => *id,
AddAuthenticator::Jwt { id, .. } => *id,
AddAuthenticator::Secp256R1 { id, .. } => *id,
AddAuthenticator::Passkey { id, .. } => *id,
}
}
}

#[derive(Serialize, Deserialize, Clone, JsonSchema, PartialEq, Debug)]
Expand All @@ -39,20 +65,22 @@ pub enum Authenticator {
Ed25519 { pubkey: Binary },
EthWallet { address: String },
Jwt { aud: String, sub: String },
Secp256R1 { pubkey: Binary },
Passkey { url: String, passkey: Binary },
}

impl Authenticator {
pub fn verify(
&self,
api: &dyn Api,
deps: Deps,
env: &Env,
tx_bytes: &Binary,
sig_bytes: &Binary,
) -> Result<bool, ContractError> {
match self {
Authenticator::Secp256K1 { pubkey } => {
let tx_bytes_hash = util::sha256(tx_bytes);
let verification = api.secp256k1_verify(&tx_bytes_hash, sig_bytes, pubkey);
let verification = deps.api.secp256k1_verify(&tx_bytes_hash, sig_bytes, pubkey);
if let Ok(ver) = verification {
if ver {
return Ok(true);
Expand All @@ -62,7 +90,7 @@ impl Authenticator {
// if the direct verification failed, check to see if they
// are signing with signArbitrary (common for cosmos wallets)
let verification = sign_arb::verify(
api,
deps.api,
tx_bytes.as_slice(),
sig_bytes.as_slice(),
pubkey.as_slice(),
Expand All @@ -71,14 +99,14 @@ impl Authenticator {
}
Authenticator::Ed25519 { pubkey } => {
let tx_bytes_hash = util::sha256(tx_bytes);
match api.ed25519_verify(&tx_bytes_hash, sig_bytes, pubkey) {
match deps.api.ed25519_verify(&tx_bytes_hash, sig_bytes, pubkey) {
Ok(verification) => Ok(verification),
Err(error) => Err(error.into()),
}
}
Authenticator::EthWallet { address } => {
let addr_bytes = hex::decode(&address[2..])?;
match eth_crypto::verify(api, tx_bytes, sig_bytes, &addr_bytes) {
match eth_crypto::verify(deps.api, tx_bytes, sig_bytes, &addr_bytes) {
Ok(_) => Ok(true),
Err(error) => Err(error),
}
Expand All @@ -93,6 +121,25 @@ impl Authenticator {
sub,
);
}
Authenticator::Secp256R1 { pubkey } => {
let tx_bytes_hash = util::sha256(tx_bytes);
verify(&tx_bytes_hash, sig_bytes.as_slice(), pubkey)?;

Ok(true)
}
Authenticator::Passkey { url, passkey } => {
let tx_bytes_hash = util::sha256(tx_bytes);
passkey::verify(
deps,
env.clone().contract.address,
url.clone(),
sig_bytes,
tx_bytes_hash,
passkey,
)?;

Ok(true)
}
}
}
}
190 changes: 190 additions & 0 deletions account/src/auth/passkey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
use crate::error::ContractResult;
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};

#[cw_serde]
struct QueryRegisterRequest {
addr: String,
challenge: String,
rp: String,
data: Binary,
}

#[cw_serde]
struct QueryRegisterResponse {
credential: Binary,
}

pub fn register(deps: Deps, addr: Addr, rp: String, data: Binary) -> ContractResult<Binary> {
let query = QueryRegisterRequest {
addr: addr.clone().into(),
challenge: addr.to_string(),
rp,
data,
};
let query_bz = to_binary(&query)?;

let query_response: QueryRegisterResponse = deps.querier.query(&Stargate {
path: "xion.v1.Query/WebAuthNVerifyRegister".to_string(),
data: query_bz,
})?;

Ok(query_response.credential)
}

#[cw_serde]
struct QueryVerifyRequest {
addr: String,
challenge: String,
rp: String,
credential: Binary,
data: Binary,
}

pub fn verify(
deps: Deps,
addr: Addr,
rp: String,
signature: &Binary,
tx_hash: Vec<u8>,
credential: &Binary,
) -> ContractResult<bool> {
let challenge = URL_SAFE_NO_PAD.encode(tx_hash);

let query = QueryVerifyRequest {
addr: addr.into(),
challenge,
rp,
credential: credential.clone(),
data: signature.clone(),
};
let query_bz = to_binary(&query)?;

deps.querier.query(&Stargate {
path: "xion.v1.Query/WebAuthNVerifyAuthenticate".to_string(),
data: query_bz,
})?;

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<u8>) -> ContractResult<Passkey> {
// 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(
// &reg,
// &PasskeyRegistration {
// rs: registration_state,
// },
// )?;
//
// Ok(passkey)
// }
//
// pub fn verify(
// url: String,
// passkey_bytes: &Binary,
// cred: &Binary,
// tx_bytes: Vec<u8>,
// ) -> 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(&register_credential).unwrap();
// let passkey = register(
// rp_origin.to_string(),
// &reg_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();
// }
// }
Loading

0 comments on commit 69d0115

Please sign in to comment.