MystenLabs / sui

Sui, a next-generation smart contract platform with high throughput, low latency, and an asset-oriented programming model powered by the Move programming language
https://sui.io
Apache License 2.0
6.18k stars 11.19k forks source link

Unable to recover expected public key from a recoverable signature. #19343

Open fariedt opened 1 month ago

fariedt commented 1 month ago

I can sign a message with an Secp256k1Keypair using sui.js, and verify that it is correct inside a Move package. However, when I try to recover the public key from the signature in Move, I get a different one than the one I expect.

I've put together a small POC that demonstrates the problem at fariedt/sigtest.

Steps to Reproduce Issue

  1. create an Secp256k1Keypair and get the public + secret keys
  2. use keccak_256() to hash a text message, and sign the hash with secp256k1.sign()
  3. send the message, signature, and public key to a Move package: it can verify the signature
  4. send the message and signature to a Move package to recover the public key: it does not match the keypair's public key.

System Information

joyqvq commented 1 month ago

The recovery ID can be a value between 0 and 3 - you can try changing the recovery id bit to 2 and 3 to see.

fariedt commented 1 month ago

Trying 2 or 3 here makes Sui throw an exception:

Error: Dry run failed, could not automatically determine a budget: MoveAbort(MoveLocation { module: ModuleId { address: 0000000000000000000000000000000000000000000000000000000000000002, name: Identifier("ecdsa_k1") }, function: 0, instruction: 0, function_name: Some("secp256k1_ecrecover") }, 0) in command 1

That is EFailToRecoverPubKey in sui/crates/sui-framework/packages/sui-framework/sources/crypto/ecdsa_k1.move

joyqvq commented 1 month ago

can you send the full signature, public key, the message being signed in hex bytes?

fariedt commented 1 month ago

There is a sample-output directory in the repo with the output of the two npm commands. Since It does verify the signature with the public key, message, and signature, I know the values I'm sending across the wire are correct.

With the .env file in the repo:

seed: tornado honey remove plug floor sausage transfer fever portion identify stick maple
secret key: 3609130ba9c137d2023c431f9f87d457e17df1267605002d7216d7039a8442b2
public key: 03564811d3a74fbdbcfb218724ca2b9b64bd9095165256daa9e8e4a9efc41f88d1
public key as bytes: 3,86,72,17,211,167,79,189,188,251,33,135,36,202,43,155,100,189,144,149,22,82,86,218,169,232,228,169,239,196,31,136,209

message to sign: dummy prefixthis is a message that will be signed
as Uint8Array: 100,117,109,109,121,32,112,114,101,102,105,120,116,104,105,115,32,105,115,32,97,32,109,101,115,115,97,103,101,32,116,104,97,116,32,119,105,108,108,32,98,101,32,115,105,103,110,101,100

keccak_256 hash as Uint8Array: 11,96,244,108,120,99,188,237,161,15,144,183,87,163,86,217,148,95,25,104,53,207,139,191,198,42,148,27,21,211,234,194
hash in hex: 0b60f46c7863bceda10f90b757a356d9945f196835cf8bbfc62a941b15d3eac2

Signature {
  r: 31586729422257255502336367426846723797902307655669122670712935973827208186113n,
  s: 44921359792549527365878282659479770596162161746909773388720449770149464919296n,
  recovery: 0
}

compact signature in hex: 45d5748e88072d7dc54233381b680b50b600ce6399d8184a96c6496398ae8901635096b065cdeb9a85643eea400d0af8125b464a3f34bc287d5f0291062e4d00

The public key as bytes matches the contents of the public key object on the testnet. This is set using npm run setup.