Summary
Enhance ECKey input validation, internal state consistency, and private-key handling so that malformed or inconsistent key material is rejected at API boundaries. This proposal also hardens keystore decryption and aligns the provider-facing APIs with the Bouncy Castle implementation used for signing.
Problem
Motivation
ECKey is widely used for transaction signing, witness block signing, and other secp256k1 operations. Because its public APIs accept key material from multiple sources, they should validate that material consistently, fail early with predictable errors, and avoid retaining or exposing sensitive mutable data.
These guarantees are especially important at cryptographic boundaries. Accepting malformed keys or constructing an internally inconsistent ECKey can defer failures until signing or another later operation, making the original cause harder to identify. Unbounded input may also consume unnecessary CPU and memory before it is rejected.
Current State
Private-key byte arrays can currently be converted to BigInteger before their encoding size is bounded, and scalar-range validation is not applied consistently across constructors and factory methods. Public-key inputs are likewise not always checked for valid encoding, curve membership, point validity, or the point at infinity.
When private and public keys are supplied together, the current APIs can pair a private scalar with an unrelated public point. This produces an ECKey whose internal state does not represent a valid key pair.
There are also several state-management and API consistency concerns. Cached address and node ID arrays are returned directly and can therefore be modified by callers. Constructors accept arbitrary security providers even though the signing implementation supports only Bouncy Castle private keys. Some APIs can bypass key-pair validation or expose private keys through string output.
At the keystore boundary, invalid decrypted keys can escape from Wallet.decrypt() as IllegalArgumentException instead of the expected CipherException. The raw decrypted private-key buffer is also not explicitly cleared after use.
Limitations or Risks
The current behavior allows invalid input to travel farther into cryptographic operations than necessary. Depending on the entry point, callers may observe different validation and exception behavior for equivalent invalid keys. Inconsistent private and public components can remain undetected until a later operation, while directly exposed cached arrays allow external code to mutate state retained by an ECKey instance.
The provider API also suggests broader compatibility than the signing implementation actually provides. In addition, decrypted private-key material may remain in memory longer than required, increasing the lifetime of sensitive data.
Proposed Solution
Proposed Design
All ECKey entry points should apply the same validation rules. Private-key byte input will be bounded before numeric conversion or elliptic-curve operations, and every private scalar will be required to fall within the valid secp256k1 range. A valid high-bit scalar encoded with the single leading zero sign byte produced by BigInteger will remain accepted, while malformed and non-canonical oversized encodings will be rejected.
Public-key input will be checked for a supported encoding and decoded as a valid, non-infinity point on the secp256k1 curve. Whenever private and public components are supplied together, ECKey will verify that they belong to the same key pair before constructing the object.
Cached address and node ID values will be returned as defensive copies, and equality will be defined by public-key identity. Provider-facing APIs will be aligned with the implementation by restricting key generation and signing keys to Bouncy Castle. APIs that bypass key-pair validation or expose private keys through string output will be removed.
Wallet.decrypt() will convert invalid decrypted-key failures into CipherException, preserving the abstraction of the keystore boundary. Its raw decrypted private-key buffer will be cleared in a finally block so cleanup occurs on both successful and failed paths.
Key Changes
The crypto module will receive consistent private- and public-key validation, key-pair verification, defensive handling of cached arrays, explicit provider restrictions, and updated equality behavior. Keystore decryption will normalize invalid-key exceptions and clear decrypted private-key bytes after use.
Focused tests will cover scalar boundaries, bounded and sign-padded private-key encodings, malformed public keys, mismatched key pairs, defensive copies, provider restrictions, exception conversion, and decrypted-buffer cleanup. Framework tests will also verify that invalid witness keystore keys continue to surface as WITNESS_KEYSTORE_LOAD without changing WitnessInitializer production logic.
Impact
The proposal improves security by validating untrusted key material before it enters later cryptographic operations and by reducing the lifetime and exposure of sensitive byte arrays. It improves stability by replacing delayed or inconsistent failures with predictable validation at API boundaries. Early rejection of malformed or oversized private-key input also avoids unnecessary numeric and elliptic-curve processing.
For developers, the supported provider behavior and the validity requirements of an ECKey become explicit and consistent. The proposal does not change consensus rules, signature verification, signature recovery, or state-transition behavior.
Compatibility
- Breaking Change: Yes.
- Default Behavior Change: Null, empty, zero, out-of-range, and non-canonical oversized private-key inputs will be rejected with
IllegalArgumentException. A valid scalar encoded with one leading zero sign byte will remain accepted. Invalid public points and mismatched key pairs will also be rejected during construction.
- Migration Required: Callers relying on
null results for invalid private-key input must validate input or handle IllegalArgumentException. Callers using arbitrary-provider constructors or removed helper methods must migrate to the supported ECKey constructors and factory methods. Keystore callers should handle invalid decrypted keys as CipherException.
Additional Notes
- Do you have ideas regarding implementation? Yes
- Are you willing to implement this feature? Yes
- SM2 behavior is outside the scope of this enhancement.
Summary
Enhance
ECKeyinput validation, internal state consistency, and private-key handling so that malformed or inconsistent key material is rejected at API boundaries. This proposal also hardens keystore decryption and aligns the provider-facing APIs with the Bouncy Castle implementation used for signing.Problem
Motivation
ECKeyis widely used for transaction signing, witness block signing, and other secp256k1 operations. Because its public APIs accept key material from multiple sources, they should validate that material consistently, fail early with predictable errors, and avoid retaining or exposing sensitive mutable data.These guarantees are especially important at cryptographic boundaries. Accepting malformed keys or constructing an internally inconsistent
ECKeycan defer failures until signing or another later operation, making the original cause harder to identify. Unbounded input may also consume unnecessary CPU and memory before it is rejected.Current State
Private-key byte arrays can currently be converted to
BigIntegerbefore their encoding size is bounded, and scalar-range validation is not applied consistently across constructors and factory methods. Public-key inputs are likewise not always checked for valid encoding, curve membership, point validity, or the point at infinity.When private and public keys are supplied together, the current APIs can pair a private scalar with an unrelated public point. This produces an
ECKeywhose internal state does not represent a valid key pair.There are also several state-management and API consistency concerns. Cached address and node ID arrays are returned directly and can therefore be modified by callers. Constructors accept arbitrary security providers even though the signing implementation supports only Bouncy Castle private keys. Some APIs can bypass key-pair validation or expose private keys through string output.
At the keystore boundary, invalid decrypted keys can escape from
Wallet.decrypt()asIllegalArgumentExceptioninstead of the expectedCipherException. The raw decrypted private-key buffer is also not explicitly cleared after use.Limitations or Risks
The current behavior allows invalid input to travel farther into cryptographic operations than necessary. Depending on the entry point, callers may observe different validation and exception behavior for equivalent invalid keys. Inconsistent private and public components can remain undetected until a later operation, while directly exposed cached arrays allow external code to mutate state retained by an
ECKeyinstance.The provider API also suggests broader compatibility than the signing implementation actually provides. In addition, decrypted private-key material may remain in memory longer than required, increasing the lifetime of sensitive data.
Proposed Solution
Proposed Design
All
ECKeyentry points should apply the same validation rules. Private-key byte input will be bounded before numeric conversion or elliptic-curve operations, and every private scalar will be required to fall within the valid secp256k1 range. A valid high-bit scalar encoded with the single leading zero sign byte produced byBigIntegerwill remain accepted, while malformed and non-canonical oversized encodings will be rejected.Public-key input will be checked for a supported encoding and decoded as a valid, non-infinity point on the secp256k1 curve. Whenever private and public components are supplied together,
ECKeywill verify that they belong to the same key pair before constructing the object.Cached address and node ID values will be returned as defensive copies, and equality will be defined by public-key identity. Provider-facing APIs will be aligned with the implementation by restricting key generation and signing keys to Bouncy Castle. APIs that bypass key-pair validation or expose private keys through string output will be removed.
Wallet.decrypt()will convert invalid decrypted-key failures intoCipherException, preserving the abstraction of the keystore boundary. Its raw decrypted private-key buffer will be cleared in afinallyblock so cleanup occurs on both successful and failed paths.Key Changes
The
cryptomodule will receive consistent private- and public-key validation, key-pair verification, defensive handling of cached arrays, explicit provider restrictions, and updated equality behavior. Keystore decryption will normalize invalid-key exceptions and clear decrypted private-key bytes after use.Focused tests will cover scalar boundaries, bounded and sign-padded private-key encodings, malformed public keys, mismatched key pairs, defensive copies, provider restrictions, exception conversion, and decrypted-buffer cleanup. Framework tests will also verify that invalid witness keystore keys continue to surface as
WITNESS_KEYSTORE_LOADwithout changingWitnessInitializerproduction logic.Impact
The proposal improves security by validating untrusted key material before it enters later cryptographic operations and by reducing the lifetime and exposure of sensitive byte arrays. It improves stability by replacing delayed or inconsistent failures with predictable validation at API boundaries. Early rejection of malformed or oversized private-key input also avoids unnecessary numeric and elliptic-curve processing.
For developers, the supported provider behavior and the validity requirements of an
ECKeybecome explicit and consistent. The proposal does not change consensus rules, signature verification, signature recovery, or state-transition behavior.Compatibility
IllegalArgumentException. A valid scalar encoded with one leading zero sign byte will remain accepted. Invalid public points and mismatched key pairs will also be rejected during construction.nullresults for invalid private-key input must validate input or handleIllegalArgumentException. Callers using arbitrary-provider constructors or removed helper methods must migrate to the supportedECKeyconstructors and factory methods. Keystore callers should handle invalid decrypted keys asCipherException.Additional Notes