How to get pub_X and Pub_Y from metamask to use in verify ECDSA Signature?

According Noir docs the function has the following signature:
fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool
How can this be used with metamask sign function?

29 Likes

You can recover the public key from the signature and slice it.

import { ethers } from "ethers";

async function main() {
  // hardhat wallet 0
  const sender = new ethers.Wallet(
    "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
  );

  console.log("\x1b[34m%s\x1b[0m", "signing message πŸ–‹: ", message);

  const signature = await sender.signMessage(message); // get the signature of the message, this will be 130 bytes (concatenated r, s, and v)

  console.log("signature πŸ“: ", signature);

  let pubKey_uncompressed = ethers.utils.recoverPublicKey(digest, signature);
  console.log("uncompressed pubkey: ", pubKey_uncompressed);

  // recoverPublicKey returns `0x{hex"4"}{pubKeyXCoord}{pubKeyYCoord}` - so slice 0x04 to expose just the concatenated x and y
  //    see https://github.com/indutny/elliptic/issues/86 for a non-explanation explanation πŸ˜‚
  let pubKey = pubKey_uncompressed.slice(4);

  let pub_key_x = pubKey.substring(0, 64);
  console.log("public key x coordinate πŸ“Š: ", pub_key_x);

  let pub_key_y = pubKey.substring(64);
  console.log("public key y coordinate πŸ“Š: ", pub_key_y);
}

main();

source

60 Likes

Thank you for recoverPublicKey. it was something I was missing.
But now I struggle to get the input to noir .
My code is :

import React, { useState } from "react";
import { ethers } from "ethers";
import { BarretenbergBackend } from "@noir-lang/backend_barretenberg";
import { Noir } from "@noir-lang/noir_js";
import circuit from "../../circuit/target/circuit.json";

function SignatureComponent() {
  const [message, setMessage] = useState("");
  const [signatureData, setSignatureData] = useState(null);

  const backend = new BarretenbergBackend(circuit);
  const noir = new Noir(circuit, backend);

  const hexStringToByteArray = (hexString) => {
    if (hexString.startsWith("0x")) {
      hexString = hexString.slice(2);
    }
    const byteArray = [];
    for (let i = 0; i < hexString.length; i += 2) {
      byteArray.push(parseInt(hexString.substring(i, i + 2), 16));
    }
    return byteArray;
  };

  const signMessage = async () => {
    try {
      if (!window.ethereum)
        throw new Error("Ethereum wallet (e.g., MetaMask) not detected!");

      await window.ethereum.request({ method: "eth_requestAccounts" });
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = await provider.getSigner();

      const signature = await signer.signMessage(message);

      const messageHash = ethers.utils.hashMessage(message);
      const recoveredPublicKey = ethers.utils.recoverPublicKey(
        messageHash,
        signature
      );
      const publicKeyBytes = ethers.utils.arrayify(recoveredPublicKey);
      const publicKeyX = publicKeyBytes.slice(0, 32);
      const publicKeyY = publicKeyBytes.slice(32, 64);

      setSignatureData({
        publicKeyX: ethers.utils.hexlify(publicKeyX),
        publicKeyY: ethers.utils.hexlify(publicKeyY),
        signature: signature,
        message: message,
        messageHash: messageHash,
      });

      const publicKeyXBytes = hexStringToByteArray(ethers.utils.hexlify(publicKeyX));
      const publicKeyYBytes = hexStringToByteArray(ethers.utils.hexlify(publicKeyY));
      const signatureBytes = hexStringToByteArray(signature).slice(0, 64);
      const messageHashBytes = hexStringToByteArray(messageHash);

      const input = {
        hashed_message: messageHashBytes,
        pub_key_x: publicKeyXBytes,
        pub_key_y: publicKeyYBytes,
        signature: signatureBytes,
      };

      const proof = await noir.generateFinalProof(input);
      const verification = await noir.verifyFinalProof(proof);
      if(verification){ console.log("Verified") } else throw "Not-Verified";

    } catch (error) {
      console.error(error);
      alert("Error signing message: " + error.message);
    }
  };

  return (
    <div>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="Enter a message to sign"
      />
      <button onClick={signMessage}>Sign Message</button>

      {signatureData && (
        <div>
          <p>Public Key X: {signatureData.publicKeyX}</p>
          <p>Public Key Y: {signatureData.publicKeyY}</p>
          <p>Signature: {signatureData.signature}</p>
          <p>Message: {signatureData.message}</p>
          <p>messageHash: {signatureData.messageHash}</p>
        </div>
      )}
    </div>
  );
}

export default SignatureComponent;

but I get error Error: invalid type: integer 1, expected struct Range at line 1 column 595 in const proof = await noir.generateFinalProof(input);

Note: Ethers version is 5.7.2

40 Likes

Let’s move this discussion to the discord where more people will see it and it’s better for tech support. tag me there with @joshc

28 Likes