14 releases

0.6.1 Feb 29, 2024
0.6.0 Jun 28, 2023
0.5.2 Jun 5, 2023
0.5.0 Oct 6, 2022
0.0.0 Nov 24, 2021

#16 in #sign

Download history 3074/week @ 2024-07-19 2911/week @ 2024-07-26 2805/week @ 2024-08-02 2477/week @ 2024-08-09 3217/week @ 2024-08-16 2594/week @ 2024-08-23 2002/week @ 2024-08-30 3004/week @ 2024-09-06 3006/week @ 2024-09-13 3587/week @ 2024-09-20 3723/week @ 2024-09-27 2850/week @ 2024-10-04 2545/week @ 2024-10-11 2373/week @ 2024-10-18 2833/week @ 2024-10-25 2292/week @ 2024-11-01

10,738 downloads per month
Used in 15 crates (5 directly)

MIT/Apache

49KB
889 lines

Sign-In with Ethereum

This crate provides a pure Rust implementation of EIP-4361: Sign In With Ethereum.

Installation

SIWE can be easily installed in any Rust project by including it in said project's cargo.toml file:

siwe = "0.6"

Features available:

  • serde for serialisation/deserialisation support;
  • ethers for EIP-1271 compliant contract wallets support; and
  • typed-builder for nicer verification options construction.

Usage

SIWE exposes a Message struct which implements EIP-4361.

Parsing a SIWE Message

Parsing is done via the Message implementation of FromStr:

let message: Message = string_message.parse()?;

Verifying and Authenticating a SIWE Message

Verification and Authentication is performed via EIP-191, using the address field of the Message as the expected signer. This returns the Ethereum public key of the signer:

let signer: Vec<u8> = message.verify_eip191(&signature)?;

The time constraints (expiry and not-before) can also be validated, at current or particular times:

if message.valid_now() { ... };

// equivalent to
if message.valid_at(&OffsetDateTime::now_utc()) { ... };

Combined verification of time constraints and authentication can be done in a single call with verify:

message.verify(&signature).await?;

Serialization of a SIWE Message

Message instances can also be serialized as their EIP-4361 string representations via the Display implementation of Message:

println!("{}", &message);

As well as in EIP-191 Personal-Signature pre-hash signing input form (if your Ethereum wallet does not support EIP-191 directly):

let eip191_bytes: Vec<u8> = message.eip191_bytes()?;

And directly as the EIP-191 Personal-Signature Hashed signing-input (made over the .eip191_string output):

let eip191_hash: [u8; 32] = message.eip191_hash()?;

Example

Parsing and verifying a Message is easy:

use hex::FromHex;
use siwe::{Message, TimeStamp, VerificationOpts};
use std::str::FromStr;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};

#[tokio::main]
async fn main() {
    let msg = r#"localhost:4361 wants you to sign in with your Ethereum account:
0x6Da01670d8fc844e736095918bbE11fE8D564163

SIWE Notepad Example

URI: http://localhost:4361
Version: 1
Chain ID: 1
Nonce: kEWepMt9knR6lWJ6A
Issued At: 2021-12-07T18:28:18.807Z"#;
    let message: Message = msg.parse().unwrap();
    let signature = <[u8; 65]>::from_hex(r#"6228b3ecd7bf2df018183aeab6b6f1db1e9f4e3cbe24560404112e25363540eb679934908143224d746bbb5e1aa65ab435684081f4dbb74a0fec57f98f40f5051c"#).unwrap();

    let verification_opts = VerificationOpts {
        domain: Some("localhost:4361".parse().unwrap()),
        nonce: Some("kEWepMt9knR6lWJ6A".into()),
        timestamp: Some(OffsetDateTime::parse("2021-12-08T00:00:00Z", &Rfc3339).unwrap()),
        ..Default::default()
    };

    if let Err(e) = message.verify(&signature, &verification_opts).await {
        // message cannot be correctly authenticated at this time
    }

    // do application-specific things
}

Disclaimer

Our Rust library for Sign-In with Ethereum has not yet undergone a formal security audit. We welcome continued feedback on the usability, architecture, and security of this implementation.

See Also

Dependencies

~6–23MB
~344K SLoC