How to sign a message on client, Prove challenge in ICP canister

I want to let users sign arbitrage message strings from client, and in my rust icp http canister be able to prove that the users icp principal public address is indeed correct (proof that they signed).

I found various potential approaches online, and LLMs have suggested various approaches, but so far none seem to be the right way. How can this be done, and whats the “right way”?

// JS clientside, send proof to server in REST http request
      const challenge = {
        timestamp_ms: now,
        user_icp_public_address
      };
      const challengeBytes = new TextEncoder().encode(
        JSON.stringify(challenge)
      );
      const signature = await icpIdentity.sign(challengeBytes);
      const signatureArray = new Uint8Array(signature);
const proof = {
        challenge,
        signature: Array.from(signatureArray),
      };
// Rust icp http canister
// in server, prove the challenge+signature is correct. 
// the user is who they say they are, and they intended this message payload

As for why I want to do this, its a simple way to enable “default api-keys” for http requests. users can just use signatures as authentication, so its simple.

2 Likes

Is it using this? What throws me off is why the need for ic-validator-ingress-message when it should be fully possibly offline with “zero dependencies” no?

2 Likes

OfficeX is using an auth flow like so:

  • users provide a seed which can deterministically set their ICP principal and public address, without needing to actually connect to world computer (same seed can create evm wallet)
  • with their ICP principal, users can cryptographically sign data payloads and verify it came from them
  • the server can prove the signed payload (the signature) and trust it came from the real user
  • we use these signatures as “config-less api keys” for our http REST endpoints
  • later we can generate actual string api-keys for subsequent REST calls

I can see many projects adopting such an auth flow for simplicity and similarity to web2 REST conventions

(Screenshot is just sandbox development)

2 Likes

The ic-validator-ingress-message is a Rust crate (i.e. library).

  1. You need to pass in the current time since you don’t want to verify the signature every single time and therefore, limit the amount of attacks, right?
  2. Curating API keys based on a signature doesn’t seem too secure to me. Why not provide them with API keys that aren’t a signature?
1 Like

Yes we definately want (1) pass in timestamp to limit calls, (2) we do provide regular API keys, the signature is just a “default auth” for the initial session when there is no api-key generated yet.

Do you know if we should be using the ic-validator-ingress-message crate, or is it just as appropriate to use the lightweight standalone-sig-verifier? (we are using standalone identities)

this question is related to How to sign data with @dfinity/agent or @dfinity/identity?, right?

did you already have a look at @dfinity/identity-secp256k1 - npm ? does that help you?

1 Like

https://mops.one/evm-txs Will let you verify a signature. You could sign your data with ethers js and validate with this library.

Thanks for suggestions! Yes i have tried the npm libs @dfinity/agent @dfinity/identity etc. Havent tried motoko as my canisters are all in Rust. Of the Rust solutions, so far I have tried:

They require adding these to my cargo.toml:

# general
ic-types = { git = "https://github.com/dfinity/ic" }
ic-crypto-sha2 = { git = "https://github.com/dfinity/ic" }
# specific
ic-crypto-standalone-sig-verifier = { git = "https://github.com/dfinity/ic" }

which throws build errors:

Crate:     rsa
Version:   0.9.7
Title:     Marvin Attack: potential key recovery through timing sidechannels
Date:      2023-11-22
ID:        RUSTSEC-2023-0071
URL:       https://rustsec.org/advisories/RUSTSEC-2023-0071
Severity:  5.9 (medium)
Solution:  No fixed upgrade is available!
Dependency tree:
rsa 0.9.7
└── ic-crypto-internal-basic-sig-rsa-pkcs1 0.9.0
    ├── ic-crypto-standalone-sig-verifier 0.9.0
    │   └── canisters-official-backend 0.1.0
    └── ic-crypto-internal-basic-sig-cose 0.9.0
        └── ic-crypto-standalone-sig-verifier 0.9.0

It says No fixed upgrade is available! implying dfinity must resolve this error in the underlying dfinity/ic repo if I am understanding this correctly?

I think this forum post is related: Using rust RSA library in ICP

For verification of a signature you don’t need any randomness, so for some crates it is enough to disable some of the default features


Edit: I have confirmed this is now working using ic-dummy-getrandom-for-wasm and standalone-sig-verifier

sample code here

1 Like