mirror of
https://github.com/agessaman/MeshCore.git
synced 2026-08-28 21:48:17 +00:00
feat(kiss): add no-crypto KISS mode
Gate the KISS modem's identity/crypto hardware commands (GET_IDENTITY, VERIFY_SIGNATURE, SIGN_DATA, ENCRYPT_DATA, DECRYPT_DATA, KEY_EXCHANGE, HASH) and the IdentityStore/filesystem init behind KISS_NO_CRYPTO, for MCUs too resource-constrained to carry KISS crypto commands or a persistent identity/filesystem. Disabled commands fall through to the existing HW_ERR_UNKNOWN_CMD path; radio config, stats, RSSI, TX/RX, and GET_RANDOM are unaffected. Adds a native_kiss_modem_no_crypto test env, wired into CI, covering all seven gated opcodes plus PING/GET_RADIO/GET_RANDOM as still-working controls.
This commit is contained in:
@@ -20,7 +20,7 @@ jobs:
|
||||
uses: ./.github/actions/setup-build-environment
|
||||
|
||||
- name: Run Unit Tests
|
||||
run: pio test -e native -e native_kiss_modem -vv
|
||||
run: pio test -e native -e native_kiss_modem -e native_kiss_modem_no_crypto -vv
|
||||
|
||||
- name: Upload Test Results
|
||||
# Upload test results even if the test step failed.
|
||||
|
||||
@@ -303,12 +303,15 @@ void KissModem::processFrame() {
|
||||
|
||||
void KissModem::handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint16_t len) {
|
||||
switch (sub_cmd) {
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
case HW_CMD_GET_IDENTITY:
|
||||
handleGetIdentity();
|
||||
break;
|
||||
#endif
|
||||
case HW_CMD_GET_RANDOM:
|
||||
handleGetRandom(data, len);
|
||||
break;
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
case HW_CMD_VERIFY_SIGNATURE:
|
||||
handleVerifySignature(data, len);
|
||||
break;
|
||||
@@ -327,6 +330,7 @@ void KissModem::handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint
|
||||
case HW_CMD_HASH:
|
||||
handleHash(data, len);
|
||||
break;
|
||||
#endif
|
||||
case HW_CMD_SET_RADIO:
|
||||
handleSetRadio(data, len);
|
||||
break;
|
||||
@@ -462,9 +466,11 @@ void KissModem::onPacketReceived(int8_t snr, int8_t rssi, const uint8_t* packet,
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
void KissModem::handleGetIdentity() {
|
||||
writeHardwareFrame(HW_RESP(HW_CMD_GET_IDENTITY), _identity.pub_key, PUB_KEY_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
void KissModem::handleGetRandom(const uint8_t* data, uint16_t len) {
|
||||
if (len < 1) {
|
||||
@@ -483,6 +489,7 @@ void KissModem::handleGetRandom(const uint8_t* data, uint16_t len) {
|
||||
writeHardwareFrame(HW_RESP(HW_CMD_GET_RANDOM), buf, requested);
|
||||
}
|
||||
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
void KissModem::handleVerifySignature(const uint8_t* data, uint16_t len) {
|
||||
if (len < PUB_KEY_SIZE + SIGNATURE_SIZE + 1) {
|
||||
writeHardwareError(HW_ERR_INVALID_LENGTH);
|
||||
@@ -570,6 +577,7 @@ void KissModem::handleHash(const uint8_t* data, uint16_t len) {
|
||||
mesh::Utils::sha256(hash, 32, data, len);
|
||||
writeHardwareFrame(HW_RESP(HW_CMD_HASH), hash, 32);
|
||||
}
|
||||
#endif
|
||||
|
||||
void KissModem::handleSetRadio(const uint8_t* data, uint16_t len) {
|
||||
if (len < 10) {
|
||||
|
||||
@@ -166,14 +166,18 @@ class KissModem {
|
||||
void handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint16_t len);
|
||||
void processTx();
|
||||
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
void handleGetIdentity();
|
||||
#endif
|
||||
void handleGetRandom(const uint8_t* data, uint16_t len);
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
void handleVerifySignature(const uint8_t* data, uint16_t len);
|
||||
void handleSignData(const uint8_t* data, uint16_t len);
|
||||
void handleEncryptData(const uint8_t* data, uint16_t len);
|
||||
void handleDecryptData(const uint8_t* data, uint16_t len);
|
||||
void handleKeyExchange(const uint8_t* data, uint16_t len);
|
||||
void handleHash(const uint8_t* data, uint16_t len);
|
||||
#endif
|
||||
void handleSetRadio(const uint8_t* data, uint16_t len);
|
||||
void handleSetTxPower(const uint8_t* data, uint16_t len);
|
||||
void handleGetRadio();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include <Arduino.h>
|
||||
#include <target.h>
|
||||
#include <helpers/ArduinoHelpers.h>
|
||||
#include <helpers/IdentityStore.h>
|
||||
#include "KissModem.h"
|
||||
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
#include <helpers/IdentityStore.h>
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#include <InternalFileSystem.h>
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
@@ -13,6 +14,7 @@
|
||||
#else
|
||||
#include <InternalFileSystem.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(KISS_UART_RX) && defined(KISS_UART_TX)
|
||||
#include <HardwareSerial.h>
|
||||
@@ -33,6 +35,7 @@ void halt() {
|
||||
while (1) ;
|
||||
}
|
||||
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
void loadOrCreateIdentity() {
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
InternalFS.begin();
|
||||
@@ -56,6 +59,7 @@ void loadOrCreateIdentity() {
|
||||
store.save("_main", identity);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void onSetRadio(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
radio_driver.setParams(freq, bw, sf, cr);
|
||||
@@ -85,7 +89,9 @@ void setup() {
|
||||
radio_driver.begin();
|
||||
|
||||
rng.begin(radio_driver.getRngSeed());
|
||||
#if !defined(KISS_NO_CRYPTO)
|
||||
loadOrCreateIdentity();
|
||||
#endif
|
||||
|
||||
sensors.begin();
|
||||
|
||||
|
||||
+17
-1
@@ -164,7 +164,7 @@ build_flags = -std=c++17
|
||||
-I src
|
||||
-I test/mocks
|
||||
test_build_src = yes
|
||||
test_ignore = test_kiss_modem
|
||||
test_ignore = test_kiss_modem, test_kiss_modem_no_crypto
|
||||
build_src_filter =
|
||||
-<*>
|
||||
+<../src/Utils.cpp>
|
||||
@@ -187,3 +187,19 @@ build_src_filter =
|
||||
+<../examples/kiss_modem/KissModem.cpp>
|
||||
lib_deps =
|
||||
google/googletest @ 1.17.0
|
||||
|
||||
[env:native_kiss_modem_no_crypto]
|
||||
platform = native
|
||||
test_framework = googletest
|
||||
build_flags = -std=c++17
|
||||
-D KISS_NO_CRYPTO=1
|
||||
-I test/mocks
|
||||
-I src
|
||||
-I examples/kiss_modem
|
||||
test_build_src = yes
|
||||
test_filter = test_kiss_modem_no_crypto
|
||||
build_src_filter =
|
||||
-<*>
|
||||
+<../examples/kiss_modem/KissModem.cpp>
|
||||
lib_deps =
|
||||
google/googletest @ 1.17.0
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "KissModem.h"
|
||||
|
||||
class SimpleStream : public Stream {
|
||||
public:
|
||||
void pushRx(const std::vector<uint8_t>& bytes) {
|
||||
for (uint8_t b : bytes) {
|
||||
_rx.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& writesSnapshot() const { return _writes; }
|
||||
|
||||
int availableForWrite() override { return 4096; }
|
||||
|
||||
size_t write(const uint8_t* buffer, size_t size) override {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
_writes.push_back(buffer[i]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t write(uint8_t b) override { return write(&b, 1); }
|
||||
|
||||
int available() override { return static_cast<int>(_rx.size()); }
|
||||
|
||||
int read() override {
|
||||
if (_rx.empty()) return -1;
|
||||
int b = _rx.front();
|
||||
_rx.pop();
|
||||
return b;
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<uint8_t> _rx;
|
||||
std::vector<uint8_t> _writes;
|
||||
};
|
||||
|
||||
class FakeRNG : public mesh::RNG {
|
||||
public:
|
||||
void random(uint8_t* dest, size_t sz) override {
|
||||
for (size_t i = 0; i < sz; i++) {
|
||||
dest[i] = (uint8_t)i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FakeRadio : public mesh::Radio {
|
||||
public:
|
||||
bool isReceiving() override { return false; }
|
||||
uint32_t getEstAirtimeFor(uint16_t) override { return 10; }
|
||||
bool startSendRaw(const uint8_t*, uint16_t) override { return true; }
|
||||
bool isSendComplete() override { return true; }
|
||||
void onSendFinished() override {}
|
||||
int16_t getNoiseFloor() override { return -120; }
|
||||
};
|
||||
|
||||
class FakeBoard : public mesh::MainBoard {
|
||||
public:
|
||||
uint16_t getBattMilliVolts() override { return 4200; }
|
||||
float getMCUTemperature() override { return 24.0f; }
|
||||
const char* getManufacturerName() override { return "test-board"; }
|
||||
void reboot() override {}
|
||||
};
|
||||
|
||||
class FakeSensors : public SensorManager {
|
||||
public:
|
||||
bool querySensors(uint8_t, CayenneLPP&) override { return false; }
|
||||
};
|
||||
|
||||
static std::vector<uint8_t> hwFrame(uint8_t sub_cmd, const std::vector<uint8_t>& payload = {}) {
|
||||
std::vector<uint8_t> frame = {KISS_FEND, KISS_CMD_SETHARDWARE, sub_cmd};
|
||||
frame.insert(frame.end(), payload.begin(), payload.end());
|
||||
frame.push_back(KISS_FEND);
|
||||
return frame;
|
||||
}
|
||||
|
||||
class KissModemNoCryptoFixture : public ::testing::Test {
|
||||
protected:
|
||||
SimpleStream serial;
|
||||
mesh::LocalIdentity identity;
|
||||
FakeRNG rng;
|
||||
FakeRadio radio;
|
||||
FakeBoard board;
|
||||
FakeSensors sensors;
|
||||
KissModem modem;
|
||||
|
||||
KissModemNoCryptoFixture()
|
||||
: modem(serial, identity, rng, radio, board, sensors) {
|
||||
modem.begin();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(KissModemNoCryptoFixture, PingStillWorksWhenCryptoDisabled) {
|
||||
serial.pushRx(hwFrame(HW_CMD_PING));
|
||||
modem.loop();
|
||||
|
||||
const std::vector<uint8_t> expected = {
|
||||
KISS_FEND, KISS_CMD_SETHARDWARE, HW_RESP(HW_CMD_PING), KISS_FEND};
|
||||
EXPECT_EQ(serial.writesSnapshot(), expected);
|
||||
}
|
||||
|
||||
TEST_F(KissModemNoCryptoFixture, GetRadioStillWorksWhenCryptoDisabled) {
|
||||
serial.pushRx(hwFrame(HW_CMD_GET_RADIO));
|
||||
modem.loop();
|
||||
|
||||
std::vector<uint8_t> expected = {KISS_FEND, KISS_CMD_SETHARDWARE, HW_RESP(HW_CMD_GET_RADIO)};
|
||||
expected.insert(expected.end(), 10, 0x00); // zero-initialized RadioConfig
|
||||
expected.push_back(KISS_FEND);
|
||||
EXPECT_EQ(serial.writesSnapshot(), expected);
|
||||
}
|
||||
|
||||
TEST_F(KissModemNoCryptoFixture, GetRandomStillWorksWhenCryptoDisabled) {
|
||||
serial.pushRx(hwFrame(HW_CMD_GET_RANDOM, {0x04}));
|
||||
modem.loop();
|
||||
|
||||
const std::vector<uint8_t> expected = {
|
||||
KISS_FEND, KISS_CMD_SETHARDWARE, HW_RESP(HW_CMD_GET_RANDOM), 0x00, 0x01, 0x02, 0x03, KISS_FEND};
|
||||
EXPECT_EQ(serial.writesSnapshot(), expected);
|
||||
}
|
||||
|
||||
class KissModemDisabledCommandTest : public ::testing::TestWithParam<uint8_t> {
|
||||
protected:
|
||||
SimpleStream serial;
|
||||
mesh::LocalIdentity identity;
|
||||
FakeRNG rng;
|
||||
FakeRadio radio;
|
||||
FakeBoard board;
|
||||
FakeSensors sensors;
|
||||
KissModem modem;
|
||||
|
||||
KissModemDisabledCommandTest()
|
||||
: modem(serial, identity, rng, radio, board, sensors) {
|
||||
modem.begin();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(KissModemDisabledCommandTest, ReturnsUnknownCommandWhenCryptoDisabled) {
|
||||
serial.pushRx(hwFrame(GetParam()));
|
||||
modem.loop();
|
||||
|
||||
const std::vector<uint8_t> expected = {
|
||||
KISS_FEND, KISS_CMD_SETHARDWARE, HW_RESP_ERROR, HW_ERR_UNKNOWN_CMD, KISS_FEND};
|
||||
EXPECT_EQ(serial.writesSnapshot(), expected);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
AllCryptoCommands, KissModemDisabledCommandTest,
|
||||
::testing::Values(
|
||||
HW_CMD_GET_IDENTITY, HW_CMD_VERIFY_SIGNATURE, HW_CMD_SIGN_DATA, HW_CMD_ENCRYPT_DATA,
|
||||
HW_CMD_DECRYPT_DATA, HW_CMD_KEY_EXCHANGE, HW_CMD_HASH));
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in New Issue
Block a user