QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MAVLinkSigningKeys.cc
Go to the documentation of this file.
5#include "Vehicle.h"
6
7#include <QtCore/QCryptographicHash>
8#include <QtCore/QSettings>
9
10QGC_LOGGING_CATEGORY(MAVLinkSigningKeysLog, "MAVLink.SigningKeys")
11
12Q_APPLICATION_STATIC(MAVLinkSigningKeys, _mavlinkSigningKeysInstance);
13
14// ── MAVLinkSigningKey ──────────────────────────────────────────────────────
15
16MAVLinkSigningKey::MAVLinkSigningKey(const QString& name, const QByteArray& keyBytes, QObject* parent)
17 : QObject(parent)
18 , _name(name)
19 , _keyBytes(keyBytes)
20{
21}
22
23// ── MAVLinkSigningKeys ─────────────────────────────────────────────────────
24
25MAVLinkSigningKeys* MAVLinkSigningKeys::instance()
26{
27 return _mavlinkSigningKeysInstance();
28}
29
30MAVLinkSigningKeys::MAVLinkSigningKeys(QObject* parent)
31 : QObject(parent)
32 , _keys(new QmlObjectListModel(this))
33{
34 _load();
35
36 auto* mvm = MultiVehicleManager::instance();
37 connect(mvm, &MultiVehicleManager::vehicleAdded, this, &MAVLinkSigningKeys::_connectVehicle);
38 connect(mvm, &MultiVehicleManager::vehicleRemoved, this, &MAVLinkSigningKeys::_disconnectVehicle);
39
40 // Wire any vehicles that already exist (in case singleton was created late)
41 for (int i = 0; i < mvm->vehicles()->count(); ++i) {
42 _connectVehicle(mvm->vehicles()->value<Vehicle*>(i));
43 }
44}
45
46MAVLinkSigningKeys::~MAVLinkSigningKeys()
47{
48}
49
50bool MAVLinkSigningKeys::isKeyInUse(const QString& name) const
51{
52 auto* mvm = MultiVehicleManager::instance();
53 for (int i = 0; i < mvm->vehicles()->count(); ++i) {
54 const auto* vehicle = mvm->vehicles()->value<Vehicle*>(i);
55 if (vehicle && vehicle->mavlinkSigningKeyName() == name) {
56 return true;
57 }
58 }
59 return false;
60}
61
62void MAVLinkSigningKeys::_connectVehicle(Vehicle* vehicle)
63{
65 emit keyUsageChanged();
66}
67
68void MAVLinkSigningKeys::_disconnectVehicle(Vehicle* vehicle)
69{
71 emit keyUsageChanged();
72}
73
74QByteArray MAVLinkSigningKeys::keyBytesAt(int index) const
75{
76 if (index >= 0 && index < _keys->count()) {
77 return _keys->value<MAVLinkSigningKey*>(index)->keyBytes();
78 }
79 return QByteArray();
80}
81
82QString MAVLinkSigningKeys::keyNameAt(int index) const
83{
84 if (index >= 0 && index < _keys->count()) {
85 return _keys->value<MAVLinkSigningKey*>(index)->name();
86 }
87 return QString();
88}
89
90QByteArray MAVLinkSigningKeys::keyBytesByName(const QString& name) const
91{
92 for (int i = 0; i < _keys->count(); ++i) {
93 const auto* key = _keys->value<MAVLinkSigningKey*>(i);
94 if (key->name() == name) {
95 return key->keyBytes();
96 }
97 }
98 return QByteArray();
99}
100
101bool MAVLinkSigningKeys::_keyExists(const QString& name) const
102{
103 for (int i = 0; i < _keys->count(); ++i) {
104 if (_keys->value<MAVLinkSigningKey*>(i)->name() == name) {
105 return true;
106 }
107 }
108 return false;
109}
110
111void MAVLinkSigningKeys::addKey(const QString& name, const QString& passphrase)
112{
113 if (name.isEmpty() || passphrase.isEmpty()) {
114 qCWarning(MAVLinkSigningKeysLog) << "Name and passphrase must not be empty";
115 return;
116 }
117
118 // Check for duplicate name
119 for (int i = 0; i < _keys->count(); ++i) {
120 if (_keys->value<MAVLinkSigningKey*>(i)->name() == name) {
121 qCWarning(MAVLinkSigningKeysLog) << "Key with name already exists:" << name;
122 return;
123 }
124 }
125
126 const QByteArray hash = QCryptographicHash::hash(passphrase.toUtf8(), QCryptographicHash::Sha256);
127
128 auto* key = new MAVLinkSigningKey(name, hash, _keys);
129 _keys->append(key);
130
131 _save();
132 emit keysChanged();
133}
134
135void MAVLinkSigningKeys::removeKey(int index)
136{
137 if (index < 0 || index >= _keys->count()) {
138 return;
139 }
140
141 _keys->removeAt(index)->deleteLater();
142 _save();
143 emit keysChanged();
144}
145
146void MAVLinkSigningKeys::_save()
147{
148 QSettings settings;
149 settings.beginGroup(kSettingsGroup);
150
151 settings.beginWriteArray(kKeysArrayKey);
152 for (int i = 0; i < _keys->count(); ++i) {
153 settings.setArrayIndex(i);
154 const auto* key = _keys->value<MAVLinkSigningKey*>(i);
155 settings.setValue(kNameKey, key->name());
156 settings.setValue(kKeyBytesKey, key->keyBytes().toHex());
157 }
158 settings.endArray();
159
160 settings.endGroup();
161}
162
163void MAVLinkSigningKeys::_load()
164{
165 _keys->clearAndDeleteContents();
166
167 bool migrated = false;
168 QSettings settings;
169
170 // Load existing named keys first so _keyExists() sees them during migration.
171 settings.beginGroup(kSettingsGroup);
172
173 const int count = settings.beginReadArray(kKeysArrayKey);
174 for (int i = 0; i < count; ++i) {
175 settings.setArrayIndex(i);
176 const QString name = settings.value(kNameKey).toString();
177 const QByteArray keyBytes = QByteArray::fromHex(settings.value(kKeyBytesKey).toByteArray());
178 if (!name.isEmpty() && keyBytes.size() == 32) {
179 _keys->append(new MAVLinkSigningKey(name, keyBytes, _keys));
180 }
181 }
182 settings.endArray();
183
184 settings.endGroup();
185
186 // Migrate old single signing key from the pre-named-keys Fact system.
187 // The old "mavlink2SigningKey" was stored as a raw passphrase at the QSettings root level.
188 if (settings.contains(kOldSigningKeySettingsKey)) {
189 const QString oldPassphrase = settings.value(kOldSigningKeySettingsKey).toString();
190 settings.remove(kOldSigningKeySettingsKey);
191 if (!oldPassphrase.isEmpty()) {
192 QString migratedName = QStringLiteral("Migrated Key");
193 int suffix = 1;
194 while (_keyExists(migratedName)) {
195 migratedName = QStringLiteral("Migrated Key %1").arg(suffix++);
196 }
197 const QByteArray keyBytes = QCryptographicHash::hash(oldPassphrase.toUtf8(), QCryptographicHash::Sha256);
198 _keys->append(new MAVLinkSigningKey(migratedName, keyBytes, _keys));
199 migrated = true;
200 qCDebug(MAVLinkSigningKeysLog) << "Migrated legacy signing key to named key system";
201 }
202 settings.sync();
203 }
204
205 if (migrated) {
206 _save();
207 }
208}
Q_APPLICATION_STATIC(MAVLinkSigningKeys, _mavlinkSigningKeysInstance)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
A single named signing key entry.
void vehicleAdded(Vehicle *vehicle)
void vehicleRemoved(Vehicle *vehicle)
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
T value(int index) const
QObject * removeAt(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
void mavlinkSigningChanged()