use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
use pot_o_core::TribeResult;
use pot_o_mining::Challenge;
use crate::peer_network::PeerInfo;
pub trait ProofAuthority: Send + Sync {
fn verify_miner_identity(&self, pubkey: &str, signature: &[u8]) -> TribeResult<bool>;
fn sign_challenge(&self, challenge: &Challenge) -> TribeResult<Vec<u8>>;
fn validate_node_connection(&self, peer: &PeerInfo) -> TribeResult<bool>;
}
pub struct Ed25519Authority {
keypair: Option<SigningKey>,
}
impl Ed25519Authority {
pub fn new(keypair_path: &str) -> Self {
let contents = match std::fs::read_to_string(keypair_path) {
Ok(s) => s,
Err(e) => {
tracing::warn!(
path = %keypair_path,
error = %e,
"Ed25519Authority: cannot read keypair file; signing disabled"
);
return Self { keypair: None };
}
};
let keypair_bytes: Vec<u8> = match serde_json::from_str(&contents) {
Ok(b) => b,
Err(e) => {
tracing::warn!(
path = %keypair_path,
error = %e,
"Ed25519Authority: invalid JSON keypair format; signing disabled"
);
return Self { keypair: None };
}
};
if keypair_bytes.len() >= 32 {
let mut seed = [0u8; 32];
seed.copy_from_slice(&keypair_bytes[..32]);
let signing_key = SigningKey::from_bytes(&seed);
tracing::info!(
pubkey = %hex::encode(signing_key.verifying_key().to_bytes()),
"Ed25519Authority: loaded signing key"
);
Self {
keypair: Some(signing_key),
}
} else {
tracing::warn!(
path = %keypair_path,
"Ed25519Authority: keypair file too short ({} bytes); signing disabled",
keypair_bytes.len()
);
Self { keypair: None }
}
}
fn public_key_from_str(pubkey: &str) -> Option<VerifyingKey> {
let bytes = hex::decode(pubkey).ok()?;
let arr: [u8; 32] = bytes.try_into().ok()?;
VerifyingKey::from_bytes(&arr).ok()
}
}
impl ProofAuthority for Ed25519Authority {
fn verify_miner_identity(&self, pubkey: &str, signature: &[u8]) -> TribeResult<bool> {
let pk = match Self::public_key_from_str(pubkey) {
Some(pk) => pk,
None => return Ok(false),
};
if signature.len() != 64 {
return Ok(false);
}
let mut sig_bytes = [0u8; 64];
sig_bytes.copy_from_slice(signature);
let sig = Signature::from_bytes(&sig_bytes);
Ok(pk.verify_strict(pubkey.as_bytes(), &sig).is_ok())
}
fn sign_challenge(&self, challenge: &Challenge) -> TribeResult<Vec<u8>> {
match &self.keypair {
Some(kp) => {
let msg = serde_json::to_vec(challenge)
.map_err(|e| pot_o_core::TribeError::SerializationError(e.to_string()))?;
let sig = kp
.try_sign(&msg)
.map_err(|e| pot_o_core::TribeError::SerializationError(e.to_string()))?;
Ok(sig.to_bytes().to_vec())
}
None => {
tracing::warn!("Ed25519Authority: no signing key loaded; returning zero signature");
Ok(vec![0u8; 64])
}
}
}
fn validate_node_connection(&self, _peer: &PeerInfo) -> TribeResult<bool> {
Ok(true)
}
}
#[derive(Debug, Clone)]
pub struct MtlsConfig {
pub ca_cert_path: String,
pub node_cert_path: String,
pub node_key_path: String,
}
pub struct MtlsAuthority {
pub config: MtlsConfig,
}
impl ProofAuthority for MtlsAuthority {
fn verify_miner_identity(&self, _pubkey: &str, _signature: &[u8]) -> TribeResult<bool> {
todo!("mTLS miner identity verification not yet implemented")
}
fn sign_challenge(&self, _challenge: &Challenge) -> TribeResult<Vec<u8>> {
todo!("mTLS challenge signing not yet implemented")
}
fn validate_node_connection(&self, _peer: &PeerInfo) -> TribeResult<bool> {
todo!("mTLS node connection validation not yet implemented")
}
}
pub struct HmacDeviceAuth {
pub shared_secret: Vec<u8>,
}
impl ProofAuthority for HmacDeviceAuth {
fn verify_miner_identity(&self, _pubkey: &str, _signature: &[u8]) -> TribeResult<bool> {
todo!("HMAC device identity verification not yet implemented")
}
fn sign_challenge(&self, _challenge: &Challenge) -> TribeResult<Vec<u8>> {
todo!("HMAC challenge signing not yet implemented")
}
fn validate_node_connection(&self, _peer: &PeerInfo) -> TribeResult<bool> {
todo!("HMAC node connection validation not yet implemented")
}
}