108 lines
3.2 KiB
C++
108 lines
3.2 KiB
C++
#pragma once
|
|
|
|
#include <Arduino.h>
|
|
#include "ContactCachePolicy.h"
|
|
#include <Identity.h>
|
|
#include <Utils.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
namespace mesh {
|
|
|
|
class ContactSecretBackend {
|
|
public:
|
|
virtual bool readSavedSecret(const uint8_t peer[32], const uint8_t identity[32],
|
|
uint8_t secret[32]) = 0;
|
|
virtual bool saveSecret(const uint8_t peer[32], const uint8_t identity[32],
|
|
const uint8_t secret[32]) = 0;
|
|
};
|
|
|
|
class ContactSecretCache {
|
|
struct Entry {
|
|
uint8_t peer[32] = {};
|
|
uint8_t secret[32] = {};
|
|
uint8_t age = 0;
|
|
bool valid = false;
|
|
} _entries[16];
|
|
uint8_t _identity[PRV_KEY_SIZE + PUB_KEY_SIZE] = {};
|
|
#if MESH_CONTACT_SECRET_FLASH_CACHE
|
|
uint8_t _identity_hash[32] = {};
|
|
#endif
|
|
bool _has_identity = false;
|
|
#if MESH_CONTACT_SECRET_FLASH_CACHE
|
|
ContactSecretBackend* _backend = nullptr;
|
|
#endif
|
|
public:
|
|
uint32_t ram_hits = 0;
|
|
uint32_t flash_hits = 0;
|
|
uint32_t calculations = 0;
|
|
uint32_t save_failures = 0;
|
|
uint32_t last_flash_read_us = 0;
|
|
uint32_t last_calculation_us = 0;
|
|
|
|
void attach(ContactSecretBackend* backend) {
|
|
#if MESH_CONTACT_SECRET_FLASH_CACHE
|
|
_backend = backend;
|
|
#else
|
|
(void)backend;
|
|
#endif
|
|
}
|
|
const uint8_t* get(const LocalIdentity& self, const uint8_t peer[32]) {
|
|
uint8_t identity[sizeof(_identity)];
|
|
self.writeTo(identity, sizeof(identity));
|
|
// Compare the complete key pair, including private material. An identity
|
|
// import must invalidate RAM and flash lookups even if its public bytes
|
|
// happen to be unchanged. Only a SHA-256 fingerprint is stored with a page.
|
|
if (!_has_identity || memcmp(identity, _identity, sizeof(identity)) != 0) {
|
|
for (auto& e : _entries) { memset(e.secret, 0, sizeof(e.secret)); e.valid = false; }
|
|
memcpy(_identity, identity, sizeof(identity));
|
|
#if MESH_CONTACT_SECRET_FLASH_CACHE
|
|
Utils::sha256(_identity_hash, sizeof(_identity_hash), identity, sizeof(identity));
|
|
#endif
|
|
_has_identity = true;
|
|
}
|
|
int chosen = -1;
|
|
bool hit = false;
|
|
for (int i = 0; i < 16; ++i) {
|
|
if (_entries[i].valid && memcmp(_entries[i].peer, peer, 32) == 0) {
|
|
chosen = i;
|
|
hit = true;
|
|
break;
|
|
}
|
|
if (chosen < 0 || !_entries[i].valid ||
|
|
(_entries[chosen].valid && _entries[i].age > _entries[chosen].age)) chosen = i;
|
|
}
|
|
Entry& e = _entries[chosen];
|
|
if (hit) {
|
|
++ram_hits;
|
|
} else {
|
|
e.valid = false;
|
|
memcpy(e.peer, peer, 32);
|
|
#if MESH_CONTACT_SECRET_FLASH_CACHE
|
|
const uint32_t started = micros();
|
|
if (_backend && _backend->readSavedSecret(peer, _identity_hash, e.secret)) {
|
|
last_flash_read_us = micros() - started;
|
|
++flash_hits;
|
|
} else
|
|
#endif
|
|
{
|
|
const uint32_t started = micros();
|
|
self.calcSharedSecret(e.secret, peer);
|
|
last_calculation_us = micros() - started;
|
|
++calculations;
|
|
#if MESH_CONTACT_SECRET_FLASH_CACHE
|
|
if (_backend && !_backend->saveSecret(peer, _identity_hash, e.secret)) ++save_failures;
|
|
#endif
|
|
}
|
|
e.valid = true;
|
|
}
|
|
for (auto& other : _entries) if (other.valid && other.age < 16) ++other.age;
|
|
e.age = 0;
|
|
return e.secret;
|
|
}
|
|
};
|
|
|
|
ContactSecretCache& contactSecretCache();
|
|
|
|
} // namespace mesh
|