Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions contracts/wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ near-contract = [
"dep:impl-tools",
"dep:near-sdk",
"digest",
"near-sdk/deterministic-account-ids",
"serde",
"std",
]
Expand All @@ -92,6 +93,7 @@ near-contract = [
defuse-wallet = { path = ".", features = ["abi", "arbitrary", "borsh", "digest", "schemars-v0_8", "serde", "near-contract"] }

bs58.workspace = true
hex = { workspace = true, features = ["serde"] }
hex-literal.workspace = true
rstest.workspace = true
serde_json.workspace = true
Expand Down
9 changes: 5 additions & 4 deletions contracts/wallet/signatures/ed25519/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use defuse_crypto::{
Curve,
ed25519::{Ed25519, Ed25519PublicKey, Ed25519Signature},
};
use defuse_wallet::{RequestMessage, SignatureSchema};
use defuse_wallet::SignatureSchema;

/// Simple [`Ed25519`] wallet [signature schema](SignatureSchema)
/// over [canonical request hash](RequestMessage::hash).
Expand All @@ -21,7 +21,7 @@ pub struct WalletEd25519;
impl SignatureSchema for WalletEd25519 {
type PublicKey = Ed25519PublicKey;

fn verify(public_key: &Self::PublicKey, msg: &RequestMessage, proof: &str) -> bool {
fn verify_hash(public_key: &Self::PublicKey, hash: &[u8; 32], proof: &str) -> bool {
let Ok(signature) = Ed25519Signature::from_str(proof) else {
return false;
};
Expand All @@ -30,7 +30,7 @@ impl SignatureSchema for WalletEd25519 {
return false;
};

Ed25519::verify(&public_key, &msg.hash(), &signature.into())
Ed25519::verify(&public_key, hash, &signature.into())
}
}

Expand All @@ -39,7 +39,8 @@ mod tests {
use std::time::Duration;

use defuse_wallet::{
AccountId, Gas, NearPromise, NearToken, Request, WalletOp, actions::FunctionCall,
AccountId, Gas, NearPromise, NearToken, Request, RequestMessage, WalletOp,
actions::FunctionCall,
};
use hex_literal::hex;
use rstest::rstest;
Expand Down
4 changes: 2 additions & 2 deletions contracts/wallet/signatures/no-sign/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::{
str::FromStr,
};

use defuse_wallet::{RequestMessage, SignatureSchema};
use defuse_wallet::SignatureSchema;

/// [`SignatureSchema`] which always rejects the signature.
///
Expand All @@ -19,7 +19,7 @@ impl SignatureSchema for NoSign {
type PublicKey = NoPublicKey;

#[inline]
fn verify(_public_key: &Self::PublicKey, _msg: &RequestMessage, _proof: &str) -> bool {
fn verify_hash(_public_key: &Self::PublicKey, _hash: &[u8; 32], _proof: &str) -> bool {
false
}
}
Expand Down
19 changes: 12 additions & 7 deletions contracts/wallet/signatures/webauthn/ed25519/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
use defuse_wallet::wallet;
use defuse_wallet_webauthn::{WalletWebauthn, ed25519::Ed25519, webauthn::IgnoreUserVerification};
use defuse_wallet_webauthn::{WalletWebauthn, ed25519::Ed25519, webauthn::RequireUserVerification};

wallet! {
#[wallet(
schema = WalletWebauthn<
Ed25519,
// `UV` (User Verified) flag is only set by FIDO2-capable devices with
// PIN / biometric setup.
// Require the `UV` (User Verified) flag: every signature MUST be
// authorized by a biometric / PIN / screen-lock verification, not
// mere user presence. This wallet's passkey is the sole key over
// funds, so on-chain enforcement is required — the client also
// requests `userVerification: "required"`, but a proof submitted
// directly to the relayer would bypass that; the contract must not
// accept a user-presence-only assertion.
//
// FIDO U2F (CTAP 1) authenticators (such as old Ledger and Yubikey
// devices) only set `UP` (User Present) flag and doesn't support `UV`
// (User Verified).
IgnoreUserVerification,
// Trade-off: FIDO U2F (CTAP 1) authenticators (e.g. old Ledger /
// YubiKey without a PIN) only set `UP` and cannot satisfy this.
// Platform passkeys (Apple/Google/Windows) always perform UV.
RequireUserVerification,
>,
metadata(
standard(standard = "wallet-webauthn-ed25519", version = "1.0.0")
Expand Down
19 changes: 12 additions & 7 deletions contracts/wallet/signatures/webauthn/p256/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
use defuse_wallet::wallet;
use defuse_wallet_webauthn::{WalletWebauthn, p256::P256, webauthn::IgnoreUserVerification};
use defuse_wallet_webauthn::{WalletWebauthn, p256::P256, webauthn::RequireUserVerification};

wallet! {
#[wallet(
schema = WalletWebauthn<
P256,
// `UV` (User Verified) flag is only set by FIDO2-capable devices with
// PIN / biometric setup.
// Require the `UV` (User Verified) flag: every signature MUST be
// authorized by a biometric / PIN / screen-lock verification, not
// mere user presence. This wallet's passkey is the sole key over
// funds, so on-chain enforcement is required — the client also
// requests `userVerification: "required"`, but a proof submitted
// directly to the relayer would bypass that; the contract must not
// accept a user-presence-only assertion.
//
// FIDO U2F (CTAP 1) authenticators (such as old Ledger and Yubikey
// devices) only set `UP` (User Present) flag and doesn't support `UV`
// (User Verified).
IgnoreUserVerification,
// Trade-off: FIDO U2F (CTAP 1) authenticators (e.g. old Ledger /
// YubiKey without a PIN) only set `UP` and cannot satisfy this.
// Platform passkeys (Apple/Google/Windows) always perform UV.
RequireUserVerification,
>,
metadata(
standard(standard = "wallet-webauthn-p256", version = "1.0.0")
Expand Down
10 changes: 5 additions & 5 deletions contracts/wallet/signatures/webauthn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use defuse_webauthn as webauthn;
use core::marker::PhantomData;

use defuse_crypto::Curve;
use defuse_wallet::{RequestMessage, SignatureSchema};
use defuse_wallet::SignatureSchema;
use defuse_webauthn::{Algorithm, UserVerification, Webauthn, WebauthnAssertion};
use serde::{Deserialize, Serialize, de::DeserializeOwned};

Expand All @@ -29,7 +29,7 @@ where
{
type PublicKey = A::PublicKey;

fn verify(public_key: &Self::PublicKey, msg: &RequestMessage, proof: &str) -> bool {
fn verify_hash(public_key: &Self::PublicKey, hash: &[u8; 32], proof: &str) -> bool {
// try to convert public key
let Ok(public_key) = <A::Curve as Curve>::PublicKey::try_from(public_key) else {
return false;
Expand All @@ -45,13 +45,13 @@ where
return false;
};

// Verify `msg.hash()` according to webauthn spec.
// Verify the given digest according to webauthn spec.
//
// We `msg.hash()` as the challenge, since:
// We use the canonical message hash as the challenge, since:
// * Authenticators are general-purpose signers and they usually
// implement blind singing.
// * This reduces length of the `proof` submitted on-chain.
Webauthn::<A, UV>::verify(&public_key, msg.hash(), &proof.assertion, &signature)
Webauthn::<A, UV>::verify(&public_key, *hash, &proof.assertion, &signature)
}
}

Expand Down
Loading
Loading