mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
Replace 4 near-identical per-harness Docker setups with unified shared infrastructure. Net result: -463 lines across 55 files, faster CI (8.5 min vs ~13.5 min), 14 scenarios (down from 21). Unified Docker image (testing/docker/): - Single Dockerfile (trixie-slim) with FIPS_TEST_MODE env var for mode dispatch: default, chaos, sidecar, tor-socks5, tor-directory - Single entrypoint.sh with conditional logic per mode - Replaces 5 Dockerfiles, 3 entrypoints, 4 resolv.conf copies Shared build and libraries (testing/scripts/, testing/lib/): - testing/scripts/build.sh: single build script with macOS zigbuild support, replaces 4 per-harness copies - testing/lib/derive_keys.py: shared key derivation module, replaces 3 copies of derive-keys.py - testing/lib/log_analysis.py: shared log analysis extracted from chaos sim/logs.py, with CLI interface and rekey cutover tracking - testing/lib/wait-converge.sh: shared convergence wait helpers (wait_for_links, wait_for_peers) using fipsctl JSON polling Scenario consolidation: - Remove 7 redundant scenarios: tcp-chain (subsumed by tcp-mesh), tcp-only (subsumed by tcp-mesh), chaos-10 (replaced by churn-mixed --nodes 10), churn-10/churn-20/churn-20-mixed (subsumed by parameterized churn-mixed), cost-mixed-7node (overlaps mixed-technology) - Add churn-mixed scenario with --nodes flag for scale testing - Reduce idle scenario durations: smoke-10 60s→30s, ethernet-only 90s→30s, cost-avoidance 120s→45s, depth-vs-cost 120s→45s, bottleneck-parent 120s→60s, mixed-technology 180s→90s CI updates: - ci-local.sh: unified image build, structured chaos suite entries with per-scenario flags, --skip-build for sidecar - ci.yml: shared binary install + image build step, updated scenario matrix, chaos_flags support for parameterized scenarios - Add tcp-mesh and congestion-stress to CI matrix - Static ping test uses active peer convergence detection instead of hardcoded 5s sleep Chaos infrastructure improvements (from discovery-rework branch): - Pre-built Docker image instead of per-service build at scale - --nodes flag in chaos.sh for runtime topology size override
112 lines
2.9 KiB
Python
Executable File
112 lines
2.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Derive deterministic nostr nsec/npub from mesh-name and node-name.
|
|
|
|
Usage: derive-keys.py <mesh-name> <node-name>
|
|
Output: nsec=<hex>\nnpub=<bech32>
|
|
|
|
Derivation: nsec = sha256(mesh_name + "|" + node_name)
|
|
npub = bech32("npub", secp256k1_pubkey_x(nsec))
|
|
|
|
Pure Python, no external dependencies.
|
|
"""
|
|
|
|
import hashlib
|
|
import sys
|
|
|
|
# --- secp256k1 ---
|
|
|
|
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
|
Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
|
|
Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
|
|
|
|
|
|
def _modinv(a, m):
|
|
return pow(a, m - 2, m)
|
|
|
|
|
|
def _point_add(p1, p2):
|
|
if p1 is None:
|
|
return p2
|
|
if p2 is None:
|
|
return p1
|
|
x1, y1 = p1
|
|
x2, y2 = p2
|
|
if x1 == x2 and y1 != y2:
|
|
return None
|
|
if x1 == x2:
|
|
lam = (3 * x1 * x1) * _modinv(2 * y1, P) % P
|
|
else:
|
|
lam = (y2 - y1) * _modinv(x2 - x1, P) % P
|
|
x3 = (lam * lam - x1 - x2) % P
|
|
y3 = (lam * (x1 - x3) - y1) % P
|
|
return (x3, y3)
|
|
|
|
|
|
def _scalar_mult(k, point):
|
|
result = None
|
|
addend = point
|
|
while k:
|
|
if k & 1:
|
|
result = _point_add(result, addend)
|
|
addend = _point_add(addend, addend)
|
|
k >>= 1
|
|
return result
|
|
|
|
|
|
# --- bech32 (BIP-173) ---
|
|
|
|
_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
|
|
|
|
|
def _bech32_polymod(values):
|
|
gen = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]
|
|
chk = 1
|
|
for v in values:
|
|
b = chk >> 25
|
|
chk = (chk & 0x1FFFFFF) << 5 ^ v
|
|
for i in range(5):
|
|
chk ^= gen[i] if ((b >> i) & 1) else 0
|
|
return chk
|
|
|
|
|
|
def _bech32_encode(hrp, data_5bit):
|
|
hrp_expand = [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
|
|
polymod = _bech32_polymod(hrp_expand + data_5bit + [0] * 6) ^ 1
|
|
checksum = [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
|
|
return hrp + "1" + "".join(_CHARSET[d] for d in data_5bit + checksum)
|
|
|
|
|
|
def _convertbits(data, frombits, tobits):
|
|
acc, bits, ret = 0, 0, []
|
|
maxv = (1 << tobits) - 1
|
|
for value in data:
|
|
acc = (acc << frombits) | value
|
|
bits += frombits
|
|
while bits >= tobits:
|
|
bits -= tobits
|
|
ret.append((acc >> bits) & maxv)
|
|
if bits:
|
|
ret.append((acc << (tobits - bits)) & maxv)
|
|
return ret
|
|
|
|
|
|
# --- public API ---
|
|
|
|
def derive(mesh_name, node_name):
|
|
nsec_hex = hashlib.sha256(f"{mesh_name}|{node_name}".encode()).hexdigest()
|
|
k = int(nsec_hex, 16)
|
|
pub = _scalar_mult(k, (Gx, Gy))
|
|
x_hex = format(pub[0], "064x")
|
|
data_5bit = _convertbits(list(bytes.fromhex(x_hex)), 8, 5)
|
|
npub = _bech32_encode("npub", data_5bit)
|
|
return nsec_hex, npub
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 3:
|
|
print(f"Usage: {sys.argv[0]} <mesh-name> <node-name>", file=sys.stderr)
|
|
sys.exit(1)
|
|
nsec, npub = derive(sys.argv[1], sys.argv[2])
|
|
print(f"nsec={nsec}")
|
|
print(f"npub={npub}")
|