Honor credential reusable flag (#2157)

- propagate reusable through credential storage, CLI, RPC, routing, and tests
- enforce reusable=false owner election with current topology
- preserve proof-backed groups when refreshing credential ACL groups
This commit is contained in:
KKRainbow
2026-04-25 00:22:40 +08:00
committed by GitHub
parent f7ea78d4f0
commit 4688ad74ad
8 changed files with 1461 additions and 205 deletions
+2
View File
@@ -318,6 +318,7 @@ message GenerateCredentialRequest {
int64 ttl_seconds = 4; // must be > 0: credential TTL in seconds (0 / omitted is invalid)
optional string credential_id = 5; // optional: user-specified credential id, reused if already exists
InstanceIdentifier instance = 6; // target network instance
optional bool reusable = 7; // default true: allow multiple peers to reuse this credential
}
message GenerateCredentialResponse {
@@ -344,6 +345,7 @@ message CredentialInfo {
bool allow_relay = 3;
int64 expiry_unix = 4;
repeated string allowed_proxy_cidrs = 5;
optional bool reusable = 6;
}
message ListCredentialsResponse {
+1
View File
@@ -11,6 +11,7 @@ message TrustedCredentialPubkey {
bool allow_relay = 3; // whether this credential node can relay data
int64 expiry_unix = 4; // expiry time (Unix timestamp)
repeated string allowed_proxy_cidrs = 5; // allowed proxy_cidrs ranges
optional bool reusable = 6; // whether multiple peers may use the same credential concurrently
}
message TrustedCredentialPubkeyProof {
+52 -4
View File
@@ -40,17 +40,24 @@ impl PeerGroupInfo {
}
impl TrustedCredentialPubkeyProof {
pub fn generate_credential_hmac(
credential: &TrustedCredentialPubkey,
pub fn generate_credential_hmac_from_bytes(
credential_bytes: &[u8],
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.update(credential_bytes);
mac.finalize().into_bytes().to_vec()
}
pub fn generate_credential_hmac(
credential: &TrustedCredentialPubkey,
network_secret: &str,
) -> Vec<u8> {
Self::generate_credential_hmac_from_bytes(&credential.encode_to_vec(), network_secret)
}
pub fn new_signed(credential: TrustedCredentialPubkey, network_secret: &str) -> Self {
let credential_hmac = Self::generate_credential_hmac(&credential, network_secret);
Self {
@@ -63,6 +70,14 @@ impl TrustedCredentialPubkeyProof {
let Some(credential) = self.credential.as_ref() else {
return false;
};
self.verify_credential_hmac_with_bytes(&credential.encode_to_vec(), network_secret)
}
pub fn verify_credential_hmac_with_bytes(
&self,
credential_bytes: &[u8],
network_secret: &str,
) -> bool {
if self.credential_hmac.is_empty() {
return false;
}
@@ -70,7 +85,7 @@ impl TrustedCredentialPubkeyProof {
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.update(credential_bytes);
mac.verify_slice(&self.credential_hmac).is_ok()
}
}
@@ -300,6 +315,7 @@ mod tests {
allow_relay: true,
expiry_unix: 123456,
allowed_proxy_cidrs: vec!["10.0.0.0/24".to_string()],
reusable: Some(true),
};
let tc = TrustedCredentialPubkeyProof::new_signed(credential, "sec-1");
@@ -315,6 +331,7 @@ mod tests {
allow_relay: false,
expiry_unix: 1,
allowed_proxy_cidrs: vec![],
reusable: Some(true),
};
let tc = TrustedCredentialPubkeyProof::new_signed(credential, "sec-1");
@@ -322,4 +339,35 @@ mod tests {
tampered.credential.as_mut().unwrap().allow_relay = true;
assert!(!tampered.verify_credential_hmac("sec-1"));
}
#[test]
fn test_trusted_credential_pubkey_hmac_with_raw_bytes() {
let credential = TrustedCredentialPubkey {
pubkey: vec![9u8; 32],
groups: vec!["raw".to_string()],
allow_relay: true,
expiry_unix: 123456,
allowed_proxy_cidrs: vec![],
reusable: Some(true),
};
let mut raw_credential_bytes = credential.encode_to_vec();
prost::encoding::encode_key(
9999,
prost::encoding::WireType::Varint,
&mut raw_credential_bytes,
);
prost::encoding::encode_varint(42, &mut raw_credential_bytes);
let proof = TrustedCredentialPubkeyProof {
credential: Some(credential),
credential_hmac: TrustedCredentialPubkeyProof::generate_credential_hmac_from_bytes(
&raw_credential_bytes,
"sec-1",
),
};
assert!(proof.verify_credential_hmac_with_bytes(&raw_credential_bytes, "sec-1"));
assert!(!proof.verify_credential_hmac("sec-1"));
}
}