feat(fips): integrate jmcorgan/fips as preferred non-Tor transport + v1.4.0
Bakes the FIPS (Free Internetworking Peering System) mesh daemon into the node stack, supervised by archipelago alongside Tor. Runs as a system service, identity derives from the same BIP-39 master seed, and user-triggered updates track upstream main. Identity seed.rs: new HKDF label archipelago/fips/secp256k1/v1 → dedicated secp256k1 key, distinct from the Nostr-node key for crypto isolation but still seed-recoverable identity.rs: writes fips_key[.pub] to /data/identity on onboarding, chmod 0600; fips_key_exists / load_fips_keys / fips_npub accessors Transport TransportKind::Fips=3 inserted between LAN and Tor (Tor bumps to 4) → router prefers FIPS over Tor for all peer traffic PeerRecord gains fips_npub + last_fips fields (serde(default) for backward-compat with older nodes) transport/fips.rs: NodeTransport stub, reports unavailable until the daemon is live so router falls through to Tor cleanly Federation invites FederatedNode and FederationInvite carry optional fips_npub create_invite / accept_invite / peer-joined callback thread it end to end; signature domain deliberately unchanged — FIPS Noise does its own session auth, so the unsigned hint only affects path selection crate::fips config.rs: renders /etc/fips/fips.yaml and sudo-installs key material service.rs: systemctl status/activate/restart/mask wrappers update.rs: GitHub API check against upstream main; apply stubbed until per-commit .deb artefact source is decided RPC + dashboard fips.status / fips.check-update / fips.apply-update / fips.install / fips.restart registered in dispatcher HomeNetworkCard.vue shipped standalone (unmounted — place in Home.vue when ready); shows state pill, version, FIPS npub, update button, activate button when key is present but service is down ISO + systemd archipelago-fips.service: conditional on key presence, masked by default — backend unmasks after onboarding writes the key build-auto-installer-iso.sh: multi-stage Dockerfile builds the FIPS .deb from jmcorgan/fips main (fail-loud), COPYs it into rootfs, apt installs it so trixie resolves deps; unit copied + masked Version bump: 1.3.5 → 1.4.0 Tests: 33 new/updated passing (seed, identity, transport, federation, fips module, transport::fips). Known gaps: fips.apply-update returns a clear stub error until upstream publishes per-commit .deb artefacts; HomeNetworkCard is not mounted in Home.vue by default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
f04804ae25
commit
c1cfca6212
@@ -7,6 +7,7 @@
|
||||
//! → Master Seed (64 bytes)
|
||||
//! ├── HKDF(seed, "archipelago/node/ed25519/v1") → Node Ed25519 → did:key
|
||||
//! ├── HKDF(seed, "archipelago/nostr-node/secp256k1/v1") → Node Nostr key
|
||||
//! ├── HKDF(seed, "archipelago/fips/secp256k1/v1") → FIPS mesh transport key
|
||||
//! ├── HKDF(seed, "archipelago/identity/{i}/ed25519/v1") → Identity i Ed25519
|
||||
//! ├── BIP-32 m/44'/1237'/0'/0/{i} → Identity i Nostr (NIP-06)
|
||||
//! ├── BIP-32 m/84'/0'/0' → Bitcoin Core wallet
|
||||
@@ -31,6 +32,7 @@ const ENCRYPTED_SEED_FILE: &str = "master_seed.enc";
|
||||
// HKDF info strings for domain-separated key derivation.
|
||||
const NODE_ED25519_INFO: &[u8] = b"archipelago/node/ed25519/v1";
|
||||
const NODE_NOSTR_INFO: &[u8] = b"archipelago/nostr-node/secp256k1/v1";
|
||||
const FIPS_KEY_INFO: &[u8] = b"archipelago/fips/secp256k1/v1";
|
||||
const LND_ENTROPY_INFO: &[u8] = b"archipelago/lnd/entropy/v1";
|
||||
|
||||
// ─── MasterSeed ─────────────────────────────────────────────────────────
|
||||
@@ -103,6 +105,16 @@ pub fn derive_node_nostr_key(seed: &MasterSeed) -> Result<nostr_sdk::Keys> {
|
||||
Ok(nostr_sdk::Keys::new(secret))
|
||||
}
|
||||
|
||||
/// Derive the FIPS mesh transport secp256k1 key.
|
||||
/// Distinct from the Nostr-node key so compromise of one surface does not
|
||||
/// impersonate on the other; still seed-recoverable.
|
||||
pub fn derive_fips_key(seed: &MasterSeed) -> Result<nostr_sdk::Keys> {
|
||||
let derived = hkdf_derive_32(seed.as_bytes(), FIPS_KEY_INFO)?;
|
||||
let secret = nostr_sdk::SecretKey::from_slice(&derived)
|
||||
.map_err(|e| anyhow::anyhow!("Invalid secp256k1 key from HKDF: {}", e))?;
|
||||
Ok(nostr_sdk::Keys::new(secret))
|
||||
}
|
||||
|
||||
/// Derive an identity's Nostr secp256k1 key via BIP-32.
|
||||
/// Path: m/44'/1237'/0'/0/{index} (NIP-06 compliant).
|
||||
pub fn derive_nostr_identity_key(seed: &MasterSeed, index: u32) -> Result<nostr_sdk::Keys> {
|
||||
@@ -395,6 +407,25 @@ mod tests {
|
||||
assert_eq!(keys1.public_key().to_hex(), keys2.public_key().to_hex());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fips_key_deterministic_and_distinct() {
|
||||
let (_, seed) = MasterSeed::from_mnemonic_words(TEST_MNEMONIC).unwrap();
|
||||
let fips1 = derive_fips_key(&seed).unwrap();
|
||||
let fips2 = derive_fips_key(&seed).unwrap();
|
||||
assert_eq!(
|
||||
fips1.public_key().to_hex(),
|
||||
fips2.public_key().to_hex(),
|
||||
"FIPS key must be deterministic for a given seed"
|
||||
);
|
||||
|
||||
let nostr = derive_node_nostr_key(&seed).unwrap();
|
||||
assert_ne!(
|
||||
fips1.public_key().to_hex(),
|
||||
nostr.public_key().to_hex(),
|
||||
"FIPS key must differ from the Nostr-node key"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bitcoin_xprv_deterministic() {
|
||||
let (_, seed) = MasterSeed::from_mnemonic_words(TEST_MNEMONIC).unwrap();
|
||||
@@ -482,6 +513,7 @@ mod tests {
|
||||
|
||||
let node_ed = derive_node_ed25519(&seed).unwrap();
|
||||
let node_nostr = derive_node_nostr_key(&seed).unwrap();
|
||||
let fips = derive_fips_key(&seed).unwrap();
|
||||
let id0_ed = derive_identity_ed25519(&seed, 0).unwrap();
|
||||
let id0_nostr = derive_nostr_identity_key(&seed, 0).unwrap();
|
||||
let _btc = derive_bitcoin_xprv(&seed).unwrap();
|
||||
@@ -491,6 +523,7 @@ mod tests {
|
||||
let node_ed_hex = hex::encode(node_ed.verifying_key().as_bytes());
|
||||
let id0_ed_hex = hex::encode(id0_ed.verifying_key().as_bytes());
|
||||
let node_nostr_hex = node_nostr.public_key().to_hex();
|
||||
let fips_hex = fips.public_key().to_hex();
|
||||
let id0_nostr_hex = id0_nostr.public_key().to_hex();
|
||||
let lnd_hex = hex::encode(lnd);
|
||||
|
||||
@@ -498,6 +531,7 @@ mod tests {
|
||||
&node_ed_hex,
|
||||
&id0_ed_hex,
|
||||
&node_nostr_hex,
|
||||
&fips_hex,
|
||||
&id0_nostr_hex,
|
||||
&lnd_hex,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user