Skip to content

Commit 9355028

Browse files
authored
Merge pull request #9 from rticommunity/feature/04-security-threat
feat: add Module 04 — Security Threat Demonstration
2 parents e4faf08 + c370b6c commit 9355028

38 files changed

Lines changed: 2887 additions & 314 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
.venv/
33
build/
44
Types.py
5+
builtin_logging_type.py
6+
*_timestamp.cmake
57
*.pyc
68
*.pem
79
*.p7s

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
77

88
## [Unreleased]
99

10+
## [1.1.0] - 2026-03-27
11+
12+
### Added
13+
14+
- **Module 04 — Security Threat Demonstration**: Two Python/PySide6 GUI
15+
applications (Threat Injector and Threat Exfiltrator) that simulate
16+
real-world DDS security attack scenarios (Rogue CA, Forged Permissions,
17+
Expired Certificate) against the operating room bus
18+
- Module 04 security infrastructure: `setup_threat_security.sh` script,
19+
OpenSSL configs, XML governance and permissions templates for rogue CA,
20+
forged permissions, and expired certificate attack modes
21+
- Module 04 DDS configuration: `ThreatParticipantLibrary.xml` and
22+
`ThreatQos.xml` with partition-scoped threat participants
23+
- Module 04 launch and kill scripts (`launch_injector.sh`,
24+
`launch_exfiltrator.sh`, `kill_all.sh`)
25+
- Module 04 local `DdsUtils.py` utility for participant and security
26+
configuration
27+
- `.gitignore` for Module 04 security directory to exclude generated
28+
certificates, keys, and OpenSSL CA database files
29+
30+
### Changed
31+
32+
- **Module 01 — PyQt5 → PySide6 migration**: Arm.py and PatientMonitor.py
33+
ported from PyQt5 to PySide6 (updated imports, enum-style Qt constants,
34+
`Signal` instead of `pyqtSignal`, `app.exec()` instead of `app.exec_()`)
35+
- Module 01 `CMakeLists.txt`: replaced manual `execute_process` codegen call
36+
with proper `connextdds_rtiddsgen_run` targets for Python type generation
37+
(both `Types.xml` and `builtin_logging_type.idl`); added `refArchTypesPy`
38+
custom target
39+
- Module 01 `README.md`: updated Python dependency references from PyQt5 to
40+
PySide6
41+
- `system_arch/qos/Qos.xml`: removed volatile durability override from
42+
secure-log DataReader QoS profile
43+
- Root `.gitignore`: added patterns for `builtin_logging_type.py` and
44+
`*_timestamp.cmake` generated files
45+
- Updated `rticonnextdds-cmake-utils` submodule to latest commit
46+
1047
## [1.0.0] - 2026-03-25
1148

1249
Initial stable release of the RTI MedTech Reference Architecture.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This repository contains documentation and module demo applications showcasing d
1313
- [Module 01: Digital Operating Room](#module-01--digital-operating-room)
1414
- [Module 02: RTI Recording Service & RTI Replay Service](#module-02--rti-recording-service--rti-replay-service)
1515
- [Module 03: Remote Teleoperation with RTI Real-Time WAN Transport](#module-03--remote-teleoperation-with-rti-real-time-wan-transport)
16+
- [Module 04: Security Threat Demonstration](#module-04--security-threat-demonstration)
1617
- [Hands-On: Architecture](#hands-on-architecture)
1718
- [Architecture Overview](#architecture-overview)
1819
- [Data Types](#data-types)
@@ -73,6 +74,8 @@ The RTI MedTech Reference Architecture demonstrates use cases and capabilities o
7374

7475
- ### [Module 03](./modules/03-remote-teleoperation/) : Remote Teleoperation with RTI Real-Time WAN Transport
7576

77+
- ### [Module 04](./modules/04-security-threat/) : Security Threat Demonstration
78+
7679
## Hands-On: Architecture
7780

7881
RTI System Designer allows you to graphically design, configure, examine, and share Connext system architectures.
@@ -269,4 +272,5 @@ Check out the the [system_arch](./system_arch/) folder, where the system archite
269272
- [Connext Real-Time WAN Transport](https://community.rti.com/static/documentation/connext-dds/7.3.0/doc/manuals/connext_dds_professional/users_manual/users_manual/PartRealtimeWAN.htm) *, used in Module 03: Remote Teleoperation with RTI Real-Time WAN Transport*
270273
- [RTI Routing Service](https://community.rti.com/static/documentation/connext-dds/7.3.0/doc/manuals/connext_dds_professional/services/routing_service/index.html) *, used in Module 03: Remote Teleoperation with RTI Real-Time WAN Transport*
271274
- [RTI Cloud Discovery Service](https://community.rti.com/static/documentation/connext-dds/7.3.0/doc/manuals/addon_products/cloud_discovery_service/index.html) *, used in Module 03: Remote Teleoperation with RTI Real-Time WAN Transport*
275+
- [RTI Security Plugins Users Manual](https://community.rti.com/static/documentation/connext-dds/7.3.0/doc/manuals/connext_dds_secure/users_manual/index.html) *, used in Module 04: Security Threat Demonstration*
272276
- [RTI Connext Third-Party Software](https://community.rti.com/static/documentation/connext-dds/7.3.0/doc/manuals/connext_dds_professional/release_notes_3rdparty/index.html)

modules/01-operating-room/CMakeLists.txt

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,17 @@ connextdds_rtiddsgen_run(
5656
UNBOUNDED
5757
)
5858

59-
# Generate C++ types for DDS Security builtin logging topic when NDDSHOME is available.
59+
# Generate Python types for application use (output alongside source files so
60+
# DdsUtils.py in this module and module 04 can import them directly)
61+
connextdds_rtiddsgen_run(
62+
IDL_FILE
63+
"${CMAKE_CURRENT_SOURCE_DIR}/../../system_arch/Types.xml"
64+
OUTPUT_DIRECTORY
65+
"${CMAKE_CURRENT_SOURCE_DIR}/src/"
66+
LANG Python
67+
)
68+
69+
# Generate C++ types for DDS Security builtin logging topic.
6070
set(BUILTIN_LOGGING_IDL "${CONNEXTDDS_DIR}/resource/idl/builtin_logging_type.idl")
6171
connextdds_rtiddsgen_run(
6272
IDL_FILE
@@ -67,10 +77,20 @@ connextdds_rtiddsgen_run(
6777
UNBOUNDED
6878
)
6979

70-
# Use command line to manually generate Python types
71-
execute_process(
72-
COMMAND bash -c "${RTICODEGEN_DIR}/rtiddsgen ../../system_arch/Types.xml -ppDisable -d src -language python -create typefiles"
73-
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
80+
# Generate Python types for DDS Security builtin logging topic (used by module 04
81+
# SecureLogUtils.py via the same _OR_SRC import path as Types.py)
82+
connextdds_rtiddsgen_run(
83+
IDL_FILE
84+
"${BUILTIN_LOGGING_IDL}"
85+
OUTPUT_DIRECTORY
86+
"${CMAKE_CURRENT_SOURCE_DIR}/src/"
87+
LANG Python
88+
)
89+
90+
add_custom_target(refArchTypesPy ALL
91+
DEPENDS
92+
${Types_PYTHON_SOURCES}
93+
${builtin_logging_type_PYTHON_SOURCES}
7494
)
7595

7696
# Build object library with types

modules/01-operating-room/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Dependencies for building the C++ applications:
6262

6363
Dependencies for the Python GUI applications (*Arm Monitor*, *Patient Monitor*):
6464

65-
- `PyQt5`Qt5 widget toolkit
65+
- `PySide6`PySide6 Qt widget toolkit
6666
- `pyqtgraph` — fast scientific plotting
6767
- `numpy` — numerical arrays
6868

@@ -74,7 +74,7 @@ sudo apt install \
7474
pkg-config \
7575
libgtkmm-3.0-dev \
7676
python3-pip \
77-
python3-pyqt5 \
77+
python3-pyside6 \
7878
python3-numpy \
7979
python3-pyqtgraph
8080
```
@@ -96,7 +96,7 @@ Then install the Python packages:
9696

9797
```bash
9898
pip install \
99-
PyQt5 \
99+
PySide6 \
100100
pyqtgraph \
101101
numpy
102102
```

modules/01-operating-room/src/Arm.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
import time
1616
import threading
1717

18-
from PyQt5.QtWidgets import (
18+
from PySide6.QtWidgets import (
1919
QApplication, QMainWindow, QWidget, QLabel, QFrame,
2020
QHBoxLayout, QVBoxLayout, QSizePolicy, QScrollArea
2121
)
22-
from PyQt5.QtCore import Qt, QTimer, QRectF, QPointF, pyqtSignal, QObject
23-
from PyQt5.QtGui import (
22+
from PySide6.QtCore import Qt, QTimer, QRectF, QPointF, Signal, QObject
23+
from PySide6.QtGui import (
2424
QPainter, QColor, QPen, QBrush, QFont, QConicalGradient, QPainterPath,
2525
QPixmap, QIcon
2626
)
@@ -84,22 +84,22 @@ def set_value(self, v: float):
8484

8585
def paintEvent(self, event):
8686
p = QPainter(self)
87-
p.setRenderHint(QPainter.Antialiasing)
87+
p.setRenderHint(QPainter.RenderHint.Antialiasing)
8888

8989
side = min(self.width(), self.height()) - 10
9090
rect = QRectF((self.width() - side) / 2,
9191
(self.height() - side) / 2,
9292
side, side)
9393

9494
# ── Background track ──────────────────────────────────────
95-
pen_bg = QPen(QColor("#1A2A40"), 8, Qt.SolidLine, Qt.FlatCap)
95+
pen_bg = QPen(QColor("#1A2A40"), 8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.FlatCap)
9696
p.setPen(pen_bg)
9797
p.drawArc(rect, 225 * 16, -270 * 16) # 270° arc, starts at 225°
9898

9999
# ── Foreground arc (value / 360 × 270°) ───────────────────
100100
span = int((self._value / 360.0) * 270 * 16)
101101
grad_color = self.color
102-
pen_fg = QPen(grad_color, 8, Qt.SolidLine, Qt.FlatCap)
102+
pen_fg = QPen(grad_color, 8, Qt.PenStyle.SolidLine, Qt.PenCapStyle.FlatCap)
103103
p.setPen(pen_fg)
104104
p.drawArc(rect, 225 * 16, -span)
105105

@@ -117,16 +117,16 @@ def paintEvent(self, event):
117117
back_x = cx - hub_r * _math.cos(angle_rad)
118118
back_y = cy + hub_r * _math.sin(angle_rad)
119119
# Draw shadow
120-
pen_shadow = QPen(QColor("#000000"), 4, Qt.SolidLine, Qt.RoundCap)
120+
pen_shadow = QPen(QColor("#000000"), 4, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)
121121
p.setPen(pen_shadow)
122122
p.drawLine(QPointF(back_x + 1, back_y + 1), QPointF(tip_x + 1, tip_y + 1))
123123
# Draw needle
124-
pen_needle = QPen(grad_color, 2.5, Qt.SolidLine, Qt.RoundCap)
124+
pen_needle = QPen(grad_color, 2.5, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)
125125
p.setPen(pen_needle)
126126
p.drawLine(QPointF(back_x, back_y), QPointF(tip_x, tip_y))
127127

128128
# ── Centre dot ────────────────────────────────────────────
129-
p.setPen(Qt.NoPen)
129+
p.setPen(Qt.PenStyle.NoPen)
130130
p.setBrush(QBrush(grad_color))
131131
p.drawEllipse(QPointF(cx, cy), 4, 4)
132132

@@ -168,7 +168,7 @@ def __init__(self, motor: SurgicalRobot.Motors, parent=None):
168168
self.name = JOINT_NAMES[motor]
169169
self._angle = 180.0
170170

171-
self.setFrameShape(QFrame.Box)
171+
self.setFrameShape(QFrame.Shape.Box)
172172
self.setStyleSheet(f"""
173173
JointRow {{
174174
background-color: {BG_PANEL};
@@ -198,9 +198,9 @@ def _build_ui(self):
198198
gauge_col.setSpacing(2)
199199
self.gauge = ArcGauge(self.color)
200200
self.gauge.set_value(180.0)
201-
gauge_col.addWidget(self.gauge, alignment=Qt.AlignHCenter)
201+
gauge_col.addWidget(self.gauge, alignment=Qt.AlignmentFlag.AlignHCenter)
202202
self.angle_lbl = QLabel("180.0°")
203-
self.angle_lbl.setAlignment(Qt.AlignCenter)
203+
self.angle_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
204204
self.angle_lbl.setStyleSheet(
205205
f"color: {self.color}; font-size: 20px; font-weight: bold; "
206206
f"background: transparent;"
@@ -254,7 +254,7 @@ def __init__(self, parent=None):
254254
super().__init__(parent)
255255
self._angles: dict = {m: 180.0 for m in _MOTORS_ORDERED}
256256
self.setMinimumWidth(260)
257-
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
257+
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
258258
self.setStyleSheet(f"background-color: {BG_PANEL};")
259259

260260
def update_angles(self, angles: dict):
@@ -266,14 +266,14 @@ def update_angles(self, angles: dict):
266266
def paintEvent(self, event):
267267
w, h = self.width(), self.height()
268268
p = QPainter(self)
269-
p.setRenderHint(QPainter.Antialiasing)
269+
p.setRenderHint(QPainter.RenderHint.Antialiasing)
270270

271271
# Background
272272
p.fillRect(self.rect(), QColor(BG_PANEL))
273273

274274
# Title
275275
p.setPen(QPen(QColor("#445566")))
276-
p.setFont(QFont("Courier New", 11, QFont.Bold))
276+
p.setFont(QFont("Courier New", 11, QFont.Weight.Bold))
277277
p.drawText(12, 22, "ARM VISUALIZATION")
278278

279279
# Scale segment length so the full arm (5 links) fills ~90 % of the
@@ -319,7 +319,7 @@ def paintEvent(self, event):
319319
color = QColor(JOINT_COLORS[motor])
320320
x0, y0 = points[i]
321321
x1, y1 = points[i + 1]
322-
pen = QPen(color, link_pen_width, Qt.SolidLine, Qt.RoundCap)
322+
pen = QPen(color, link_pen_width, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)
323323
p.setPen(pen)
324324
p.drawLine(QPointF(x0, y0), QPointF(x1, y1))
325325

@@ -351,7 +351,7 @@ def paintEvent(self, event):
351351
# joint name label (abbreviated, 3 chars)
352352
name = JOINT_NAMES[motor][:3]
353353
p.setPen(QPen(color))
354-
p.setFont(QFont("Courier New", 16, QFont.Bold))
354+
p.setFont(QFont("Courier New", 16, QFont.Weight.Bold))
355355
label_x = cx + jr + 8
356356
label_y = cy + 6
357357
p.drawText(QPointF(label_x, label_y), name)
@@ -402,7 +402,7 @@ def _build_ui(self):
402402
if not _logo_px.isNull():
403403
logo_lbl = QLabel()
404404
logo_lbl.setStyleSheet("background: transparent;")
405-
logo_lbl.setPixmap(_logo_px.scaled(56, 56, Qt.KeepAspectRatio, Qt.SmoothTransformation))
405+
logo_lbl.setPixmap(_logo_px.scaled(56, 56, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
406406
h_layout.addWidget(logo_lbl)
407407

408408
rti_lbl = QLabel("RTI Connext")
@@ -593,7 +593,7 @@ def run(self):
593593
self.window.show()
594594
print("Started Arm")
595595

596-
app.exec_()
596+
app.exec()
597597

598598
print("Shutting down Arm")
599599
self.arm_status.status = Common.DeviceStatuses.OFF

0 commit comments

Comments
 (0)