mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-20 17:52:35 +00:00
* Re-implement PKI from #1509 co-authored-by: edinnen <ethanjdinnen@protonmail.com> * Set the key lengnth to actually make PKI work. * Remove unused variable and initialize keys to null * move printBytes() to meshUtils * Don't reset PKI key son reboot unless needed. * Remove double encryption for PKI messages * Cleanup encrypt logic * Add the MESHTASTIC_EXCLUDE_PKI option, and set it for minimal builds. Required for STM32 targets for now. * Use SHA-256 for PKI key hashing, and add MESHTASTIC_EXCLUDE_PKI_KEYGEN for STM32 * Fix a crash when node is null * Don't send PKI encrypted packets while licensed * use chIndex 8 for PKI * Don't be so clever, that you corrupt incoming packets * Pass on channel 8 for now * Typo * Lock keys once non-zero * We in fact need 2 scratch buffers, to store the encrypted bytes, unencrypted bytes, and decoded protobuf. * Lighter approach to retaining known key * Attach the public key to PKI decrypted packets in device memory * Turn PKI back off for STM32 :( * Don't just memcp over a protobuf * Don't PKI encrypt nodeinfo packets * Add a bit more memory logging around nodeDB * Use the proper macro to refer to NODENUM_BROADCAST * Typo fix * Don't PKI encrypt ROUTING (naks and acks) * Adds SecurityConfig protobuf * Add admin messages over PKI * Disable PKI for the WIO-e5 * Add MINIMUM_SAFE_FREE_HEAP macro and set to safe 1.5k * Add missed "has_security" * Add the admin_channel_enabled option * STM32 again * add missed configuration.h at the top of files * Add EXCLUDE_TZ and RTC * Enable PKI build on STM32 once again * Attempt 1 at moving PKI to aes-ccm * Fix buffers for encrypt/decrypt * Eliminate unused aes variable * Add debugging lines * Set hash to 0 for PKI * Fix debug lines so they don't print pointers. * logic fix and more debug * Rather important typo * Check for short packets before attempting decrypt * Don't forget to give cryptoEngine the keys! * Use the right scratch buffer * Cleanup * moar cleanups * Minor hardening * Remove some in-progress stuff * Turn PKI back off on STM32 * Return false * 2.5 protos * Sync up protos * Add initial cryptography test vector tests * re-add MINIMUM_SAFE_FREE_HEAP * Housekeeping and comment fixes * Add explanatory comment about weak dh25519 keys --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
@@ -37,6 +37,7 @@ static MemoryDynamic<meshtastic_MeshPacket> staticPool;
|
||||
Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
|
||||
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
static uint8_t ScratchEncrypted[MAX_RHPACKETLEN];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -307,70 +308,105 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
|
||||
return true; // If packet was already decoded just return
|
||||
|
||||
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
|
||||
size_t rawSize = p->encrypted.size;
|
||||
if (rawSize > sizeof(bytes)) {
|
||||
LOG_ERROR("Packet too large to attempt decryption! (rawSize=%d > 256)\n", rawSize);
|
||||
return false;
|
||||
}
|
||||
bool decrypted = false;
|
||||
ChannelIndex chIndex = 0;
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||
memcpy(ScratchEncrypted, p->encrypted.bytes, rawSize);
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// Attempt PKI decryption first
|
||||
if (p->channel == 0 && p->to == nodeDB->getNodeNum() && p->to > 0 && nodeDB->getMeshNode(p->from) != nullptr &&
|
||||
nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 &&
|
||||
rawSize > 8) {
|
||||
LOG_DEBUG("Attempting PKI decryption\n");
|
||||
|
||||
// Try to find a channel that works with this hash
|
||||
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
|
||||
// Try to use this hash/channel pair
|
||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||
// Try to decrypt the packet if we can
|
||||
size_t rawSize = p->encrypted.size;
|
||||
if (rawSize > sizeof(bytes)) {
|
||||
LOG_ERROR("Packet too large to attempt decription! (rawSize=%d > 256)\n", rawSize);
|
||||
return false;
|
||||
}
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||
|
||||
// printBytes("plaintext", bytes, p->encrypted.size);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
if (crypto->decryptCurve25519(p->from, p->id, rawSize, ScratchEncrypted, bytes)) {
|
||||
LOG_INFO("PKI Decryption worked!\n");
|
||||
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
|
||||
LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n");
|
||||
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
|
||||
LOG_ERROR("Invalid portnum (bad psk?)!\n");
|
||||
rawSize -= 8;
|
||||
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded) &&
|
||||
p->decoded.portnum != meshtastic_PortNum_UNKNOWN_APP) {
|
||||
decrypted = true;
|
||||
LOG_INFO("Packet decrypted using PKI!\n");
|
||||
p->pki_encrypted = true;
|
||||
memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32);
|
||||
p->public_key.size = 32;
|
||||
// memcpy(bytes, ScratchEncrypted, rawSize); // TODO: Rename the bytes buffers
|
||||
// chIndex = 8;
|
||||
} else {
|
||||
// parsing was successful
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
|
||||
/* Not actually ever used.
|
||||
// Decompress if needed. jm
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
// Decompress the payload
|
||||
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
int decompressed_len;
|
||||
|
||||
memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
|
||||
|
||||
decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
|
||||
|
||||
// LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
|
||||
|
||||
memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len);
|
||||
|
||||
// Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP
|
||||
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
||||
} */
|
||||
|
||||
printPacket("decoded message", p);
|
||||
#if ENABLE_JSON_LOGGING
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
#elif ARCH_PORTDUINO
|
||||
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
|
||||
return false;
|
||||
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
|
||||
if (!decrypted) {
|
||||
// Try to find a channel that works with this hash
|
||||
for (chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
|
||||
// Try to use this hash/channel pair
|
||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||
// Try to decrypt the packet if we can
|
||||
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||
|
||||
// printBytes("plaintext", bytes, p->encrypted.size);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
|
||||
LOG_ERROR("Invalid protobufs in received mesh packet id=0x%08x (bad psk?)!\n", p->id);
|
||||
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
|
||||
LOG_ERROR("Invalid portnum (bad psk?)!\n");
|
||||
} else {
|
||||
decrypted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decrypted) {
|
||||
// parsing was successful
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
|
||||
/* Not actually ever used.
|
||||
// Decompress if needed. jm
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
|
||||
// Decompress the payload
|
||||
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
int decompressed_len;
|
||||
|
||||
memcpy(compressed_in, p->decoded.payload.bytes, p->decoded.payload.size);
|
||||
|
||||
decompressed_len = unishox2_decompress_simple(compressed_in, p->decoded.payload.size, decompressed_out);
|
||||
|
||||
// LOG_DEBUG("\n\n**\n\nDecompressed length - %d \n", decompressed_len);
|
||||
|
||||
memcpy(p->decoded.payload.bytes, decompressed_out, decompressed_len);
|
||||
|
||||
// Switch the port from PortNum_TEXT_MESSAGE_COMPRESSED_APP to PortNum_TEXT_MESSAGE_APP
|
||||
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
||||
} */
|
||||
|
||||
printPacket("decoded message", p);
|
||||
#if ENABLE_JSON_LOGGING
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
#elif ARCH_PORTDUINO
|
||||
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
|
||||
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
} else {
|
||||
LOG_WARN("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return 0 for success or a Routing_Errror code for failure
|
||||
@@ -384,7 +420,6 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
||||
|
||||
/* Not actually used, so save the cycles
|
||||
// Only allow encryption on the text message app.
|
||||
// TODO: Allow modules to opt into compression.
|
||||
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) {
|
||||
|
||||
@@ -432,10 +467,28 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
|
||||
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||
p->channel = hash;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
|
||||
if (!owner.is_licensed && p->to != NODENUM_BROADCAST && node != nullptr && node->user.public_key.size > 0 &&
|
||||
p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP &&
|
||||
p->decoded.portnum != meshtastic_PortNum_ROUTING_APP) { // TODO: check for size due to 8 byte tag
|
||||
LOG_DEBUG("Using PKI!\n");
|
||||
if (numbytes + 8 > MAX_RHPACKETLEN)
|
||||
return meshtastic_Routing_Error_TOO_LARGE;
|
||||
crypto->encryptCurve25519(p->to, getFrom(p), p->id, numbytes, bytes, ScratchEncrypted);
|
||||
numbytes += 8;
|
||||
memcpy(p->encrypted.bytes, ScratchEncrypted, numbytes);
|
||||
p->channel = 0;
|
||||
} else {
|
||||
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
}
|
||||
#else
|
||||
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
#endif
|
||||
|
||||
// Copy back into the packet and set the variant type
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
p->encrypted.size = numbytes;
|
||||
p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag;
|
||||
}
|
||||
@@ -539,4 +592,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
||||
handleReceived(p);
|
||||
|
||||
packetPool.release(p);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user