SROS2 Broadcast Encryption System Design

Random Ideas / Brainstorming

[21/05/2021]

  1. Maintain one keystore that contains public key for all users
  2. Each node should have access to its own public key (cert) and private key
  3. To-dos: understand existing x509 key encryption/decryption. Source: Github Repo on M2Crypto

[22/05/2021]

  1. Datatype handling: in ROS2, the datatype of a publisher/subscriber should be pre-defined. However, the transmitted message cannot be in random datatype. In case that publishers and subscribers both know the datatype (by default), a robust handler for all types of messages should be identified.

[25/05/2021]

  1. Need to clearly handle different types of data in ROS2. Need a good parser.
  2. Want to understand how the data can be parsed to different types of messages by ROS2. After receiving the raw bytes, we probably need to do the same thing. Check [Design Ideas] point 4.
  3. Need to fix the parsing issue in Encryption module.

[26/05/2021] Important Updates

  1. After testing, ROS2 does not support native bytestring. Therefore, a bytestring to string encoding conversion is done before sending the data. Due to this consideration, we convert the transformed data to a StringList (custom ROS2 data type)

[16/06/2021] Design test benchmarks. The benchmark together with the installation procedure can be found at: https://github.com/ros2/performance_test. Need to examine how to get this benchmark work with SROS and how to use custom encryption layer.

[01/07/2021] Design the test environment. This part is described in details in section Test Environment Design.

[29/10/2021] The design, implementation as well as experiments are completed and submitted to USENIX 2022.

Design Ideas

  1. One centralized keystore list: contain all the nodes and corresponding public key. In this specific implementations, two public key lists should be maintained. One is the original x509 certificate system adopted by ROS2, another is the signature verification system adopted by broadcast encryption (for current implementation we use ECDSA). The sample file structure is shown below:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Keystore
    --> public_keys
    --> x509: {id: cert, id: cert, id: cert}
    --> ECDSA: {id: key, id: key, id: key}
    ## Below is ROS2 native implementation
    --> Enclave
    --> enclave1
    --> publisher_node
    --> public_key # used for broadcast encryption
    --> private_key # used for broadcast encryption
    --> x509_cert
    --> x509_key
    --> permission.xml
    --> permission.p7s
    --> subscriber_node
    ...
    --> enclave2
    ...

    --> Keystore_certs
  1. Each node has access to its private keys, including x509 and ECDSA

  2. For the message communicates between nodes, they should be firstly encrypted. The encryption design is discussed in the next section.

  3. A data parser is used to parse different ROS2 msg into bytes

    Pseudocode as below.

    Publisher:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class MinimalPublisher(Node):

    def __init__(self):
    super().__init__('minimal_publisher')

    # self.publisher_ = self.create_publisher(String, 'topic', 10)

    >> self.publisher_ = self.create_publisher(Byte: encrypted_msg, 'topic', 10)

    timer_period = 0.5 # seconds
    self.timer = self.create_timer(timer_period, self.timer_callback)
    self.i = 0

    def timer_callback(self):
    msg = String()
    msg.data = 'Hello World: %d' % self.i

    >> encrypted_msg = encrypt(msg) # in String

    >> self.publisher_.publish(encrypted_msg)


    self.get_logger().info('Publishing: "%s"' % msg.data)
    self.i += 1

    And subscriber:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class MinimalSubscriber(Node):

    def __init__(self):
    super().__init__('minimal_subscriber')
    self.subscription = self.create_subscription(
    Byte,
    'topic',
    self.listener_callback,
    10)
    self.subscription # prevent unused variable warning

    def listener_callback(self, encrypted_msg):
    msg = decrypt(encrypted_msg) # in the right format as it should be, using pickle
    self.get_logger().info('I heard: "%s"' % msg.data)
  4. Probably Need to use a serialization method to handle the data. The pseudocode of encryption/decryption is shown below:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def Encrypt(msg):
    byte_data = pickle.dump(msg)
    encrypted_msg = broadcast_encrypt(byte_data) # note that this is still in String
    return byte_data

    def Decrypt(msg):
    pickled_data = broadcast_decrypt(byte_data) # pickle String
    msg = pickle.load(pickled_data) # in the original preferred format
    return msg
  5. In this case, parsing is not an issue, since the transmitted data can be transmitted in byte-array. Now the only consideration is performance. Will test it out in implementation.

Package Structure

Pseudocode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class BroadcastEncryption:
def __init__(self):
self.identify = {
id: cert,
id2: cert2
}

def loadKeystore(self):
"""
load the id:cert paris from the keystore in case that it is not initialized. Load package info
"""
return None

def encrypt(self, msg: ros2_msg, recp: list) -> encrypted_msg: String (decode from bytestring)
"""
Call the encrypt method to encrypt any type of ros2_msg
"""
return encrypted_msg: byte_array

def decrypt(self, encrypted_msg: byte, recp: list) -> msg: ros2_msg
"""
Decryption methods
"""
return decrypted_msg: native ROS2 msg type

Installation Guide (Sample Version)

  1. Create custom message type StringArray in ROS2. Follow this tutorial

    1
    2
    3
    4
    5
    6
    # in ./src directory
    ros2 pkg create --build-type ament_cmake tutorial_interfaces
    # move to tutorial_interfaces directory
    mkdir msg
    # move to msg directory, create StringArray.msg with content
    strin

Performance Benchmark Design

To showcase the performance of the proposed method, we can use the benchmark cases widely accepted in the industry, such as:

  1. https://github.com/irobot-ros/ros2-performance and the related papers as a start.
  2. https://gitlab.com/ApexAI/performance_test, https://www.rti.com/products/performance.

The second one is officially referred by RTI Connext DDS implementation, which makes it more convincing.

The general idea is to test the latency of different publish/subscribe behaviors defined by the benchmark communication framework.

The performance benchmark with SROS set is achieved thanks to the support from Gitlab community. The details can be found here: https://gitlab.com/ApexAI/performance_test/-/issues/122

Test Environment Design

The goal is to establish a test environment to demonstrate the possible attacks, and show that the proposed solution can resolve the issues.

Currently we see ROS2 RMF is a good example of developing and testing multi-robots in a real-world like environment. Demo Hotel World is a good starting point.

To configure SROS in this example, we propose to set RMW_SECURITY_STRATEGY to false to avoid excessive complex rules. We only limit the critical topics in this test environment.