#[non_exhaustive]pub enum Version {
V0,
V1,
}Expand description
The packet framing used by a Transcript.
The version is an immutable part of a protocol’s definition.
Version::V0 uses schema-dependent framing and requires the protocol’s complete
packet-history language to be uniquely decodable. Version::V1 provides injective framing
for arbitrary byte packets.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
V0
Use schema-dependent suffix-length framing.
V0 commits data || varint(length(data)). This suffix is ambiguous for unrestricted
packet histories because packet data can imitate an earlier packet’s length.
§Safety
A protocol may use V0 only when its complete set of accepted histories is uniquely decodable. One sufficient condition is:
- the namespace is one fixed value;
- every history has a fixed packet count and order; and
- the payload language accepted at each position is prefix-free, such as one fixed-size value or a canonical self-delimiting encoding.
A history containing only one arbitrary packet is also unambiguous because
n + varint_size(n) is strictly increasing. This is why a one-packet namespace may be
summarized before a fixed-schema protocol continues from the resulting summary.
The proof applies to the complete packet schema, not to each payload in isolation. Fixed-size encodings alone do not make optional, repeated, or reordered packets safe. Changing a packet’s encoding, when it may appear, or how often it may repeat requires checking unique decodability for the full set of accepted histories again.
This fixed schema is safe: every accepted history contains the same namespace, one 8-byte round, and one 32-byte public key.
fn summarize(round: u64, public_key: [u8; 32]) -> Summary {
let round = round.to_be_bytes();
Transcript::new(b"_COMMONWARE_CRYPTOGRAPHY_TRANSCRIPT_V0_FIXED", Version::V0)
.commit(round.as_slice())
.commit(public_key.as_slice())
.summarize()
}
assert_ne!(summarize(7, [1; 32]), summarize(8, [1; 32]));By contrast, unrestricted packet boundaries are unsafe. These distinct V0 histories commit the same bytes.
let zeros = [0u8; 127];
let split = Transcript::new(b"", Version::V0)
.commit(zeros.as_slice())
.commit([0x80].as_slice())
.summarize();
let mut merged = zeros.to_vec();
merged.push(0x7f);
let merged = Transcript::new(b"", Version::V0)
.commit(merged.as_slice())
.summarize();
assert_eq!(split, merged);V1
Use injective packet framing for arbitrary packet contents.
V1 commits data || reverse(varint(length(data))). Canonical varints are prefix-free, so
their reversals are suffix-free. Starting at the end of a history, the final length and then
its exact payload can be recovered repeatedly. Packet data cannot alter those boundaries.
Empty packets remain distinct from no packet, and Transcript::append retains constant
framing memory because only the pending length is stored.
§Safety
V1 is safe for arbitrary byte packets, variable packet lengths, optional or repeated packets, and schemas that evolve to include them. It binds byte packets and their boundaries; it cannot repair a non-injective application encoding where two semantic values already produce the same packet bytes.
The V0 collision above is separated under V1:
let zeros = [0u8; 127];
let split = Transcript::new(b"", Version::V1)
.commit(zeros.as_slice())
.commit([0x80].as_slice())
.summarize();
let mut merged = zeros.to_vec();
merged.push(0x7f);
let merged = Transcript::new(b"", Version::V1)
.commit(merged.as_slice())
.summarize();
assert_ne!(split, merged);Trait Implementations§
impl Copy for Version
impl Eq for Version
impl StructuralPartialEq for Version
Auto Trait Implementations§
impl Freeze for Version
impl RefUnwindSafe for Version
impl Send for Version
impl Sync for Version
impl Unpin for Version
impl UnsafeUnpin for Version
impl UnwindSafe for Version
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more