#!/usr/bin/env python3 """Exercise the production fleet envelope with real Ed25519, never native mocks.""" import hashlib 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'; } 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; if (!Fleet::parseTargets(argv[3], strlen(argv[3]), targets)) { 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 && (std::string(argv[1]) == "decode" || std::string(argv[1]) == "decode2")) { 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; if (!Fleet::decode(publisher, key.data(), bytes.data(), bytes.size(), uint32_t(std::stoul(argv[5])), self.data(), output)) { 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 (std::string(argv[1]) == "decode2") 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("