Commit Graph
4 Commits
Author SHA1 Message Date
jwinterm e838565ed7 feat: electrum client (webpki TLS, bech32, checked sats)
BTC/LTC Electrum/Fulcrum client, reworked clean from the chain-audit
playbook (not ported). Every byte from a server is treated as hostile.

Security:
- TLS verified against the webpki root store WITH hostname checking; the
  accept-any-certificate verifier is gone (closes the MITM hole). The
  connector is built once and reused.
- Addresses decoded via the `bech32` crate (full checksum + bech32m +
  witness version) and base58check (checksum); the hand-rolled bech32
  underflow panic is removed. Coin/network enforced (reject ltc-on-btc,
  testnet-on-mainnet) — no byte-indexing of untrusted input.
- Amounts pass a fallible checked f64->sat converter (rejects
  non-finite/negative/>2^53) with checked_add summation; no silent
  saturation to 0 / u64::MAX. estimatefee requires finite, clamps.
- Bounded response read + per-exchange timeout (no OOM, no slowloris).
- JSON-RPC response id correlated with the request.
- Untrusted upstream text is control-stripped + length-capped before it
  reaches a (privately-logged) NodeError.

Single `call` primitive centralizes failover + id + envelope mapping;
public methods (balance/utxos/history/tip/verbose-tx/broadcast/fee) are
thin wrappers. Tip-attribution helpers dropped; total_sent_from now
declines (None) without resolved prevouts instead of mislabeling a
receive-with-change as a send.

20 L2 tests: address->script KATs (P2WPKH/P2WSH/taproot/legacy), network
rejection, panic-safety on short/garbage input, checked-sat bounds,
envelope id correlation, bounded/oversized read, duplex round-trip.
2026-06-22 20:57:08 -04:00
jwinterm 99cfc49cb2 feat: AppState + server wiring (config, pool, migrations, session manager)
- AppState (lib.rs): Config + Database + SessionManager + in-memory web-challenge
  store; injected into handlers via State<Arc<AppState>>
- main.rs: fail-closed Config::from_env, pg pool, run embedded migrations on
  start, build state, serve /health (auth/wallet routes added as handlers land)
- Cargo.toml: sqlx 'migrate' feature for embedded migrations
2026-06-22 14:36:51 -04:00
jwinterm 9ececb03af feat: NIP-98 auth + wallet-signature verification (hardened)
core/crypto/nip98.rs:
- verify_nip98 (login-grade): binds url+method+freshness
- verify_signed_action (state-change): adds single-use nonce + purpose +
  canonical request-descriptor hash + optional target-id/instance-id, each an
  exactly-one tag, constant-time compared, all checked on the signature-
  authenticated event (id recomputed + schnorr-verified before any tag trusted)
- hardening from adversarial review: length-guard pubkey/id/sig before k256
  construction (no panic on short input), saturating freshness window (no
  overflow wrap); regressions for all three

core/crypto/signatures.rs: BIP-137 ECDSA + Ed25519 verification returning
Result<(),AppError> (Ok only on valid; malformed->400, mismatch->401) — closes
the Ok(false) fail-open.

Cargo.toml: overflow-checks=true in release (defense-in-depth for auth paths).
2026-06-22 13:47:20 -04:00
jwinterm 143f00701b chore: initial foundation — server skeleton, autodocs, CI
- minimal Axum server exposing GET /health
- OpenAPI contract generated from handlers (gen_openapi -> openapi.json),
  drift-gated in CI so the spec can never silently diverge from the code
- CI: secret-scan + gitleaks, openapi drift gate, fmt/clippy/build/test/doc-test, cargo-deny
- Cargo.lock tracked for reproducible builds; publish=false; pinned toolchain
- README, SECURITY.md, deny.toml, .env.example
2026-06-22 06:18:57 -04:00