Update Fedimint configuration and enhance onboarding process
- Upgraded Fedimint version to v0.10.0 in docker-compose.yml and manifest.yml, adding support for the built-in Guardian UI. - Modified .gitignore to exclude deploy-config.sh script. - Enhanced onboarding process in AuthManager to persist onboarding state and validate password strength during user setup. - Updated API to handle onboarding completion and password change requests, ensuring a smoother user experience. - Improved configuration management to support Nostr discovery and Tor proxy settings, enhancing node identity features.
This commit is contained in:
122
core/archipelago/src/identity.rs
Normal file
122
core/archipelago/src/identity.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
//! Node identity: persistent Ed25519 key for private identification.
|
||||
//! Enables future P2P features (file transfer, streaming, ecash/Lightning).
|
||||
//! Supports did:key (W3C) for Web5/DID interoperability.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
|
||||
use rand::rngs::OsRng;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::fs;
|
||||
|
||||
const NODE_KEY_FILE: &str = "node_key";
|
||||
const NODE_KEY_PUB_FILE: &str = "node_key.pub";
|
||||
|
||||
/// Persistent node identity (Ed25519 keypair).
|
||||
/// Survives reboots; used for signing, verification, and node address.
|
||||
pub struct NodeIdentity {
|
||||
signing_key: SigningKey,
|
||||
identity_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl NodeIdentity {
|
||||
/// Load existing identity or create and persist a new one.
|
||||
pub async fn load_or_create(identity_dir: &Path) -> Result<Self> {
|
||||
fs::create_dir_all(identity_dir)
|
||||
.await
|
||||
.context("Failed to create identity directory")?;
|
||||
|
||||
let key_path = identity_dir.join(NODE_KEY_FILE);
|
||||
let pub_path = identity_dir.join(NODE_KEY_PUB_FILE);
|
||||
|
||||
let signing_key = if key_path.exists() {
|
||||
let bytes = fs::read(&key_path)
|
||||
.await
|
||||
.context("Failed to read node key")?;
|
||||
let arr: [u8; 32] = bytes
|
||||
.try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid node key length"))?;
|
||||
SigningKey::from_bytes(&arr)
|
||||
} else {
|
||||
let signing_key = SigningKey::generate(&mut OsRng);
|
||||
fs::write(&key_path, signing_key.to_bytes())
|
||||
.await
|
||||
.context("Failed to write node key")?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions(&key_path, std::fs::Permissions::from_mode(0o600))
|
||||
.await
|
||||
.context("Failed to set key permissions")?;
|
||||
}
|
||||
fs::write(&pub_path, signing_key.verifying_key().as_bytes())
|
||||
.await
|
||||
.context("Failed to write node public key")?;
|
||||
tracing::info!("🔑 Generated new node identity at {}", identity_dir.display());
|
||||
signing_key
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
signing_key,
|
||||
identity_dir: identity_dir.to_path_buf(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Public key as hex string (for ServerInfo, Nostr, etc.)
|
||||
pub fn pubkey_hex(&self) -> String {
|
||||
hex::encode(self.signing_key.verifying_key().as_bytes())
|
||||
}
|
||||
|
||||
/// Stable node ID derived from pubkey (first 16 chars of hex).
|
||||
pub fn node_id(&self) -> String {
|
||||
self.pubkey_hex().chars().take(16).collect()
|
||||
}
|
||||
|
||||
/// Sign data; returns hex-encoded signature.
|
||||
pub fn sign(&self, data: &[u8]) -> String {
|
||||
hex::encode(self.signing_key.sign(data).to_bytes())
|
||||
}
|
||||
|
||||
/// Verify a signature from a peer (pubkey hex, data, signature hex).
|
||||
pub fn verify(pubkey_hex: &str, data: &[u8], sig_hex: &str) -> Result<bool> {
|
||||
let bytes = hex::decode(pubkey_hex).context("Invalid pubkey hex")?;
|
||||
let verifying_key = VerifyingKey::from_bytes(
|
||||
bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid pubkey length"))?,
|
||||
)?;
|
||||
let sig_bytes = hex::decode(sig_hex).context("Invalid signature hex")?;
|
||||
let sig = Signature::from_bytes(
|
||||
sig_bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| anyhow::anyhow!("Invalid signature length"))?,
|
||||
);
|
||||
Ok(verifying_key.verify(data, &sig).is_ok())
|
||||
}
|
||||
|
||||
/// Node address format for invites: archipelago://<onion>#<pubkey>
|
||||
pub fn node_address(&self, onion: &str) -> String {
|
||||
format!("archipelago://{}#{}", onion.trim_end_matches('/'), self.pubkey_hex())
|
||||
}
|
||||
|
||||
/// DID in did:key format (W3C did:key method, Ed25519).
|
||||
/// Format: did:key:z<base58btc(multicodec_ed25519_pub + 32-byte pubkey)>
|
||||
pub fn did_key(&self) -> String {
|
||||
did_key_from_pubkey_hex(&self.pubkey_hex()).expect("pubkey_hex is valid")
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Ed25519 pubkey (hex) to did:key format.
|
||||
/// Used by RPC when identity is loaded from state.
|
||||
pub fn did_key_from_pubkey_hex(pubkey_hex: &str) -> Result<String> {
|
||||
let bytes = hex::decode(pubkey_hex).context("Invalid pubkey hex")?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(anyhow::anyhow!("Invalid pubkey length"));
|
||||
}
|
||||
let mut multicodec_pubkey = [0u8; 34];
|
||||
multicodec_pubkey[0] = 0xed;
|
||||
multicodec_pubkey[1] = 0x01;
|
||||
multicodec_pubkey[2..34].copy_from_slice(&bytes);
|
||||
Ok(format!("did:key:z{}", bs58::encode(multicodec_pubkey).into_string()))
|
||||
}
|
||||
Reference in New Issue
Block a user