Protocol: freeze device proof and browser CSRF contracts
Verify Protocol / module (push) Successful in 1m12s
Verify Protocol / verify (push) Successful in 22s

This commit is contained in:
sechmachine
2026-08-11 21:21:28 +07:00
parent afcd5d99db
commit b6a4f773e4
14 changed files with 323 additions and 5 deletions
+64
View File
@@ -59,6 +59,44 @@ let transcript = "versevdi/tunnel-admission/v17:session7:gateway8:audience43:" +
guard String(data: request.deviceAdmissionTranscript(), encoding: .utf8) == transcript else {
fatalError("unexpected device admission transcript")
}
let proofServerID = Data(repeating: 1, count: 16)
let proofPrincipalID = Data(repeating: 2, count: 16)
let proofDeviceID = Data(repeating: 3, count: 16)
let proofChallenge = Data(repeating: 4, count: 32)
let proofTranscript = try deviceRegistrationProofTranscript(
serverID: proofServerID, principalID: proofPrincipalID, deviceID: proofDeviceID,
challenge: proofChallenge, expiryUnixMilliseconds: 1
)
guard proofTranscript.count == 112,
String(data: proofTranscript.prefix(24), encoding: .utf8) == "versevdi-device-proof-v1",
Array(proofTranscript.suffix(8)) == [0, 0, 0, 0, 0, 0, 0, 1] else {
fatalError("unexpected device registration proof transcript")
}
let invalidProofInputs: [(String, String, Data, Data, Data, Data, Int64)] = [
("server-short", "server_id", Data(repeating: 0, count: 15), proofPrincipalID, proofDeviceID, proofChallenge, 0),
("server-long", "server_id", Data(repeating: 0, count: 17), proofPrincipalID, proofDeviceID, proofChallenge, 0),
("principal-short", "principal_id", proofServerID, Data(repeating: 0, count: 15), proofDeviceID, proofChallenge, 0),
("principal-long", "principal_id", proofServerID, Data(repeating: 0, count: 17), proofDeviceID, proofChallenge, 0),
("device-short", "device_id", proofServerID, proofPrincipalID, Data(repeating: 0, count: 15), proofChallenge, 0),
("device-long", "device_id", proofServerID, proofPrincipalID, Data(repeating: 0, count: 17), proofChallenge, 0),
("challenge-short", "challenge", proofServerID, proofPrincipalID, proofDeviceID, Data(repeating: 0, count: 31), 0),
("challenge-long", "challenge", proofServerID, proofPrincipalID, proofDeviceID, Data(repeating: 0, count: 33), 0),
("negative-expiry", "expiry_unix_milliseconds", proofServerID, proofPrincipalID, proofDeviceID, proofChallenge, -1),
]
for (name, field, serverID, principalID, deviceID, challenge, expiry) in invalidProofInputs {
do {
_ = try deviceRegistrationProofTranscript(
serverID: serverID, principalID: principalID, deviceID: deviceID,
challenge: challenge, expiryUnixMilliseconds: expiry
)
fatalError("\(name) was accepted")
} catch let error as ContractValidationError {
guard error.field == field,
error.code == (field == "expiry_unix_milliseconds" ? "minimum" : "invalid_length") else {
fatalError("\(name) returned the wrong validation error")
}
}
}
let incompatible = try CapabilityProfile(
transport: "quic-tls13", framing: "datagram-v1", media: "encoded",
audio: "encoded", sourceRateControl: "server", clientDecode: ["hevc-opus"]
@@ -272,6 +310,32 @@ fn main() {
+ &"g".repeat(43) + "1:016:" + &"n".repeat(16)
+ "10:quic-tls1311:datagram-v17:encoded7:encoded6:server1:19:h264-opus";
assert_eq!(request.device_admission_transcript(), transcript.into_bytes());
let proof_server_id = vec![1u8; 16];
let proof_principal_id = vec![2u8; 16];
let proof_device_id = vec![3u8; 16];
let proof_challenge = vec![4u8; 32];
let proof = device_registration_proof_transcript(
&proof_server_id, &proof_principal_id, &proof_device_id, &proof_challenge, 1,
).unwrap();
assert_eq!(proof.len(), 112);
assert_eq!(&proof[..24], b"versevdi-device-proof-v1");
assert_eq!(&proof[104..], &[0, 0, 0, 0, 0, 0, 0, 1]);
for (server_id, principal_id, device_id, challenge, expiry, field, code) in [
(vec![0; 15], proof_principal_id.clone(), proof_device_id.clone(), proof_challenge.clone(), 0, "server_id", "invalid_length"),
(vec![0; 17], proof_principal_id.clone(), proof_device_id.clone(), proof_challenge.clone(), 0, "server_id", "invalid_length"),
(proof_server_id.clone(), vec![0; 15], proof_device_id.clone(), proof_challenge.clone(), 0, "principal_id", "invalid_length"),
(proof_server_id.clone(), vec![0; 17], proof_device_id.clone(), proof_challenge.clone(), 0, "principal_id", "invalid_length"),
(proof_server_id.clone(), proof_principal_id.clone(), vec![0; 15], proof_challenge.clone(), 0, "device_id", "invalid_length"),
(proof_server_id.clone(), proof_principal_id.clone(), vec![0; 17], proof_challenge.clone(), 0, "device_id", "invalid_length"),
(proof_server_id.clone(), proof_principal_id.clone(), proof_device_id.clone(), vec![0; 31], 0, "challenge", "invalid_length"),
(proof_server_id.clone(), proof_principal_id.clone(), proof_device_id.clone(), vec![0; 33], 0, "challenge", "invalid_length"),
(proof_server_id.clone(), proof_principal_id.clone(), proof_device_id.clone(), proof_challenge.clone(), -1, "expiry_unix_milliseconds", "minimum"),
] {
assert_eq!(
device_registration_proof_transcript(&server_id, &principal_id, &device_id, &challenge, expiry),
Err(ValidationError::new(field, code)),
);
}
assert!(TunnelAdmissionRequest::new(
"2".into(), "session".into(), "gateway".into(), "audience".into(),
"g".repeat(43), 0, "n".repeat(16), "s".repeat(86), capabilities.clone(),