ecdsaed25519solana

Unable to verify message signed by sol-wallet-adapter


Having created a signed message I'm unsure how to use the resulting signature to verify the message using the publicKey.

My use case is, I'm wanting to use a Solana Wallet to login to an API server with a pattern like:

  1. GET message: String (from API server)
  2. sign message with privateKey
  3. POST signature (to API server)
  4. verify signature with stored publicKey

I've attempted to use nodeJS crypto.verify to decode the signed message on the API side but am a bit out of my depth digging into Buffers and elliptic curves:

// Front-end code
const toHexString = (buffer: Buffer) =>
  buffer.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");

const data = new TextEncoder().encode('message to verify');
const signed = await wallet.sign(data, "hex");
await setLogin({ // sends API post call to backend
  variables: {
    publicAddress: walletPublicKey,
    signature: toHexString(signed.signature),
  },
});

// Current WIP for backend code
const ALGORITHM = "ed25519";
const fromHexString = (hexString) =>
  new Uint8Array(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const signature = fromHexString(args.signature);
const nonceUint8 = new TextEncoder().encode('message to verify');
const verified = crypto.verify(
  ALGORITHM,
  nonceUint8,
  `-----BEGIN PUBLIC KEY-----\n${user.publicAddress}\n-----END PUBLIC KEY-----`,
  signature
);
console.log("isVerified: ", verified);

I'm pretty sure I'm going about this the wrong way and there must be an obvious method I'm missing.

As the space matures I expect a verify function or lib will appear to to consume the output of const signed = await wallet.sign(data, "hex");

Something like:

import { VerifyMessage } from '@solana/web3.js';

const verified = VerifyMessage(message, publicKey, signature, 'hex');

But after 3 days of pushing hard I'm starting to hit my limits and my brain is failing. Any help or direction where to look much appreciated 🙏


Solution

  • Solved with input from the fantastic Project Serum discord devs. High level solution is to use libs that are also used in the sol-wallet-adapter repo, namely tweetnacl and bs58:

    const signatureUint8 = base58.decode(args.signature);
    const nonceUint8 = new TextEncoder().encode(user?.nonce);
    const pubKeyUint8 = base58.decode(user?.publicAddress);
    
    nacl.sign.detached.verify(nonceUint8, signatureUint8, pubKeyUint8)
    // true