Skip to content

Commit

Permalink
Put the interoperability adjustments behind the feature "security_in_…
Browse files Browse the repository at this point in the history
…fastdds_compatibility_mode". Document the FastDDS-compatibility mode.
  • Loading branch information
ohuopio committed Mar 14, 2024
1 parent 4a6e4f2 commit d2e1571
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ security = [
"dep:openssl", "dep:cryptoki"
]

# If feature "security_in_fastdds_compatibility_mode" is enabled,
# the DDS Security implementation is adjusted so that RustDDS interoperates with FastDDS.
# This means doing some things against the spec. For more info, see the security readme.
security_in_fastdds_compatibility_mode = ["security"]

# If feature "build_openssl" is enabled (along with feature "security"),
# a local copy of OpenSSL will be built.
# Otherwise, we try to use the system installation of OpenSSL.
Expand Down
38 changes: 38 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Please see the [DDS Security Specification](https://www.omg.org/spec/DDS-SECURIT

In order to use the security functionality, 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.

**Note**: if RustDDS needs to interoperate with DomainParticipants based on [FastDDS](https://github.com/eProsima/Fast-DDS), enable the feature `security_in_fastdds_compatibility_mode` instead. For more info, see the section [Security in FastDDS-compatible mode](#security-in-fastdds-compatible-mode) below.



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

Configuring security for DomainParticipants needs two Certificate 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 Certificate Authority and the Permissions Certificate Authority.
Expand Down Expand Up @@ -73,3 +77,37 @@ The following security configuration files are needed:
Configuration files can be created using any method, but the OpenSSL tool is recommended.

Please see the examples and scripts in the directory [examples/security_configuration_files](examples/security_configuration_files).


# Security in FastDDS-compatible mode

Currently, our default DDS Security implementation is not compatible with the implementation in FastDDS. Because of this, RustDDS compiled with the default security implementation cannot establish a secure connection with a DomainParticipant based on FastDDS. The problem here is that, at least from our perspective, the FastDDS implementation slightly deviates from the Security specification at some points.

To remedy this, we have created the feature `security_in_fastdds_compatibility_mode`. When RustDDS is compiled with this feature enabled, the security functionality is adjusted to obtain interoperability with FastDDS. The section below describes these adjustments.

**Note**: Because FastDDS is a widely used DDS implementation that likely interoperates with some other DDS implementations, it might be that the feature `security_in_fastdds_compatibility_mode` is required to obtain interoperability also with some other DDS implementation.

## Adjustments to Security implementation in the compatibility mode

### 1. In PermissionsToken and IdentityToken, use the algorithm identifiers expected by FastDDS
- The DDS Security specification (section 9.3.2.1 DDS:Auth:PKI-DH IdentityToken) states that In PermissionsToken and IdentityToken, the algorithm identifier (a string) is either `RSA-2048` or `EC-prime256v1`
- FastDDS uses the identifiers ``RSASSA-PSS-SHA256`` and ``ECDSA-SHA256`` which are similar, but used elsewhere in the spec.
- Because of this, FastDDS does not recognize the algorithm identifiers sent by RustDDS, and refuses to establish a connection

**Compatiblity fix** ✅: Use the identifiers that FastDDS expects also in RustDDS

**Proper fix**: Use the correct identifiers in FastDDS. TODO: submit an issue about it to FastDDS.

### 2. Use the same string representation of certificate Subject name as FastDDS
- To represent a certificate’s Subject Name as a string, FastDDS uses the OpenSSL function [X509_NAME_oneline](https://www.openssl.org/docs/manmaster/man3/X509_NAME_oneline.html). In the OpenSSL documentation, this function is said to produce a non-standard output, and “its use is strongly discouraged in new applications and it could be deprecated in a future release”.
- RustDDS uses the [string formatting from the x509_cert crate](https://docs.rs/x509-cert/latest/x509_cert/name/struct.RdnSequence.html#impl-Display-for-RdnSequence) , which produces a representation according to the [RFC 4514](https://datatracker.ietf.org/doc/html/rfc4514), which seems to be the standard nowadays. A function equivalent to the X509_NAME_oneline used by FastDDS is not available in the openssl or certificate crates that RustDDS is using. Such a function seems to be available in the [boring_sys](https://docs.rs/boring-sys/latest/boring_sys/fn.X509_NAME_oneline.html) crate, but using this would require bringing in a complex new crate just for this quite small functionality & tinkering with unsafe code.
- The problem arises when FastDDS compares the CA subject name string (standard) that RustDDS has sent inside a PermissionsToken to its own string (non-standard) that FastDDS has computed from the permissions CA certificate. Since these two strings don’t match, FastDDS deduces that it does not know the CA that RustDDS is using and refuses to establish a connection.
- Note: ideally the Subject Name comparison method would be more robust than a simple string comparison
- Example formats:
- RustDDS (RFC 4514): ``CN=permissions_ca_common_name,O=Example Organization``
- FastDDS (X509_NAME_oneline): ``/O=Example Organization/CN=permissions_ca_common_name``


**Compatiblity fix** ✅: In RustDDS, we use a function for converting the subject name string from the standard format to the X509_NAME_oneline format expected by FastDDS. The conversion isn’t perfect (according to OpenSSL documentation, X509_NAME_oneline “has various quirks and inconsistencies”), but it should work in simple cases.

**Proper fix**: Does FastDDS even need to compare the CA Subject Name we send in PermissionsToken to its own version of the Subject Name? The spec says that the subject name field in the PermissionsToken is optional, and does not require checking it in any way. What the spec *does* require is that the signed permissions document sent by RustDDS is verified against the local permissions CA cerificate. Doesn't this already guarantee that we're using the same permissions CA?
8 changes: 6 additions & 2 deletions src/security/authentication/authentication_builtin/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ const CA_ALGO_PROPERTY_NAME: &str = "dds.ca.algo";

// Algorithm identifiers used in IdentityToken and PermissionsToken
// Correct identifiers from the spec:
// const RSA_2048_ALGO_NAME: &str = "RSA-2048";
// const EC_PRIME_ALGO_NAME: &str = "EC-prime256v1";
#[cfg(not(feature = "security_in_fastdds_compatibility_mode"))]
const RSA_2048_ALGO_NAME: &str = "RSA-2048";
#[cfg(not(feature = "security_in_fastdds_compatibility_mode"))]
const EC_PRIME_ALGO_NAME: &str = "EC-prime256v1";

// Identifiers that FastDDS expects (the same as the signature algorithm
// identifiers in the spec):
#[cfg(feature = "security_in_fastdds_compatibility_mode")]
const RSA_2048_ALGO_NAME: &str = "RSASSA-PSS-SHA256";
#[cfg(feature = "security_in_fastdds_compatibility_mode")]
const EC_PRIME_ALGO_NAME: &str = "ECDSA-SHA256";

pub(in crate::security) const RSA_2048_KEY_LENGTH: usize = 256;
Expand Down
12 changes: 10 additions & 2 deletions src/security/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ use crate::security::{
};
//use crate::security_error;

#[cfg(feature = "security_in_fastdds_compatibility_mode")]
fn rfc4514_to_openssl_oneline(dn_rfc4514: &str) -> String {
// This function converts a RFC 4514 string representation of a Distinguished
// Name to the format produced by the deprecated OpenSSL function
// Name to the format produced by the legacy OpenSSL function
// x509_name_oneline, which FastDDS uses. The conversion is needed for
// interoperability.
// The fuction
Expand Down Expand Up @@ -240,7 +241,14 @@ impl DistinguishedName {

pub fn serialize(&self) -> String {
let rfc4514_string = self.0.to_string();
rfc4514_to_openssl_oneline(&rfc4514_string)
#[cfg(not(feature = "security_in_fastdds_compatibility_mode"))]
{
rfc4514_string
}
#[cfg(feature = "security_in_fastdds_compatibility_mode")]
{
rfc4514_to_openssl_oneline(&rfc4514_string)
}
}

// TODO is this a too strict equivalence?
Expand Down

0 comments on commit d2e1571

Please sign in to comment.