Skip to main content

DeRecProtocol

Struct DeRecProtocol 

Source
pub struct DeRecProtocol<ChannelStore: DeRecChannelStore, ShareStore: DeRecShareStore, SecretStore: DeRecSecretStore, UserSecretStore: DeRecUserSecretStore, StateStore: DeRecStateStore, Transport: DeRecTransport> {
    pub channel_store: ChannelStore,
    pub share_store: ShareStore,
    pub secret_store: SecretStore,
    pub user_secret_store: UserSecretStore,
    pub state_store: StateStore,
    pub transport: Transport,
    pub own_transport: TransportProtocol,
    /* private fields */
}
Expand description

Internal state of the single secret container managed by the protocol.

Created on the first ProtectSecret flow and reused (with incrementing version) on every subsequent call. Higher-level DeRec protocol orchestrator.

Generic over:

The caller provides concrete implementations; the library imposes no runtime or I/O requirements.

§Lifecycle

DeRecProtocolBuilder::new(secret_id).<setters>.build()?
  │
  ├── create_contact / start(Pairing)          → pairing
  ├── start(ProtectSecret)                     → sharing
  ├── start(VerifyShares)                      → verification
  ├── start(Pairing { Owner })                 (recovery re-pair)
  │     └── start(Discovery)                   → discovery       (emits SecretsDiscovered)
  ├── start(RecoverSecret)                     → recovery        (emits SecretRecovered)
  │     └── restore(&secret, version)          → commit recovered secret into canonical state
  ├── start(UpdateChannelInfo)                 → endpoint/info update (either side)
  └── start(Unpair)                            → unpair          (Owner-initiated; ack
                                                                  semantics governed by
                                                                  [`DeRecProtocolBuilder::with_unpair_ack`])

loop { process(incoming_bytes) → Vec<DeRecEvent> }

See DeRecFlow for the full set of orchestrator entry points and the role each requires on the targeted channel(s).

Fields§

§channel_store: ChannelStore§share_store: ShareStore§secret_store: SecretStore§user_secret_store: UserSecretStore§state_store: StateStore

Set via DeRecProtocolBuilder::with_state_store. Holds in-flight orchestrator state (verification challenges, recovery accumulators, pending unpair acks) so stateless / load-balanced deployments preserve it across process restarts.

§transport: Transport§own_transport: TransportProtocol

Implementations§

Source§

impl<Ch: DeRecChannelStore, Sh: DeRecShareStore, Ss: DeRecSecretStore, Us: DeRecUserSecretStore, T: DeRecTransport, St: DeRecStateStore> DeRecProtocol<Ch, Sh, Ss, Us, St, T>

Source

pub fn new( secret_id: u64, channel_store: Ch, share_store: Sh, secret_store: Ss, user_secret_store: Us, state_store: St, transport: T, own_transport: TransportProtocol, threshold: usize, keep_versions_count: usize, timeout_in_secs: u64, ) -> Result<Self>

Construct a DeRecProtocol directly from its components.

Prefer DeRecProtocolBuilder for the type-checked construction path; both entry points run the same runtime validation and surface the same errors.

§Errors

Returns crate::Error::InvalidInput if threshold < 2. A threshold of 0 or 1 collapses threshold secret sharing and lets a single helper reconstruct the secret unilaterally — two is the minimum value that preserves secret confidentiality against one compromised helper.

Source

pub fn secret_id(&self) -> u64

Returns the secret identifier this protocol instance was configured with.

Source

pub fn replica_id(&self) -> Option<u64>

Returns the configured local replica id, or None if the protocol was built without DeRecProtocolBuilder::with_replica_id.

Apps can use this to surface “replica flows are enabled” to the user, or to inspect their own identity for logging/diagnostics. The id is the same value that the orchestrator auto-injects under derec.replica_id in outbound replica-mode PairRequest / PairResponse envelopes.

Source

pub async fn create_contact( &mut self, channel_id: Option<ChannelId>, contact_mode: ContactMode, nonce: Option<u64>, ) -> Result<ContactMessage>

Generate an out-of-band contact message (QR code payload, deep link, …).

Either party (Owner or Helper) may call this to begin a pairing session. The returned ContactMessage should be delivered out-of-band to the peer. Any material the library needs later — either the ephemeral pairing secret (InlineKeys / HashedKeys) or the contact itself (NoKeys) — is persisted automatically via the configured stores.

§Channel ID

Pass Some(id) to use a specific channel identifier, or None to have the library generate a random one. Applications targeting NoKeys mode typically pass a small human-typable value (4 digits) for manual entry.

§Contact mode
  • ContactMode::InlineKeys embeds the initiator’s ML-KEM + ECIES public keys directly in the contact. Simplest to use; the contact is ~1.2 KB.
  • ContactMode::HashedKeys embeds only a SHA-384 binding hash over the keys. The contact stays small enough for a QR code; the scanner obtains the real keys via a PrePair round-trip and validates them against the hash. Requires the own_transport set on this protocol to be ephemeral — the plaintext PrePair traffic must not be linkable to a long-lived endpoint.
  • ContactMode::NoKeys carries no key material and no commitment — only channel_id, nonce, and transport_protocol. Small enough to be hand-typed. Keys are generated on the fly by the creator when the corresponding PrePairRequest arrives; trust rests entirely on the OOB delivery channel being fully trusted (e.g. a verified email from an already-KYC-authenticated institution). Applications MUST rate-limit inbound PrePairRequests per channel_id and expire outstanding NoKeys contacts on a short timer.
§Nonce
  • None: the library generates a fresh cryptographically-random u64. Recommended default for InlineKeys and HashedKeys where the nonce is a security parameter.
  • Some(n): application-controlled value. Required for NoKeys where the recipient typically types it in; also valid for the other modes if the app wants deterministic control.
Source

pub fn set_communication_info(&mut self, info: HashMap<String, String>)

Replace this node’s local communication info.

Only mutates local state — to propagate the change to paired peers, follow up with DeRecFlow::UpdateChannelInfo.

§Destructive replacement

The supplied map fully replaces the current value. An empty map will be transmitted as “clear all entries” when a subsequent UpdateChannelInfo flow runs, which the peer will mirror into its stored map. Pass the complete new map, not a delta.

Source

pub fn set_own_transport( &mut self, own_transport: impl IntoOwnTransport, ) -> Result<()>

Replace this node’s local transport endpoint.

Only mutates local state — to propagate the change to paired peers, follow up with DeRecFlow::UpdateChannelInfo.

§Endpoint changeover discipline

When UpdateChannelInfo is broadcast, each receiving peer routes its response (and all subsequent messages) to the NEW endpoint. The application MUST therefore:

  1. Bring up the new endpoint and start listening on it before initiating the UpdateChannelInfo flow.
  2. Keep the old endpoint operational during the changeover. Peers that have not yet processed the update still route to the old address; in-flight messages may also be targeted there.
  3. Retire the old endpoint only once every targeted peer has surfaced DeRecEvent::ChannelInfoUpdated (or DeRecEvent::ChannelInfoUpdateRejected), plus a grace window for in-flight traffic.

Failing to keep both endpoints reachable during this window will cause messages to be lost. Set the local node’s transport endpoint.

Accepts anything implementing IntoOwnTransport: a typed crate::transport::TransportProtocol, a &str, or a String. Validation runs eagerly — a malformed URI surfaces as crate::Error::Transport instead of being stored and later propagated to peers.

§Errors

Returns crate::Error::Transport if the supplied value fails URI validation (empty, oversize, control characters, or scheme mismatch).

Source

pub async fn start(&mut self, flow: DeRecFlow) -> Result<Vec<DeRecEvent>>

Unified entry point for initiating any protocol flow.

Returns the flow’s per-target *Started / *Failed events (one PairingStarted for DeRecFlow::Pairing; one per targeted channel for fan-out flows). See each *Started / *Failed variant on DeRecEvent for the per-flow shape.

§Errors
  • Programmer errors (invalid input, missing preconditions, role mismatch) surface as Err before any fan-out begins. Once fan-out starts for a multi-target flow, per-channel transport failures become *Failed events in the returned vec — they do not abort the round.
  • Single-channel flows (DeRecFlow::Pairing, DeRecFlow::Unpair) return Err on send failure — no *Failed event exists, so the single-Err signal is unambiguous.
Source

pub async fn accept(&mut self, action: PendingAction) -> Result<Vec<DeRecEvent>>

Accept a pending action from an DeRecEvent::ActionRequired event.

Executes the “do work + send response” path for the given action, returning the same events that auto-respond would have produced.

Source

pub async fn reject( &mut self, action: PendingAction, status: StatusEnum, memo: &str, ) -> Result<()>

Reject a pending action from an DeRecEvent::ActionRequired event.

Builds and sends a rejection response to the peer with the given status and memo. The status parameter allows the caller to specify the exact failure reason (e.g. StatusEnum::Rejected, StatusEnum::Fail, StatusEnum::TooFrequent, etc.).

Source

pub async fn process( &mut self, message: &[u8], ) -> Result<Vec<DeRecEvent>, ProcessError>

Feed any incoming wire bytes here regardless of which flow they belong to.

The library:

  1. Decodes the outer DeRecMessage envelope to read channel_id
  2. Looks up the channel’s key material to determine the message kind
  3. Dispatches to the appropriate message handler based on the channel state
  4. Returns the events the application should react to
§Security: bounding inbound message size

This function does not enforce an upper bound on message.len(), and no library entry point that ingests peer wire bytes does either. Legitimate envelopes span many orders of magnitude:

  • Tens of bytes for empty acks / ping-class messages.
  • A few KB for pairing material and verification proofs.
  • Hundreds of KB to several MB for StoreShareRequest carrying a share of a large secret.
  • Many MB for ReplicaSync envelopes carrying an entire secret (O(num_secrets × num_helpers × max_secret_bytes)).

Any cap tight enough to provide meaningful DoS resistance would risk silently truncating a legitimate replica sync — at which point the secret can become unrecoverable. The protocol therefore delegates inbound-size bounding to the application’s transport layer, which knows the deployment’s max secret size, helper count, and replica fan-out and can pick a ceiling that fits.

Callers MUST refuse oversized envelopes upstream (e.g. enforce a max HTTP body / WebSocket frame size consistent with their configuration) before handing bytes to this function.

Malformed bytes — including truncation, varint overflow, and any prost-level decode failure — surface as ProcessError wrapping Error::ProtobufDecode. This function never panics on adversarial input. Protobuf recursion depth is bounded by prost’s decoder; DeRec’s schema is shallow (~3 levels), so no additional caller-side recursion limit is required.

Source

pub async fn get_fingerprint(&self, channel_id: ChannelId) -> Result<String>

Compute the fingerprint for a paired channel.

Returns a formatted string like "1234-5678-9012-3456" derived from the channel’s shared key via SHA-256. Both parties will derive the same fingerprint for the same shared key, enabling visual out-of-band verification.

Returns an error if the channel has no shared key (not yet paired).

Source

pub async fn verify_fingerprint( &mut self, channel_id: ChannelId, fingerprint: &str, ) -> Result<bool>

Verify that a fingerprint matches the one derived from a channel’s shared key.

If the fingerprint matches, the channel status is updated from Pending to Paired, enabling it to process protocol messages. Returns true on match, false otherwise. Returns an error if the channel has no shared key.

Source

pub async fn restore( &mut self, secret: &Secret, recovered_version: u32, ) -> Result<Vec<DeRecEvent>>

Rebuild this protocol’s secret_id namespace from a crate::protocol::types::Secret handed up by a DeRecEvent::SecretRecovered event.

§Caller flow
fresh DeRecProtocol → empty stores
  → re-pair with helpers on a fresh channel-id namespace
  → start(RecoverSecret { secret_id, version })
  → SecretRecovered { secret } event arrives
  → DeRecProtocol::restore(&secret, version)

On success: canonical helper channels are persisted with SharedKey + owner-side tracking shares at recovered_version; canonical replica channels are persisted with the group key from secret.replicas.shared_key; the user-secret snapshot is committed at recovered_version; the protocol’s replica_id is adopted from secret.owner_replica_id if previously unset; every other channel under self.secret_id (i.e. the recovery-mode channels) is unpaired (request sent to the helper, local state dropped). The protocol resumes normal operation immediately — the next start(ProtectSecret) publishes recovered_version + 1 to the restored helpers.

The snapshot write is the commit point — nothing is removed before it succeeds. Any mid-flight failure leaves state the next restore call will detect as one of the precondition errors below.

§Errors

Precondition / invariant failures surface as crate::Error::Restore wrapping one of:

Store I/O failures mid-restore propagate as the underlying crate::Error::ShareStore, crate::Error::ChannelStore, or crate::Error::SecretStore variant.

Auto Trait Implementations§

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> Freeze for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: Freeze, ShareStore: Freeze, SecretStore: Freeze, UserSecretStore: Freeze, StateStore: Freeze, Transport: Freeze,

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> RefUnwindSafe for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: RefUnwindSafe, ShareStore: RefUnwindSafe, SecretStore: RefUnwindSafe, UserSecretStore: RefUnwindSafe, StateStore: RefUnwindSafe, Transport: RefUnwindSafe,

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> Send for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: Send, ShareStore: Send, SecretStore: Send, UserSecretStore: Send, StateStore: Send, Transport: Send,

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> Sync for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: Sync, ShareStore: Sync, SecretStore: Sync, UserSecretStore: Sync, StateStore: Sync, Transport: Sync,

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> Unpin for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: Unpin, ShareStore: Unpin, SecretStore: Unpin, UserSecretStore: Unpin, StateStore: Unpin, Transport: Unpin,

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> UnsafeUnpin for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: UnsafeUnpin, ShareStore: UnsafeUnpin, SecretStore: UnsafeUnpin, UserSecretStore: UnsafeUnpin, StateStore: UnsafeUnpin, Transport: UnsafeUnpin,

§

impl<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport> UnwindSafe for DeRecProtocol<ChannelStore, ShareStore, SecretStore, UserSecretStore, StateStore, Transport>
where ChannelStore: UnwindSafe, ShareStore: UnwindSafe, SecretStore: UnwindSafe, UserSecretStore: UnwindSafe, StateStore: UnwindSafe, Transport: UnwindSafe,

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
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
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

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

Source§

fn vzip(self) -> V