Skip to main content

SharedSecurityGate

Struct SharedSecurityGate 

Source
pub struct SharedSecurityGate { /* private fields */ }
Expand description

Thread-safe security gate. Clone gives a second reference to the same plugin instance — all clones operate on the same key store.

Implementations§

Source§

impl SharedSecurityGate

Source

pub fn new( domain_id: u32, governance: Governance, crypto: Box<dyn CryptographicPlugin>, ) -> Self

Constructor. The plugin is owned by the gate (Box).

Source

pub fn domain_id(&self) -> Result<u32, SecurityGateError>

Returns the domain id the gate runs for.

Source

pub fn message_protection(&self) -> Result<ProtectionKind, SecurityGateError>

ProtectionKind for the message level — derived from the first matching <domain_rule>.

Source

pub fn data_protection(&self) -> Result<ProtectionLevel, SecurityGateError>

ProtectionLevel for user DATA — derived from the data_protection_kind of the domain’s first <topic_rule> (FU2 S3). Used by the user outbound path as a fallback when the matched reader has not announced an explicit security_info level via SEDP — this way ZeroDDS↔ZeroDDS encrypts user data according to its own governance, while SPDP/SEDP metatraffic bootstraps plaintext via rtps_protection_kind=NONE.

§Errors

Mutex poison.

Source

pub fn metadata_protection(&self) -> Result<ProtectionLevel, SecurityGateError>

ProtectionLevel for submessage metadata — from the metadata_protection_kind of the domain’s first <topic_rule>. Controls (together with Self::data_protection) the endpoint_security_attributes that ZeroDDS announces via SEDP — cross-vendor the mask must match cyclone/OpenDDS byte-exactly (otherwise “security_attributes mismatch” → no endpoint match).

§Errors

Mutex poison.

Source

pub fn discovery_protection(&self) -> Result<ProtectionLevel, SecurityGateError>

ProtectionLevel for protected discovery — from the DOMAIN-wide discovery_protection_kind (DDS-Security §8.4.2.4 is_discovery_ protected). != None ⟹ secured endpoints are announced via the secure SEDP (DCPSPublicationsSecure/DCPSSubscriptionsSecure) instead of plaintext SEDP; the DATA/HEARTBEAT/GAP of the secure-SEDP writers are protected with the participant data key via encode_datawriter_submessage.

§Errors

Mutex poison.

Source

pub fn topic_discovery_protected(&self) -> Result<bool, SecurityGateError>

true if the domain’s first <topic_rule> sets enable_discovery_protection. Controls the endpoint bit IS_DISCOVERY_PROTECTED of the EndpointSecurityAttributes (§9.4.2.4) — this is a TOPIC-level flag, NOT the domain-wide Self::discovery_protection (= discovery_protection_kind, which only protects the SEDP channel). cyclone derives the endpoint mask from this boolean; ZeroDDS must announce byte-exactly the same, otherwise “security_attributes mismatch” → no endpoint match.

§Errors

Mutex poison.

Source

pub fn topic_read_protected(&self) -> Result<bool, SecurityGateError>

true if the domain’s first <topic_rule> sets enable_read_access_control → endpoint bit IS_READ_PROTECTED (0x01) of the EndpointSecurityAttributes (§9.4.2.4). cyclone sets it for access-control topics; ZeroDDS must announce byte-exactly the same mask.

§Errors

Mutex poison.

Source

pub fn topic_write_protected(&self) -> Result<bool, SecurityGateError>

true if the domain’s first <topic_rule> sets enable_write_access_control → endpoint bit IS_WRITE_PROTECTED (0x02) of the EndpointSecurityAttributes (§9.4.2.4).

§Errors

Mutex poison.

Source

pub fn rtps_protection(&self) -> Result<ProtectionLevel, SecurityGateError>

ProtectionLevel for the whole RTPS message — DOMAIN-wide rtps_protection_kind (§8.4.2.4 is_rtps_protected). Flows into the ParticipantSecurityAttributes mask (§9.4.2.4) that ZeroDDS announces via SPDP.

§Errors

Mutex poison.

Source

pub fn liveliness_protection( &self, ) -> Result<ProtectionLevel, SecurityGateError>

ProtectionLevel for liveliness (BuiltinParticipantMessageSecure) — DOMAIN-wide liveliness_protection_kind (§8.4.2.4).

§Errors

Mutex poison.

Source

pub fn ensure_local(&self) -> Result<CryptoHandle, SecurityGateError>

Registers the local participant with the crypto plugin (idempotent).

§Errors

CryptoSetup if the plugin does not accept the identity handle.

Source

pub fn local_token(&self) -> Result<Vec<u8>, SecurityGateError>

Token of the local participant (to be sent to the remote via SEDP).

§Errors

See SecurityGateError.

Source

pub fn register_remote_with_token( &self, remote_identity: IdentityHandle, shared_secret: SharedSecretHandle, token: &[u8], ) -> Result<CryptoHandle, SecurityGateError>

Registers a remote peer and installs its token. The returned CryptoHandle identifies the slot in which the remote key is stored — needed again at decode_inbound_message.

§Errors

See SecurityGateError.

Source

pub fn register_remote_by_guid( &self, peer_key: PeerKey, remote_identity: IdentityHandle, shared_secret: SharedSecretHandle, token: &[u8], ) -> Result<CryptoHandle, SecurityGateError>

Registers a remote peer with peer-key mapping. After this call Self::transform_inbound_from can find the peer by its PeerKey (GuidPrefix).

Idempotent: a repeated call with the same key does not overwrite the old slot — the caller must explicitly call Self::forget_remote to be able to rotate.

§Errors

See SecurityGateError.

Source

pub fn register_remote_by_guid_from_secret( &self, peer_key: PeerKey, remote_identity: IdentityHandle, shared_secret: SharedSecretHandle, ) -> Result<CryptoHandle, SecurityGateError>

FU2 S1.2: Registers a remote peer only with the Kx key (from the handshake SharedSecret), WITHOUT a data token. The data key is installed later via Self::set_remote_data_token_by_guid from the received ParticipantCryptoToken.

Maps peer_key → slot; idempotent (a repeated call with the same key returns the existing slot).

§Errors

CryptoSetup if the plugin does not accept the secret.

Source

pub fn set_remote_data_token_by_guid( &self, peer_key: &PeerKey, token: &[u8], ) -> Result<(), SecurityGateError>

FU2 S1.2: Installs the data key of a peer already registered via Self::register_remote_by_guid_from_secret from its received ParticipantCryptoToken (token exchange).

§Errors

PolicyViolation if the peer is not registered; Crypto on an invalid token.

Source

pub fn transform_kx_outbound_for( &self, peer_key: &PeerKey, plaintext: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

FU2 S1.2: Encrypts a VolatileSecure payload (e.g. a ParticipantCryptoToken) with the peer’s Kx key (encode_kx_submessage). The peer must be registered via Self::register_remote_by_guid_from_secret.

§Errors

PolicyViolation (unknown peer) / Crypto.

Source

pub fn transform_kx_inbound_from( &self, peer_key: &PeerKey, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

FU2 S1.2: Decrypts a Kx-protected VolatileSecure payload of a peer (decode_kx_submessage).

§Errors

PolicyViolation (unknown peer) / Crypto (tag mismatch).

Source

pub fn encode_kx_datawriter_for( &self, peer_key: &PeerKey, data_submessage: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Cross-vendor VolatileSecure: protects a DATA submessage as a cyclone-conform SEC_PREFIX/SEC_BODY/SEC_POSTFIX sequence with the peer’s Kx key (encode_kx_datawriter_submessage).

§Errors

PolicyViolation (unknown peer) / Crypto.

Source

pub fn decode_kx_datawriter_from( &self, peer_key: &PeerKey, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Counterpart: decodes a peer’s SEC_PREFIX/SEC_BODY/SEC_POSTFIX sequence back to the original DATA submessage (decode_kx_datawriter_submessage).

§Errors

PolicyViolation (unknown peer) / Crypto (tag mismatch).

Source

pub fn encode_data_datawriter_local( &self, data_submessage: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Cross-vendor user DATA (send): protects a DATA submessage with the local data key as a cyclone-conform SEC_PREFIX/BODY/POSTFIX sequence (encode_data_datawriter_submessage). cyclone decodes it with the (= local) key sent by ZeroDDS via datawriter_crypto_tokens.

§Errors

CryptoSetup (no local slot) / Crypto.

Source

pub fn register_local_endpoint( &self, is_writer: bool, ) -> Result<CryptoHandle, SecurityGateError>

Registers a local endpoint crypto slot with its own key material. is_writer = DataWriter (else DataReader). Returns the endpoint handle.

§Errors

CryptoSetup (no local participant) / plugin error.

Source

pub fn create_endpoint_token( &self, endpoint: CryptoHandle, ) -> Result<Vec<u8>, SecurityGateError>

Serializes the per-endpoint datawriter/datareader_crypto_token (the key material of the local endpoint slot), to be sent to the matched remote endpoint via VolatileSecure.

§Errors

Crypto (unknown endpoint handle).

Source

pub fn endpoint_payload_token(&self, endpoint: CryptoHandle) -> Option<Vec<u8>>

Installs a received per-endpoint token. The key material is indexed via its transformation_key_id (remote_by_key_id), so that Self::decode_data_by_key_id maps the submessages of this remote endpoint — independent of the participant key.

§Errors

CryptoSetup (no local slot) / Crypto. Optional second (payload) token of a DataWriter endpoint for the cyclone dual-key model (metadata != data, e.g. meta-sign-data). None = single key (all other profiles).

Source

pub fn install_remote_endpoint_token( &self, token: &[u8], ) -> Result<(), SecurityGateError>

Installs a remote endpoint crypto token (volatile key exchange): passes the received token via set_remote_participant_crypto_tokens to the crypto plugin (participant handle CryptoHandle(0) = per-endpoint).

Source

pub fn encode_data_datawriter_by_handle( &self, endpoint: CryptoHandle, data_submessage: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Encodes a DATA submessage with the per-endpoint key (not the participant). Wire-identical to cyclone encode_datawriter_submessage.

§Errors

Crypto (unknown endpoint handle / encode error).

Source

pub fn decode_data_datawriter_from( &self, peer_key: &PeerKey, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Cross-vendor user DATA (recv): decodes a SEC_* sequence with the sender’s data key (peer slot, filled via token exchange).

§Errors

PolicyViolation (unknown peer) / Crypto (tag mismatch).

Source

pub fn decode_data_by_key_id( &self, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Cross-vendor (recv) by key_id: decodes a SEC_* submessage without knowing the endpoint handle — the plugin looks up the remote key material by the transformation_key_id in the CryptoHeader (§9.5.2.1.1). Needed for protected discovery, where a peer has its own key per secure builtin endpoint.

§Errors

Crypto (no key for the key_id / tag mismatch).

Source

pub fn encode_serialized_payload( &self, endpoint: CryptoHandle, payload: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

§9.5.3.3.1 data_protection (send): encrypts the SerializedPayload of ONE endpoint (per-endpoint writer key) as the inner layer. Cross-vendor needed: with data_protection_kind=ENCRYPT cyclone expects the encrypted payload (decode_serialized_payload).

§Errors

Crypto (no key / seal error).

Source

pub fn decode_serialized_payload( &self, encoded: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

§9.5.3.3.1 data_protection (recv): key via transformation_key_id.

§Errors

Crypto (no key for key_id / tag mismatch).

Source

pub fn authenticated_peer_prefixes(&self) -> Vec<PeerKey>

Prefixes of all peers with which a data key is installed (participant slot table). STABLE — unlike completed_peer_prefixes (derived from the handshake entries GC’d after completion), this entry persists. Source for re-sending per-endpoint crypto tokens to late-matching user endpoints.

Source

pub fn decode_serialized_payload_from( &self, peer_key: &PeerKey, encoded: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

§9.5.3.3.1 data_protection (recv) fallback: key via the sender prefix (GuidPrefix slot table, token exchange) instead of key_id. zero↔zero indexes the remote key material via the peer slot, not necessarily via a unique transformation_key_id — analogous to decode_data_datawriter_from.

§Errors

PolicyViolation (unknown peer) / Crypto (tag mismatch).

Source

pub fn decode_serialized_payload_kx( &self, peer_key: &PeerKey, encoded: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

§9.5.3.3.1 data_protection (recv) fallback with the Kx/participant key of the peer slot (transformation_key_id=0). cyclone encrypts the data_protection payload with the SharedSecret-derived participant key instead of the per-endpoint DataWriter key — the same handle indexes the Kx key material in kx_slots (§10.5.2.1.2 Tab. 73).

§Errors

PolicyViolation (unknown peer) / Crypto (tag mismatch).

Source

pub fn forget_remote(&self, peer_key: &PeerKey) -> Result<(), SecurityGateError>

Removes the peer-key → slot mapping. The slot itself stays in the plugin (key cleanup is the plugin’s job on re-register).

Source

pub fn slot_for( &self, peer_key: &PeerKey, ) -> Result<Option<CryptoHandle>, SecurityGateError>

Returns the slot for a peer key, if registered.

Source

pub fn transform_inbound_from( &self, peer_key: &PeerKey, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Inbound transform with peer-key lookup. The RTPS header contains the sender’s GuidPrefix on bytes 8..20 — the caller must pass this here as peer_key.

If the peer is not registered and the message looks encrypted: PolicyViolation (unknown sender sends SRTPS).

§Errors

See SecurityGateError.

Source

pub fn transform_outbound( &self, message: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Outbound message: wrap if governance requires message protection.

§Errors

See SecurityGateError.

Source

pub fn transform_outbound_group( &self, peer_keys: &[PeerKey], plaintext: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Group outbound with receiver-specific MACs.

Uses encode_secured_submessage_multi when all receivers are already registered in the gate via peer keys. Returns a single wire datagram with a multi-MAC SEC_POSTFIX; the caller can unicast the same datagram to all matched readers.

The resulting wire is NOT an RTPS message-level wrapper — it is a submessage sequence (SEC_PREFIX/BODY/POSTFIX). The caller must put the RTPS header in front itself or use the transform_outbound_multi_wrapped variant (follows if needed — for stage 7 the submessage sequence suffices).

§Errors
  • Crypto passed through.
  • PolicyViolation if a peer key is not registered.
Source

pub fn transform_inbound_group( &self, sender_peer_key: &PeerKey, own_peer_key: &PeerKey, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Group inbound: decodes a multi-MAC submessage sequence and validates our own MAC.

sender_peer_key is the sender’s GuidPrefix (as registered in Self::register_remote_by_guid). The receiver MAC key comes from ensure_local() — at encoding the sender set exactly this slot handle as a remote_list entry (via register_remote_by_guid(our_prefix, our_local_token)).

§Errors
  • PolicyViolation if sender_peer_key is not registered.
  • Crypto / Wrapper on a tag/MAC mismatch.
Source

pub fn transform_outbound_for( &self, _peer_key: &PeerKey, message: &[u8], level: ProtectionLevel, ) -> Result<Vec<u8>, SecurityGateError>

Peer-specific outbound transform.

Unlike Self::transform_outbound, this method ignores the domain rule and instead applies the caller-given ProtectionLevel. This is the API that the heterogeneous-security writer tick (per-reader serializer) calls per matched reader.

  • ProtectionLevel::None → plaintext passthrough
  • ProtectionLevel::Sign → RTPS message wrap (HMAC/GCM tag)
  • ProtectionLevel::Encrypt → RTPS message wrap (AEAD ciphertext)

The sign/encrypt distinction today uses the same encoder as Self::transform_outbound — the current AesGcmCryptoPlugin does not differentiate. Receiver-specific MACs and further extensions retrofit the distinction. The peer_key is carried along (for future per-peer crypto handles) but not yet passed to the plugin.

§Errors

See SecurityGateError. Never an error in the None path.

Source

pub fn allow_unauthenticated(&self) -> Result<bool, SecurityGateError>

Returns the allow_unauthenticated_participants flag from the domain rule. Default false if no rule exists for the domain — conservative-safe stance.

Source

pub fn classify_inbound( &self, bytes: &[u8], iface: &NetInterface, ) -> InboundVerdict

Classifies an incoming RTPS datagram against the domain rule, peer registration, wire format and network interface .

Decision matrix:

  1. bytes.len() < 20Malformed.
  2. Extract peer_key from bytes[8..20].
  3. If the packet is SRTPS-wrapped → standard unwrap path (transform_inbound_from). On a crypto error CryptoError, on an unknown peer PolicyViolation.
  4. If the packet is plain AND the domain requires ProtectionKind::None → Accept.
  5. If the packet is plain AND the domain requires protection:
    • the interface is Loopback or LocalHostAccept (bytes do not leave the host kernel — spec-conform plaintext on host-local transport)
    • allow_unauthenticated_participants = trueAccept
    • otherwise → LegacyBlocked

The iface context is currently consulted in the rules only for the loopback fast path; the finer per-interface peer/topic classification is handled by the PolicyEngine from stage 8 on (governance-XML <interface_bindings>).

Source

pub fn transform_inbound( &self, remote_slot: CryptoHandle, wire: &[u8], ) -> Result<Vec<u8>, SecurityGateError>

Inbound message: unwrap if SRTPS_PREFIX is detected, otherwise passthrough or PolicyViolation.

remote_slot points to the slot in which the sender key is stored (from Self::register_remote_with_token).

§Errors

See SecurityGateError.

Trait Implementations§

Source§

impl Clone for SharedSecurityGate

Source§

fn clone(&self) -> SharedSecurityGate

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for SharedSecurityGate

Source§

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

Formats the value using the given formatter. 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
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<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
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> 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.