Files
ZephCore/zephcore/include/mesh/Identity.h
T
liquidraver a7d073900e core: restore upstream shape for the shared mesh core
- Mesh, Dispatcher, Packet, packet pool and the core headers rebuilt from
  Arduino MeshCore's text; ZephCore changes re-applied, each fenced
  `// ZEPHCORE:` (diff vs upstream: 4540 -> 1147 lines)
- upstream header layout; MainBoard gains handleCommand/loop and the other
  upstream members; RNG::nextInt back in Utils.cpp
- packet logging moved to the roles' log hooks (helpers/PacketLog.h)
- zero-hop direct ACKs deduplicated and length-checked as upstream
- RadioIncludes.h -> src/, LoRaConfig.h -> helpers/
- .editorconfig: shared files keep upstream formatting

Verified: 10 role builds, per-function disassembly review, bench DM/ACK
regression (T1000-E, Wio L1, XIAO S3 on 1.17.4) incl. via live repeater.
2026-09-23 13:39:48 +02:00

125 lines
5.0 KiB
C++

#pragma once
#include <mesh/Utils.h>
#include <stddef.h>
#include <string.h>
namespace mesh {
/**
* \brief An identity in the mesh, with given Ed25519 public key, ie. a party whose signatures can be VERIFIED.
*/
class Identity {
public:
uint8_t pub_key[PUB_KEY_SIZE];
Identity();
Identity(const char* pub_hex);
Identity(const uint8_t* _pub) { memcpy(pub_key, _pub, PUB_KEY_SIZE); }
int copyHashTo(uint8_t* dest) const {
memcpy(dest, pub_key, PATH_HASH_SIZE); // hash is just prefix of pub_key
return PATH_HASH_SIZE;
}
int copyHashTo(uint8_t* dest, uint8_t len) const {
memcpy(dest, pub_key, len); // hash is just prefix of pub_key
return len;
}
bool isHashMatch(const uint8_t* hash) const {
return memcmp(hash, pub_key, PATH_HASH_SIZE) == 0;
}
bool isHashMatch(const uint8_t* hash, uint8_t len) const {
return memcmp(hash, pub_key, len) == 0;
}
/**
* \brief Performs Ed25519 signature verification.
* \param sig IN - must be SIGNATURE_SIZE buffer.
* \param message IN - the original message which was signed.
* \param msg_len IN - the length in bytes of message.
* \returns true, if signature is valid.
*/
bool verify(const uint8_t* sig, const uint8_t* message, int msg_len) const;
bool matches(const Identity& other) const { return memcmp(pub_key, other.pub_key, PUB_KEY_SIZE) == 0; }
bool matches(const uint8_t* other_pubkey) const { return memcmp(pub_key, other_pubkey, PUB_KEY_SIZE) == 0; }
// ZEPHCORE: buffer I/O instead of Arduino Stream (readFrom/writeTo/printTo).
bool readFrom(const uint8_t* src, size_t len);
bool writeTo(uint8_t* dest, size_t max_len) const;
};
/**
* \brief An Identity generated on THIS device, ie. with public/private Ed25519 key pair being on this device.
*/
class LocalIdentity : public Identity {
uint8_t prv_key[PRV_KEY_SIZE];
public:
LocalIdentity();
LocalIdentity(const char* prv_hex, const char* pub_hex);
LocalIdentity(RNG* rng); // create new random
// ZEPHCORE: derive the Ed25519 key pair from a 32-byte seed produced
// externally (ZephyrRNG::mixIdentitySeed), instead of through an RNG wrapper.
void fromSeed(const uint8_t seed[SEED_SIZE]);
/**
* \brief Ed25519 digital signature.
* \param sig OUT - must be SIGNATURE_SIZE buffer.
* \param message IN - the raw message bytes to sign.
* \param msg_len IN - the length in bytes of message.
*/
void sign(uint8_t* sig, const uint8_t* message, int msg_len) const;
/**
* \brief the ECDH key exhange, with Ed25519 public key transposed to Ex25519.
* \param secret OUT - the 'shared secret' (must be PUB_KEY_SIZE bytes)
* \param other IN - the second party in the exchange.
*/
void calcSharedSecret(uint8_t* secret, const Identity& other) const { calcSharedSecret(secret, other.pub_key); }
/**
* \brief the ECDH key exhange, with Ed25519 public key transposed to Ex25519.
* \param secret OUT - the 'shared secret' (must be PUB_KEY_SIZE bytes)
* \param other_pub_key IN - the public key of second party in the exchange (must be PUB_KEY_SIZE bytes)
*/
void calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_key) const;
/**
* \brief Validates that a given private key can be used for ECDH / shared-secret operations.
* \param prv IN - the private key to validate (must be PRV_KEY_SIZE bytes)
* \returns true, if the private key is valid for login.
*/
static bool validatePrivateKey(const uint8_t prv[64]);
// ZEPHCORE: no Arduino Stream I/O. The buffer format below (prv || pub, or prv
// alone) is protocol-facing — CMD_EXPORT/IMPORT_PRIVATE_KEY and the `prv.key`
// CLI — and stays byte-identical to upstream. Never use it for flash storage.
bool readFrom(const uint8_t* src, size_t len);
size_t writeTo(uint8_t* dest, size_t max_len) const;
// ZEPHCORE: on-flash format pub || prv, matching upstream's IdentityStore
// (LocalIdentity::writeTo(Stream&)). Kept apart from the buffer format because
// the two orders differ, and conflating them is how a node ends up advertising
// a key it cannot sign for.
size_t writeToStorage(uint8_t* dest, size_t max_len) const;
// Tolerant reader for every layout either project has written, told apart by
// deriving pub from the candidate prv (the stored pub is a checksum of it):
// len == 64 -> prv only (ZephCore repeater/room <= 1.17.x)
// len == 96 -> pub || prv (Arduino MeshCore, ZephCore >= 1.17.2)
// len == 96 -> prv || pub (ZephCore companion <= 1.17.x)
// pub_key is always the derived value. False when no layout coheres.
bool readFromStorage(const uint8_t* src, size_t len);
// Last resort after readFromStorage() fails: adopt one half of a 96-byte blob
// if exactly one half passes validatePrivateKey() (ambiguity = refuse). RAM
// only; the file is left alone so the evidence survives and the warning
// repeats every boot. Changes the advertised pub_key to the one the private
// key actually owns.
bool recoverFromStorage(const uint8_t* src, size_t len);
};
}