Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_CheckFirmware/AP_CheckFirmware_secure_command.cpp
Views: 1798
/*1support checking board ID and firmware CRC in the bootloader2*/3#include "AP_CheckFirmware.h"4#include <AP_HAL/HAL.h>56#if AP_CHECK_FIRMWARE_ENABLED && AP_SIGNED_FIRMWARE && !defined(HAL_BOOTLOADER_BUILD)78#include "monocypher.h"9#include <AP_Math/AP_Math.h>1011#if HAL_GCS_ENABLED12#include <GCS_MAVLink/GCS.h>13#endif1415extern const AP_HAL::HAL &hal;1617/*18find public keys in bootloader, or return NULL if signature not found1920this assumes the public keys are in the first sector21*/22const struct ap_secure_data *AP_CheckFirmware::find_public_keys(void)23{24#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS25const uint32_t page_size = hal.flash->getpagesize(0);26const uint32_t flash_addr = hal.flash->getpageaddr(0);27const uint8_t *flash = (const uint8_t *)flash_addr;28const uint8_t key[] = AP_PUBLIC_KEY_SIGNATURE;29return (const struct ap_secure_data *)memmem(flash, page_size, key, sizeof(key));30#else31return nullptr;32#endif33}3435/*36return true if all keys are zeros37*/38bool AP_CheckFirmware::all_zero_keys(const struct ap_secure_data *sec_data)39{40const uint8_t zero_key[AP_PUBLIC_KEY_LEN] {};41/*42look over all public keys, if one matches then we are OK43*/44for (const auto &public_key : sec_data->public_key) {45if (memcmp(public_key.key, zero_key, AP_PUBLIC_KEY_LEN) != 0) {46return false;47}48}49return true;50}515253#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS54/*55return true if 1k of data is all 0xff (empty flash)56*/57static bool empty_1k(const uint8_t *data)58{59for (uint32_t i=0; i<1024; i++) {60if (data[i] != 0xFFU) {61return false;62}63}64return true;65}66#endif6768/*69read bootloader into memory. This is complicated by the potential presence70of persistent data from temperature calibration at the end of the sector7172Also note this assumes the public keys are in the first sector if73the bootloader covers more than one sector. This is a reasonable74assumption given the linker file75*/76AP_CheckFirmware::bl_data *AP_CheckFirmware::read_bootloader(void)77{78#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS79struct bl_data *bld = NEW_NOTHROW bl_data;80if (bld == nullptr) {81return nullptr;82}83const uint32_t page_size = hal.flash->getpagesize(0);84const uint32_t flash_addr = hal.flash->getpageaddr(0);85const uint8_t *flash = (uint8_t *)flash_addr;86const uint16_t block_size = 1024;87uint16_t num_blocks = page_size / block_size;88/*89find first empty block90*/91for (uint16_t i=0; i<num_blocks; i++) {92if (empty_1k(&flash[block_size*i])) {93break;94}95bld->length1 += block_size;96}97bld->data1 = NEW_NOTHROW uint8_t[bld->length1];98if (bld->data1 == nullptr) {99delete bld;100return nullptr;101}102memcpy(bld->data1, flash, bld->length1);103flash += bld->length1;104num_blocks -= bld->length1 / block_size;105106/*107find first non-empty block, which should be the persistent data if-any108*/109bld->offset2 = bld->length1;110while (num_blocks > 0) {111if (!empty_1k(&flash[bld->offset2])) {112break;113}114num_blocks--;115bld->offset2 += block_size;116}117if (num_blocks > 0) {118// we have persistent data to save119bld->length2 = num_blocks * block_size;120bld->data2 = NEW_NOTHROW uint8_t[bld->length2];121if (bld->data2 == nullptr) {122delete bld;123return nullptr;124}125memcpy(bld->data2, &flash[bld->offset2], bld->length2);126}127return bld;128#else129return nullptr;130#endif131}132133#if HAL_GCS_ENABLED134uint8_t AP_CheckFirmware::session_key[8];135136/*137make a session key138*/139static void make_session_key(uint8_t key[8])140{141struct {142uint32_t time_us;143uint8_t unique_id[12];144uint16_t rand1;145uint16_t rand2;146} data {};147static_assert(sizeof(data) % 4 == 0, "data must be multiple of 4 bytes");148149// get data which will not apply on a different board, and includes some randomness150uint8_t uid_len = 12;151hal.util->get_system_id_unformatted(data.unique_id, uid_len);152data.time_us = AP_HAL::micros();153data.rand1 = get_random16();154data.rand2 = get_random16();155const uint64_t c64 = crc_crc64((const uint32_t *)&data, sizeof(data)/sizeof(uint32_t));156memcpy(key, (uint8_t *)&c64, 8);157}158159160/*161write bootloader from memory162*/163bool AP_CheckFirmware::write_bootloader(const struct bl_data *bld)164{165#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS166const uint32_t flash_addr = hal.flash->getpageaddr(0);167EXPECT_DELAY_MS(3000);168if (!hal.flash->erasepage(0)) {169GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Bootloader erase failed");170return false;171}172EXPECT_DELAY_MS(3000);173if (!hal.flash->write(flash_addr, bld->data1, bld->length1)) {174GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Bootloader write1 failed");175return false;176}177EXPECT_DELAY_MS(3000);178if (bld->length2 != 0 &&179!hal.flash->write(flash_addr+bld->offset2, bld->data2, bld->length2)) {180GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Bootloader write1 failed");181return false;182}183return true;184#else185return false;186#endif187}188189/*190check signature in a command against bootloader public keys191*/192bool AP_CheckFirmware::check_signature(const mavlink_secure_command_t &pkt)193{194const struct ap_secure_data *sec_data = find_public_keys();195if (sec_data == nullptr) {196return false;197}198if (all_zero_keys(sec_data)) {199// allow through if no keys are setup200return true;201}202if (pkt.sig_length != 64) {203// monocypher signatures are 64 bytes204return false;205}206/*207look over all public keys, if one matches then we are OK208*/209for (const auto &public_key : sec_data->public_key) {210crypto_check_ctx ctx {};211crypto_check_ctx_abstract *actx = (crypto_check_ctx_abstract*)&ctx;212crypto_check_init(actx, &pkt.data[pkt.data_length], public_key.key);213214crypto_check_update(actx, (const uint8_t*)&pkt.sequence, sizeof(pkt.sequence));215crypto_check_update(actx, (const uint8_t*)&pkt.operation, sizeof(pkt.operation));216crypto_check_update(actx, pkt.data, pkt.data_length);217if (pkt.operation != SECURE_COMMAND_GET_SESSION_KEY) {218crypto_check_update(actx, session_key, sizeof(session_key));219}220if (crypto_check_final(actx) == 0) {221// good signature222return true;223}224}225return false;226}227228/*229set public keys in bootloader230*/231bool AP_CheckFirmware::set_public_keys(uint8_t key_idx, uint8_t num_keys, const uint8_t *key_data)232{233auto *bld = read_bootloader();234if (bld == nullptr) {235GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Failed to load bootloader into memory");236return false;237}238const uint8_t key[] = AP_PUBLIC_KEY_SIGNATURE;239struct ap_secure_data *sec_data = (struct ap_secure_data *)memmem(bld->data1, bld->length1, key, sizeof(key));240if (sec_data == nullptr) {241delete bld;242GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Failed to find key signature");243return false;244}245memcpy(sec_data->public_key[key_idx].key, key_data, num_keys*AP_PUBLIC_KEY_LEN);246247/*248pack so non-zero keys are at the start249*/250const uint8_t zero_key[AP_PUBLIC_KEY_LEN] {};251uint8_t max_keys = AP_PUBLIC_KEY_MAX_KEYS;252for (uint8_t i=0; max_keys>1 && i<max_keys-1; i++) {253if (memcmp(zero_key, sec_data->public_key[i].key, AP_PUBLIC_KEY_LEN) == 0) {254memmove(sec_data->public_key[i].key, sec_data->public_key[i+1].key, AP_PUBLIC_KEY_LEN*(max_keys-(i+1)));255max_keys--;256i--;257}258}259memset(sec_data->public_key[max_keys-1].key, 0, AP_PUBLIC_KEY_LEN*(AP_PUBLIC_KEY_MAX_KEYS-max_keys));260261bool ret = write_bootloader(bld);262delete bld;263return ret;264}265266/*267handle a SECURE_COMMAND268*/269void AP_CheckFirmware::handle_secure_command(mavlink_channel_t chan, const mavlink_secure_command_t &pkt)270{271mavlink_secure_command_reply_t reply {};272reply.result = MAV_RESULT_UNSUPPORTED;273reply.sequence = pkt.sequence;274reply.operation = pkt.operation;275276if (uint16_t(pkt.data_length) + uint16_t(pkt.sig_length) > sizeof(pkt.data)) {277reply.result = MAV_RESULT_DENIED;278goto send_reply;279}280if (!check_signature(pkt)) {281reply.result = MAV_RESULT_DENIED;282goto send_reply;283}284285switch (pkt.operation) {286287case SECURE_COMMAND_GET_SESSION_KEY: {288make_session_key(session_key);289reply.data_length = sizeof(session_key);290memcpy(reply.data, session_key, reply.data_length);291reply.result = MAV_RESULT_ACCEPTED;292break;293}294295case SECURE_COMMAND_GET_PUBLIC_KEYS: {296const struct ap_secure_data *sec_data = find_public_keys();297if (pkt.data_length != 2) {298reply.result = MAV_RESULT_UNSUPPORTED;299goto send_reply;300}301const uint8_t key_idx = pkt.data[0];302uint8_t num_keys = pkt.data[1];303const uint8_t max_fetch = (sizeof(reply.data)-1) / AP_PUBLIC_KEY_LEN;304if (key_idx >= AP_PUBLIC_KEY_MAX_KEYS ||305num_keys > max_fetch ||306key_idx+num_keys > AP_PUBLIC_KEY_MAX_KEYS ||307sec_data == nullptr) {308reply.result = MAV_RESULT_FAILED;309goto send_reply;310}311312// remove zero keys313const uint8_t zero_key[AP_PUBLIC_KEY_LEN] {};314while (num_keys > 0 &&315memcmp(zero_key, &sec_data->public_key[key_idx+num_keys-1], AP_PUBLIC_KEY_LEN) == 0) {316num_keys--;317}318319reply.data_length = 1+num_keys*AP_PUBLIC_KEY_LEN;320reply.data[0] = key_idx;321memcpy(&reply.data[1], &sec_data->public_key[key_idx], reply.data_length-1);322reply.result = MAV_RESULT_ACCEPTED;323break;324}325326case SECURE_COMMAND_SET_PUBLIC_KEYS: {327if (pkt.data_length < AP_PUBLIC_KEY_LEN+1) {328reply.result = MAV_RESULT_FAILED;329goto send_reply;330}331const uint8_t key_idx = pkt.data[0];332const uint8_t num_keys = (pkt.data_length-1) / AP_PUBLIC_KEY_LEN;333if (num_keys == 0) {334reply.result = MAV_RESULT_FAILED;335goto send_reply;336}337if (key_idx >= AP_PUBLIC_KEY_MAX_KEYS ||338key_idx+num_keys > AP_PUBLIC_KEY_MAX_KEYS) {339reply.result = MAV_RESULT_FAILED;340goto send_reply;341}342if (set_public_keys(key_idx, num_keys, &pkt.data[1])) {343GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Bootloader update OK");344reply.result = MAV_RESULT_ACCEPTED;345} else {346reply.result = MAV_RESULT_FAILED;347}348break;349}350351case SECURE_COMMAND_REMOVE_PUBLIC_KEYS: {352if (pkt.data_length != 2) {353reply.result = MAV_RESULT_FAILED;354goto send_reply;355}356const uint8_t key_idx = pkt.data[0];357const uint8_t num_keys = pkt.data[1];358if (num_keys == 0) {359reply.result = MAV_RESULT_FAILED;360goto send_reply;361}362if (key_idx >= AP_PUBLIC_KEY_MAX_KEYS ||363key_idx+num_keys > AP_PUBLIC_KEY_MAX_KEYS) {364reply.result = MAV_RESULT_FAILED;365goto send_reply;366}367uint8_t *data = NEW_NOTHROW uint8_t[num_keys*AP_PUBLIC_KEY_LEN];368if (data == nullptr) {369reply.result = MAV_RESULT_FAILED;370goto send_reply;371}372if (set_public_keys(key_idx, num_keys, data)) {373GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Bootloader update OK");374reply.result = MAV_RESULT_ACCEPTED;375} else {376reply.result = MAV_RESULT_FAILED;377}378delete[] data;379break;380}381}382383send_reply:384// send reply385mavlink_msg_secure_command_reply_send_struct(chan, &reply);386}387388/*389implement secure command operations for updating public keys390*/391void AP_CheckFirmware::handle_msg(mavlink_channel_t chan, const mavlink_message_t &msg)392{393switch (msg.msgid) {394case MAVLINK_MSG_ID_SECURE_COMMAND: {395mavlink_secure_command_t pkt;396mavlink_msg_secure_command_decode(&msg, &pkt);397handle_secure_command(chan, pkt);398break;399}400}401}402403#endif // HAL_GCS_ENABLED404405/*406check that a bootloader is OK to flash. We don't want to allow407flashing of a bootloader unless we either have no public keys setup408or the bootloader has public keys embedded. This prevents an easy409mistake of including an insecure bootloader in ROMFS with a secure build410*/411bool AP_CheckFirmware::check_signed_bootloader(const uint8_t *fw, uint32_t fw_size)412{413const struct ap_secure_data *sec_data = find_public_keys();414if (sec_data == nullptr || all_zero_keys(sec_data)) {415// current bootloader doesn't have public keys, so OK to load any bootloader416return true;417}418const uint8_t key[] = AP_PUBLIC_KEY_SIGNATURE;419sec_data = (const struct ap_secure_data *)memmem(fw, fw_size, key, sizeof(key));420if (sec_data == nullptr || all_zero_keys(sec_data)) {421// new bootloader doesn't have any public keys, not allowed422return false;423}424return true;425}426427#endif // AP_CHECK_FIRMWARE_ENABLED428429430