QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
SigningKeyManager.qml
Go to the documentation of this file.
1pragma ComponentBehavior: Bound
2
3import QtQuick
4import QtQuick.Controls
5import QtQuick.Layouts
6
7import QGroundControl
8import QGroundControl.Controls
9
10SettingsGroupLayout {
11 id: _signingKeyManager
12
13 Layout.fillWidth: true
14 heading: qsTr("MAVLink 2 Signing")
15 headingDescription: qsTr("Signing keys should only be sent to the vehicle over secure links (e.g. USB).")
16
17 property Vehicle _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
18
19 // Wipe clipboard 30s after Export so a casually-copied key doesn't linger across paste targets.
20 Timer {
21 id: clipboardWipeTimer
22 interval: 30000
23 repeat: false
24 onTriggered: QGroundControl.copyToClipboard("")
25 }
26
27 QGCPopupDialogFactory {
28 id: addKeyDialogFactory
29 dialogComponent: addKeyDialogComponent
30 }
31
32 Component {
33 id: addKeyDialogComponent
34
35 QGCPopupDialog {
36 id: addKeyDialog
37 title: qsTr("Add Signing Key")
38 buttons: Dialog.Ok | Dialog.Cancel
39 acceptButtonEnabled: keyNameField.text !== "" &&
40 (addKeyDialog.useRawKey
41 ? rawKeyField.text.length === 64
42 : passphraseField.text.length >= addKeyDialog._minPassphraseLength)
43
44 property bool useRawKey: false
45 readonly property int _minPassphraseLength: 8
46
47 onAccepted: {
48 let ok = false
49 if (addKeyDialog.useRawKey) {
50 ok = QGroundControl.mavlinkSigningKeys.addRawKey(keyNameField.text, rawKeyField.text)
51 } else {
52 ok = QGroundControl.mavlinkSigningKeys.addKey(keyNameField.text, passphraseField.text)
53 }
54 if (!ok) {
55 addKeyDialog.preventClose = true
56 errorLabel.text = qsTr("Could not add key. Name may already exist or input is invalid.")
57 }
58 }
59
60 ColumnLayout {
61 spacing: ScreenTools.defaultFontPixelHeight / 2
62
63 QGCLabel { text: qsTr("Key Name") }
64 QGCTextField {
65 id: keyNameField
66 Layout.fillWidth: true
67 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 30
68 placeholderText: qsTr("Enter a friendly name")
69 }
70
71 RowLayout {
72 spacing: ScreenTools.defaultFontPixelWidth
73
74 QGCRadioButton {
75 text: qsTr("Passphrase")
76 checked: !addKeyDialog.useRawKey
77 onClicked: addKeyDialog.useRawKey = false
78 }
79 QGCRadioButton {
80 text: qsTr("Raw Key (hex)")
81 checked: addKeyDialog.useRawKey
82 onClicked: addKeyDialog.useRawKey = true
83 }
84 }
85
86 QGCTextField {
87 id: passphraseField
88 visible: !addKeyDialog.useRawKey
89 Layout.fillWidth: true
90 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 30
91 placeholderText: qsTr("Enter passphrase (min %1 chars)").arg(addKeyDialog._minPassphraseLength)
92 echoMode: TextInput.Password
93 inputMethodHints: Qt.ImhNoPredictiveText
94 }
95
96 QGCLabel {
97 visible: !addKeyDialog.useRawKey && passphraseField.text.length > 0 && passphraseField.text.length < addKeyDialog._minPassphraseLength
98 text: qsTr("Passphrase too short (%1/%2)").arg(passphraseField.text.length).arg(addKeyDialog._minPassphraseLength)
99 color: QGroundControl.globalPalette.warningText
100 font.pointSize: ScreenTools.smallFontPointSize
101 }
102
103 RowLayout {
104 visible: addKeyDialog.useRawKey
105 spacing: ScreenTools.defaultFontPixelWidth / 2
106
107 QGCTextField {
108 id: rawKeyField
109 Layout.fillWidth: true
110 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 30
111 placeholderText: qsTr("64 hex characters")
112 maximumLength: 64
113 inputMethodHints: Qt.ImhNoPredictiveText
114 validator: RegularExpressionValidator { regularExpression: /[0-9a-fA-F]*/ }
115 }
116
117 QGCButton {
118 text: qsTr("Generate")
119 onClicked: rawKeyField.text = QGroundControl.mavlinkSigningKeys.generateRandomHexKey()
120 }
121 }
122
123 QGCLabel {
124 visible: addKeyDialog.useRawKey && rawKeyField.text.length > 0 && rawKeyField.text.length !== 64
125 text: qsTr("%1/64 hex characters").arg(rawKeyField.text.length)
126 color: QGroundControl.globalPalette.warningText
127 font.pointSize: ScreenTools.smallFontPointSize
128 }
129
130 QGCLabel {
131 id: errorLabel
132 visible: text !== ""
133 color: QGroundControl.globalPalette.warningText
134 font.pointSize: ScreenTools.smallFontPointSize
135 Layout.fillWidth: true
136 wrapMode: Text.WordWrap
137 }
138 }
139 }
140 }
141
142 LabelledLabel {
143 Layout.fillWidth: true
144 label: qsTr("Active Key")
145 labelText: _signingKeyManager._activeVehicle && _signingKeyManager._activeVehicle.signingController.signingStatus.keyName !== ""
146 ? _signingKeyManager._activeVehicle.signingController.signingStatus.keyName
147 : qsTr("None")
148 visible: _signingKeyManager._activeVehicle
149 }
150
151 Repeater {
152 model: QGroundControl.mavlinkSigningKeys.keys
153
154 RowLayout {
155 id: keyDelegate
156 Layout.fillWidth: true
157 spacing: ScreenTools.defaultFontPixelWidth
158
159 required property var object
160 required property int index
161
162 property string _keyName: keyDelegate.object.name
163 property bool _keyIsActive: { void(QGroundControl.mavlinkSigningKeys.keyUsageRevision); return QGroundControl.mavlinkSigningKeys.isKeyInUse(keyDelegate._keyName) }
164 property bool _keyIsActiveVehicle: _signingKeyManager._activeVehicle && _signingKeyManager._activeVehicle.signingController.signingStatus.keyName === keyDelegate._keyName
165 property bool _anyKeyActive: _signingKeyManager._activeVehicle && _signingKeyManager._activeVehicle.signingController.signingStatus.keyName !== ""
166 property bool _otherKeyActive: keyDelegate._anyKeyActive && !keyDelegate._keyIsActiveVehicle
167 property bool _signingPending: _signingKeyManager._activeVehicle && _signingKeyManager._activeVehicle.signingController.signingStatus.pending
168
169 QGCLabel {
170 text: keyDelegate._keyName
171 Layout.fillWidth: true
172 }
173
174 // Quiet hint when a different key on this vehicle is the active one — explains why
175 // Enable/Disable are both hidden on this row.
176 QGCLabel {
177 text: qsTr("(another key active)")
178 visible: keyDelegate._otherKeyActive
179 font.pointSize: ScreenTools.smallFontPointSize
180 opacity: 0.7
181 }
182
183 QGCButton {
184 text: keyDelegate._signingPending ? qsTr("Configuring…") : qsTr("Enable")
185 visible: !keyDelegate._anyKeyActive
186 enabled: _signingKeyManager._activeVehicle && !keyDelegate._signingPending
187 onClicked: {
188 if (!_signingKeyManager._activeVehicle) {
189 return
190 }
191 const linkName = _signingKeyManager._activeVehicle.vehicleLinkManager
192 ? _signingKeyManager._activeVehicle.vehicleLinkManager.primaryLinkName
193 : qsTr("active link")
194 QGroundControl.showMessageDialog(
195 _signingKeyManager,
196 qsTr("Send Signing Key"),
197 qsTr("This will transmit key '%1' to the vehicle over '%2'. Only proceed if this link is secure (USB or trusted local network).").arg(keyDelegate._keyName).arg(linkName),
198 Dialog.Ok | Dialog.Cancel,
199 function () {
200 if (_signingKeyManager._activeVehicle) {
201 _signingKeyManager._activeVehicle.signingController.enable(keyDelegate._keyName)
202 }
203 })
204 }
205 }
206
207 QGCButton {
208 text: keyDelegate._signingPending ? qsTr("Disabling…") : qsTr("Disable")
209 visible: keyDelegate._keyIsActiveVehicle
210 enabled: _signingKeyManager._activeVehicle && !keyDelegate._signingPending
211 onClicked: {
212 if (!_signingKeyManager._activeVehicle) {
213 return
214 }
215 // ArduPilot refuses SETUP_SIGNING while armed; disabling a flying vehicle leaves GCS unsigned vs. signed-required.
216 if (_signingKeyManager._activeVehicle.armed) {
217 QGroundControl.showMessageDialog(
218 _signingKeyManager,
219 qsTr("Disable Signing While Armed?"),
220 qsTr("Vehicle is armed. ArduPilot will refuse to disable signing while armed and PX4 will not accept the disable packet without a valid signature. The disable attempt will likely time out and leave the link in an inconsistent state.\n\nDisarm the vehicle first."),
221 Dialog.Cancel)
222 return
223 }
224 _signingKeyManager._activeVehicle.signingController.disable()
225 }
226 }
227
228 QGCButton {
229 text: qsTr("Export")
230 visible: !keyDelegate._keyIsActive
231 onClicked: {
232 let hex = QGroundControl.mavlinkSigningKeys.keyHexByName(keyDelegate._keyName)
233 if (hex !== "") {
234 QGroundControl.copyToClipboard(hex)
235 clipboardWipeTimer.restart()
236 QGroundControl.showMessageDialog(
237 _signingKeyManager,
238 qsTr("Export Key: %1").arg(keyDelegate._keyName),
239 qsTr("Key copied to clipboard. Store it securely — it will be cleared from the clipboard in 30 seconds."),
240 Dialog.Ok)
241 }
242 }
243 }
244
245 QGCButton {
246 text: qsTr("Delete")
247 visible: !keyDelegate._keyIsActive
248 onClicked: QGroundControl.showMessageDialog(
249 _signingKeyManager,
250 qsTr("Delete Signing Key"),
251 qsTr("Are you sure you want to delete '%1'?\n\nIf a vehicle still has this key configured, you will no longer be able to communicate with it over a signed connection. Raw or generated keys cannot be recovered — Export the hex first if you may need it later.").arg(keyDelegate._keyName),
252 Dialog.Ok | Dialog.Cancel,
253 function () { QGroundControl.mavlinkSigningKeys.removeKey(keyDelegate._keyName) })
254 }
255 }
256 }
257
258 QGCLabel {
259 text: qsTr("No keys configured")
260 visible: QGroundControl.mavlinkSigningKeys.keys.count === 0
261 }
262
263 QGCButton {
264 text: qsTr("Add Key")
265 onClicked: addKeyDialogFactory.open()
266 }
267}