mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Config system overhaul: - Add mesh-public topology (5 Docker nodes + external pub node at 217.77.8.91) - Refactor generate-configs.sh to read topology YAML files directly, replacing hardcoded lookup functions, with multi-char node ID support - Generate npubs.env with all node npubs; sourced by test scripts and injected into containers via docker-compose env_file directive - Add .env with COMPOSE_PROFILES=mesh so docker compose defaults to mesh topology without requiring --profile Deterministic mesh identity derivation: - Add derive-keys.py: pure Python tool (no deps) deriving nsec/npub from sha256(mesh-name|node-id) via secp256k1 and BIP-173 bech32 - generate-configs.sh and build.sh accept optional mesh-name argument; Docker node identities are derived while external nodes keep hardcoded keys from topology YAML Docker compose improvements: - Add mesh-public profile service definitions - build.sh now runs docker compose build automatically, providing a single command for the full binary+configs+images pipeline - Ping test script supports mesh-public topology Container services: - Add HTTP server on port 8000 (IPv6-bound) serving static page, accessible over FIPS overlay via npub.fips hostnames - Add rsync to container packages Documentation: - Comprehensive README update covering topology system, identity derivation, npubs.env, and container background services (SSH, iperf3, HTTP) with usage examples
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}")
|