QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
VehicleSigningController.cc
Go to the documentation of this file.
2
3#include <QtCore/QMetaEnum>
4
5#include "MAVLinkProtocol.h"
6#include "MAVLinkSigning.h"
8#include "QGCApplication.h"
10#include "SecureMemory.h"
11#include "SigningController.h"
12#include "Vehicle.h"
13#include "VehicleLinkManager.h"
14
15QGC_LOGGING_CATEGORY(VehicleSigningControllerLog, "Vehicle.SigningController")
16
17VehicleSigningController::VehicleSigningController(Vehicle* vehicle) : QObject(vehicle), _vehicle(vehicle)
18{
19 qCDebug(VehicleSigningControllerLog) << "VehicleSigningController ctor — vehicle" << _vehicle->id();
20
21 _retryTimer.setInterval(kRetransmitIntervalMs);
22 connect(&_retryTimer, &QTimer::timeout, this, &VehicleSigningController::_onRetryTimer);
23
24 connect(vehicle->vehicleLinkManager(), &VehicleLinkManager::primaryLinkChanged, this,
25 &VehicleSigningController::_onPrimaryLinkChanged);
26
27 if (const SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock()) {
28 if (SigningController* ctrl = sharedLink->signing()) {
29 _connect(ctrl);
30 }
31 }
32}
33
35{
36 qCDebug(VehicleSigningControllerLog) << "VehicleSigningController dtor — vehicle" << _vehicle->id();
37}
38
39void VehicleSigningController::_onPrimaryLinkChanged()
40{
41 _disconnect(_active);
42
43 if (const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock()) {
44 if (SigningController* ctrl = sharedLink->signing()) {
45 _connect(ctrl);
46 }
47 }
48}
49
50void VehicleSigningController::_connect(SigningController* ctrl)
51{
52 if (!ctrl || ctrl == _active) {
53 return;
54 }
55 _active = ctrl;
56 // Permanent listeners; signingConfirmed/signingFailed wired per-operation via Qt::SingleShotConnection in
57 // enable/disable.
58 connect(ctrl, &SigningController::stateChanged, this, &VehicleSigningController::_onSigningStateChanged);
59 connect(ctrl, &SigningController::keyAutoDetected, this, &VehicleSigningController::_onSigningStateChanged);
60 connect(ctrl, &SigningController::alertRaised, this, &VehicleSigningController::_onSigningAlertRaised);
61 _refreshStatus();
62}
63
64void VehicleSigningController::_disconnect(SigningController* ctrl)
65{
66 if (!ctrl) {
67 return;
68 }
69 if (ctrl->state() == SigningController::State::Enabling || ctrl->state() == SigningController::State::Disabling) {
70 ctrl->cancelPending();
71 }
72 disconnect(ctrl, nullptr, this, nullptr);
73 if (_active == ctrl) {
74 _active = nullptr;
75 }
76 _refreshStatus();
77}
78
79void VehicleSigningController::_onSigningStateChanged()
80{
81 _refreshStatus();
82}
83
84void VehicleSigningController::_refreshStatus()
85{
86 _signingStatus = _active ? _active->status() : SigningStatus{};
87}
88
89void VehicleSigningController::_onSigningAlertRaised(const QString& detail)
90{
91 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] signing alert:" << detail;
92 qgcApp()->showAppMessage(tr("Vehicle %1: %2").arg(_vehicle->id()).arg(detail));
93}
94
95void VehicleSigningController::_onSigningConfirmed(const QString& confirmedKey)
96{
97 _stopRetransmit();
98 if (auto link = _vehicle->vehicleLinkManager()->primaryLink().lock()) {
100 }
101 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] signing confirmed — key"
102 << (confirmedKey.isEmpty() ? "<disabled>" : confirmedKey);
103}
104
105void VehicleSigningController::_onSigningFailed(const SigningFailure& failure)
106{
107 _stopRetransmit();
108 qCWarning(VehicleSigningControllerLog)
109 << "[veh" << _vehicle->id() << "] signing failed:"
110 << QMetaEnum::fromType<SigningFailure::Reason>().valueToKey(static_cast<int>(failure.reason)) << failure.detail;
111 emit signingFailed(failure);
112 qgcApp()->showAppMessage(failure.detail);
113}
114
115void VehicleSigningController::_startRetransmit()
116{
117 _retryTimer.start();
118}
119
120void VehicleSigningController::_stopRetransmit()
121{
122 _retryTimer.stop();
123 QGC::secureZero(_pendingKey);
124 _pendingHasKey = false;
125}
126
127void VehicleSigningController::_onRetryTimer()
128{
129 if (!_active || _active->state() == SigningController::State::On ||
130 _active->state() == SigningController::State::Off) {
131 _stopRetransmit();
132 return;
133 }
134 const SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
135 if (!sharedLink) {
136 return;
137 }
138 const QByteArrayView keyView = _pendingHasKey ? QByteArrayView(reinterpret_cast<const char*>(_pendingKey.data()),
139 static_cast<qsizetype>(_pendingKey.size()))
140 : QByteArrayView{};
141 (void)_sendSetupSigning(sharedLink, keyView);
142 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] retransmitted SETUP_SIGNING ("
143 << (_pendingHasKey ? "enable" : "disable") << ")";
144}
145
146SharedLinkInterfacePtr VehicleSigningController::_activeLink(const char* op) const
147{
148 if (!_active) {
149 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "]" << op << ": no signing controller";
150 return {};
151 }
152 SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
153 if (!sharedLink) {
154 qCWarning(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "]" << op << ": no primary link";
155 }
156 return sharedLink;
157}
158
159void VehicleSigningController::_wireConfirmHandlers()
160{
161 connect(_active, &SigningController::signingConfirmed, this, &VehicleSigningController::_onSigningConfirmed,
162 Qt::SingleShotConnection);
163 connect(_active, &SigningController::signingFailed, this, &VehicleSigningController::_onSigningFailed,
164 Qt::SingleShotConnection);
165}
166
167bool VehicleSigningController::_sendAndStartRetransmit(const SharedLinkInterfacePtr& sharedLink, QByteArrayView keyView)
168{
169 if (!_sendSetupSigning(sharedLink, keyView)) {
170 _active->cancelPending(tr("Failed to transmit SETUP_SIGNING to vehicle"));
171 return false;
172 }
173
174 _startRetransmit();
175 return true;
176}
177
178bool VehicleSigningController::_sendSetupSigning(const SharedLinkInterfacePtr& sharedLink, QByteArrayView keyView)
179{
180 const auto channel = static_cast<mavlink_channel_t>(sharedLink->mavlinkChannel());
181 const mavlink_system_t targetSystem{static_cast<uint8_t>(_vehicle->id()),
182 static_cast<uint8_t>(_vehicle->defaultComponentId())};
183
186 MAVLinkProtocol::getComponentId(), targetSystem, keyView, msg)) {
187 qCWarning(VehicleSigningControllerLog)
188 << "[veh" << _vehicle->id() << "] failed to encode SETUP_SIGNING (channel" << channel << ")";
189 return false;
190 }
191
192 // Send twice for lossy-link redundancy; enable goes unsigned (signing not yet installed), disable goes signed.
193 for (uint8_t i = 0; i < 2; ++i) {
194 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
195 }
196 return true;
197}
198
199void VehicleSigningController::enable(const QString& keyName)
200{
201 const SharedLinkInterfacePtr sharedLink = _activeLink("enable");
202 if (!sharedLink) {
203 return;
204 }
205
206 const auto keyBytes = MAVLinkSigningKeys::instance()->keyBytesByName(keyName);
207 if (!keyBytes) {
208 qCCritical(VehicleSigningControllerLog)
209 << "[veh" << _vehicle->id() << "] enable: unknown signing key:" << keyName;
210 return;
211 }
212
213 // Atomic FSM commit BEFORE wiring/transmit — re-entry rejected without putting bytes on the wire.
214 if (auto fail = _active->tryBeginEnable(static_cast<uint8_t>(_vehicle->id()), keyName, *keyBytes)) {
215 qgcApp()->showAppMessage(fail->detail);
216 emit signingFailed(*fail);
217 return;
218 }
219
220 _pendingKey = *keyBytes;
221 _pendingHasKey = true;
222 const QByteArrayView keyView(reinterpret_cast<const char*>(_pendingKey.data()), _pendingKey.size());
223 _wireConfirmHandlers();
224 if (!_sendAndStartRetransmit(sharedLink, keyView)) {
225 QGC::secureZero(_pendingKey);
226 _pendingHasKey = false;
227 return;
228 }
229
230 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id() << "] SETUP_SIGNING sent — key" << keyName
231 << "awaiting signed HEARTBEAT";
232}
233
235{
236 const SharedLinkInterfacePtr sharedLink = _activeLink("disable");
237 if (!sharedLink) {
238 return;
239 }
240
241 if (auto fail = _active->tryBeginDisable(static_cast<uint8_t>(_vehicle->id()))) {
242 qgcApp()->showAppMessage(fail->detail);
243 emit signingFailed(*fail);
244 return;
245 }
246
247 // Wipe any leftover key bytes from a prior enable; disable retransmits send no key.
248 QGC::secureZero(_pendingKey);
249 _pendingHasKey = false;
250
251 _wireConfirmHandlers();
252 if (!_sendAndStartRetransmit(sharedLink, QByteArrayView{})) {
253 return;
254 }
255
256 qCDebug(VehicleSigningControllerLog) << "[veh" << _vehicle->id()
257 << "] disable SETUP_SIGNING sent — awaiting unsigned HEARTBEAT";
258}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
mavlink_channel_t
#define qgcApp()
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static int getComponentId()
void resetSequenceTracking(LinkInterface *link)
Reset sequence tracking so signing transitions don't inflate loss counters.
static MAVLinkProtocol * instance()
int getSystemId() const
static MAVLinkSigningKeys * instance()
std::optional< MAVLinkSigning::SigningKey > keyBytesByName(const QString &name) const
Key bytes for the key with the given name, or nullopt if not found.
Owns MAVLink signing state and the deferred-confirmation state machine for one LinkInterface.
void signingConfirmed(const QString &keyName)
Emitted exactly once per begin*() on success. keyName is the enabled key, or empty for disable.
void cancelPending(const QString &detail={})
void alertRaised(const QString &detail)
void keyAutoDetected(const QString &keyName)
void signingFailed(SigningFailure failure)
Emitted exactly once per begin*() on failure (timeout, init error, cancel, re-entry).
Reason a signing operation failed. Used by SigningController error path and Vehicle::signingFailed.
WeakLinkInterfacePtr primaryLink() const
Per-vehicle signing facade. Owns the wiring between Vehicle and the active SigningController (which l...
Q_INVOKABLE void enable(const QString &keyName)
void signingFailed(SigningFailure info)
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:579
int id() const
Definition Vehicle.h:429
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1390
int defaultComponentId() const
Definition Vehicle.h:682
bool encodeSetupSigning(mavlink_channel_t channel, uint8_t srcSysId, uint8_t srcCompId, mavlink_system_t target_system, QByteArrayView keyBytes, mavlink_message_t &message)
void secureZero(void *data, size_t size)