Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/jhelovuo/RustDDS
Browse files Browse the repository at this point in the history
  • Loading branch information
SelimV committed Oct 31, 2023
2 parents 1b90316 + 7052b6f commit dc3567e
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 21 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
name: format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
Expand All @@ -20,15 +20,15 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cargo test -- --test-threads=1

clippy:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
Expand All @@ -40,6 +40,6 @@ jobs:
msrv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: dtolnay/[email protected]
- run: cargo check
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout source
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Install nightly toolchain
uses: dtolnay/rust-toolchain@nightly
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rustdds"
version = "0.8.5"
version = "0.8.6"
authors = ["Juhana Helovuo <[email protected]>",
"Oiva Moisio <[email protected]>",
"Miska Melkinen <[email protected]>",
Expand Down Expand Up @@ -41,7 +41,7 @@ bit-vec = "0.6.2"
speedy = "0.8.0"
log = "0.4.11"
num-traits = "0.2"
num-derive = "0.3"
num-derive = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_repr="0.1"
byteorder = { version = "1.3", features = ["i128"] }
Expand Down Expand Up @@ -87,7 +87,7 @@ hex-literal = "0.4"
anyhow = "1.0" # for test cases

# ros_visualizer
crossterm = "0.26"
crossterm = "0.27"
tui = { version = "0.19", default-features = false, features = ['crossterm'] }

# shapes-demo
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
![continuous-integration](https://github.com/jhelovuo/RustDDS/actions/workflows/CI.yml/badge.svg)
[![codecov](https://codecov.io/gh/jhelovuo/RustDDS/branch/master/graph/badge.svg)](https://codecov.io/gh/jhelovuo/RustDDS)

[RustDDS][rustdds-url] is a pure Rust implementation of [Data Distribution Service](https://www.omg.org/spec/DDS/), developed by [Atostek Oy][atostek-url].
Atostek provides support and software development services related to DDS, ROS2, and robotics software in general. As a part of our work, we have open-sourced the RustDDS implementation.
[RustDDS][rustdds-url] is a pure Rust implementation of [Data Distribution Service](https://www.omg.org/spec/DDS/). The latest [released version](https://crates.io/crates/rustdds)
is available on [crates.io](https://crates.io/) and API documentation on [docs.rs](https://docs.rs/rustdds/latest/rustdds/). The [GitHub repository](https://github.com/jhelovuo/RustDDS) tracks development.

RustDDS is developed by [Atostek Oy][atostek-url]. Atostek provides support and software development services related to DDS, ROS2, and robotics software in general. As a part of our work, we have open-sourced the RustDDS implementation.

We have tried to translate the key ideas of the DDS application interface to Rust concepts, but also follow Rust conventions. Consequently, the API is not exactly as written in the DDS specification, but a functionally equivalent approximation using Rust concepts and conventions.

Expand All @@ -18,6 +20,14 @@ Currently, the implementation is complete enough to do data exchange with [ROS2]

The [ros2-client](https://crates.io/crates/ros2-client) is recommended for talking to ROS components. The `ros2` module within RustDDS should not be used anymore.

## Version 0.8.6

* Feature `security` is nearing completion. RustDDS can securely talk to itself, but interoperability testing against other DDS implementaitons is still in progress.
* Fix several bugs in SequenceNumber handling.
* RTPS Writer data sending rewritten.
* Fixed bug: Source timestamps were missing on retransmitted data.


## Version 0.8.5

* Feature `security` merged to master, but it is still work-in-progress, so does not work yet.
Expand Down
76 changes: 76 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Security in RustDDS

Security support in DDS means the abilities to

* Cryptographically authenticate other DomainPArticipants
* Cryptographically check the domain acecss permisions of each DomainParticipant
* Encrypt and sign RTPS communications
* Security event logging
* Data tagging (not implemented in RustDDS)

Please see the [DDS Security Specification](https://www.omg.org/spec/DDS-SECURITY/1.1/About-DDS-SECURITY) v1.1 from OMG for technical details.

# Using security in RustDDS

In order to use the security functionality, use enable the Cargo feature `security` in RustDDS. By default, it is not enabled, because it adds a large body of code and some processing overhead.

Security needs to be confgured in order to be used. There are several mandatory configuration files that need to be supplied ot RustDDS. These configuration files and their format and semantics are not unique to RustDDS, but specified in the OMG DDS Security specification. The security configration files should also be interoperable between compliant DDS implementations.

Configuring security for DomainParticipants needs two Certification Authority roles, or CAs. A CA is someone who has the ability to issue and sign the various configuration files. The two CAs are the Identity Certification Authority and the Permissions Certificate Authority.

It is possible that a single CA performs both of these roles. This is a matter of security configuration.


The job of the Identity CA is to issue and sign certificates that prove the identity of DomainParticipants. Each DomainParticiapnt must have their own identity.

The job of the Permissions CA is to sign permissions documents for the DomainParticipants. These documents are sets of rules

The following security configuraiton files are needed:

## Identity CA Certificate

* Most important content is the CA's public key. It is used to verify signatures that should have been by the CA.
* This is an X.509 Certificate `.pem` file.

## Participant Identity Certificate

* X.509 Certificate `.pem` file
* This file fives the Subject Name and corresponding public key for a DomainParticipant.
* Signed by Identity CA.
* Not secret. Sent as plaintext to other DomainParticiapnts during authentication.

## Participant Private Key

* X.509 private key
* Secret, should be known only by the PArticipant it belongs to.
* Used to sign Authentication protocol messages to prove that we are the Subject Name stated in our Identity Certificate.

## Permissions CA Certificate

* Used to verify the auhenticity of permisisons documents, both our own and those presented to us over the authentication protocol.
* X.509 Certificate (`.pem`)

## Participant Permissions

* An XML document giving domain access permissions to one or more Particiapnts.
* XSD Schema given in the DDS Security Specification.
* Permissions allow/deny publish, subscribe, and/or relay access to various Topics in a Domain.
* Signed by Permissions CA.
* PKCS #7 signed document (`.p7s`) using S/MIME encoding.

## Domain Governance Document

* An XML document defining the domain-wide acecss rules not specific to any Participant.
* Are unauthenticated participants allowed at all?
* Is discovery protocol secured?
* Is RTPS liveliness (heartbeat) messaging secured?
* Topic-specific access rules, e.g. is data or metadata encrypted or signed, is reading or writing access controlled.
* XSD Schema given in the DDS Security Specification.
* Signed by Permissions CA.
* PKCS #7 signed document (`.p7s`) using S/MIME encoding.

# Creating configuration files

Configuration files can be created using any method, but the OpenSSL tool is recommended.

Please see the examples and scripts in the subdirectory [example_security_configuration_files](example_security_configuration_files/).
99 changes: 89 additions & 10 deletions src/dds/statusevents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,23 @@ use futures::stream::{FusedStream, Stream};
use mio_06::Evented;
use mio_extras::channel as mio_channel;
use mio_08::{self, event, Interest, Registry, Token};
use chrono::Utc;

use crate::{
dds::{
qos::QosPolicyId,
result::{ReadError, ReadResult},
},
Duration, GUID, Topic, QosPolicies,
messages::{protocol_version::ProtocolVersion, vendor_id::VendorId,},
mio_source::*,
read_error_poisoned,
structure::guid::GuidPrefix,
};

#[cfg(feature = "security")]
use crate::discovery::secure_discovery::AuthenticationStatus;

/// This trait corresponds to set_listener() of the Entity class in DDS spec.
/// Types implementing this trait can be registered to a poll and
/// polled for status events.
Expand Down Expand Up @@ -225,25 +232,97 @@ impl<'a, T> FusedStream for StatusReceiverStream<'a, T> {
// -------------------------------------------------------------------------------

#[derive(Debug, Clone)]
pub enum DomainParticipantStatus {
PublisherStatus(PublisherStatus),
SubscriberStatus(SubscriberStatus),
TopicStatus(TopicStatus),
pub enum DomainParticipantStatusEvent {
ParticipantDiscovered {
dpd: ParticipantDescription,
},
ParticipantLost{
id: GuidPrefix,
reason: LostReason,
},
InconsistentTopic {
previous_specs: Topic, // What was our ide aof the Topic
discovered_as: Topic, // What incoming Discovery data tells us about Topic
discovery_from: GUID, // Who sent the Disocvery data
},
/// Discovery detects a new topic
TopicDetected {
},
/// New Reader detected (or created locally). Detection happens regardless of
/// the remote being matched or not by a local Endpoint.
ReaderDetected {
reader: EndpointDescription,
},
/// New Writer detected
WriterDetected {
writer: EndpointDescription,
},
ReaderLost {
guid: GUID,
reason: LostReason,
},
WriterLost {
guid: GUID,
reason: LostReason,
},

#[cfg(feature = "security")]
Authentication {
status: AuthenticationStatus,
},
/// The CA has revoked the identity of some Participant.
/// We may be currently communicating with the Participant, or it may be unknown to us.
#[cfg(feature = "security")]
IdentityRevoked {
participant: GUID,
},
/// Domain access permissions of some Participant have been revoked / changed.
#[cfg(feature = "security")]
PermissionsRevoked {
participant: GUID,
// TODO: How to get more details on what was revoked, or was something added?
},
}

/// Why some remote entity is considered to be no longer with us.
#[derive(Debug, Clone)]
pub enum SubscriberStatus {
DataOnReaders,
DataReaderStatus(DataReaderStatus),
pub enum LostReason {
/// Participant announced via Discovery that it is leaving
Disposed,
/// Lease time exceeded => timeout
Timout {
lease: Duration, // What was the discovered lease duration
elapsed: Duration, // How much time has actually elapsed from last contact
}
}

pub type PublisherStatus = DataWriterStatus;
/// This is a rewrite/summary of SpdpDiscoveredParticipantData from discovery.
/// The original is not used to avoid circular dependency between participant and discovery.
/// Some of the more technical details have been left out
#[derive(Debug, Clone)]
pub struct ParticipantDescription {
pub updated_time: chrono::DateTime<Utc>,
pub protocol_version: ProtocolVersion,
pub vendor_id: VendorId,
pub guid: GUID,
pub lease_duration: Option<Duration>,
pub entity_name: Option<String>,
#[cfg(feature = "security")]
pub supports_security: bool,
}

/// This is a summary of SubscriptionBuiltinTopicData / PublicationBuiltinTopicData from discovery.
/// The original is not used to avoid circular dependency between participant and discovery.
#[derive(Debug, Clone)]
pub enum TopicStatus {
InconsistentTopic { count: CountWithChange },
pub struct EndpointDescription {
pub updated_time: chrono::DateTime<Utc>,
pub guid: GUID,
pub topic_name: String,
pub type_name: String,
pub qos: QosPolicies,
}


#[derive(Debug, Clone)]
pub enum DataReaderStatus {
/// Sample was rejected, because resource limits would have been exceeded.
Expand Down
2 changes: 1 addition & 1 deletion src/discovery/secure_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use super::{

// Enum for authentication status of a remote participant
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum AuthenticationStatus {
pub enum AuthenticationStatus {
Authenticated,
Authenticating, // In the process of being authenticated
Unauthenticated, /* Not authenticated, but still allowed to communicate with in a limited way
Expand Down

0 comments on commit dc3567e

Please sign in to comment.