fix(signers): await the post-sign signature verification - #815
fix(signers): await the post-sign signature verification#815eastagiletracker wants to merge 1 commit into
Conversation
sign() called the async verify() without awaiting it, so the guard evaluated a Promise: !promise is always false and assert(promise) always passes. A signature that fails verification was returned to the caller instead of raising UnableToVerify. The bitcoin signer additionally passed the 65 byte recoverable signature to verify(), which only accepts the 64 byte compact form, so the check would have rejected every valid signature once awaited. EthereumSigner.verify compared the recovered 64 byte ethereum public key against the supplied one verbatim, so it returned false for the SEC1 compressed keys the keyring stores for imported private keys. Normalize both sides before comparing.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Included review availability: Your plan includes up to 2 reviews per rolling hour; 1 remains after this review. WalkthroughBitcoin, Ethereum, and Polkadot signers now await signature verification before returning signatures. Ethereum normalizes compressed and uncompressed public keys. Tests cover valid signatures and mismatched key failures. ChangesSigner verification
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The change enforces post-signature verification across the affected signers while preserving valid signature formats and compatibility; no actionable merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Warning |
This PR proposes awaiting the post-sign signature verification in the Bitcoin, Ethereum and Polkadot signers so the
UnableToVerifyguard actually runs, and fixing the two places where that guard could never have passed. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/364. You can sign in with your GitHub ID to claim ownership of the project.What's wrong
All three signers end
sign()with a self-check that re-verifies the signature they are about to return.verify()isasync, and none of the three call sites awaits it, so the check evaluates aPromise:!promiseis alwaysfalseandassert(promise)always passes, so a signature that does not verify is handed back to the caller instead of raising.KeyRing.sign()inpackages/keyring/src/index.tsdispatches straight into these three classes, so this is the extension's signing path for every secp256k1, ecdsa, ed25519, sr25519 and Bitcoin account.Two further defects were sitting behind the dead guard, and both had to be fixed for it to be usable:
verify(), which only accepts the 64-byte compact form. Awaiting alone would have rejected every valid signature. It now verifiesrsig[0], the compact signature, and still returns the 65-byte value unchanged.falsefor the SEC1 compressed keys the keyring stores for imported private keys — exactly the format in your ownpackages/keyring/tests/sign.test.tsfixtures (0x03330102...).verify()now brings both sides to the 64-byte form withimportPublicbefore comparing, which leaves 64-byte callers untouched.Reproducing it on current
mainSigning with a key pair whose public key does not match the private key returns a signature instead of throwing, on
b9ba802(currentmain):The same two tests, plus the Bitcoin and the three Polkadot ones, are red on the unpatched tree and green with the change: 5 rejection tests, 1 compressed-key test, 1 Bitcoin happy-path test. The clearest confirmation that this code is live came from your own suite — with the
awaitadded but before the compressed-key fix,packages/keyring/tests/sign.test.tswent red onkeyring should sign ethereum messagesandkeyring should sign raw keypairs, because the guard was finally running and correctly rejecting the compressed public keys those two tests import.Verification
yarn build:all && yarn testwas run onb9ba802before any change and again with the change applied, under Node 22.18.0 as pinned in.nvmrc. The baseline is fully green and stays fully green; the only difference is the 7 added tests (signer-bitcoin3 to 5,signer-ethereum5 to 7,signer-polkadot6 to 9), withkeyringat 19 passed in both runs.yarn lintreports no changes in the three touched packages.Nothing about the signatures produced for a correct key pair changes — the existing byte-for-byte signature assertions in all three packages pass untouched.
EthereumSigner.verifyis widened, not narrowed: an unparseable public key falls back to the old comparison rather than throwing, so a caller that gotfalsebefore still getsfalse.How this was managed
This work was tracked as https://eastagiletracker.com/projects/364/stories/223034 on a board imported from this repository's own issues and pull requests (770 stories, 9 labels), which you can browse at https://eastagiletracker.com/projects/364.
If you'd rather not receive contributions like this, reply
no-more-prson this pull request and we won't open any further ones on your repositories.Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com
Summary by CodeRabbit
Bug Fixes
Tests