Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Secp256k1 Signature in typescript to be 65-bytes recoverable format #4776

Merged
merged 1 commit into from
Sep 24, 2022

Conversation

joyqvq
Copy link
Contributor

@joyqvq joyqvq commented Sep 24, 2022

  • tx signed with ts-sdk is currently failing to execute in sui
  • this is because the noble/secp256k1 uses DER encoded signature (70 bytes) instead of 64 bytes compact signature + 1 byte of recovery_id (as we defined in rust).
  • this fixes that that we produce 65-bytes sig in ts and able to execute tx successfully using ts-sdk.
  • also added verify_sig only for lower s (reject upper s).
  • confirms that results are identical using rust keytool and rust sdk example for signature value.

rust sdk example:

cargo run --example transfer_coins
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/examples/transfer_coins`
struct: TransactionData { kind: Single(TransferSui(TransferSui { recipient: 0xdf703f8fe5ccfe92037bd01cf88306856e9c8db6, amount: Some(10) })), sender: 0x6d425cb43a52f960a2285730f032d2ac02e77282, gas_payment: (0x4346f73ba3a5014e21fe583fbb99476e6f142b70, SequenceNumber(6), o#42ec9eb8d08e2cf3ffb13e0d431c910ea6f8f72f564b868f81b8e65961f31acb), gas_price: 1, gas_budget: 1000 }

serialized_tx: "VHJhbnNhY3Rpb25EYXRhOjoAA99wP4/lzP6SA3vQHPiDBoVunI22AQoAAAAAAAAAbUJctDpS+WCiKFcw8DLSrALncoJDRvc7o6UBTiH+WD+7mUdubxQrcAYAAAAAAAAAIELsnrjQjizz/7E+DUMckQ6m+PcvVkuGj4G45llh8xrLAQAAAAAAAADoAwAAAAAAAA=="
signature: "ATMqqSpb6tGzy4xVAHJIrNnfHfOfaepR4mn3hHsXCOWxbefKKXaQWQKcwe1BSmRs5qUihxvC31PRbXGHz6mTBOIBA0GiX8XPj5q2rti2YOoxT54RyTVUho0rVMdGZivV9bFU"

sui keytool:

sui keytool sign --address 0x6d425cb43a52f960a2285730f032d2ac02e77282 --data VHJhbnNhY3Rpb25EYXRhOjoAA99wP4/lzP6SA3vQHPiDBoVunI22AQoAAAAAAAAAbUJctDpS+WCiKFcw8DLSrALncoJDRvc7o6UBTiH+WD+7mUdubxQrcAYAAAAAAAAAIELsnrjQjizz/7E+DUMckQ6m+PcvVkuGj4G45llh8xrLAQAAAAAAAADoAwAAAAAAAA==
2022-09-24T04:41:01.283907Z  INFO sui::keytool: Data to sign : VHJhbnNhY3Rpb25EYXRhOjoAA99wP4/lzP6SA3vQHPiDBoVunI22AQoAAAAAAAAAbUJctDpS+WCiKFcw8DLSrALncoJDRvc7o6UBTiH+WD+7mUdubxQrcAYAAAAAAAAAIELsnrjQjizz/7E+DUMckQ6m+PcvVkuGj4G45llh8xrLAQAAAAAAAADoAwAAAAAAAA==
2022-09-24T04:41:01.283924Z  INFO sui::keytool: Address : 0x6d425cb43a52f960a2285730f032d2ac02e77282
2022-09-24T04:41:01.284245Z  INFO sui::keytool: Flag Base64: AQ==
2022-09-24T04:41:01.284249Z  INFO sui::keytool: Public Key Base64: A0GiX8XPj5q2rti2YOoxT54RyTVUho0rVMdGZivV9bFU
2022-09-24T04:41:01.284251Z  INFO sui::keytool: Signature : MyqpKlvq0bPLjFUAckis2d8d859p6lHiafeEexcI5bFt58opdpBZApzB7UFKZGzmpSKHG8LfU9FtcYfPqZME4gE=

typescript test:

  it('Secp256kc1 keypair signData test', async () => {
    const arr = Uint8Array.from([207, 102, 158, 149, 211, 74, 210, 150, 202, 21, 69, 24, 165, 148, 248, 78, 149, 223, 216, 194, 29, 39, 62, 113, 145, 91, 49, 122, 14, 53, 197, 184]);
    const keypair = Secp256k1Keypair.fromSeed(arr);
    const signer = new RawSigner(
      keypair,
      new JsonRpcProvider('https://gateway.devnet.sui.io:443')
    );
    const transfer = await signer.transferSui({
      amount: 10,
      suiObjectId: "0x4346f73ba3a5014e21fe583fbb99476e6f142b70",
      gasBudget: 1000,
      recipient: "0xdf703f8fe5ccfe92037bd01cf88306856e9c8db6"
  });
  console.log('transfer', transfer);
  });

serialized_tx: VHJhbnNhY3Rpb25EYXRhOjoAA99wP4/lzP6SA3vQHPiDBoVunI22AQoAAAAAAAAAbUJctDpS+WCiKFcw8DLSrALncoJDRvc7o6UBTiH+WD+7mUdubxQrcAYAAAAAAAAAIELsnrjQjizz/7E+DUMckQ6m+PcvVkuGj4G45llh8xrLAQAAAAAAAADoAwAAAAAAAA==
sig: MyqpKlvq0bPLjFUAckis2d8d859p6lHiafeEexcI5bFt58opdpBZApzB7UFKZGzmpSKHG8LfU9FtcYfPqZME4gE=
signature_scheme: Secp256k1

transfer {
  certificate: {
    transactionDigest: 'P6mf4k+TGd8uVwmmpfut1xVQGd6bBeG2o7JM08+ruQI=',
    data: {
      transactions: [Array],
      sender: '0x6d425cb43a52f960a2285730f032d2ac02e77282',
      gasPayment: [Object],
      gasBudget: 1000
    },
    txSignature: 'ATMqqSpb6tGzy4xVAHJIrNnfHfOfaepR4mn3hHsXCOWxbefKKXaQWQKcwe1BSmRs5qUihxvC31PRbXGHz6mTBOIBA0GiX8XPj5q2rti2YOoxT54RyTVUho0rVMdGZivV9bFU',
    authSignInfo: { epoch: 0, signature: [Array], signers_map: [Array] }
  },
  effects: {
    status: { status: 'success' },
    gasUsed: { computationCost: 45, storageCost: 48, storageRebate: 32 },
    transactionDigest: 'P6mf4k+TGd8uVwmmpfut1xVQGd6bBeG2o7JM08+ruQI=',
    created: [ [Object] ],
    mutated: [ [Object] ],
    gasObject: { owner: [Object], reference: [Object] },
    events: [ [Object] ],
    dependencies: [ 'pBb2xu0bg+K7wQ71TKu6gJG8ISxlv6sHzNUZ1QkMb6o=' ]
  },
  timestamp_ms: null,
  parsed_data: null
}

@github-actions
Copy link
Contributor

💳 Wallet Extension has been built, you can download the packaged extension here: https://github.com/MystenLabs/sui/actions/runs/3117243828#artifacts


secp.utils.hmacSha256Sync = (key: Uint8Array, ...msgs: Uint8Array[]) => {
const h = hmac.create(sha256, key);
msgs.forEach(msg => h.update(msg));
msgs.forEach((msg) => h.update(msg));
Copy link
Contributor Author

@joyqvq joyqvq Sep 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some unrelated lint, they are generated with npx prettier --write .

@@ -87,11 +87,11 @@ export class Secp256k1Keypair implements Keypair {
const signData = encoder.encode('sui validation');
const msgHash = sha256(signData);
const signature = secp.signSync(msgHash, secretKey);
if (!secp.verify(signature, msgHash, publicKey)) {
if (!secp.verify(signature, msgHash, publicKey, { strict: true })) {
Copy link
Contributor Author

@joyqvq joyqvq Sep 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict = true rejects s value if it is higher than 1/2 prime order.

secp.signSync(msgHash, this.keypair.secretKey)
);
const [sig, rec_id] = secp.signSync(msgHash, this.keypair.secretKey, {
canonical: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

canonical = true means a signature s should be no more than 1/2 prime order.

@joyqvq joyqvq marked this pull request as ready for review September 24, 2022 06:21
@joyqvq joyqvq merged commit 8066d2a into main Sep 24, 2022
@joyqvq joyqvq deleted the raw-sign-fix branch September 24, 2022 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants