use rand::Rng;
use secp256k1::{Secp256k1, XOnlyPublicKey};
use sha2::{Digest, Sha256};
use super::{IdentityError, NodeAddr};
const AUTH_DOMAIN: &[u8] = b"fips-auth-v1";
#[derive(Clone, Copy, Debug)]
pub struct AuthChallenge([u8; 32]);
impl AuthChallenge {
pub fn generate() -> Self {
let mut bytes = [0u8; 32];
rand::rng().fill_bytes(&mut bytes);
Self(bytes)
}
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn verify(&self, response: &AuthResponse) -> Result<NodeAddr, IdentityError> {
let digest = auth_challenge_digest(&self.0, response.timestamp);
let secp = Secp256k1::new();
secp.verify_schnorr(&response.signature, &digest, &response.pubkey)
.map_err(|_| IdentityError::SignatureVerificationFailed)?;
Ok(NodeAddr::from_pubkey(&response.pubkey))
}
}
#[derive(Clone, Debug)]
pub struct AuthResponse {
pub pubkey: XOnlyPublicKey,
pub timestamp: u64,
pub signature: secp256k1::schnorr::Signature,
}
pub(super) fn auth_challenge_digest(challenge: &[u8; 32], timestamp: u64) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(AUTH_DOMAIN);
hasher.update(challenge);
hasher.update(timestamp.to_be_bytes());
let result = hasher.finalize();
let mut digest = [0u8; 32];
digest.copy_from_slice(&result);
digest
}