mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-14 22:07:17 +00:00
- tests/ (114 host cases over production sources), the CI workflow (ASan/UBSan on PRs and master pushes) and REGRESSION_TEST_PLAN.md are tracked - .gitignore grouped and deduplicated (173 -> 96 lines, same ignored set)
86 lines
4.6 KiB
C++
86 lines
4.6 KiB
C++
#include "test.h"
|
|
#include <mesh/Identity.h>
|
|
#include <algorithm>
|
|
#include <array>
|
|
|
|
namespace {
|
|
// RFC 8032 section 7.1, tests 1 and 2 (public test keys, never device identities).
|
|
// https://www.rfc-editor.org/rfc/rfc8032.txt
|
|
struct Vector { const char *seed; const char *pub; const char *message; const char *signature; };
|
|
const Vector vectors[] = {
|
|
{"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
|
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", "",
|
|
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155"
|
|
"5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"},
|
|
{"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
|
|
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", "72",
|
|
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da"
|
|
"085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00"}
|
|
};
|
|
std::vector<uint8_t> hex(const char *text) {
|
|
std::string value(text); CHECK(value.size() % 2 == 0);
|
|
std::vector<uint8_t> result;
|
|
for (size_t i = 0; i < value.size(); i += 2) result.push_back(std::stoul(value.substr(i, 2), nullptr, 16));
|
|
return result;
|
|
}
|
|
mesh::LocalIdentity identity(unsigned index = 0) {
|
|
mesh::LocalIdentity id; auto seed = hex(vectors[index].seed); id.fromSeed(seed.data()); return id;
|
|
}
|
|
}
|
|
TEST(identity_vectors, "UNIT-IDENTITY-001", "RFC8032 Ed25519 public keys and exact signatures") {
|
|
for (const auto& vector : vectors) {
|
|
auto seed = hex(vector.seed), pub = hex(vector.pub), msg = hex(vector.message), signature = hex(vector.signature);
|
|
mesh::LocalIdentity id; id.fromSeed(seed.data());
|
|
CHECK(std::equal(pub.begin(), pub.end(), id.pub_key));
|
|
uint8_t output[64], empty = 0;
|
|
auto data = msg.empty() ? &empty : msg.data();
|
|
id.sign(output, data, int(msg.size()));
|
|
CHECK(std::equal(signature.begin(), signature.end(), output));
|
|
CHECK(id.verify(signature.data(), data, int(msg.size())));
|
|
}
|
|
}
|
|
TEST(identity_tamper, "UNIT-IDENTITY-002", "Reject altered signature message and wrong signer") {
|
|
auto id = identity(1); auto signature = hex(vectors[1].signature);
|
|
uint8_t msg = 0x72;
|
|
for (unsigned byte = 0; byte < 64; ++byte) {
|
|
auto bad = signature; bad[byte] ^= 1; CHECK(!id.verify(bad.data(), &msg, 1));
|
|
}
|
|
msg ^= 1; CHECK(!id.verify(signature.data(), &msg, 1));
|
|
msg = 0x72; CHECK(!identity(0).verify(signature.data(), &msg, 1));
|
|
}
|
|
TEST(identity_storage, "UNIT-IDENTITY-003", "Canonical legacy and private-only storage retain signing identity") {
|
|
auto id = identity();
|
|
uint8_t wire[96], storage[96], privateOnly[64];
|
|
CHECK(id.writeTo(wire, sizeof(wire)) == 96 && id.writeToStorage(storage, sizeof(storage)) == 96);
|
|
CHECK(id.writeTo(privateOnly, sizeof(privateOnly)) == 64);
|
|
CHECK(std::equal(wire, wire + 64, storage + 32));
|
|
CHECK(std::equal(wire + 64, wire + 96, storage));
|
|
for (auto fixture : {std::vector<uint8_t>(wire, wire + 96), std::vector<uint8_t>(storage, storage + 96),
|
|
std::vector<uint8_t>(privateOnly, privateOnly + 64)}) {
|
|
mesh::LocalIdentity restored; CHECK(restored.readFromStorage(fixture.data(), fixture.size()));
|
|
CHECK(restored.matches(id));
|
|
uint8_t sig[64], empty = 0; restored.sign(sig, &empty, 0);
|
|
auto expected = hex(vectors[0].signature); CHECK(std::equal(expected.begin(), expected.end(), sig));
|
|
}
|
|
}
|
|
TEST(identity_storage_reject, "UNIT-IDENTITY-004", "Malformed storage fails without replacing existing identity") {
|
|
auto id = identity(), other = identity(1);
|
|
uint8_t before[96], bad[96], after[96]; id.writeToStorage(before, 96); other.writeToStorage(bad, 96);
|
|
bad[0] ^= 1;
|
|
CHECK(!id.readFromStorage(bad, 96));
|
|
for (size_t size : {0u, 31u, 63u, 65u, 95u}) CHECK(!id.readFromStorage(bad, size));
|
|
id.writeToStorage(after, 96); CHECK(std::equal(before, before + 96, after));
|
|
std::array<uint8_t, 98> output; output.fill(0xCC);
|
|
CHECK(id.writeToStorage(output.data() + 1, 95) == 0);
|
|
CHECK(std::all_of(output.begin(), output.end(), [](auto b) { return b == 0xCC; }));
|
|
CHECK(id.writeToStorage(output.data() + 1, 96) == 96);
|
|
CHECK(output.front() == 0xCC && output.back() == 0xCC);
|
|
}
|
|
TEST(identity_ecdh, "UNIT-IDENTITY-005", "Real Ed25519-to-X25519 exchange agrees in both directions") {
|
|
auto alice = identity(), bob = identity(1);
|
|
uint8_t ab[32], ba[32]; alice.calcSharedSecret(ab, bob); bob.calcSharedSecret(ba, alice);
|
|
CHECK(std::equal(ab, ab + 32, ba));
|
|
CHECK(std::any_of(ab, ab + 32, [](auto b) { return b != 0; }));
|
|
// Pairwise property only: an independent ECDH known-answer fixture remains planned.
|
|
}
|