Protocol: split client session authority
Verify Protocol / verify (push) Successful in 1m2s
Verify Protocol / module (push) Successful in 1m45s

This commit is contained in:
sechmachine
2026-08-12 11:50:41 +07:00
parent b6a4f773e4
commit afbcea62f9
14 changed files with 536 additions and 4 deletions
+98
View File
@@ -4,6 +4,7 @@
from __future__ import annotations
import pathlib
import re
import shutil
import subprocess
import tempfile
@@ -24,6 +25,32 @@ def run_failure(command: list[str], directory: pathlib.Path, expected: str) -> N
raise RuntimeError("expected failure: %s\n%s%s" % (" ".join(command), result.stdout, result.stderr))
def protobuf_message_fields(name: str) -> list[tuple[str, int]]:
result = subprocess.run(
["protoc", "--decode=google.protobuf.FileDescriptorSet", "google/protobuf/descriptor.proto"],
input=(ROOT / "gen/protobuf/tunnel-v1.pb").read_bytes(),
capture_output=True,
check=False,
)
if result.returncode != 0:
raise RuntimeError(result.stderr.decode())
lines = result.stdout.decode().splitlines()
marker = f' name: "{name}"'
try:
name_index = lines.index(marker)
start = max(index for index in range(name_index) if lines[index] == " message_type {")
except (ValueError, StopIteration) as exc:
raise RuntimeError(f"protobuf descriptor missing message {name}") from exc
depth = 0
block: list[str] = []
for line in lines[start:]:
depth += line.count("{") - line.count("}")
block.append(line)
if depth == 0:
break
return [(field, int(number)) for field, number in re.findall(r' field \{\n name: "([^"]+)"\n number: (\d+)', "\n".join(block))]
def main() -> int:
with tempfile.TemporaryDirectory(prefix="versevdi-generated-contracts-") as temporary:
workspace = pathlib.Path(temporary)
@@ -136,6 +163,52 @@ for invalid in [
fatalError("invalid tunnel admission request was accepted")
} catch { }
}
let clientAuthority = try ClientSessionAuthority(
version: "1", sessionId: "session", gatewayId: "gateway", audience: "audience",
reconnectSequence: 2, expiresAt: "2099-01-01T00:00:00Z", capabilities: capability
)
let clientAuthorityJSON = try clientAuthority.encodeJSON()
let clientAuthorityObject = try JSONSerialization.jsonObject(with: clientAuthorityJSON) as! [String: Any]
guard Set(clientAuthorityObject.keys) == Set([
"version", "session_id", "gateway_id", "audience", "reconnect_sequence", "expires_at", "capabilities"
]), !String(data: clientAuthorityJSON, encoding: .utf8)!.contains("provider_") else {
fatalError("client authority was not exactly provider-free")
}
_ = try ClientSessionAuthority.decodeJSON(clientAuthorityJSON)
for field in ["version", "session_id", "gateway_id", "audience", "reconnect_sequence", "expires_at", "capabilities"] {
var missing = clientAuthorityObject
missing.removeValue(forKey: field)
do {
_ = try ClientSessionAuthority.decodeJSON(try JSONSerialization.data(withJSONObject: missing))
fatalError("client authority accepted missing \(field)")
} catch { }
}
for (field, value) in [
("provider_profile", "apollo"),
("provider_identity", "provider-1"),
("provider_url", "https://provider.invalid"),
("management_host", "provider.invalid"),
("unknown", "true"),
] {
var injected = clientAuthorityObject
injected[field] = value
do {
_ = try ClientSessionAuthority.decodeJSON(try JSONSerialization.data(withJSONObject: injected))
fatalError("client authority accepted injected \(field)")
} catch { }
}
for expiresAt in ["not-a-time", "2099-01-01T00:00:00+00:00", "2099-01-01T00:00:00.100Z"] {
var invalidExpiry = clientAuthorityObject
invalidExpiry["expires_at"] = expiresAt
do {
_ = try ClientSessionAuthority.decodeJSON(try JSONSerialization.data(withJSONObject: invalidExpiry))
fatalError("client authority accepted invalid expiry")
} catch { }
}
do {
_ = try ClientSessionAuthority.decodeJSON(clientAuthorityJSON + Data(" {}".utf8))
fatalError("client authority accepted trailing JSON")
} catch { }
do {
_ = try AllocationPolicy(
minimumKbps: 100, targetKbps: 50, maximumKbps: 25, tier: "standard",
@@ -348,6 +421,16 @@ fn main() {
"1".into(), "session".into(), "gateway".into(), "audience".into(),
"g".repeat(43), 0, "short".into(), "s".repeat(86), capabilities.clone(),
).is_err());
let client_authority = ClientSessionAuthority::new(
"1".into(), "session".into(), "gateway".into(), "audience".into(), 2,
"2099-01-01T00:00:00Z".into(), capabilities.clone(),
).unwrap();
assert_eq!(client_authority.sessionId(), "session");
assert_eq!(client_authority.capabilities(), &capabilities);
assert!(ClientSessionAuthority::new(
"1".into(), "session".into(), "gateway".into(), "audience".into(), 2,
"not-a-time".into(), capabilities.clone(),
).is_err());
assert!(intersect_capability_profiles(&[capabilities.clone(), capabilities.clone()]).is_ok());
let incompatible = CapabilityProfile::new(
"quic-tls13".into(), "datagram-v1".into(), "encoded".into(),
@@ -441,6 +524,21 @@ fn main() {
)
run(["rustc", str(rust), "-o", str(workspace / "rust-contracts")], ROOT)
run([str(workspace / "rust-contracts")], ROOT)
expected_protobuf_fields = [
("version", 1),
("session_id", 2),
("gateway_id", 3),
("audience", 4),
("reconnect_sequence", 5),
("expires_at", 6),
("capabilities", 7),
]
actual_protobuf_fields = protobuf_message_fields("ClientSessionAuthority")
if actual_protobuf_fields != expected_protobuf_fields:
raise RuntimeError(
f"ClientSessionAuthority protobuf fields = {actual_protobuf_fields}; "
f"want {expected_protobuf_fields}"
)
rust_unknown = workspace / "unknown.rs"
shutil.copyfile(ROOT / "gen/rust/protocol.rs", rust_unknown)
with rust_unknown.open("a", encoding="utf-8") as output: