LCOV - code coverage report
Current view: top level - GCS_MAVLink - GCS_Signing.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 38 98 38.8 %
Date: 2026-09-09 11:02:08 Functions: 6 9 66.7 %

          Line data    Source code
       1             : /*
       2             :   Code for handling MAVLink2 signing
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "GCS_config.h"
      19             : 
      20             : #if HAL_GCS_ENABLED
      21             : 
      22             : #include "GCS.h"
      23             : 
      24             : #if AP_MAVLINK_SIGNING_ENABLED
      25             : 
      26             : extern const AP_HAL::HAL& hal;
      27             : 
      28             : // storage object
      29             : StorageAccess GCS_MAVLINK::_signing_storage(StorageManager::StorageKeys);
      30             : 
      31             : // magic for versioning of the structure
      32             : #define SIGNING_KEY_MAGIC 0x3852fcd1
      33             : 
      34             : // structure stored in FRAM
      35             : struct SigningKey {
      36             :     uint32_t magic;
      37             :     uint64_t timestamp;
      38             :     uint8_t secret_key[32];
      39             : };
      40             : 
      41             : // shared signing_streams structure
      42             : mavlink_signing_streams_t GCS_MAVLINK::signing_streams;
      43             : 
      44             : // last time we saved the timestamp
      45             : uint32_t GCS_MAVLINK::last_signing_save_ms;
      46             : 
      47           0 : bool GCS_MAVLINK::signing_key_save(const struct SigningKey &key)
      48             : {
      49           0 :     if (_signing_storage.size() < sizeof(key)) {
      50           0 :         return false;
      51             :     }
      52           0 :     return _signing_storage.write_block(0, &key, sizeof(key));
      53             : }
      54             : 
      55     1359871 : bool GCS_MAVLINK::signing_key_load(struct SigningKey &key)
      56             : {
      57     1359871 :     if (_signing_storage.size() < sizeof(key)) {
      58           0 :         return false;
      59             :     }
      60     1359871 :     if (!_signing_storage.read_block(&key, 0, sizeof(key))) {
      61           0 :         return false;
      62             :     }
      63     1359871 :     if (key.magic != SIGNING_KEY_MAGIC) {
      64     1359871 :         return false;
      65             :     }
      66           0 :     return true;
      67             : }
      68             : 
      69             : /*
      70             :   handle a setup_signing message
      71             :  */
      72           0 : void GCS_MAVLINK::handle_setup_signing(const mavlink_message_t &msg) const
      73             : {
      74             :     // setting up signing key when armed generally not useful /
      75             :     // possibly not a good idea
      76           0 :     if (hal.util->get_soft_armed()) {
      77           0 :         send_text(MAV_SEVERITY_WARNING, "ERROR: Won't setup signing when armed");
      78           0 :         return;
      79             :     }
      80             : 
      81             :     // decode
      82             :     mavlink_setup_signing_t packet;
      83           0 :     mavlink_msg_setup_signing_decode(&msg, &packet);
      84             : 
      85           0 :     struct SigningKey key {};
      86           0 :     key.magic = SIGNING_KEY_MAGIC;
      87           0 :     key.timestamp = packet.initial_timestamp;
      88           0 :     memcpy(key.secret_key, packet.secret_key, 32);
      89             : 
      90           0 :     if (!signing_key_save(key)) {
      91           0 :         send_text(MAV_SEVERITY_WARNING, "ERROR: Failed to save signing key");
      92           0 :         return;
      93             :     }
      94             : 
      95             :     // activate it immediately on all links:
      96           0 :     for (uint8_t i=0; i<MAVLINK_COMM_NUM_BUFFERS; i++) {
      97           0 :         GCS_MAVLINK *backend = gcs().chan(i);
      98           0 :         if (backend == nullptr) {
      99           0 :             return;
     100             :         }
     101           0 :         backend->load_signing_key();
     102             :     }
     103             : }
     104             : 
     105             : 
     106             : /*
     107             :   callback to accept unsigned (or incorrectly signed) packets
     108             :  */
     109             : extern "C" {
     110             : 
     111             : static const uint32_t accept_list[] = {
     112             :     MAVLINK_MSG_ID_RADIO_STATUS,
     113             :     MAVLINK_MSG_ID_RADIO
     114             : };
     115             :     
     116           0 : static bool accept_unsigned_callback(const mavlink_status_t *status, uint32_t msgId)
     117             : {
     118           0 :     if (status == mavlink_get_channel_status(MAVLINK_COMM_0)) {
     119             :         // always accept channel 0, assumed to be secure channel. This
     120             :         // is USB on ChibiOS boards
     121           0 :         return true;
     122             :     }
     123           0 :     for (uint8_t i=0; i<ARRAY_SIZE(accept_list); i++) {
     124           0 :         if (accept_list[i] == msgId) {
     125           0 :             return true;
     126             :         }
     127             :     }
     128           0 :     return false;
     129             : }
     130             : }
     131             : 
     132             : /*
     133             :   load signing key
     134             :  */
     135        6331 : void GCS_MAVLINK::load_signing_key(void)
     136             : {
     137             :     struct SigningKey key;
     138        6331 :     if (option_enabled(Option::MAVLINK2_SIGNING_DISABLED) || !signing_key_load(key)) {
     139        6331 :         return;
     140             :     }
     141           0 :     memcpy(signing.secret_key, key.secret_key, 32);
     142           0 :     signing.link_id = (uint8_t)chan;
     143             :     // use a timestamp 1 minute past the last recorded
     144             :     // timestamp. Combined with saving the key once every 30s this
     145             :     // prevents a window for replay attacks
     146           0 :     signing.timestamp = key.timestamp + 60UL * 100UL * 1000UL;
     147           0 :     signing.flags = MAVLINK_SIGNING_FLAG_SIGN_OUTGOING;
     148           0 :     signing.accept_unsigned_callback = accept_unsigned_callback;
     149             : 
     150             :     // if timestamp and key are all zero then we disable signing
     151           0 :     bool all_zero = (key.timestamp == 0);
     152           0 :     for (uint8_t i=0; i<sizeof(key.secret_key); i++) {
     153           0 :         if (signing.secret_key[i] != 0) {
     154           0 :             all_zero = false;
     155           0 :             break;
     156             :         }
     157             :     }
     158           0 :     if (all_zero) {
     159             :         // disable signing
     160           0 :         _channel_status.signing = nullptr;
     161           0 :         _channel_status.signing_streams = nullptr;
     162             :     } else {
     163           0 :         _channel_status.signing = &signing;
     164           0 :         _channel_status.signing_streams = &signing_streams;
     165             :     }
     166             : }
     167             : 
     168             : /*
     169             :   update signing timestamp. This is called when we get GPS lock
     170             :   timestamp_usec is since 1/1/1970 (the epoch)
     171             :  */
     172     1352867 : void GCS_MAVLINK::update_signing_timestamp(uint64_t timestamp_usec)
     173             : {
     174     1352867 :     uint64_t signing_timestamp = (timestamp_usec / (1000*1000ULL));
     175             :     // this is the offset from 1/1/1970 to 1/1/2015
     176     1352867 :     const uint64_t epoch_offset = 1420070400;
     177     1352867 :     if (signing_timestamp > epoch_offset) {
     178     1352867 :         signing_timestamp -= epoch_offset;
     179             :     }
     180             : 
     181             :     // convert to 10usec units
     182     1352867 :     signing_timestamp *= 100 * 1000ULL;
     183             : 
     184             :     // increase signing timestamp on any links that have signing
     185    22998579 :     for (uint8_t i=0; i<MAVLINK_COMM_NUM_BUFFERS; i++) {
     186    21645712 :         mavlink_channel_t chan = (mavlink_channel_t)(MAVLINK_COMM_0 + i);
     187    21645712 :         mavlink_status_t *status = mavlink_get_channel_status(chan);
     188    21645712 :         if (status && status->signing && status->signing->timestamp < signing_timestamp) {
     189           0 :             status->signing->timestamp = signing_timestamp;
     190             :         }
     191             :     }
     192             : 
     193             :     // save to stable storage
     194     1352867 :     save_signing_timestamp(true);
     195     1352867 : }
     196             : 
     197             : 
     198             : /*
     199             :   save the signing timestamp periodically
     200             :  */
     201     1376497 : void GCS_MAVLINK::save_signing_timestamp(bool force_save_now)
     202             : {
     203     1376497 :     uint32_t now = AP_HAL::millis();
     204             :     // we save the timestamp every 30s, unless forced by a GPS update
     205     1376497 :     if (!force_save_now &&  now - last_signing_save_ms < 30*1000UL) {
     206     1376497 :         return;
     207             :     }
     208             : 
     209     1353540 :     last_signing_save_ms = now;
     210             : 
     211             :     struct SigningKey key;
     212     1353540 :     if (!signing_key_load(key)) {
     213     1353540 :         return;
     214             :     }
     215           0 :     bool need_save = false;
     216             : 
     217           0 :     for (uint8_t i=0; i<MAVLINK_COMM_NUM_BUFFERS; i++) {
     218           0 :         mavlink_channel_t chan = (mavlink_channel_t)(MAVLINK_COMM_0 + i);
     219           0 :         const mavlink_status_t *status = mavlink_get_channel_status(chan);
     220           0 :         if (status && status->signing && status->signing->timestamp > key.timestamp) {
     221           0 :             key.timestamp = status->signing->timestamp;
     222           0 :             need_save = true;
     223             :         }
     224             :     }
     225           0 :     if (need_save) {
     226             :         // save updated key
     227           0 :         signing_key_save(key);
     228             :     }
     229             : }
     230             : 
     231             : /*
     232             :   return true if signing is enabled on this channel
     233             :  */
     234      311551 : bool GCS_MAVLINK::signing_enabled(void) const
     235             : {
     236      311551 :     const mavlink_status_t *status = mavlink_get_channel_status(chan);
     237      311551 :     if ((status != nullptr) && status->signing && (status->signing->flags & MAVLINK_SIGNING_FLAG_SIGN_OUTGOING)) {
     238           0 :         return true;
     239             :     }
     240      311551 :     return false;
     241             : }
     242             : #endif  // AP_MAVLINK_SIGNING_ENABLED
     243             : 
     244             : /*
     245             :   return packet overhead in bytes for a channel
     246             :  */
     247   256264032 : uint8_t GCS_MAVLINK::packet_overhead_chan(mavlink_channel_t chan)
     248             : {
     249             :     /*
     250             :       reserve 100 bytes for parameters when a GCS fails to fetch a
     251             :       parameter due to lack of buffer space. The reservation lasts 2
     252             :       seconds
     253             :      */
     254   256264032 :     uint8_t reserved_space = 0;
     255   256264032 :     if (reserve_param_space_start_ms != 0 &&
     256           0 :         AP_HAL::millis() - reserve_param_space_start_ms < 2000) {
     257           0 :         reserved_space = 100;
     258             :     } else {
     259   256264032 :         reserve_param_space_start_ms = 0;
     260             :     }
     261             :     
     262   256264032 :     const mavlink_status_t *status = mavlink_get_channel_status(chan);
     263   256264032 :     if ((status != nullptr) && status->signing && (status->signing->flags & MAVLINK_SIGNING_FLAG_SIGN_OUTGOING)) {
     264           0 :         return MAVLINK_NUM_NON_PAYLOAD_BYTES + MAVLINK_SIGNATURE_BLOCK_LEN + reserved_space;
     265             :     }
     266   256264032 :     return MAVLINK_NUM_NON_PAYLOAD_BYTES + reserved_space;
     267             : }
     268             : 
     269             : #if !AP_MAVLINK_SIGNING_ENABLED
     270             : 
     271             : void GCS_MAVLINK::handle_setup_signing(const mavlink_message_t &msg) const
     272             : {
     273             :     GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Signing is not enabled in this firmware");
     274             : }
     275             : 
     276             : #endif  // !AP_MAVLINK_SIGNING_ENABLED
     277             : 
     278             : #endif  // HAL_GCS_ENABLED

Generated by: LCOV version 1.14