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:
ChannelStore— paired channel storage (DeRecChannelStore)ShareStore— share storage (DeRecShareStore)SecretStore— secret storage (DeRecSecretStore)Transport— outbound transport (DeRecTransport)
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§secret_store: SecretStore§user_secret_store: UserSecretStore§state_store: StateStoreSet 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: TransportSet via DeRecProtocolBuilder::with_transport.
own_transport: TransportProtocolImplementations§
Source§impl<Ch: DeRecChannelStore, Sh: DeRecShareStore, Ss: DeRecSecretStore, Us: DeRecUserSecretStore, T: DeRecTransport, St: DeRecStateStore> DeRecProtocol<Ch, Sh, Ss, Us, St, T>
impl<Ch: DeRecChannelStore, Sh: DeRecShareStore, Ss: DeRecSecretStore, Us: DeRecUserSecretStore, T: DeRecTransport, St: DeRecStateStore> DeRecProtocol<Ch, Sh, Ss, Us, St, T>
Sourcepub 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>
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.
Sourcepub fn secret_id(&self) -> u64
pub fn secret_id(&self) -> u64
Returns the secret identifier this protocol instance was configured with.
Sourcepub fn replica_id(&self) -> Option<u64>
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.
Sourcepub async fn create_contact(
&mut self,
channel_id: Option<ChannelId>,
contact_mode: ContactMode,
nonce: Option<u64>,
) -> Result<ContactMessage>
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::InlineKeysembeds the initiator’s ML-KEM + ECIES public keys directly in the contact. Simplest to use; the contact is ~1.2 KB.ContactMode::HashedKeysembeds 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 aPrePairround-trip and validates them against the hash. Requires theown_transportset on this protocol to be ephemeral — the plaintext PrePair traffic must not be linkable to a long-lived endpoint.ContactMode::NoKeyscarries no key material and no commitment — onlychannel_id,nonce, andtransport_protocol. Small enough to be hand-typed. Keys are generated on the fly by the creator when the correspondingPrePairRequestarrives; 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 inboundPrePairRequests perchannel_idand expire outstanding NoKeys contacts on a short timer.
§Nonce
None: the library generates a fresh cryptographically-randomu64. Recommended default forInlineKeysandHashedKeyswhere the nonce is a security parameter.Some(n): application-controlled value. Required forNoKeyswhere the recipient typically types it in; also valid for the other modes if the app wants deterministic control.
Sourcepub fn set_communication_info(&mut self, info: HashMap<String, String>)
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.
Sourcepub fn set_own_transport(
&mut self,
own_transport: impl IntoOwnTransport,
) -> Result<()>
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:
- Bring up the new endpoint and start listening on it before
initiating the
UpdateChannelInfoflow. - 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.
- Retire the old endpoint only once every targeted peer has
surfaced
DeRecEvent::ChannelInfoUpdated(orDeRecEvent::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).
Sourcepub async fn start(&mut self, flow: DeRecFlow) -> Result<Vec<DeRecEvent>>
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
Errbefore any fan-out begins. Once fan-out starts for a multi-target flow, per-channel transport failures become*Failedevents in the returned vec — they do not abort the round. - Single-channel flows (
DeRecFlow::Pairing,DeRecFlow::Unpair) returnErron send failure — no*Failedevent exists, so the single-Errsignal is unambiguous.
Sourcepub async fn accept(&mut self, action: PendingAction) -> Result<Vec<DeRecEvent>>
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.
Sourcepub async fn reject(
&mut self,
action: PendingAction,
status: StatusEnum,
memo: &str,
) -> Result<()>
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.).
Sourcepub async fn process(
&mut self,
message: &[u8],
) -> Result<Vec<DeRecEvent>, ProcessError>
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:
- Decodes the outer
DeRecMessageenvelope to readchannel_id - Looks up the channel’s key material to determine the message kind
- Dispatches to the appropriate message handler based on the channel state
- 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
StoreShareRequestcarrying a share of a large secret. - Many MB for
ReplicaSyncenvelopes 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.
Sourcepub async fn get_fingerprint(&self, channel_id: ChannelId) -> Result<String>
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).
Sourcepub async fn verify_fingerprint(
&mut self,
channel_id: ChannelId,
fingerprint: &str,
) -> Result<bool>
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.
Sourcepub async fn restore(
&mut self,
secret: &Secret,
recovered_version: u32,
) -> Result<Vec<DeRecEvent>>
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:
RestoreError::AlreadyRestoredwhen a user-secret snapshot exists for thissecret_id.RestoreError::Conflictwhen one or more channels live at canonical helper / replica ids carried bysecret.RestoreError::Invariantwhen the recoveredSecretis internally inconsistent (e.g. non-emptyreplicaswith emptyreplicas.shared_key).
Store I/O failures mid-restore propagate as the underlying
crate::Error::ShareStore, crate::Error::ChannelStore,
or crate::Error::SecretStore variant.
Auto Trait Implementations§
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> 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