ZoeChallenge

Enum ZoeChallenge 

Source
pub enum ZoeChallenge {
    Key(Box<KeyChallenge>),
    Unknown {
        discriminant: u32,
        data: Vec<u8>,
    },
}
Expand description

Forward-compatible challenge system for connection-level authentication

The Zoe protocol uses a challenge-response handshake immediately after QUIC connection establishment to verify possession of cryptographic private keys. This happens before any service streams are created, ensuring all connections have verified credentials.

§Protocol Flow

  1. QUIC Connection: Client connects using ML-DSA-44 mutual TLS
  2. Challenge Phase: Server sends ZoeChallenge on first bi-directional stream
  3. Response Phase: Client responds with ZoeChallengeResponse
  4. Verification: Server verifies proofs and sends ZoeChallengeResult
  5. Service Phase: Normal service streams can now be established

§Wire Format

All challenge messages are serialized using postcard format for compact binary encoding.

§Challenge Message (Server → Client)

| Field                | Type              | Description                    |
|---------------------|-------------------|--------------------------------|
| challenge_type      | u8                | Forward-compatible enum tag    |
| challenge_data      | Vec<u8>           | Serialized challenge content   |

§Response Message (Client → Server)

| Field                | Type              | Description                    |
|---------------------|-------------------|--------------------------------|
| response_type       | u8                | Forward-compatible enum tag    |
| response_data       | Vec<u8>           | Serialized response content    |

§Result Message (Server → Client)

| Field                | Type              | Description                    |
|---------------------|-------------------|--------------------------------|
| result_type         | u8                | Forward-compatible enum tag    |
| result_data         | Vec<u8>           | Serialized result content      |

§Security Properties

  • Replay Protection: Each challenge includes a unique nonce
  • Server Binding: Signatures include server’s public key
  • Time Bounds: Challenges have expiration timestamps
  • Connection Scoped: Verified keys are tied to specific QUIC connections
  • Forward Secrecy: New challenges generated for each connection

§Example Usage

use zoe_wire_protocol::{ZoeChallenge, ZoeChallengeResponse, KeyChallenge};

// Server sends challenge
let challenge = ZoeChallenge::Key(KeyChallenge {
    nonce: generate_nonce(),
    server_public_key: server_key.to_bytes().to_vec(),
    expires_at: current_time() + 30,
});

// Client creates multiple key proofs
let response = ZoeChallengeResponse::Key(KeyResponse {
    key_proofs: vec![
        KeyProof { public_key: key1_bytes, signature: sig1_bytes },
        KeyProof { public_key: key2_bytes, signature: sig2_bytes },
    ],
});

Variants§

§

Key(Box<KeyChallenge>)

Multi-key ML-DSA challenge allowing clients to prove multiple private keys

This challenge type allows clients to prove possession of multiple ML-DSA private keys in a single handshake round-trip. This is useful for:

  • Role-based authentication: Different keys for personal, work, admin roles
  • Key rotation: Proving both old and new keys during transition periods
  • Delegation: Proving keys for multiple identities or organizations
  • Batch verification: Efficient verification of multiple keys at once

The client must sign (nonce || server_public_key) with each private key they wish to prove possession of.

§

Unknown

Unknown challenge type for forward compatibility

Fields

§discriminant: u32
§data: Vec<u8>

Trait Implementations§

Source§

impl Clone for ZoeChallenge

Source§

fn clone(&self) -> ZoeChallenge

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ZoeChallenge

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ZoeChallenge

Source§

fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for ZoeChallenge

Source§

fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Classify for T

§

type Classified = T

§

fn classify(self) -> T

§

impl<T> Classify for T

§

type Classified = T

§

fn classify(self) -> T

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> Declassify for T

§

type Declassified = T

§

fn declassify(self) -> T

§

impl<T> Declassify for T

§

type Declassified = T

§

fn declassify(self) -> T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FutureExt for T

§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<T> DartSafe for T

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

§

impl<T> TaskRetFutTrait for T
where T: Send,