Works best when

  • Recipient is a constrained signer that cannot evaluate ECDH over the curve in real time
  • Sender does not need to know the recipient's identity (settlement contract just transfers to a supplied destination)
  • Recipient generates destinations rarely enough that re-derivation cost is acceptable

Avoid when

  • Auditor needs view-only access to incoming transfers (no view-key split; use EIP-5564)
  • Sender wants to push to a recipient who has not pre-derived a destination (use stealth addresses)
  • Long-lived recipient secret cannot be tolerated under the threat model

I2I vs I2U — context differences

Institution to institution

I2I

Counterparty payment account derives a fresh destination per settlement event under a long-lived treasury secret.

Institution to end user

I2U

User wallet derives a fresh receive destination per receive context under a long-lived recipient secret.

Post-quantum exposure

Risk · high
Vector
secp256k1 keys broken by Shor. HNDL is high: address-to-recipient mapping is preserved on-chain forever.
Mitigation
Migrate the curve and the recipient-side derivation to a post-quantum primitive

Components

  • Long-lived recipient secret: 32 bytes provisioned at enrollment; held on a constrained device.
  • PRF: HMAC-SHA256 (universal in conservative crypto APIs). Any PRF with 256-bit output suffices.
  • Curve operation: secp256k1 scalar multiplication. Performed by the recipient or by an auxiliary device that learns nothing else; need not happen on the constrained device itself if it lacks the cycles.
  • Address hash: keccak256, producing the 20-byte Ethereum address from the public key.

Protocol

The construction is a PRF-derived deterministic key per (recipientSecret, context) pair:

  1. recipient derivedScalar = HMAC-SHA256(recipientSecret, DOMAIN_TAG || context...).
  2. recipient derivedPrivkey = derivedScalar used as a secp256k1 scalar in [1, n-1]; on rare >= n outputs, RFC 6979-style reject-and-rehash.
  3. recipient derivedPubkey = derivedPrivkey * G (secp256k1 scalar multiplication).
  4. recipient destination = keccak256(derivedPubkey)[-20:].
  5. sender Transfer funds to destination (recipient supplied it under their authority).
  6. recipient Spend from destination later by re-deriving derivedPrivkey from the same (recipientSecret, context).

DOMAIN_TAG MUST be a domain-separated, application-specific constant. context MUST include all parameters that distinguish destinations within the recipient's lifetime (e.g., event identifier, contract address, sequence number).

Guarantees & threat model

  • Unlinkability against external observers: HMAC-SHA256 is a PRF. Without the recipient secret, two destinations look uniformly random and uncorrelated. Address-space collisions are bounded by birthday probability over 160-bit addresses (about 2^-80 per pair).
  • Spendability: the recipient (with the secret and the public context) re-derives derivedPrivkey exactly. No chain scanning, no published view key.
  • Non-interactivity: the sender does not need a view pubkey, ephemeral keypair, or per-recipient state.
  • Threat model: adversary observes the chain and may compromise non-recipient parties. Out of scope: device seizure (long-lived secret residual); post-quantum adversary (recoverable curve).

Trade-offs

  • No view / spend split. Whoever holds the recipient secret both generates and spends. EIP-5564 supports a view-only auditor; this pattern does not. Audit access requires sharing the secret (full spend authority) or a separate disclosure mechanism.
  • Long-lived recipient secret. Held on the device for its full lifetime. Device seizure exposes all past and future destinations. Mitigations are operational (frequent re-enrollment).
  • No detection of unsolicited inbound transfers. Unlike EIP-5564 chain-scanning, the recipient knows about transfers only to destinations they themselves derived.
  • Constrained-device limitations. The curve operation and keccak256 are not always available on conservative crypto APIs. Some deployments offload steps 3 and 4 to a companion device.
  • Sloppy notation in spec writeups. The shorthand destination = truncate(HMAC(secret, context)) is broken if read literally: a 20-byte truncation produces an unspendable address. The four-step construction above is required for spendability. Audit production code for the full path.

Example

A privacy-preserving payroll system. Each employee's wallet holds a long-lived recipient secret. For each monthly payroll cycle, the wallet derives a fresh receive address via HMAC-SHA256 over (recipientSecret, monthIdentifier, employerId), then computes the secp256k1 public key and the keccak256 address. The employee provides this address to the employer's payroll system; the employer disburses salary to the address. Observers see a fresh address each month with no public linkage between an employee's monthly receipts. The wallet later re-derives the private key from the same secret and month identifier to spend.

See also

Open-source implementations