mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 00:26:59 +00:00
Bump rand (0.8→0.10), rtnetlink (0.14→0.20), tun (0.7→0.8), simple-dns (0.9→0.11), socket2 (0.5→0.6), and criterion (0.5→0.8). Migrate all rand call sites: thread_rng()→rng(), gen()→random(), gen_range()→random_range(), RngCore→Rng trait. Work around secp256k1 0.30 requiring rand 0.8 by generating random bytes directly and constructing SecretKey from slice. Migrate rtnetlink to builder-based API: LinkSetRequest replaced with LinkUnspec builder + change(), RouteAddRequest replaced with RouteMessageBuilder. Remove bloom benchmark (criterion 0.8 incompatible with old harness config).
68 lines
2.0 KiB
Rust
68 lines
2.0 KiB
Rust
//! Authentication challenge-response protocol.
|
|
|
|
use rand::Rng;
|
|
use secp256k1::{Secp256k1, XOnlyPublicKey};
|
|
use sha2::{Digest, Sha256};
|
|
|
|
use super::{IdentityError, NodeAddr};
|
|
|
|
/// Domain separation string for authentication challenges.
|
|
const AUTH_DOMAIN: &[u8] = b"fips-auth-v1";
|
|
|
|
/// A 32-byte random authentication challenge.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct AuthChallenge([u8; 32]);
|
|
|
|
impl AuthChallenge {
|
|
/// Generate a new random challenge.
|
|
pub fn generate() -> Self {
|
|
let mut bytes = [0u8; 32];
|
|
rand::rng().fill_bytes(&mut bytes);
|
|
Self(bytes)
|
|
}
|
|
|
|
/// Create a challenge from bytes.
|
|
pub fn from_bytes(bytes: [u8; 32]) -> Self {
|
|
Self(bytes)
|
|
}
|
|
|
|
/// Return the challenge bytes.
|
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
|
&self.0
|
|
}
|
|
|
|
/// Verify a response to this challenge.
|
|
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))
|
|
}
|
|
}
|
|
|
|
/// Response to an authentication challenge.
|
|
#[derive(Clone, Debug)]
|
|
pub struct AuthResponse {
|
|
/// The responder's public key.
|
|
pub pubkey: XOnlyPublicKey,
|
|
/// Timestamp included in the signed message.
|
|
pub timestamp: u64,
|
|
/// Schnorr signature over the challenge digest.
|
|
pub signature: secp256k1::schnorr::Signature,
|
|
}
|
|
|
|
/// Compute the digest for an authentication challenge.
|
|
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
|
|
}
|