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
+25
View File
@@ -4,6 +4,7 @@ package protocol
import (
"bytes"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
@@ -5564,6 +5565,30 @@ func EncodeVersionNegotiation(value VersionNegotiation) ([]byte, error) {
return json.Marshal(value)
}
func DeviceRegistrationProofTranscript(serverID, principalID, deviceID, challenge []byte, expiryUnixMilliseconds int64) ([]byte, error) {
for _, value := range []struct {
field string
bytes []byte
length int
}{{"server_id", serverID, 16}, {"principal_id", principalID, 16}, {"device_id", deviceID, 16}, {"challenge", challenge, 32}} {
if len(value.bytes) != value.length {
return nil, ValidationError{Violations: []FieldViolation{{Field: value.field, Code: "invalid_length"}}}
}
}
if expiryUnixMilliseconds < 0 {
return nil, ValidationError{Violations: []FieldViolation{{Field: "expiry_unix_milliseconds", Code: "minimum"}}}
}
transcript := make([]byte, 0, 112)
transcript = append(transcript, "versevdi-device-proof-v1"...)
transcript = append(transcript, serverID...)
transcript = append(transcript, principalID...)
transcript = append(transcript, deviceID...)
transcript = append(transcript, challenge...)
var expiry [8]byte
binary.BigEndian.PutUint64(expiry[:], uint64(expiryUnixMilliseconds))
return append(transcript, expiry[:]...), nil
}
var ErrNoCapabilityOverlap = errors.New("no capability overlap")
func IntersectCapabilityProfiles(profiles ...CapabilityProfile) (CapabilityProfile, error) {
+1 -1
View File
@@ -12,7 +12,7 @@
"3"
]
},
"generator_sha256": "8a153cf1e99682d010ff91c754ef056c64aece8f8bbca0ca58f8eef2b9039119",
"generator_sha256": "00c1905fc611ca9e226cd90da761b48b8e203734b10542befea397a30082d360",
"protocol_version": "1.0.0",
"schema_sha256": "dea3dd210c53d5a2d37050dd6afd8b0ac5bb8edcb7ab25a02e4026489ce8a00f"
}
+15
View File
@@ -1976,6 +1976,21 @@ impl VersionNegotiation {
pub fn features(&self) -> &Vec<String> { &self.features }
}
pub fn device_registration_proof_transcript(server_id: &[u8], principal_id: &[u8], device_id: &[u8], challenge: &[u8], expiry_unix_milliseconds: i64) -> Result<Vec<u8>, ValidationError> {
for (field, value, length) in [("server_id", server_id, 16), ("principal_id", principal_id, 16), ("device_id", device_id, 16), ("challenge", challenge, 32)] {
if value.len() != length { return Err(ValidationError::new(field, "invalid_length")); }
}
if expiry_unix_milliseconds < 0 { return Err(ValidationError::new("expiry_unix_milliseconds", "minimum")); }
let mut transcript = Vec::with_capacity(112);
transcript.extend_from_slice(b"versevdi-device-proof-v1");
transcript.extend_from_slice(server_id);
transcript.extend_from_slice(principal_id);
transcript.extend_from_slice(device_id);
transcript.extend_from_slice(challenge);
transcript.extend_from_slice(&(expiry_unix_milliseconds as u64).to_be_bytes());
Ok(transcript)
}
pub fn intersect_capability_profiles(profiles: &[CapabilityProfile]) -> Result<CapabilityProfile, ValidationError> {
let mut selected = profiles.first().ok_or_else(|| ValidationError::new("capabilities", "no_overlap"))?.clone();
selected.validate().map_err(|_| ValidationError::new("capabilities", "no_overlap"))?;
+15
View File
@@ -2583,6 +2583,21 @@ public struct VersionNegotiation: Codable, Equatable {
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public func deviceRegistrationProofTranscript(serverID: Data, principalID: Data, deviceID: Data, challenge: Data, expiryUnixMilliseconds: Int64) throws -> Data {
for (field, value, length) in [("server_id", serverID, 16), ("principal_id", principalID, 16), ("device_id", deviceID, 16), ("challenge", challenge, 32)] {
if value.count != length { throw ContractValidationError(field: field, code: "invalid_length") }
}
if expiryUnixMilliseconds < 0 { throw ContractValidationError(field: "expiry_unix_milliseconds", code: "minimum") }
var transcript = Data("versevdi-device-proof-v1".utf8)
transcript.append(serverID)
transcript.append(principalID)
transcript.append(deviceID)
transcript.append(challenge)
var expiry = UInt64(expiryUnixMilliseconds).bigEndian
Swift.withUnsafeBytes(of: &expiry) { transcript.append(contentsOf: $0) }
return transcript
}
public extension TunnelAdmissionRequest {
func deviceAdmissionTranscript() -> Data {
var fields = [sessionId, gatewayId, audience, grant, String(reconnectSequence), clientNonce, capabilities.transport, capabilities.framing, capabilities.media, capabilities.audio, capabilities.sourceRateControl, String(capabilities.clientDecode.count)]