Skip to content

feat(evm-core): secp256k1 keys, BIP-44 derivation, and EIP-55 addresses #217

Description

@Emmyt24

Depends on: #213. Blocks: #218, #220, #225.

Description

octo-wallet-core is entirely Stellar: SEP-0005 SLIP-0010
ed25519 derivation at m/44'/148'/index' (derive.rs:16),
strkey addresses, XDR signing. EVM needs a different curve (secp256k1), a different derivation
standard (BIP-32/BIP-44, non-hardened change/index levels), a different coin type (60), and
a different address format (last 20 bytes of the keccak-256 of the uncompressed public key, with an
EIP-55 mixed-case checksum).

Create crates/evm-core (octo-evm-core) providing these primitives and an EvmAdapter
implementing ChainAdapter. This issue also owns resolving the alloy MSRV question (see §2)
and its resolution binds #218, #225, #226.

Requirements and context

  • This crate handles secret key material. It inherits wallet-core's full lint wall and
    zeroize-on-drop discipline. A panic in this crate can surface key bytes in a backtrace.
  • Derivation path: m/44'/60'/0'/0/{index} — note the last two levels are non-hardened, unlike
    Stellar's all-hardened SEP-0005 path. This is what allows xpub-based address derivation, and it
    is also why a leaked child private key plus the xpub compromises every sibling key. Document
    that consequence prominently; feat(store): EVM per-customer deposit addresses via HD derivation #220 and feat(wallet): EVM deposit sweep engine #224 depend on understanding it.
  • Reuse octo-crypto for sealing unchanged. Do not add a second
    encryption scheme. The context AAD string must distinguish chains (e.g. "octo:eip155:1") so a
    sealed EVM key cannot be opened in a Stellar context.
  • Test vectors are mandatory, per CONTRIBUTING.md: "crypto and derivation
    code must include test vectors."
  • Security: low-s signature normalisation (EIP-2) is required or signatures are malleable.
    Constant-time secret comparison. Never construct a signing key from an uncontrolled byte slice
    without validating it is in the curve order.

Suggested execution

Branch: feat/evm-core-keys-and-addresses

Implement changes

  • Add crates/evm-core; resolve and document the crate-selection decision (alloy-primitives
    vs k256 + sha3 + bip32) against MSRV 1.84.1 and cargo deny check in a short ADR in
    docs/architecture.md.
  • derive.rs: BIP-32 secp256k1 derivation from the same BIP-39 mnemonic type already used, at
    m/44'/60'/0'/0/{index}, returning a Zeroizing secret.
  • address.rs: keccak-256 → 20-byte address; EIP-55 checksum encode; a validate_address that
    accepts all-lowercase, all-uppercase, and correctly-checksummed mixed case, and rejects an
    incorrect mixed-case checksum
    (a typo-detection property that silently vanishes if you
    lowercase before comparing).
  • signer.rs: sign a 32-byte digest, produce (r, s, v) with low-s normalisation, and recover
    the signer address. Mirror the "no raw-XDR oracle" posture of
    wallet-core/src/signer.rs: expose typed operations, not
    a sign-arbitrary-bytes primitive.
  • EvmAdapter implementing ChainAdapter, passing feat(chain): Introduce the octo-chain adapter trait and refactor Stellar behind it #213's chain_conformance_suite.

Test and commit

  • BIP-32/BIP-44 test vectors from BIP-32's own Test Vector 1 and 2, plus a known
    mnemonic→address set cross-checked against an independent implementation (MetaMask or
    ethers.js) — record the source in the test file.
  • EIP-55 test vectors: every example from the EIP-55 spec, plus the negative cases
    (wrong-checksum mixed case must be rejected).
  • Signature tests: known (digest, key) → (r, s, v) vectors; assert s is always in the lower
    half of the curve order; round-trip recovery returns the signing address.
  • Assert secrets are zeroized on drop.
  • A test that a seed sealed with an eip155 context fails to open with a stellar context.

Example commit message

feat(evm-core): secp256k1 derivation, EIP-55 addresses, signing

Adds BIP-44 m/44'/60'/0'/0/i derivation, keccak-256 address encoding
with EIP-55 checksum validation, and low-s normalised signing (EIP-2).

Seals reuse octo-crypto with a chain-scoped AAD context so an EVM key
cannot be opened in a Stellar context.

Verified against BIP-32 vectors 1-2 and the full EIP-55 example set.

Refs #217

Guidelines

Security-sensitive: requires maintainer review and will not be merged without test vectors from an
authoritative source. Cite where each vector came from.


Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third Campaignarea/contractStellar transaction/signing layer: wallet-core, cryptodifficulty/hardHard, complex, cross-cuttingrisk/security-sensitiveTouches key material, signing, or fund movement — review carefullystatus/needs-maintainer-reviewRequires a maintainer as co-reviewer before mergetype/epicTracking issue for a multi-issue epic

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions