Skip to content

Fix ECDSA edge cases and harden new implementation - #46

Open
turbolent wants to merge 2 commits into
tarak/go1.26-cgo-onlyfrom
bastian/improvements
Open

Fix ECDSA edge cases and harden new implementation#46
turbolent wants to merge 2 commits into
tarak/go1.26-cgo-onlyfrom
bastian/improvements

Conversation

@turbolent

@turbolent turbolent commented Aug 10, 2026

Copy link
Copy Markdown
Member

Depends on #44

  • return untyped nil interfaces on key decoding errors,
    from a single path after the curve switch
  • return false instead of panicking in Equals(nil)
  • check the hasher output in bytes, based on len(h) rather than hasher.Size(),
    which also rejects hashers computing fewer bytes than they declare
  • clip the capacity of secp256k1 signatures sliced from the [R||S||V] buffer,
    so an append can't reach the recovery byte
  • build the public key at private key construction
    instead of lazily in PublicKey(), which was not safe for concurrent use
  • read the secp256k1 curve constants from go-ethereum instead of hardcoding them,
    and derive (N-1)/2 from N for both curves
  • bound the second write of padToSizeAndConcat to output[size:2*size]
  • decode compressed P-256 keys through publicKeyECDSAP256,
    so both decoding paths build the key the same way
    and the length check stays in ParseUncompressedPublicKey
  • move the signatureFlipS test helper out of ecdsa.go
  • add tests: RFC 6979 known-answer vectors and a dishonest hasher
  • add generic checks to sign_test_utils.go, so they cover all algorithms:
    untyped nil returns on invalid key lengths (including DecodePublicKeyCompressed)
    and Equals(nil)

The RFC 6979 vectors only assert the correctness of the current underlying
implementation. The package requires neither deterministic nor low-S signatures,
and the test is documented as removable if the underlying implementation stops
providing both.

- return untyped nil interfaces on key decoding errors
- return false instead of panicking in Equals(nil)
- reject hashers whose computed hash is shorter than their declared size
- clip the capacity of secp256k1 signatures sliced from the [R||S||V] buffer
- make lazy PublicKey() construction safe for concurrent use
- derive (N-1)/2 from N and cross-check the secp256k1 constants
  against go-ethereum at initialization
- restore the P-256 public key length check
- add tests: RFC 6979 known-answer vectors, concurrent PublicKey,
  nil inputs, dishonest hasher, compressed edge points
Comment thread ecdsa_p256.go Outdated
Comment thread ecdsa_p256.go Outdated
Comment thread ecdsa_secp256k1.go Outdated
Comment thread ecdsa_secp256k1.go Outdated
Comment thread ecdsa_test.go
Comment thread ecdsa_test.go Outdated
Comment thread ecdsa_test.go Outdated
Comment thread ecdsa_test.go Outdated
Comment thread ecdsa.go Outdated
Comment thread ecdsa.go
Comment thread ecdsa.go Outdated
Comment thread ecdsa.go Outdated
- build the public key eagerly at private key construction
  instead of lazily in PublicKey(), which removes the concurrency guards
- read the secp256k1 curve constants from go-ethereum
  instead of hardcoding them and cross-checking
- check the hasher output in bytes only, based on len(h),
  and drop the redundant hasher.Size() check
- drop the P-256 public key length check,
  already covered by ParseUncompressedPublicKey
- return the untyped nil once after the switch in the decoding functions
- move the nil-input checks to sign_test_utils.go so they cover all algos,
  and extend them to DecodePublicKeyCompressed
- document that the RFC 6979 vectors only test the current implementation,
  not a property the package guarantees
- drop the concurrent PublicKey and compressed edge point tests
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.

2 participants