#pragma once #include "AES.h" #include "CTR.h" #include "concurrency/LockGuard.h" #include "configuration.h" #include "mesh-pb-constants.h" #include #include extern concurrency::Lock *cryptLock; struct CryptoKey { uint8_t bytes[32]; /// # of bytes, or -1 to mean "invalid key - do not use" int8_t length; }; /** * see docs/software/crypto.md for details. * */ #define MAX_BLOCKSIZE 256 #define TEST_CURVE25519_FIELD_OPS // Exposes Curve25519::isWeakPoint() for testing keys #define XEDDSA_SIGNATURE_SIZE 64 class CryptoEngine { public: #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t public_key[32] = {0}; #endif virtual ~CryptoEngine() {} #if !(MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey); virtual bool ensurePkiKeys(meshtastic_Config_SecurityConfig &security, meshtastic_User &user); #endif #if !(MESHTASTIC_EXCLUDE_XEDDSA) bool xeddsa_sign(uint32_t fromNode, uint32_t packetId, uint32_t portnum, const uint8_t *payload, size_t payloadLen, uint8_t *signature); bool xeddsa_verify(const uint8_t *pubKey, uint32_t fromNode, uint32_t packetId, uint32_t portnum, const uint8_t *payload, size_t payloadLen, const uint8_t *signature); #endif void setDHPrivateKey(uint8_t *_private_key); // The remotePublic key parameter takes the public_key bytes container from // a stored node header. NodeInfoLite is the on-device storage type since // the slim refactor flattened UserLite into it. virtual bool encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtastic_NodeInfoLite_public_key_t remotePublic, uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut); virtual bool decryptCurve25519(uint32_t fromNode, meshtastic_NodeInfoLite_public_key_t remotePublic, uint64_t packetNum, size_t numBytes, const uint8_t *bytes, uint8_t *bytesOut); virtual bool setDHPublicKey(uint8_t *publicKey); virtual void hash(uint8_t *bytes, size_t numBytes); virtual void aesSetKey(const uint8_t *key, size_t key_len); virtual void aesEncrypt(uint8_t *in, uint8_t *out); std::unique_ptr aes = nullptr; #endif /** * Set the key used for encrypt, decrypt. * * As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext. * * @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt) * @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the * provided pointer) */ virtual void setKey(const CryptoKey &k); /** * Encrypt a packet * * @param bytes is updated in place */ virtual void encryptPacket(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes); virtual void encryptAESCtr(CryptoKey key, uint8_t *nonce, size_t numBytes, uint8_t *bytes); #ifndef PIO_UNIT_TESTING protected: #endif /** Our per packet nonce */ uint8_t nonce[16] = {0}; CryptoKey key = {}; #if !(MESHTASTIC_EXCLUDE_PKI) uint8_t shared_key[32] = {0}; uint8_t private_key[32] = {0}; #if !(MESHTASTIC_EXCLUDE_XEDDSA) uint8_t xeddsa_public_key[32] = {0}; uint8_t xeddsa_private_key[32] = {0}; void curve_to_ed_pub(const uint8_t *curve_pubkey, uint8_t *ed_pubkey); // Single-entry cache for curve_to_ed_pub conversion (avoids expensive field inversion per packet) uint8_t cached_curve_pubkey[32] = {0}; uint8_t cached_ed_pubkey[32] = {0}; #endif #endif /** * Init our 128 bit nonce for a new packet * * The NONCE is constructed by concatenating (from MSB to LSB): * a 64 bit packet number (stored in little endian order) * a 32 bit sending node number (stored in little endian order) * a 32 bit block counter (starts at zero) */ void initNonce(uint32_t fromNode, uint64_t packetId, uint32_t extraNonce = 0); }; extern CryptoEngine *crypto;