crypto: complete OpenSSL evacuation — libsecp256k1 EC math + ctaes + RNG + drop BIP70/rpcssl #47

Open
opened 2026-07-24 00:23:02 +00:00 by dobbscoin · 3 comments
dobbscoin commented 2026-07-24 00:23:02 +00:00

Goal

Finish what #33 starts. #33 removes OpenSSL from signature encoding consensus (the strict-DER parser is now OFF's own code); it explicitly leaves the EC math on OpenSSL's ECDSA_verify. This issue removes OpenSSL from everything that's left — EC math, wallet AES, RNG, GUI payment protocol, RPC TLS — so neither daemon nor wallet links libssl/libcrypto at all. Retires the OpenSSL-1.0.2 build pin.

Why

  • Build health: the 1.0.2 pin exists only because the wallet uses BIGNUM-on-EC_KEY APIs that OpenSSL 3 deleted. Drop the link → build against modern OpenSSL (unused) or nothing. Single biggest source of the multi-step build dance.
  • Consensus safety: while verify math runs through OpenSSL, a future OpenSSL behavior shift can drift consensus. #33 only closed the encoding half.
  • Security: libsecp256k1 is purpose-built, constant-time, deterministic; the 0.10-era key.cpp EC paths are long-dead upstream.

Hard prerequisite

#33 (BIP66) must be active on mainnet (h=1,055,555) before the verify swap. libsecp256k1 only accepts strict-DER; swapping verify before every newly-mined block is guaranteed strict-DER risks rejecting a legitimately-mined block = consensus split. Order is non-negotiable. (This issue was filed automatically at activation for that reason.)

Scope — seven sub-tasks, roughly independent

  1. Vendor libsecp256k1 into src/secp256k1/, wire into the build. Pin the commit Bitcoin Core 0.12–0.13 shipped (matches the EC math OFF already validates).
  2. ECDSA verify → libsecp256k1 (key.cpp / pubkey.cpp). The consensus-sensitive step.
    • Landmine A: buried pre-checkpoint blocks may carry non-canonical sigs OpenSSL waved through — confirm reindex-from-zero behavior, or keep an OpenSSL fallback gated below the BIP66 height.
    • Landmine B: low-S is policy (mempool), not consensus — preserve that split or you fork.
  3. ECDSA sign → libsecp256k1 (key.cpp). Not consensus-critical (only affects what we emit). Free win: RFC6979 deterministic nonces.
  4. AES → ctaes (crypter.cpp). In-tree constant-time AES-256-CBC. Acceptance gate: round-trip lock/unlock/spend on a pre-change encrypted wallet.dat.
  5. RNG (random.cpp): replace RAND_bytes / RAND_add with Core's modern RNG (platform entropy + ChaCha20).
  6. Drop BIP70 payment protocol (Qt paymentserver.cpp X.509 path) — a -lcrypto X.509 user, no merchant use case.
  7. Remove -rpcssl (rpcserver.cpp boost::asio::ssl context @ ~L626, rpcprotocol.h SSLIOStreamDevice, the -rpcssl* options in init.cpp). This is the -lssl (TLS) user — distinct sub-library from the -lcrypto users above, so the final unlink can't happen while it lives. Pure deletion, zero consensus surface: every RPC consumer is on loopback and TLS is terminated at the reverse proxy. Upstream deleted it in 0.12 alongside the libevent HTTP-server rewrite, but OFF can just drop the SSL branch and keep the plaintext boost::asio path bound to 127.0.0.1 — no libevent migration required. Deprecate in favor of stunnel/SSH tunnels.

Payoff commit

Remove -lssl -lcrypto from the link — gated on both sub-task 6 (BIP70, the -lcrypto/X.509 user) and sub-task 7 (rpcssl, the -lssl/TLS user) being gone, since those are the only consumers of the respective libs left after 1–5. Then drop OpenSSL from depends/ + configure.ac, delete the bignum.h wrappers. Build works against system OpenSSL 3 (unused) or with OpenSSL entirely absent. The 1.0.2 pin is gone.

Test plan

  • Reindex-from-zero on a mainnet datadir (validates buried non-canonical sigs don't break the verify swap).
  • Full functional/regtest suite, esp. script/sig tests.
  • Encrypted-wallet round trip (lock/unlock/spend) on a pre-change wallet.dat.
  • Cross-version testnet soak across the BIP66 height (identical block acceptance new-vs-current).
  • Sign side: produce + verify sigs, confirm RFC6979 determinism.

Sequencing / risk

  • Steps 4/5/6 have no consensus surface — can land anytime.
  • Step 2 (verify swap) is consensus-sensitive: rides normal soak discipline; never bundle into a release doing other consensus work.
  • Suggested vehicle: a v2.1.x crypto-modernization line, after the bipsoft → main merge and with #33 live.

Blocks: depends on #33 (BIP66). Drafted 2026-06-27; primer in #development-off. Filed automatically at #33 mainnet activation.

## Goal Finish what #33 starts. #33 removes OpenSSL from signature *encoding* consensus (the strict-DER parser is now OFF's own code); it explicitly leaves the EC *math* on OpenSSL's `ECDSA_verify`. This issue removes OpenSSL from everything that's left — EC math, wallet AES, RNG, GUI payment protocol, RPC TLS — so neither daemon nor wallet links `libssl`/`libcrypto` at all. Retires the OpenSSL-1.0.2 build pin. ## Why - **Build health:** the 1.0.2 pin exists only because the wallet uses BIGNUM-on-`EC_KEY` APIs that OpenSSL 3 deleted. Drop the link → build against modern OpenSSL (unused) or nothing. Single biggest source of the multi-step build dance. - **Consensus safety:** while verify math runs through OpenSSL, a future OpenSSL behavior shift can drift consensus. #33 only closed the *encoding* half. - **Security:** libsecp256k1 is purpose-built, constant-time, deterministic; the 0.10-era `key.cpp` EC paths are long-dead upstream. ## Hard prerequisite **#33 (BIP66) must be active on mainnet (h=1,055,555) before the verify swap.** libsecp256k1 only accepts strict-DER; swapping verify before every newly-mined block is guaranteed strict-DER risks rejecting a legitimately-mined block = consensus split. Order is non-negotiable. (This issue was filed automatically at activation for that reason.) ## Scope — seven sub-tasks, roughly independent 1. **Vendor libsecp256k1** into `src/secp256k1/`, wire into the build. Pin the commit Bitcoin Core 0.12–0.13 shipped (matches the EC math OFF already validates). 2. **ECDSA verify → libsecp256k1** (`key.cpp` / `pubkey.cpp`). The consensus-sensitive step. - *Landmine A:* buried pre-checkpoint blocks may carry non-canonical sigs OpenSSL waved through — confirm reindex-from-zero behavior, or keep an OpenSSL fallback gated below the BIP66 height. - *Landmine B:* **low-S is policy (mempool), not consensus** — preserve that split or you fork. 3. **ECDSA sign → libsecp256k1** (`key.cpp`). Not consensus-critical (only affects what *we* emit). Free win: RFC6979 deterministic nonces. 4. **AES → ctaes** (`crypter.cpp`). In-tree constant-time AES-256-CBC. Acceptance gate: round-trip lock/unlock/spend on a pre-change encrypted wallet.dat. 5. **RNG** (`random.cpp`): replace `RAND_bytes` / `RAND_add` with Core's modern RNG (platform entropy + ChaCha20). 6. **Drop BIP70 payment protocol** (Qt `paymentserver.cpp` X.509 path) — a `-lcrypto` X.509 user, no merchant use case. 7. **Remove `-rpcssl`** (`rpcserver.cpp` `boost::asio::ssl` context @ ~L626, `rpcprotocol.h` `SSLIOStreamDevice`, the `-rpcssl*` options in `init.cpp`). This is the **`-lssl`** (TLS) user — distinct sub-library from the `-lcrypto` users above, so the final unlink can't happen while it lives. Pure deletion, zero consensus surface: every RPC consumer is on loopback and TLS is terminated at the reverse proxy. Upstream deleted it in 0.12 alongside the libevent HTTP-server rewrite, but OFF can just drop the SSL branch and keep the plaintext `boost::asio` path bound to 127.0.0.1 — no libevent migration required. Deprecate in favor of stunnel/SSH tunnels. ## Payoff commit Remove `-lssl -lcrypto` from the link — gated on **both** sub-task 6 (BIP70, the `-lcrypto`/X.509 user) **and** sub-task 7 (rpcssl, the `-lssl`/TLS user) being gone, since those are the only consumers of the respective libs left after 1–5. Then drop OpenSSL from `depends/` + `configure.ac`, delete the `bignum.h` wrappers. Build works against system OpenSSL 3 (unused) or with OpenSSL entirely absent. The 1.0.2 pin is gone. ## Test plan - Reindex-from-zero on a mainnet datadir (validates buried non-canonical sigs don't break the verify swap). - Full functional/regtest suite, esp. script/sig tests. - Encrypted-wallet round trip (lock/unlock/spend) on a pre-change wallet.dat. - Cross-version testnet soak across the BIP66 height (identical block acceptance new-vs-current). - Sign side: produce + verify sigs, confirm RFC6979 determinism. ## Sequencing / risk - Steps 4/5/6 have no consensus surface — can land anytime. - Step 2 (verify swap) is consensus-sensitive: rides normal soak discipline; never bundle into a release doing other consensus work. - Suggested vehicle: a **v2.1.x crypto-modernization line**, after the bipsoft → main merge and with #33 live. --- *Blocks: depends on #33 (BIP66). Drafted 2026-06-27; primer in #development-off. Filed automatically at #33 mainnet activation.*
dobbscoin commented 2026-07-27 21:51:18 +00:00

Triage: out of scope for v2.1.0-Nodens, targeted at v2.1.1+.

Nodens is deliberately a small, already-soaked release: the Phase-2 ACP first-light wiring (#40), the RFC 6598 netbase fix (#44), -miningaddress (#46), and the #48 fresh-datadir init fix. The OpenSSL evacuation is a large crypto-surface migration (EC math → libsecp256k1, ctaes, RNG, BIP70/rpcssl removal) with no height gate forcing its schedule — bundling it into a release whose job is checkpoint first-light would put the riskiest possible change in the least appropriate vehicle.

Its [post-#33] prerequisite is now satisfied (BIP66 strict-DER activated cleanly at h=1,055,555 and #33 is closed), so this is genuinely unblocked — just queued behind Nodens. Leaving open.

Triage: **out of scope for v2.1.0-Nodens**, targeted at v2.1.1+. Nodens is deliberately a small, already-soaked release: the Phase-2 ACP first-light wiring (#40), the RFC 6598 netbase fix (#44), `-miningaddress` (#46), and the #48 fresh-datadir init fix. The OpenSSL evacuation is a large crypto-surface migration (EC math → libsecp256k1, ctaes, RNG, BIP70/rpcssl removal) with no height gate forcing its schedule — bundling it into a release whose job is checkpoint first-light would put the riskiest possible change in the least appropriate vehicle. Its `[post-#33]` prerequisite is now satisfied (BIP66 strict-DER activated cleanly at h=1,055,555 and #33 is closed), so this is genuinely unblocked — just queued behind Nodens. Leaving open.
dobbscoin commented 2026-07-30 17:29:59 +00:00

Full-tree dependency survey complete (2026-07-30). Every claim in the issue body checks out, and the survey found two subsystems the subtask list missed — one of them is now the hardest item in the evacuation:

  1. There is no src/crypto/. Consensus hashing — txid double-SHA256, Hash160, OP_SHA256/OP_HASH160, BIP32 HMAC-SHA512 — runs entirely on OpenSSL via hash.{h,cpp}. Upstream's native src/crypto/ (0.11) never reached this fork. Mechanical, bit-identical port, but a real subtask this issue didn't list.
  2. CBigNum is not a payoff-commit cleanup. bignum.h declares class CBigNum : public BIGNUM — literally inheriting the OpenSSL struct, impossible on 1.1+/3.x — and its consumers are the script interpreter's arithmetic opcodes, SetCompact/GetCompact target math, the retarget path, and GetBlockWork. Retiring it means arith_uint256 for work/target plus CScriptNum for script (upstream PR #4988 lineage, with its ≤4-byte-operand equivalence argument) — the single most consensus-hazardous step here, deserving its own release + soak. Extra wrinkle: the LWMA-3 work back-ported arith_uint256-idiom code into CBigNum (see the note at the top of pow.cpp), which must be unwound.

Other confirmations: no vendored libsecp256k1 anywhere (sign AND verify are OpenSSL EC_KEY, incl. hand-rolled EC_KEY_regenerate_key + SEC1 pubkey recovery with direct sig->r/sig->s access); the Quark PoW is already OpenSSL-free; -lssl -lcrypto are injected into global LIBS by configure.ac so the link is invisible at the Makefile level; db.cpp's <openssl/rand.h> is a dead include; and the #33 prerequisite is satisfied (BIP66 active on mainnet since h=1,055,555, 2026-07-23).

The validated, phased implementation plan — 5 phases across ~5 releases, cheapest → most hazardous, with per-phase acceptance gates (bit-exact EVP_BytesToKey KDF for wallet compat, reindex-from-zero for the verify swap, script-vector equivalence for CScriptNum) — lands at research/issue47-openssl-evacuation-plan.md via PR #56. Phases 0–2 (banner/dead-include trivia, -rpcssl + BIP70 deletions, RNG/cleanse/ctaes) have zero consensus surface and can start any time.

**Full-tree dependency survey complete (2026-07-30).** Every claim in the issue body checks out, and the survey found **two subsystems the subtask list missed** — one of them is now the hardest item in the evacuation: 1. **There is no `src/crypto/`.** Consensus hashing — txid double-SHA256, `Hash160`, `OP_SHA256`/`OP_HASH160`, BIP32 HMAC-SHA512 — runs entirely on OpenSSL via `hash.{h,cpp}`. Upstream's native `src/crypto/` (0.11) never reached this fork. Mechanical, bit-identical port, but a real subtask this issue didn't list. 2. **CBigNum is not a payoff-commit cleanup.** `bignum.h` declares `class CBigNum : public BIGNUM` — literally inheriting the OpenSSL struct, impossible on 1.1+/3.x — and its consumers are the script interpreter's arithmetic opcodes, `SetCompact`/`GetCompact` target math, the retarget path, and `GetBlockWork`. Retiring it means `arith_uint256` for work/target **plus** `CScriptNum` for script (upstream PR #4988 lineage, with its ≤4-byte-operand equivalence argument) — the single most consensus-hazardous step here, deserving its own release + soak. Extra wrinkle: the LWMA-3 work back-ported `arith_uint256`-idiom code *into* CBigNum (see the note at the top of `pow.cpp`), which must be unwound. Other confirmations: no vendored libsecp256k1 anywhere (sign AND verify are OpenSSL `EC_KEY`, incl. hand-rolled `EC_KEY_regenerate_key` + SEC1 pubkey recovery with direct `sig->r`/`sig->s` access); the Quark PoW is already OpenSSL-free; `-lssl -lcrypto` are injected into global `LIBS` by `configure.ac` so the link is invisible at the Makefile level; `db.cpp`'s `<openssl/rand.h>` is a dead include; and the #33 prerequisite is **satisfied** (BIP66 active on mainnet since h=1,055,555, 2026-07-23). The validated, phased implementation plan — 5 phases across ~5 releases, cheapest → most hazardous, with per-phase acceptance gates (bit-exact `EVP_BytesToKey` KDF for wallet compat, reindex-from-zero for the verify swap, script-vector equivalence for CScriptNum) — lands at `research/issue47-openssl-evacuation-plan.md` via PR #56. Phases 0–2 (banner/dead-include trivia, `-rpcssl` + BIP70 deletions, RNG/cleanse/ctaes) have zero consensus surface and can start any time.
dobbscoin commented 2026-07-30 17:34:20 +00:00

Plan amended (same doc, research/issue47-openssl-evacuation-plan.md, now merged): the sibling dobbscoin-source tree turns out to be a maintainer-owned donor for most phases — genuine Core 0.10 layout, already modernized to build on OpenSSL 3. It carries src/crypto/ native hashing, vendored secp256k1 with the sign path wired, an OpenSSL-3-safe ecwrapper (verify + SEC1 recovery), CScriptNum plus upstream's scriptnum equivalence-test harness (including the test-only reference bignum.h), opaque-safe BIGNUM wrappers for KGW/DigiShield pow math, and a proven-compatible EVP_BytesToKey crypter in heap-CTX form.

Strategic consequence — new milestone M1: "builds on OpenSSL 3" and "OpenSSL-free" are separable. Porting the donor's compat patterns retires the OpenSSL-1.0.2 build pin at ~v2.1.2, long before full evacuation; phases 3-5 then proceed on security/consensus-hygiene grounds rather than build pain. One caveat documented: OFF's interpreter is 0.8-lineage, so CScriptNum semantics get ported into it rather than lifting the 0.10 interpreter wholesale.

Plan amended (same doc, `research/issue47-openssl-evacuation-plan.md`, now merged): the sibling **dobbscoin-source** tree turns out to be a maintainer-owned donor for most phases — genuine Core 0.10 layout, already modernized to build on OpenSSL 3. It carries `src/crypto/` native hashing, vendored secp256k1 with the sign path wired, an OpenSSL-3-safe `ecwrapper` (verify + SEC1 recovery), `CScriptNum` **plus upstream's scriptnum equivalence-test harness** (including the test-only reference bignum.h), opaque-safe BIGNUM wrappers for KGW/DigiShield pow math, and a proven-compatible `EVP_BytesToKey` crypter in heap-CTX form. Strategic consequence — new milestone **M1**: "builds on OpenSSL 3" and "OpenSSL-free" are separable. Porting the donor's compat patterns retires the OpenSSL-1.0.2 build pin at ~v2.1.2, long before full evacuation; phases 3-5 then proceed on security/consensus-hygiene grounds rather than build pain. One caveat documented: OFF's interpreter is 0.8-lineage, so CScriptNum semantics get ported *into* it rather than lifting the 0.10 interpreter wholesale.
Sign in to join this conversation.
No labels
enhancement
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
SubGeniusFinance/Offerings-to-Cthulhu#47
No description provided.