Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
86 changes: 86 additions & 0 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"defuse",
"erc191",
"fees",
"sr25519",
"io-utils",
"map-utils",
"near-utils",
Expand Down Expand Up @@ -52,6 +53,7 @@ defuse.path = "defuse"
defuse-deadline.path = "deadline"
defuse-erc191.path = "erc191"
defuse-fees.path = "fees"
defuse-sr25519.path = "sr25519"
defuse-io-utils.path = "io-utils"
defuse-map-utils.path = "map-utils"
defuse-near-utils.path = "near-utils"
Expand Down Expand Up @@ -100,6 +102,7 @@ rand = "0.9"
rand_chacha = "0.9"
proptest = "1.5"
rstest = "0.25"
schnorrkel = { version = "0.11", default-features = false }
schemars = "0.8"
serde_json = "1"
serde_with = "3.9"
Expand Down
1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ defuse-crypto = { workspace = true, features = ["serde"] }
defuse-deadline.workspace = true
defuse-erc191.workspace = true
defuse-fees.workspace = true
defuse-sr25519.workspace = true
defuse-nep245.workspace = true
defuse-nep413.workspace = true
defuse-map-utils.workspace = true
Expand Down
1 change: 1 addition & 0 deletions core/src/payload/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod multi;
pub mod nep413;
pub mod raw;
pub mod sep53;
pub mod sr25519;
pub mod tip191;
pub mod ton_connect;
pub mod webauthn;
Expand Down
8 changes: 8 additions & 0 deletions core/src/payload/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use defuse_crypto::{Payload, PublicKey, SignedPayload};
use defuse_erc191::SignedErc191Payload;
use defuse_nep413::SignedNep413Payload;
use defuse_sep53::SignedSep53Payload;
use defuse_sr25519::SignedSr25519Payload;
use defuse_tip191::SignedTip191Payload;
use defuse_ton_connect::SignedTonConnectPayload;
use derive_more::derive::From;
Expand Down Expand Up @@ -51,6 +52,10 @@ pub enum MultiPayload {
/// SEP-53: The standard for signing data off-chain for Stellar accounts.
/// See [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md)
Sep53(SignedSep53Payload),

/// Sr25519: Message signing for Polkadot/Substrate chains using Sr25519 signatures.
/// Follows Substrate's signing conventions. Compatible with Polkadot.js and other Substrate wallets.
Sr25519(SignedSr25519Payload),
}

impl Payload for MultiPayload {
Expand All @@ -68,6 +73,7 @@ impl Payload for MultiPayload {
Self::WebAuthn(payload) => payload.hash(),
Self::TonConnect(payload) => payload.hash(),
Self::Sep53(payload) => payload.hash(),
Self::Sr25519(payload) => payload.hash(),
}
}
}
Expand All @@ -85,6 +91,7 @@ impl SignedPayload for MultiPayload {
Self::WebAuthn(payload) => payload.verify(),
Self::TonConnect(payload) => payload.verify().map(PublicKey::Ed25519),
Self::Sep53(payload) => payload.verify().map(PublicKey::Ed25519),
Self::Sr25519(payload) => payload.verify().map(PublicKey::Sr25519),
}
}
}
Expand All @@ -105,6 +112,7 @@ where
Self::WebAuthn(payload) => payload.extract_defuse_payload(),
Self::TonConnect(payload) => payload.extract_defuse_payload(),
Self::Sep53(payload) => payload.extract_defuse_payload(),
Self::Sr25519(payload) => payload.extract_defuse_payload(),
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions core/src/payload/sr25519.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use defuse_sr25519::SignedSr25519Payload;
use near_sdk::serde::{Deserialize, Serialize, de::DeserializeOwned};

use super::{DefusePayload, ExtractDefusePayload};

impl<T> ExtractDefusePayload<T> for SignedSr25519Payload
where
T: DeserializeOwned,
{
type Error = near_sdk::serde_json::Error;

fn extract_defuse_payload(self) -> Result<DefusePayload<T>, Self::Error> {
near_sdk::serde_json::from_str(&self.message)
}
}
1 change: 1 addition & 0 deletions crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ed25519-dalek.workspace = true
hex.workspace = true
near-sdk = { workspace = true, features = ["unstable"] }
p256.workspace = true
schnorrkel.workspace = true
serde_with = { workspace = true, optional = true }
strum.workspace = true
thiserror.workspace = true
Expand Down
4 changes: 3 additions & 1 deletion crypto/src/curve/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
mod ed25519;
mod p256;
mod secp256k1;
mod sr25519;

use crate::{ParseCurveError, parse::checked_base58_decode_array};

pub use self::{ed25519::*, p256::*, secp256k1::*};
pub use self::{ed25519::*, p256::*, secp256k1::*, sr25519::*};

use near_sdk::bs58;
use strum::{Display, EnumString, IntoStaticStr};
Expand Down Expand Up @@ -32,6 +33,7 @@ pub enum CurveType {
Ed25519,
Secp256k1,
P256,
Sr25519,
}

pub trait TypedCurve: Curve {
Expand Down
36 changes: 36 additions & 0 deletions crypto/src/curve/sr25519.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use schnorrkel::{PublicKey as SchnorrkelPublicKey, Signature as SchnorrkelSignature};

use super::{Curve, CurveType, TypedCurve};

pub struct Sr25519;

impl Curve for Sr25519 {
type PublicKey = [u8; 32];
type Signature = [u8; 64];

type Message = [u8];
type VerifyingKey = Self::PublicKey;

#[inline]
fn verify(
signature: &Self::Signature,
message: &Self::Message,
public_key: &Self::VerifyingKey,
) -> Option<Self::PublicKey> {
let public_key_parsed = SchnorrkelPublicKey::from_bytes(public_key).ok()?;
let signature_parsed = SchnorrkelSignature::from_bytes(signature).ok()?;

// verify_simple in schnorrkel 0.11 signature is:
// pub fn verify_simple(&self, ctx: &[u8], msg: &[u8], signature: &Signature)
// Using "substrate" as the default context following Substrate/Polkadot convention
public_key_parsed
.verify_simple(b"substrate", message, &signature_parsed)
.ok()?;

Some(*public_key)
}
}

impl TypedCurve for Sr25519 {
const CURVE_TYPE: CurveType = CurveType::Sr25519;
}
17 changes: 16 additions & 1 deletion crypto/src/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use core::{
use near_sdk::{AccountId, AccountIdRef, bs58, env, near};

use crate::{
Curve, CurveType, Ed25519, P256, ParseCurveError, Secp256k1, parse::checked_base58_decode_array,
Curve, CurveType, Ed25519, P256, ParseCurveError, Secp256k1, Sr25519,
parse::checked_base58_decode_array,
};

#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
Expand All @@ -20,6 +21,7 @@ pub enum PublicKey {
Ed25519(<Ed25519 as Curve>::PublicKey),
Secp256k1(<Secp256k1 as Curve>::PublicKey),
P256(<P256 as Curve>::PublicKey),
Sr25519(<Sr25519 as Curve>::PublicKey),
}

impl PublicKey {
Expand All @@ -29,6 +31,7 @@ impl PublicKey {
Self::Ed25519(_) => CurveType::Ed25519,
Self::Secp256k1(_) => CurveType::Secp256k1,
Self::P256(_) => CurveType::P256,
Self::Sr25519(_) => CurveType::Sr25519,
}
}

Expand All @@ -39,6 +42,7 @@ impl PublicKey {
Self::Ed25519(data) => data,
Self::Secp256k1(data) => data,
Self::P256(data) => data,
Self::Sr25519(data) => data,
}
}

Expand Down Expand Up @@ -72,6 +76,16 @@ impl PublicKey {
hex::encode(&env::keccak256_array([b"p256".as_slice(), pk].concat())[12..32])
)
}
Self::Sr25519(pk) => {
// Similar to P256, we use the Eth Implicit schema with
// "sr25519" prefix to avoid collisions
format!(
"0x{}",
hex::encode(
&env::keccak256_array([b"sr25519".as_slice(), pk].concat())[12..32]
)
)
}
}
.try_into()
.unwrap_or_else(|_| unreachable!())
Expand Down Expand Up @@ -122,6 +136,7 @@ impl FromStr for PublicKey {
CurveType::Ed25519 => checked_base58_decode_array(data).map(Self::Ed25519),
CurveType::Secp256k1 => checked_base58_decode_array(data).map(Self::Secp256k1),
CurveType::P256 => checked_base58_decode_array(data).map(Self::P256),
CurveType::Sr25519 => checked_base58_decode_array(data).map(Self::Sr25519),
}
}
}
Expand Down
Loading