* Improve performance of encrypted packets with a shared secret cache
Every PKI encrypt, decrypt and ack-proof ran Curve25519::dh2 plus a SHA256
to derive the pairwise key, so a node in a conversation paid a full X25519
per packet, on the main loop, under cryptLock. On a RAK4631 that is ~96 ms
of the ~210 ms it takes to handle a DM.
setCryptoSharedSecret() derives the key only when it is not already held
for that peer, keeping the last MAX_CACHED_SHARED_SECRETS derivations
(8 on nRF52, 2 on STM32WL, 10 elsewhere, under 400 bytes) and evicting the
least recently used. encryptCurve25519, decryptCurve25519 and
ackProofCompute all go through it, so the ack proof gets the same cache
without a second DH path. The cache is emptied whenever our own private
key changes, since every secret in it is then stale.
The lookup key is the first 4 bytes of the peer's public key. A collision
makes us derive against the wrong cached secret, which costs a failed
decrypt for that pair; it cannot disclose either secret.
Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLHxcWJuvSoSSMpz3LWdt3
* Do not let an empty cache slot answer for a zero-prefixed peer key
The cache used lookup_key == 0 to mean "slot unused", so a peer key whose
first 4 bytes are zero matched every unused slot and was handed that slot's
zeroed secret as a hit. The all-zero key is exactly such a key, which is how
test_proof_rejects_weak_peer_key caught it: dh2's weak-point check never ran.
Entries carry an explicit valid flag instead, which the struct's existing
padding absorbs. A weak peer key is also now rejected before the cache is
consulted, since with 4 bytes of lookup key it could otherwise collide with a
cached peer and be served that peer's secret rather than being refused.
Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLHxcWJuvSoSSMpz3LWdt3
* Key the shared secret cache by the whole peer public key
A four-byte lookup key is grindable: anyone can generate a keypair whose
public key shares those bytes with a peer they want to shadow, get their own
entry cached, and then be handed the secret this node uses to talk to that
peer - readable by them, since they hold the matching private key. That is
disclosure of traffic meant for the peer, and a forgeable ack proof in its
name, not the failed exchange a chance collision would cause.
Entries hold the peer key itself and are matched on all 32 bytes, so the
weak-key check dh2 does on a miss can no longer be skipped either and the
explicit isWeakPoint guard goes away with it. The cache costs 66 bytes per
entry: 528 on nRF52, 660 where the default 10 entries apply.
Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLHxcWJuvSoSSMpz3LWdt3
* Allowlist the cache's uptime shift for the millis deadline guard
The guard's regex reads `millis() >> 22` as a comparison against the uptime
clock. It is a right shift, coarsening uptime into the ~1.165 hour units the
cache stamps entries with, and the eviction arithmetic handles that stamp's
8-bit wrap itself.
Co-Authored-By: Jonathan Bennett <jbennett@incomsystems.biz>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLHxcWJuvSoSSMpz3LWdt3
---------
Co-authored-by: Jason B. Cox <contact@jasonbcox.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* Bind request_id and reply_id into the XEdDSA signing buffer
A signed reply can be re-pointed at a different message today. The client sets
reply_id on an outgoing text to make a tapback, firmware signs the resulting
broadcast, but reply_id lives in the Data envelope rather than the payload the
signature covers - and channel crypto is AES-CTR with no MAC, so anyone holding
the PSK can rewrite it in flight and the signature still verifies. request_id
has the same shape and is bound with it.
Packets carrying neither field keep the existing [from|id|portnum|payload]
layout, byte-identical to what v2.8.0 alphas are signing today, so the bulk of
signed traffic - broadcasts - stays verifiable in both directions across the
upgrade. Only packets that actually carry one of the two fields use the extended
layout. Both sides pick the layout from the packet's own decoded fields, so
nothing is transmitted to select it.
Open question for review, deliberately not decided here: a format/version byte
in the buffer would be cleaner than a conditional layout, because the safety
argument for the conditional form has to be re-derived whenever a portnum is
added. It costs nothing on the wire since the buffer is never transmitted, but
it changes every signature and so breaks verification against the alphas that
are already signing. If we want it, better done once and before 2.8.0 leaves
alpha.
Tests: request_id/reply_id flips alongside the existing from/id/portnum negative
cases; a hand-built alpha-format signature that must still verify, and must not
be reinterpretable as the extended layout or vice versa; and receive-path cases
for a retargeted tapback, a retargeted response, and an ordinary signed
broadcast that must be unaffected.
* Sign the whole Data envelope, in one unambiguous layout
Replaces the conditional two-layout signing buffer with a single fixed one:
version(1) | from(4) | id(4) | to(4) | portnum(4) | request_id(4)
| reply_id(4) | emoji(4) | bitfield(4) | flags(1) | payload(N)
The conditional scheme was ambiguous. Base was header || arbitrary payload, so
any byte string the extended layout emitted was also a legal base payload: an
attacker could move eight payload bytes into request_id/reply_id and truncate a
signed message while its signature still verified. No marker placed only in the
extended layout fixes that, because base can always reproduce it. A fixed-length
header does - the payload boundary is total - XEDDSA_SIGNED_HEADER_LEN and never
depends on content.
Binding reply_id alone was also not enough, because the fields around it are just
as malleable:
emoji a reaction is a text packet with the emoji in the payload,
reply_id naming the parent, and this flag telling the client to
render it as a reaction. Flipping it turns a signed reply into a
signed reaction, so it has to travel with reply_id.
bitfield bit 0 is OK_TO_MQTT, the sender's consent to upload to a public
broker, and the exploitable direction is the one that leaks. The
whole uint32 is signed so bits 2..31 are covered in advance, and
presence is signed separately so stripping it is not the same as
sending it zero.
want_response bit 1 of bitfield mirrors it and Router merges the two with |=,
so signing either alone protects neither.
to without it a signed broadcast can be re-addressed as a direct
message and still verify, delivering a public statement as an
apparent private one. Relays rewrite hop_limit, next_hop and
relay_node, never `to`.
Left out: dest and source (one write in the tree, no readers), channel (the wire
carries a hash where the decoded packet carries an index), and the hop fields,
which relays rewrite by design. Signing the encoded Data wholesale is not an
option either - a relay that holds the channel key decodes and re-encodes it, so
byte fidelity is lost and unknown fields are stripped. The ack_proof excision
trick does not transfer for the same reason: Routing survives because it rides
inside the opaque payload, which Data itself does not.
sign/verify now take the Data rather than a field list, so adding to the covered
set cannot silently miss a call site. Integers are explicitly little-endian, as
in ackProofCompute. The buffer is sized from the schema's own maximum payload
rather than from what the fits-on-air gate currently admits, because an overflow
makes buildSigningBuffer return 0 and signing fail with no error; MAX_BLOCKSIZE
is left alone so the AES-CTR guard and scratch buffers sharing it are unaffected.
This changes every signature, so it is not compatible with the v2.8.0 alphas. It
is deliberately being done while 2.8.0 is still prerelease: a verify failure
against an authoritative key is an unconditional drop, so the same change after
2.8.0 goes stable would split signed traffic across versions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012hLcVif8GDEmA2k77hmFG8
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Add AEAD (AES-CCM) authenticated encryption for PSK channels
Extend PSK channel encryption with optional AES-CCM authenticated
encryption (use_aead flag in ChannelSettings). When enabled, messages
include a 12-byte authentication tag that prevents forgery, bit-flipping,
and injection attacks by anyone with the channel PSK.
Changes:
- Add encryptPacketCCM/decryptPacketCCM to CryptoEngine with key
promotion (16-byte keys zero-padded to 32 for AESSmall256 compat)
- Move AES-CCM primitives (aes-ccm.h/cpp, aesSetKey, aesEncrypt)
outside PKI guard so they're available unconditionally
- Add isAEADEnabled() to Channels with hash differentiation (XOR 0xAE)
- Add AEAD encrypt/decrypt branches in Router perhapsEncode/perhapsDecode
with no CTR fallback on AEAD channels
- Add use_aead field to channel.pb.h (bool, tag 8)
- Add MESHTASTIC_AEAD_OVERHEAD constant to RadioInterface.h
- Add comprehensive test suite: round-trip (AES-128/256), tamper
detection (ciphertext, tag, sweep), wrong PSK, wrong sender,
packet-too-small, deterministic output verification
Addresses firmware#4030.
* Apply clang-format to match project style
* Guard AEAD path against empty PSK and check encrypt return value
- Add early return in encryptPacketCCM/decryptPacketCCM when
psk.length == 0, preventing null dereference in aesSetKey
- Check encryptPacketCCM return value in Router::perhapsEncode
(both PKI and non-PKI paths), returning BAD_REQUEST on failure
instead of silently transmitting corrupt packets
- Add unit test for empty PSK (encrypt and decrypt must return
false without crashing)
* Use true AES-128 for 16-byte PSKs instead of promoting to AES-256
aesSetKey now dispatches based on key length: 16 bytes creates
AESSmall128, 32 bytes creates AESSmall256. The aes member type
changes from AESSmall256 to BlockCipher (polymorphic base class).
This removes the unnecessary key promotion that added two extra
AES rounds (14 vs 12) with no security benefit since the entropy
stays at 128 bits for 16-byte keys.
encryptPacketCCM/decryptPacketCCM now pass psk.length directly
to aes_ccm_ae/aes_ccm_ad instead of promoting to 32.
New tests: ECB AES-128 with NIST vectors, AEAD test verifying
AES-128 and AES-256 produce different ciphertexts with same key
material and cross-key decryption fails.
* Reject the invalid-key sentinel in the AEAD paths
CryptoKey documents length == -1 as "invalid key - do not use", but the
AEAD guards only tested for 0. Since length is int8_t and the aes_ccm_*
key length parameter is size_t, a -1 would widen into a huge unsigned
length and be handed to the cipher instead of being rejected.
Both callers in Router.cpp are gated on a non-negative channel hash, and
generateHash() already returns -1 exactly when getKey() yields an invalid
key, so the sentinel cannot reach these functions today. Guard against it
anyway rather than relying on callers to keep that invariant.
* Tie MESHTASTIC_AEAD_OVERHEAD to CryptoEngine::AEAD_TAG_SIZE
The packet-size boundary checks in perhapsEncode/perhapsDecode budget for
MESHTASTIC_AEAD_OVERHEAD, but the tag actually written is AEAD_TAG_SIZE.
Nothing tied the two together, so changing one would have silently produced
oversized packets or truncated payloads. Assert they match instead of
coupling RadioInterface.h to CryptoEngine.
Also trims the sentinel comment to the two-line limit in AGENTS.md.
* Add RFC 3610 known-answer vectors and widen the tamper sweep
Packet Vectors #1, #2 and #7 pin aes_ccm_ae()/aes_ccm_ad() to published data
rather than to their own output, covering M=8 and M=10, a trailing partial block
in every case, and rejection of a modified AAD. Test 1 in test_AES_CCM_AEAD is
relabelled as the smoke test it actually is.
The per-byte tamper loop now walks the whole buffer including the tag, instead of
only the first four ciphertext bytes.
* Cover the second nonce input and tighten the AEAD test buffers
Test 10 only ever varied fromNode, leaving packetId — the other half of the
nonce — unexercised. It now checks each one wrong on its own, both wrong, and
both right, so the negative assertions cannot pass vacuously.
The undersized-packet test wrote into a one-byte buffer and only survived
because decryptPacketCCM() returns before touching it; size it for the whole
input so a regressed length guard fails an assertion instead of the stack.
Also assert makePsk() cannot overrun CryptoKey::bytes.
* Rewrite Unicode dashes to ASCII in AEAD comments
The ascii-dash formatter that landed in develop rewrites U+2014/U+2013 to
an ASCII hyphen. Three files on this branch still carried em dashes in
comments, so Trunk Check went red once develop was merged in. Comments
only, no code change.
* Authenticate sender and destination IDs as AEAD associated data
The nonce binds the sender and the packet id, but nothing bound the
destination, so `to` could be rewritten in flight and the tag would still
validate. Pass `from || to` as associated data to aes_ccm_ae/aes_ccm_ad so
a redirected packet fails authentication.
The hop fields stay out of the AAD on purpose: relays legitimately rewrite
hop_limit, hop_start, relay_node and next_hop.
Adds a sub-test covering redirection to another node and promotion of a
unicast to a broadcast; both must be rejected, and the unmodified
destination must still round-trip.
This changes the on-the-wire format for AEAD packets. Nothing ships with
use_aead yet, so there is no deployed traffic to stay compatible with.
* fix(crypto): repair EXCLUDE_PKI builds and guard AEAD channel config
aes-ccm.cpp is compiled in every build now and calls CryptoEngine::aesSetKey
and CryptoEngine::aesEncrypt, whose definitions were still inside the
!(MESHTASTIC_EXCLUDE_PKI) block in CryptoEngine.cpp, so MESHTASTIC_EXCLUDE_PKI=1
failed at the link step. Move both definitions outside the guard, and move the
pending-public-key declarations back inside it next to the fields they read.
fixupChannel() clears use_aead on a channel that resolves to no key material.
That combination kept a valid-looking channel hash while every encode returned
BAD_REQUEST and every decode dropped, with nothing in the config to show why.
encryptPacketCCM/decryptPacketCCM are virtual, so a platform engine can back
them with hardware CCM the way it already overrides encryptAESCtr.
perhapsEncode() carries one copy of the AEAD/CTR branch instead of an identical
copy in each arm of the MESHTASTIC_EXCLUDE_PKI ifdef.
Tests: three use_aead cases in test_channel_keys covering the hash split, the
no-key clear, and a secondary that borrows the primary's key.
* fix(crypto): move CryptoEngine::hash out of the PKI guard
hash() is plain SHA256, and PortduinoGlue calls it unguarded to derive a MAC address from the CH341 serial, so MESHTASTIC_EXCLUDE_PKI=1 failed to compile. With this and the previous commit that build links clean.
* fix(channels): resolve primaryIndex before hashing in onConfigChanged
A keyless secondary resolves its key through primaryIndex, so fixing up channels in the same pass that finds the primary hashed the early slots against the previous one and cleared their use_aead against a key they do in fact inherit. Split the pass, and re-run the fixups in the no-primary restore path, which moves the primary after the fact. Also splits the thirteen AES-CCM AEAD scenarios into separate test functions so a Unity failure names the one that broke.
* chore(crypto): trim the AEAD maintainer commits
Shortens three comments that outgrew the one-to-two line house rule, drops a truncated sentence and the braces around a single return in perhapsEncode(), and removes a channel test that the moved-primary regression test already covers. No behaviour change.
---------
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
* Fix out-of-bounds write in aes_ccm_encr for partial blocks
aes_ccm_encr() writes a full 16-byte AES block to the output before XOR-ing with
the input, so a trailing partial block writes up to 15 bytes past the length the
caller asked for. Every caller in the tree passes a buffer with enough slack, so
nothing misbehaves today, but the decrypt path clears it by only a few bytes.
Encrypt into a temporary block and XOR out of it, matching what
aes_ccm_encr_auth() and aes_ccm_decr_auth() already do in this same file. The
ciphertext is unchanged.
* Add regression test for the CCM partial-block write and drop stale workarounds
The guard bytes past the caller's buffer catch the overflow without relying on a
sanitizer, so the test is meaningful in the native environment too.
encryptCurve25519() no longer needs to write extraNonce before aes_ccm_ae(): the
call stays inside numBytes now, so the copy after it is the only one required.
The comment warning about the 15-byte overshoot no longer describes the code.
Audit of the XEdDSA packet-signing implementation (#10478) surfaced several
issues in when unsigned packets are accepted on receive or emitted on send.
This fixes them and adds regression coverage.
- Unicast NodeInfo exchange no longer breaks against signer nodes: the
NodeInfoModule downgrade drop is gated to broadcasts, since senders never
sign unicast (want_response replies, directed exchanges).
- Replace the payload-size sign heuristic with an exact encoded-size gate
(signedDataFits) and mirror it on the receive side, removing a dead band
where 167-168 B broadcasts were signed then failed TOO_LARGE.
- Extract the receive policy into checkXeddsaReceivePolicy() and apply it to
plaintext-MQTT decoded downlink, which previously skipped signature
verification and downgrade protection entirely.
- Reject signatures whose length is neither 0 nor 64 as malformed, so a
crafted partial signature can't inflate the size estimate and dodge the
unsigned-downgrade drop.
- Hold cryptLock on the MQTT verify path (shared Ed25519 key cache).
- Clear any client-preset signature on packets we originate, on all builds.
- Randomized (hedged) signing per the Signal XEdDSA spec: bump the
meshtastic/Crypto pin to the build where XEdDSA::sign mixes 32 bytes of
caller randomness into the nonce as Z (meshtastic/Crypto#3), and seed those
bytes in xeddsa_sign from HardwareRNG (checked, with a seeded-CSPRNG
fallback). test_crypto pins that repeated signs differ and both verify.
Adds test coverage: test_packet_signing groups A-E (receive matrix, send
policy, NodeInfo backstop, encoding invariants, decoded-ingress policy),
test_mqtt end-to-end downlink cases, and a test_crypto randomization check.
* Test commit for XEdDSA support
* Update to Crypto lib in Meshtatic org
* Generate a new node identity on key generation (#7628)
* Generate a new node identity on key generation
* Fixes
* Fixes
* Fixes
* Messed up
* Fixes
* Update src/modules/AdminModule.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/mesh/NodeDB.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Figured it out!
* Cleanup
* Update src/mesh/NodeDB.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/mesh/NodeDB.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update src/modules/AdminModule.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update crypto commit hash
* Some fixes for xeddsa pr (#9610)
* fix: add null check for getMeshNode() in NodeInfoModule
getMeshNode() can return nullptr for unknown nodes. Dereferencing
without a check crashes the firmware when receiving NodeInfo from
a node not yet in the database.
* fix: enforce XEdDSA signature verification and prevent stripping
Previously, failed signature verification still allowed the packet
through, making signatures purely cosmetic. Now:
- Failed verification drops the packet (DECODE_FAILURE)
- Successfully verified nodes get HAS_XEDDSA_SIGNED bitfield set
- Unsigned packets from previously-signing nodes are rejected
- Log levels reduced from WARN/ERROR to DEBUG/WARN as appropriate
* fix: include packet metadata in XEdDSA signature
The signature now covers [fromNode | packetId | portnum | payload]
instead of just the payload bytes. This prevents:
- Replay attacks (different packetId fails verification)
- Reattribution (different fromNode fails verification)
- Portnum redirection (different portnum fails verification)
Also adds a key initialization check to xeddsa_sign (returns false
if XEdDSA keys are all zeros) and checks the return value in the
encode path.
* fix: handle existing key pair in AdminModule security config
When a user provides both a valid private key and public key via
admin config, the crypto engine's DH private key and owner public
key were never loaded. DMs and XEdDSA signing would silently break.
Add an else branch to load both keys into the crypto engine.
* perf: cache Ed25519 public key conversion in xeddsa_verify
curve_to_ed_pub() performs field element parsing, inversion, and
multiplication on every call. Since packets from the same node
tend to arrive in bursts, a single-entry cache avoids repeating
this expensive conversion for consecutive packets from one sender.
* fix: skip identity cleanup when node number is unchanged
createNewIdentity() was called on every generateCryptoKeyPair(),
including normal boots where the same key is regenerated. This
caused unnecessary NodeDB writes and old-node cleanup logic to
run when the node number hadn't actually changed.
Also fixes only zeroing byte[0] of the old node's public key
instead of clearing the entire array.
* fix: replace hardcoded 120 with derived XEDDSA_SIGNATURE_SIZE constant
The payload size check for XEdDSA signing used a magic number (120).
Replace with a derivation from DATA_PAYLOAD_LEN and XEDDSA_SIGNATURE_SIZE
so the limit adjusts automatically if constants change. This also
increases the max signable payload from 120 to 169 bytes, which is
still safe since the actual encoded size is checked after pb_encode.
* fix: add const qualifiers to XEdDSA verify and curve_to_ed_pub inputs
pubKey, payload, and signature parameters in xeddsa_verify are
input-only and should not be modified. Same for curve_pubkey in
curve_to_ed_pub.
* chore: remove commented-out old Crypto dependency in portduino.ini
* Leave out the admin module change for now
---------
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
* trunk
* protobuf re-update
* Protobufs
* Merge resolution fix
* Put XEDDSA on the right bit
* NodeDB update to new nodeInfoLite accessors, etc
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Refine unsigned packet rejection logic in Router (#10534)
* use hardware random to fill the first 32 signature bytes with entropy prior to signing.
* Add XEdDSA packet-signing policy tests and update dependencies for macos
* Minor fixes
* integrate XEdDSA support and update dependencies across multiple modules
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Wessel <github@weebl.me>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
I thought git would be smart enough to understand all the whitespace changes but even with all the flags I know to make it ignore theses it still blows up if there are identical changes on both sides.
I have a solution but it require creating a new commit at the merge base for each conflicting PR and merging it into develop.
I don't think blowing up all PRs is worth for now, maybe if we can coordinate this for V3 let's say.
This reverts commit 0d11331d18.
* 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>