feat(credential): enforce signed credential distribution across mixed admin/shared topology (#1972)

This commit is contained in:
KKRainbow
2026-03-10 08:37:33 +08:00
committed by GitHub
parent ef44027f57
commit 694b8d349d
15 changed files with 894 additions and 186 deletions
+68
View File
@@ -1,4 +1,5 @@
use hmac::{Hmac, Mac};
use prost::Message;
use sha2::Sha256;
use crate::common::PeerId;
@@ -38,6 +39,42 @@ impl PeerGroupInfo {
}
}
impl TrustedCredentialPubkeyProof {
pub fn generate_credential_hmac(
credential: &TrustedCredentialPubkey,
network_secret: &str,
) -> Vec<u8> {
let mut mac = Hmac::<Sha256>::new_from_slice(network_secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(b"easytier credential proof");
mac.update(&credential.encode_to_vec());
mac.finalize().into_bytes().to_vec()
}
pub fn new_signed(credential: TrustedCredentialPubkey, network_secret: &str) -> Self {
let credential_hmac = Self::generate_credential_hmac(&credential, network_secret);
Self {
credential: Some(credential),
credential_hmac,
}
}
pub fn verify_credential_hmac(&self, network_secret: &str) -> bool {
let Some(credential) = self.credential.as_ref() else {
return false;
};
if self.credential_hmac.is_empty() {
return false;
}
let mut mac = Hmac::<Sha256>::new_from_slice(network_secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(b"easytier credential proof");
mac.update(&credential.encode_to_vec());
mac.verify_slice(&self.credential_hmac).is_ok()
}
}
impl From<RouteConnBitmap> for sync_route_info_request::ConnInfo {
fn from(val: RouteConnBitmap) -> Self {
Self::ConnBitmap(val)
@@ -254,4 +291,35 @@ mod tests {
println!("verify took {:?} for {} iterations", duration, iterations);
println!("Avg time per iteration: {:?}", duration / iterations as u32);
}
#[test]
fn test_trusted_credential_pubkey_hmac_valid() {
let credential = TrustedCredentialPubkey {
pubkey: vec![7u8; 32],
groups: vec!["ops".to_string(), "guest".to_string()],
allow_relay: true,
expiry_unix: 123456,
allowed_proxy_cidrs: vec!["10.0.0.0/24".to_string()],
};
let tc = TrustedCredentialPubkeyProof::new_signed(credential, "sec-1");
assert!(tc.verify_credential_hmac("sec-1"));
assert!(!tc.verify_credential_hmac("sec-2"));
}
#[test]
fn test_trusted_credential_pubkey_hmac_tampered() {
let credential = TrustedCredentialPubkey {
pubkey: vec![8u8; 32],
groups: vec!["g1".to_string()],
allow_relay: false,
expiry_unix: 1,
allowed_proxy_cidrs: vec![],
};
let tc = TrustedCredentialPubkeyProof::new_signed(credential, "sec-1");
let mut tampered = tc.clone();
tampered.credential.as_mut().unwrap().allow_relay = true;
assert!(!tampered.verify_credential_hmac("sec-1"));
}
}