1
$\begingroup$

I'm currently undertaking a course on Blockchain cryptography. And our first project is to transfer tokens from one wallet to another using client / server application. All mock of course.

This project is using the prescribed https://www.npmjs.com/package/ethereum-cryptography

The theory is this as I understand it:

Client side

  • generate public and private key
  • Create what I call an action object - {operation: "transfer", from (public key), to (public key), amount}
  • Hash the action
  • Sign the hash with private key to create a signature
  • Post the {action, signature} to the server

Server side

  • Verify the action using the signature and public key
  • Perform the transfer between wallets (if valid)

I'm pretty sure that this is correct.

Now what I had to do required quite a bit of investigation and work arounds and it just feels like it should have been simpler. I'd like to know if there is a better way. Here is what I had to do:

Client side

  • Client side - generate public and private key
  • Create what I call an action - {operation: "transfer", from (public key), to (public key), amount}
  • Hash the action with keccak256(utf8ToBytes(JSON.stringify(action)))
  • Sign the hash with private key to create a Signature using secp256k1.sign(actionHash, privateKey)
  • work around -> Because the returned Signature contains bigints (which seemingly don't serialize), I had to use https://www.npmjs.com/package/json-bigint to stringify it
  • Post the {action, signature} to the server

Server side

  • work around -> Use json-bigint with { useNativeBigInt: true } option to parse the signature (for BigInt(..) in the next step)
  • Whoops, I missed a detail previously. The (typed) signature of verify is:
    verify: (signature: Hex | {
        r: bigint;
        s: bigint;
    }, msgHash: Hex, publicKey: Hex, opts?: import("./abstract/weierstrass.js").VerOpts | undefined) => boolean;

and as I discuss below, I couldn't get the Hex version of signature to work and despite it just seeming to take a {r: bigInt, s: bigInt}, verify complained that I need to pass a Signature object.

class Signature {
    constructor({r, s, recovery}) {
        // const {r, s, recovery} = SigLikeObj;
        this.r = BigInt(r);
        this.s = BigInt(s);
        this.recovery = recovery;
    }
}
  • Verify it with secp256k1.verify(signatureFixed, actionHash, publicKey)
  • Perform transfer

The json-bigint requirement is a detail, but the need to create a class Signature seems wrong.

I saw that the signature in verify() can be a Hex though I couldn't see a way to create this.

I tried this on the Server:

    const signatureObj = JSONbig.parse(signatureJson);
    const signatureHex = toHex(new Uint8Array(signatureObj))
    const isValid = secp256k1.verify(signatureHex, actionHash, publicKey);

But verification fails this way.

Am I missing something - is there an easier way to handle the class Signature or to make a Hex out of it?

$\endgroup$

1 Answer 1

1
$\begingroup$

Doh! I updated the question with more detail of the error and then that action highlighted that I completely missed something so obvious.

I just needed to do this for the signature problem:

    const signatureObj = JSONbig.parse(signatureJson);
    const signatureObj2 = { r: signatureObj.r, s: signatureObj.s }
    const isValid = secp256k1.verify(signatureObj2, actionHash, publicKey);

I must admit I've solved a lot of issues by trying to explain the problem on StackExchange. Most often I work it out before posting the question. Oh well, I hope I've helped others.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.