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.

1 Like

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?

1 Like

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)

1 Like

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)