feat(mavlink): mavlink signing support (#25284)

* Applying PR #17084

* Comitting missing changes

* Adding incoming SETUP_SIGNING handling

* Adding proper message decoding for SETUP_SIGNING

* Adding persistance of sign key in chunks of 32 bits into parameters

* Allowing SETUP_SIGNING to be handled only on usb_uart

* Removing unused type and variable

* Changing the default for Mavlink Timestamp

* Fixing styling

* Merging

* Merging submodules

* Replacing parameters with sdcard storage for secured key and ts

* Fixing styles

* Isolating signing related items in separate class

* Adding new files

* Syncing with main

* Fixing styles

* Changing the signing logic to work only if key and ts properly initialized, adding store the ts on stop

* Updated submodules to latest versions

* Updated gz to proper version

* libfc-sensor-api to proper version

* libcanard to proper version

* Updated fuzztest to proper version

* Updated public_regulated_data_types to proper version

* Updated mip_sdk to proper version

* Updated pydronecan to proper version

* Updated rosidl to proper version

* Fixing styles

* Fixing cyclonedds version

* initializing sign control in the member declaration

* Update src/modules/mavlink/mavlink_main.h

Co-authored-by: Jacob Dahl <37091262+dakejahl@users.noreply.github.com>

* Fixing comments

* Fixing duplicate method

* Fixing defines

* Fixing styles

* Fixing the define errors

* replace duplicate logic with write_key_and_timestamp() function

* add docs

* Update docs/en/mavlink/message_signing.md

Co-authored-by: Julian Oes <julian@oes.ch>

* Update src/modules/mavlink/mavlink_sign_control.cpp

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

* Update src/modules/mavlink/mavlink_sign_control.h

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

* Update docs/en/mavlink/message_signing.md

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

* rename to MAV_SIGN_CFG, fix copyright dates, fix docs SHA type, rename secrets file

* fix newlines

---------

Co-authored-by: Jacob Dahl <37091262+dakejahl@users.noreply.github.com>
Co-authored-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Co-authored-by: Julian Oes <julian@oes.ch>
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
This commit is contained in:
yulianoifa-mobius
2026-03-09 22:47:03 +02:00
committed by GitHub
parent a32b43af0a
commit 358574f9f6
10 changed files with 483 additions and 3 deletions
+1
View File
@@ -120,6 +120,7 @@ px4_add_module(
mavlink_stream.cpp
mavlink_timesync.cpp
mavlink_ulog.cpp
mavlink_sign_control.cpp
MavlinkStatustextHandler.cpp
open_drone_id_translations.cpp
tune_publisher.cpp
+34 -1
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2012-2023 PX4 Development Team. All rights reserved.
* Copyright (c) 2012-2026 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -95,6 +95,17 @@ void mavlink_end_uart_send(mavlink_channel_t chan, int length) { mavlink_module_
mavlink_status_t *mavlink_get_channel_status(uint8_t channel) { return mavlink_module_instances[channel]->get_status(); }
mavlink_message_t *mavlink_get_channel_buffer(uint8_t channel) { return mavlink_module_instances[channel]->get_buffer(); }
static bool accept_unsigned_callback(const mavlink_status_t *status, uint32_t message_id)
{
Mavlink *m = Mavlink::get_instance_for_status(status);
if (m != nullptr) {
return m -> accept_unsigned(m->sign_mode(), m -> is_usb_uart(), message_id);
}
return false;
}
static void usage();
hrt_abstime Mavlink::_first_start_time = {0};
@@ -314,6 +325,20 @@ Mavlink::get_instance_for_device(const char *device_name)
return nullptr;
}
Mavlink *
Mavlink::get_instance_for_status(const mavlink_status_t *status)
{
LockGuard lg{mavlink_module_mutex};
for (Mavlink *inst : mavlink_module_instances) {
if (status == mavlink_get_channel_status(inst->get_instance_id())) {
return inst;
}
}
return nullptr;
}
#ifdef MAVLINK_UDP
Mavlink *
Mavlink::get_instance_for_network_port(unsigned long port)
@@ -1029,6 +1054,12 @@ Mavlink::handle_message(const mavlink_message_t *msg)
* NOTE: this is called from the receiver thread
*/
if (is_usb_uart()) {
if (_sign_control.check_for_signing(msg)) {
return;
}
}
if (get_forwarding_on()) {
/* forward any messages to other mavlink instances */
Mavlink::forward_message(msg, this);
@@ -1930,6 +1961,8 @@ Mavlink::task_main(int argc, char *argv[])
}
}
_sign_control.start(_instance_id, get_status(), &accept_unsigned_callback);
int ch;
_baudrate = 57600;
_datarate = 0;
+12 -2
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2012-2023 PX4 Development Team. All rights reserved.
* Copyright (c) 2012-2026 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -83,6 +83,7 @@
#include "mavlink_events.h"
#include "mavlink_messages.h"
#include "mavlink_receiver.h"
#include "mavlink_sign_control.h"
#include "mavlink_shell.h"
#include "mavlink_ulog.h"
@@ -120,6 +121,7 @@ public:
{
_task_should_exit.store(true);
_receiver.request_stop();
_sign_control.write_key_and_timestamp();
}
void display_status();
@@ -134,6 +136,7 @@ public:
mavlink_message_t *get_buffer() { return &_mavlink_buffer; }
mavlink_status_t *get_status() { return &_mavlink_status; }
static Mavlink *get_instance_for_status(const mavlink_status_t *status);
void setProtocolVersion(uint8_t version);
uint8_t getProtocolVersion() const { return _protocol_version; };
@@ -142,8 +145,8 @@ public:
static int get_status_all_instances(bool show_streams_status);
static bool serial_instance_exists(const char *device_name, Mavlink *self);
static bool component_was_seen(int system_id, int component_id, Mavlink &self);
static void forward_message(const mavlink_message_t *msg, Mavlink *self);
static bool component_was_seen(int system_id, int component_id, Mavlink &self);
bool check_events() const { return _should_check_events.load(); }
void check_events_enable() { _should_check_events.store(true); }
@@ -467,6 +470,7 @@ public:
bool ftp_enabled() const { return _ftp_on; }
bool hash_check_enabled() const { return _param_mav_hash_chk_en.get(); }
int32_t sign_mode() const { return _param_mav_sign_cfg.get(); }
bool forward_heartbeats_enabled() const { return _param_mav_hb_forw_en.get(); }
bool failure_injection_enabled() const { return _param_sys_failure_injection_enabled.get(); }
@@ -490,9 +494,14 @@ public:
bool radio_status_critical() const { return _radio_status_critical; }
bool accept_unsigned(int32_t sign_mode, bool is_usb_uart, uint32_t message_id) { return _sign_control.accept_unsigned(sign_mode, is_usb_uart, message_id); }
private:
MavlinkReceiver _receiver;
MavlinkSignControl _sign_control{};
int _instance_id{-1};
int _task_id{-1};
@@ -632,6 +641,7 @@ private:
(ParamBool<px4::params::MAV_USEHILGPS>) _param_mav_usehilgps,
(ParamBool<px4::params::MAV_FWDEXTSP>) _param_mav_fwdextsp,
(ParamBool<px4::params::MAV_HASH_CHK_EN>) _param_mav_hash_chk_en,
(ParamInt<px4::params::MAV_SIGN_CFG>) _param_mav_sign_cfg,
(ParamBool<px4::params::MAV_HB_FORW_EN>) _param_mav_hb_forw_en,
(ParamInt<px4::params::MAV_RADIO_TOUT>) _param_mav_radio_timeout,
(ParamInt<px4::params::SYS_HITL>) _param_sys_hitl,
+9
View File
@@ -49,6 +49,15 @@ PARAM_DEFINE_INT32(MAV_SYS_ID, 1);
*/
PARAM_DEFINE_INT32(MAV_COMP_ID, 1);
/**
* MAVLink protocol signing
* @group MAVLink
* @value 0 Message signing disabled
* @value 1 Signing enabled except on USB
* @value 2 Signing always enabled
*/
PARAM_DEFINE_INT32(MAV_SIGN_CFG, 0);
/**
* MAVLink protocol version
* @group MAVLink
@@ -0,0 +1,199 @@
/****************************************************************************
*
* Copyright (c) 2026 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file mavlink_sign_control.cpp
* Mavlink messages signing control helpers implementation.
*
* @author Yulian oifa <yulian.oifa@mobius-software.com>
*/
#include "mavlink_sign_control.h"
#include <sys/stat.h>
static mavlink_signing_streams_t global_mavlink_signing_streams = {};
static const uint32_t unsigned_messages[] = {
MAVLINK_MSG_ID_RADIO_STATUS,
MAVLINK_MSG_ID_ADSB_VEHICLE,
MAVLINK_MSG_ID_COLLISION
};
MavlinkSignControl::MavlinkSignControl()
{
}
MavlinkSignControl::~MavlinkSignControl()
{
}
void MavlinkSignControl::start(int _instance_id, mavlink_status_t *_mavlink_status,
mavlink_accept_unsigned_t accept_unsigned_callback)
{
_mavlink_signing.link_id = _instance_id;
_mavlink_signing.flags = MAVLINK_SIGNING_FLAG_SIGN_OUTGOING;
_mavlink_signing.accept_unsigned_callback = accept_unsigned_callback;
_is_signing_initialized = false;
int mkdir_ret = mkdir(MAVLINK_FOLDER_PATH, S_IRWXU);
if (mkdir_ret != 0 && errno != EEXIST) {
PX4_ERR("failed creating module storage dir: %s (%i)", MAVLINK_FOLDER_PATH, errno);
} else {
int _fd = ::open(MAVLINK_SECRET_FILE, O_CREAT | O_RDONLY, PX4_O_MODE_600);
if (_fd == -1) {
if (errno != ENOENT) {
PX4_ERR("failed creating mavlink secret key file: %s (%i)", MAVLINK_SECRET_FILE, errno);
}
} else {
//if we dont have enough bytes we simply ignore it , because it may be not set yet
ssize_t bytes_read = ::read(_fd, _mavlink_signing.secret_key, MAVLINK_SECRET_KEY_LENGTH);
if (bytes_read == MAVLINK_SECRET_KEY_LENGTH) {
bytes_read = ::read(_fd, &_mavlink_signing.timestamp, MAVLINK_SECRET_KEY_TIMESTAMP_LENGTH);
if (bytes_read == MAVLINK_SECRET_KEY_TIMESTAMP_LENGTH) {
if (_mavlink_signing.timestamp != 0 || !is_array_all_zeros(_mavlink_signing.secret_key, MAVLINK_SECRET_KEY_LENGTH)) {
_is_signing_initialized = true;
}
}
}
close(_fd);
}
}
//lets reset it to nulls if it was not read properly
if (!_is_signing_initialized) {
for (size_t i = 0; i < MAVLINK_SECRET_KEY_LENGTH; ++i) {
_mavlink_signing.secret_key[i] = 0;
}
_mavlink_signing.timestamp = 0;
}
// copy pointer of the signing to status struct
_mavlink_status -> signing = &_mavlink_signing;
_mavlink_status -> signing_streams = &global_mavlink_signing_streams;
}
bool MavlinkSignControl::check_for_signing(const mavlink_message_t *msg)
{
if (msg->msgid != MAVLINK_MSG_ID_SETUP_SIGNING) {
return false;
}
mavlink_setup_signing_t setup_signing;
mavlink_msg_setup_signing_decode(msg, &setup_signing);
//setup signing provides new key , lets update it
//we update it only in case everything was stored properly
memcpy(_mavlink_signing.secret_key, setup_signing.secret_key, MAVLINK_SECRET_KEY_LENGTH);
_mavlink_signing.timestamp = setup_signing.initial_timestamp;
if (setup_signing.initial_timestamp != 0 || !is_array_all_zeros(setup_signing.secret_key, MAVLINK_SECRET_KEY_LENGTH)) {
_is_signing_initialized = true;
} else {
_is_signing_initialized = false;
}
write_key_and_timestamp();
return true;
}
void MavlinkSignControl::write_key_and_timestamp()
{
int _fd = ::open(MAVLINK_SECRET_FILE, O_CREAT | O_WRONLY | O_TRUNC, PX4_O_MODE_600);
if (_fd == -1) {
if (errno != ENOENT) {
PX4_ERR("failed opening mavlink secret key file for writing: %s (%i)", MAVLINK_SECRET_FILE, errno);
}
} else {
ssize_t bytes_write = ::write(_fd, _mavlink_signing.secret_key, MAVLINK_SECRET_KEY_LENGTH);
if (bytes_write == MAVLINK_SECRET_KEY_LENGTH) {
bytes_write = ::write(_fd, &_mavlink_signing.timestamp, MAVLINK_SECRET_KEY_TIMESTAMP_LENGTH);
}
close(_fd);
}
}
bool MavlinkSignControl::accept_unsigned(int32_t sign_mode, bool is_usb_uart, uint32_t message_id)
{
// if signing is not initilized properly or has all zeroes we will allow any message
if (!_is_signing_initialized) {
return true;
}
// Always accept a few select messages even if unsigned
for (unsigned i = 0; i < sizeof(unsigned_messages) / sizeof(unsigned_messages[0]); i++) {
if (unsigned_messages[i] == message_id) {
return true;
}
}
switch (sign_mode) {
// If signing is not required always return true
case MavlinkSignControl::PROTO_SIGN_OPTIONAL:
return true;
// Accept USB links if enabled
case MavlinkSignControl::PROTO_SIGN_NON_USB:
return is_usb_uart;
case MavlinkSignControl::PROTO_SIGN_ALWAYS:
// fallthrough
default:
return false;
}
}
bool MavlinkSignControl::is_array_all_zeros(uint8_t arr[], size_t size)
{
for (size_t i = 0; i < size; ++i) {
if (arr[i] != 0) {
return false;
}
}
return true;
}
@@ -0,0 +1,99 @@
/****************************************************************************
*
* Copyright (c) 2026 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file mavlink_sign_control.h
* Mavlink messages signing control helpers.
*
* @author Yulian oifa <yulian.oifa@mobius-software.com>
*/
#ifndef MAVLINK_SIGN_CONTROL_H_
#define MAVLINK_SIGN_CONTROL_H_
#define MAVLINK_SD_ROOT_PATH CONFIG_BOARD_ROOT_PATH "/"
#define MAVLINK_FOLDER_PATH MAVLINK_SD_ROOT_PATH"/mavlink"
#define MAVLINK_SECRET_FILE MAVLINK_FOLDER_PATH"/mavlink-signing-key.bin"
#define MAVLINK_SECRET_KEY_TIMESTAMP_LENGTH 8 ///< size of timestamp in bytes
#define MAVLINK_SECRET_KEY_LENGTH 32 ///< size of key in bytes
#include "mavlink_receiver.h"
class Mavlink;
class MavlinkSignControl
{
public:
MavlinkSignControl();
~MavlinkSignControl();
enum PROTO_SIGN {
PROTO_SIGN_OPTIONAL = 0,
PROTO_SIGN_NON_USB,
PROTO_SIGN_ALWAYS
};
/**
* Initialize signing and read configuration from file
*/
void start(int _instance_id, mavlink_status_t *_mavlink_status, mavlink_accept_unsigned_t accept_unsigned_callback);
/**
* Checks whether the message is SETUP_SIGNING, and if yes , updates local key
*/
bool check_for_signing(const mavlink_message_t *msg);
/**
* stores the key and timestamp from memory to file
*/
void write_key_and_timestamp();
/**
* Checks whether should accept unsigned message for specific sign mode
*/
bool accept_unsigned(int32_t sign_mode, bool is_usb_uart, uint32_t message_id);
static bool is_array_all_zeros(uint8_t arr[], size_t size);
private:
mavlink_signing_t _mavlink_signing {};
/**
* Checks whether the key has been initialized
*/
bool _is_signing_initialized;
};
#endif /* MAVLINK_SIGN_CONTROL_H_ */