mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-13 17:35:37 +00:00
feat(credential): enforce signed credential distribution across mixed admin/shared topology (#1972)
This commit is contained in:
@@ -13,6 +13,11 @@ message TrustedCredentialPubkey {
|
||||
repeated string allowed_proxy_cidrs = 5; // allowed proxy_cidrs ranges
|
||||
}
|
||||
|
||||
message TrustedCredentialPubkeyProof {
|
||||
TrustedCredentialPubkey credential = 1;
|
||||
bytes credential_hmac = 2;
|
||||
}
|
||||
|
||||
message RoutePeerInfo {
|
||||
// means next hop in route table.
|
||||
uint32 peer_id = 1;
|
||||
@@ -40,7 +45,7 @@ message RoutePeerInfo {
|
||||
bytes noise_static_pubkey = 18;
|
||||
|
||||
// Trusted credential public keys published by admin nodes (holding network_secret)
|
||||
repeated TrustedCredentialPubkey trusted_credential_pubkeys = 19;
|
||||
repeated TrustedCredentialPubkeyProof trusted_credential_pubkeys = 19;
|
||||
}
|
||||
|
||||
message PeerIdVersion {
|
||||
@@ -313,22 +318,18 @@ message PeerConnNoiseMsg2Pb {
|
||||
|
||||
message RelayNoiseMsg1Pb {
|
||||
uint32 version = 1;
|
||||
string a_network_name = 2;
|
||||
optional uint32 a_session_generation = 3;
|
||||
common.UUID a_conn_id = 4;
|
||||
string client_encryption_algorithm = 5;
|
||||
}
|
||||
|
||||
message RelayNoiseMsg2Pb {
|
||||
string b_network_name = 1;
|
||||
uint32 role_hint = 2;
|
||||
PeerConnSessionActionPb action = 3;
|
||||
uint32 b_session_generation = 4;
|
||||
optional bytes root_key_32 = 5;
|
||||
uint32 initial_epoch = 6;
|
||||
common.UUID b_conn_id = 7;
|
||||
common.UUID a_conn_id_echo = 8;
|
||||
optional bytes secret_proof_32 = 9;
|
||||
string server_encryption_algorithm = 10;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user