#!/usr/bin/env python3 """Exercise the production fleet envelope with real Ed25519, never native mocks.""" import hashlib import math from pathlib import Path import struct import subprocess import tempfile import unittest from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey ROOT = Path(__file__).resolve().parents[1] NOW = 1791500000 CHANNEL = bytes(range(1, 17)) DOMAIN = b"MeshCoreFleet1" PUBLISHER = Ed25519PrivateKey.from_private_bytes(bytes(range(1, 33))) PUBLIC = PUBLISHER.public_key().public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw) OTHER = Ed25519PrivateKey.from_private_bytes(bytes(range(33, 65))) OTHER_PUBLIC = OTHER.public_key().public_bytes( serialization.Encoding.Raw, serialization.PublicFormat.Raw) FIXTURE = r''' #include #include #include #include #include #include #include #include inline unsigned fleet_sign_calls = 0; // The production Identity interface is retained. OpenSSL implements its // signatures here so these tests cannot inherit native's always-true mock. namespace mesh { Identity::Identity() { memset(pub_key, 0, sizeof(pub_key)); } LocalIdentity::LocalIdentity(RNG* rng) { memset(prv_key, 0, sizeof(prv_key)); rng->random(prv_key, 32); EVP_PKEY* key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, prv_key, 32); assert(key); size_t length = sizeof(pub_key); assert(EVP_PKEY_get_raw_public_key(key, pub_key, &length) == 1 && length == sizeof(pub_key)); EVP_PKEY_free(key); } void LocalIdentity::sign(uint8_t* signature, const uint8_t* message, int length) const { ++fleet_sign_calls; EVP_PKEY* key = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, prv_key, 32); EVP_MD_CTX* context = EVP_MD_CTX_new(); assert(key && context); assert(EVP_DigestSignInit(context, nullptr, nullptr, nullptr, key) == 1); size_t size = SIGNATURE_SIZE; assert(EVP_DigestSign(context, signature, &size, message, size_t(length)) == 1); assert(size == SIGNATURE_SIZE); EVP_MD_CTX_free(context); EVP_PKEY_free(key); } bool Identity::verify(const uint8_t* signature, const uint8_t* message, int length) const { EVP_PKEY* key = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_key, sizeof(pub_key)); EVP_MD_CTX* context = EVP_MD_CTX_new(); assert(key && context); assert(EVP_DigestVerifyInit(context, nullptr, nullptr, nullptr, key) == 1); const bool valid = EVP_DigestVerify(context, signature, SIGNATURE_SIZE, message, size_t(length)) == 1; EVP_MD_CTX_free(context); EVP_PKEY_free(key); return valid; } } // namespace mesh struct FixedRng : mesh::RNG { void random(uint8_t* output, size_t size) override { for (size_t i = 0; i < size; ++i) output[i] = uint8_t(i + 1); } }; std::vector unhex(const char* input) { const std::string text(input); assert(text.size() % 2 == 0); std::vector bytes(text.size() / 2); for (size_t i = 0; i < bytes.size(); ++i) bytes[i] = uint8_t(std::stoul(text.substr(i * 2, 2), nullptr, 16)); return bytes; } void hex(const uint8_t* bytes, size_t size) { constexpr char digits[] = "0123456789abcdef"; for (size_t i = 0; i < size; ++i) std::cout << digits[bytes[i] >> 4] << digits[bytes[i] & 15]; std::cout << '\n'; } struct RegionNames { std::vector configured, home; static bool match(void* context, mesh::FleetCommand::RegionTarget kind, const char* name, size_t length) { const auto& regions = *static_cast(context); const auto& names = kind == mesh::FleetCommand::RegionTarget::Configured ? regions.configured : regions.home; for (const auto& candidate : names) { const char* canonical = candidate.c_str() + (!candidate.empty() && candidate[0] == '#'); if (strlen(canonical) == length && !memcmp(canonical, name, length)) return true; } return false; } }; struct GeoLocation { bool known = false; int32_t latitude = 0, longitude = 0; static bool match(void* context, int32_t latitude, int32_t longitude, uint32_t radius) { const auto& location = *static_cast(context); return location.known && mesh::FleetCommand::withinRadius(latitude, longitude, radius, location.latitude, location.longitude); } }; int main(int argc, char** argv) { using Fleet = mesh::FleetCommand; const std::string operation = argc > 1 ? argv[1] : ""; if (argc == 7 && (operation == "encode" || operation == "encode-wide")) { FixedRng rng; mesh::LocalIdentity publisher(&rng); const auto key = unhex(argv[2]); assert(key.size() == Fleet::KeySize); std::array target; if (!Fleet::parseTarget(argv[3], target.data())) { std::cout << "reject\n"; return 0; } std::array output; output.fill(0xA5); const unsigned signed_before = fleet_sign_calls; const size_t capacity = operation == "encode-wide" ? output.size() : Fleet::MaxEnvelopeLength; const size_t size = Fleet::encode(publisher, key.data(), uint32_t(std::stoul(argv[4])), uint32_t(std::stoul(argv[5])), target.data(), argv[6], output.data(), capacity); assert(size <= Fleet::MaxEnvelopeLength); if (!size) { assert(fleet_sign_calls == signed_before); for (uint8_t byte : output) assert(byte == 0xA5); std::cout << "reject\n"; } else { assert(fleet_sign_calls == signed_before + 1); for (size_t i = size; i < output.size(); ++i) assert(output[i] == 0xA5); hex(output.data(), size); } return 0; } if (argc == 7 && (operation == "encode2" || operation == "encode2-wide")) { FixedRng rng; mesh::LocalIdentity publisher(&rng); const auto key = unhex(argv[2]); assert(key.size() == Fleet::KeySize); Fleet::Targets targets; memset(&targets, 0xA5, sizeof(targets)); if (!Fleet::parseTargets(argv[3], strlen(argv[3]), targets)) { assert(!targets.count && !targets.length); for (uint8_t byte : targets.data) assert(byte == 0); assert(fleet_sign_calls == 0); std::cout << "reject\n"; return 0; } std::array output; output.fill(0xA5); const unsigned signed_before = fleet_sign_calls; const size_t capacity = operation == "encode2-wide" ? output.size() : Fleet::MaxEnvelopeLength; const size_t size = Fleet::encode(publisher, key.data(), uint32_t(std::stoul(argv[4])), uint32_t(std::stoul(argv[5])), targets, argv[6], output.data(), capacity); assert(size <= Fleet::MaxEnvelopeLength); if (!size) { assert(fleet_sign_calls == signed_before); for (uint8_t byte : output) assert(byte == 0xA5); std::cout << "reject\n"; } else { assert(fleet_sign_calls == signed_before + 1); for (size_t i = size; i < output.size(); ++i) assert(output[i] == 0xA5); hex(output.data(), size); } return 0; } if (argc == 5 && operation == "fragment") { const auto envelope = unhex(argv[2]); const unsigned index = unsigned(std::stoul(argv[3])); assert(index <= 255); const size_t capacity = std::stoul(argv[4]); std::array output; assert(capacity <= output.size()); output.fill(0xA5); const unsigned signed_before = fleet_sign_calls; const size_t size = Fleet::fragment(envelope.data(), envelope.size(), uint8_t(index), output.data(), capacity); assert(fleet_sign_calls == signed_before && size <= Fleet::MaxPayloadLength); if (!size) { for (uint8_t byte : output) assert(byte == 0xA5); std::cout << "reject\n"; } else { for (size_t i = size; i < output.size(); ++i) assert(output[i] == 0xA5); hex(output.data(), size); } return 0; } if (argc == 3 && operation == "parse-fragment") { const auto payload = unhex(argv[2]); Fleet::Fragment output{123, 300, 1, payload.data(), 100}; if (!Fleet::parseFragment(payload.data(), payload.size(), output)) { assert(!output.sequence && !output.total_length && !output.index && !output.data && !output.length); std::cout << "reject\n"; } else { std::cout << output.sequence << '\n' << output.total_length << '\n' << unsigned(output.index) << '\n'; hex(output.data, output.length); } return 0; } if (argc == 7 && operation == "within-radius") { std::cout << int(Fleet::withinRadius(int32_t(std::stol(argv[2])), int32_t(std::stol(argv[3])), uint32_t(std::stoul(argv[4])), int32_t(std::stol(argv[5])), int32_t(std::stol(argv[6])))) << '\n'; return 0; } if ((argc == 7 && (operation == "decode" || operation == "decode2")) || (argc == 8 && operation == "decode-regions") || (argc == 11 && operation == "decode-targets")) { const auto public_key = unhex(argv[2]), key = unhex(argv[3]), self = unhex(argv[4]), bytes = unhex(argv[6]); assert(public_key.size() == PUB_KEY_SIZE && self.size() == PUB_KEY_SIZE && key.size() == Fleet::KeySize); mesh::Identity publisher(public_key.data()); Fleet::Decoded output; RegionNames regions; if (operation == "decode-regions" || operation == "decode-targets") { const std::string selected(argv[7]); size_t cursor = 0; while (cursor < selected.size()) { const size_t end = selected.find(',', cursor); const std::string name = selected.substr(cursor, end == std::string::npos ? end : end - cursor); if (!name.compare(0, 7, "region:")) regions.configured.push_back(name.substr(7)); else { assert(!name.compare(0, 5, "home:")); regions.home.push_back(name.substr(5)); } if (end == std::string::npos) break; cursor = end + 1; } } GeoLocation location; if (operation == "decode-targets") { assert(!strcmp(argv[8], "0") || !strcmp(argv[8], "1")); location.known = !strcmp(argv[8], "1"); location.latitude = int32_t(std::stol(argv[9])); location.longitude = int32_t(std::stol(argv[10])); } if (!Fleet::decode(publisher, key.data(), bytes.data(), bytes.size(), uint32_t(std::stoul(argv[5])), self.data(), output, operation == "decode-regions" || operation == "decode-targets" ? RegionNames::match : nullptr, ®ions, operation == "decode-targets" ? GeoLocation::match : nullptr, &location)) { assert(output.sequence == 0 && output.expires == 0 && !output.broadcast && output.command[0] == 0); std::cout << "reject\n"; } else { std::cout << output.sequence << '\n' << output.expires << '\n' << output.command << '\n'; if (operation != "decode") std::cout << int(output.broadcast) << '\n'; } return 0; } return 2; } ''' def envelope(command="get radio2", sequence=NOW, expires=NOW + 120, target=b"\0" * 16, channel=CHANNEL, publisher=PUBLISHER): command = command.encode("ascii") if isinstance(command, str) else command unsigned = b"FMC1" + struct.pack("