Skip to content

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

Open
Lost-Z wants to merge 1 commit into
Octo-Protocol-org:mainfrom
Lost-Z:feat/evm-core-keys-and-addresses
Open

feat(evm-core): secp256k1 derivation, EIP-55 addresses, signing#233
Lost-Z wants to merge 1 commit into
Octo-Protocol-org:mainfrom
Lost-Z:feat/evm-core-keys-and-addresses

Conversation

@Lost-Z

@Lost-Z Lost-Z commented Aug 25, 2026

Copy link
Copy Markdown

Summary

Adds crates/evm-core (octo-evm-core): the EVM counterpart of octo-wallet-core, providing the secp256k1 key/address/signing primitives this epic's later crates build on.

  • derive.rs: BIP-32/BIP-44 secp256k1 derivation at m/44'/60'/0'/0/{index} from the same BIP-39 mnemonic type already used for Stellar — note the non-hardened change/address_index tail (unlike SEP-0005's all-hardened path). Module docs spell out the consequence prominently: a leaked non-hardened child private key + the account xpub recovers the whole sub-tree, which feat(store): EVM per-customer deposit addresses via HD derivation #220 and feat(wallet): EVM deposit sweep engine #224 need to build around.
  • address.rs: keccak-256 → 20-byte address, EIP-55 mixed-case checksum encode/validate (accepts all-lower, all-upper, correct mixed-case; rejects a wrong mixed-case checksum without ever lowercasing before comparing, so the typo-detection property is preserved).
  • signer.rs: sign a 32-byte digest → (r, s, v) with EIP-2 low-s normalisation, plus signer-address recovery. Mirrors wallet-core/src/signer.rs's "no raw-XDR oracle" posture — it signs a caller-supplied digest, not an arbitrary/variable-length payload.
  • chain_adapter.rs: EvmAdapter + a local chain_conformance_suite stand-in for feat(chain): Introduce the octo-chain adapter trait and refactor Stellar behind it #213's ChainAdapter trait (feat(chain): Introduce the octo-chain adapter trait and refactor Stellar behind it #213 hadn't landed yet — see the module docs for the exact swap-over plan once it does).
  • Reuses octo-crypto for sealing unchanged (no second encryption scheme); the AAD context is "octo:eip155:<chain>", so a sealed EVM key cannot be opened under a Stellar context or a different EVM chain's context.
  • Adds an ADR to docs/architecture.md for the crate-selection decision (k256 + sha3 + a from-scratch BIP-32 walk, vs. alloy-primitives), covering the MSRV 1.84.1 constraint and the cargo deny license/bans surface.

Security-sensitive: full lint wall (#![forbid(unsafe_code)], denies unwrap/expect/panic/lossy casts outside tests), constant-time scalar validation in the BIP-32 walk, zeroize-on-drop for seeds and derived secrets.

Test plan

  • cargo test -p octo-evm-core — 32 tests: BIP-32 Test Vectors 1 & 2 (decoded from the spec's own base58check xprv strings, not hand-transcribed), the full EIP-55 spec example set plus negative (bad-checksum) cases, a mnemonic→address vector cross-checked against independent Python eth-account/eth-keys and Node ethers.js v6 output, signature vectors with an explicit low-s assertion, round-trip sign→recover, zeroize-on-drop for both the seed and a derived secret, and seal/open context-isolation (EVM↔Stellar, and EVM chain↔chain).
  • cargo clippy -p octo-evm-core --all-targets -- -D warnings (default features and --features test-fixtures) — clean.
  • cargo fmt --check — clean.
  • cargo clippy --workspace --all-targets --locked -- -D warnings and cargo test --workspace --locked — clean, no regressions elsewhere.
  • cargo deny check licenses bans sources — no new license/ban issues from this crate's dependencies (k256, sha3, subtle, bs58). licenses reports one pre-existing failure (webpki-roots's CDLA-Permissive-2.0, pulled transitively via reqwest/sqlx, already present on main before this PR); advisories currently can't run at all in this environment due to a CVSS-4.0 parsing incompatibility between the available cargo-deny 0.17 (the newest build compatible with the 1.84.1 MSRV pin) and a current RUSTSEC entry — both pre-existing environment/tooling issues, unrelated to this change.

Closes #217

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.
Adds an ADR in docs/architecture.md for the k256+sha3 vs. alloy-primitives
crate-selection decision (MSRV 1.84.1 and cargo-deny surface).

Closes Octo-Protocol-org#217
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.

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

1 participant