Skip to content

Feature request: zeroization API for ECDSAPrivateKey / Bip32PrivateKey #18

Description

@carfdev

Context

We use blockchain_utils (currently 7.1.0) as the signing backend for an air-gapped Ethereum cold wallet. Private key material is a high-value target, and we zeroize every buffer we own as soon as it's no longer needed. We ran into a case where we cannot do that for keys held inside this package's own objects.

Problem

ECDSAPrivateKey stores the key scalar as an immutable field:

// crypto/crypto/ec/ecdsa/private_key.dart
class ECDSAPrivateKey {
  final BigInt secretMultiplier;
  ...
}

.raw / .toBytes() (and the same for Bip32PrivateKey.raw) compute a fresh List<int> from secretMultiplier on every call rather than exposing a live mutable buffer. This means:

  • Callers can zeroize the List<int> they get back from .raw, but that has no effect on the key material actually retained by the ECDSAPrivateKey/Bip32PrivateKey/Bip32/Bip44 object itself.
  • There is currently no API to clear that retained state (confirmed via a full grep of the package source for fillRange/zeroiz/wipe/destroy — no hits in any key/signer class).
  • This also applies to signer construction: ETHSigner.fromKeyBytes(privateKey) builds its own independent ECDSAPrivateKey internally, which is equally unclearable, even after the caller zeroizes its own input buffer.

So any application holding a Bip32/Bip44/ECDSAPrivateKey (e.g. after deriving an address-level key) has no way to remove the raw private key from memory before that object is garbage-collected, regardless of how careful it is with its own buffers.

Ask

A minimal API to explicitly clear retained private key material, e.g.:

class ECDSAPrivateKey {
  BigInt secretMultiplier; // non-final, or backed by a mutable buffer
  void zeroize() { secretMultiplier = BigInt.zero; /* or overwrite backing bytes */ }
}

with the equivalent exposed on Bip32PrivateKey/Bip32/Bip44 (zeroizing the whole derivation chain, since parent/master keys are also retained).

Happy to discuss the right shape for this (e.g. keeping BigInt but adding an explicit invalidation flag vs. switching the internal representation to a mutable byte buffer) — opening this primarily to confirm whether this is something upstream would accept, before proposing an implementation.

Environment

  • blockchain_utils 7.1.0
  • Reported from a downstream security review of a cold-wallet application; full source-level analysis of .raw/.toBytes() behavior available on request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions