Files
HaloKeymind/test/test_client_acl_response.py
T

111 lines
5.1 KiB
Python

#!/usr/bin/env python3
"""Execute legacy ACL replies through the production route and replay guards."""
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
from test_replay_reset_integration import extract_braced
ROOT = Path(__file__).resolve().parents[1]
FIXTURE = ROOT / "test/fixtures/client_acl_response"
# Unmodified official decoder, meshcore-dev/meshcore_py commit
# 1e6fdd47a36ae32c30246a8486b7a9492665579f, src/meshcore/parsing.py (MIT).
# https://github.com/meshcore-dev/meshcore_py/blob/1e6fdd47a36ae32c30246a8486b7a9492665579f/src/meshcore/parsing.py
def parse_acl(buf):
i = 0
res = []
while i + 7 <= len(buf):
key = buf[i : i + 6].hex()
perm = buf[i + 6]
if key != "000000000000":
res.append({"key": key, "perm": perm})
i = i + 7
return res
def production_acl_methods():
source = (ROOT / "examples/simple_repeater/MyMesh.cpp").read_text()
handler = extract_braced(source, "int MyMesh::handleRequest(")
prefix = handler[:handler.index(" if (payload[0] == REQ_TYPE_GET_STATUS)")]
acl = extract_braced(handler, "if (payload[0] == REQ_TYPE_GET_ACCESS_LIST)")
generated = prefix + acl + "\nreturn 0;\n}\n"
receive = extract_braced(source, "if (type == PAYLOAD_TYPE_REQ) { // request")
generated += """
void MyMesh::receive(mesh::Packet* packet, uint8_t* data, size_t len) {
ClientInfo* client = &sender;
const uint8_t type = PAYLOAD_TYPE_REQ;
uint8_t secret[PUB_KEY_SIZE] = {};
""" + receive + "\n}\n"
mesh_source = (ROOT / "src/Mesh.cpp").read_text()
generated += "namespace mesh {\n"
generated += "#define MAX_COMBINED_PATH (MAX_PACKET_PAYLOAD - 2 - CIPHER_BLOCK_SIZE)\n"
for signature in (
"Packet* Mesh::createPathReturn(const Identity& dest,",
"Packet* Mesh::createPathReturn(const uint8_t* dest_hash,",
"Packet* Mesh::createDatagram(",
):
generated += extract_braced(mesh_source, signature) + "\n"
packet_source = (ROOT / "src/Packet.cpp").read_text()
for signature in ("Packet::Packet()", "bool Packet::isValidPathLen("):
generated += extract_braced(packet_source, signature) + "\n"
generated += "}\n"
return generated
class ClientAclResponseTest(unittest.TestCase):
def test_production_acl_route_admission_and_replay(self):
compiler = shutil.which("g++") or shutil.which("clang++")
if compiler is None:
self.skipTest("a host C++17 compiler is required")
generated = production_acl_methods()
with tempfile.TemporaryDirectory(prefix=".tmp-acl-response-", dir=ROOT) as directory:
work = Path(directory)
(work / "production.inc").write_text(generated, encoding="ascii")
binary = work / "acl-response.exe"
command = [compiler, "-std=c++17", "-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter",
f"-I{work}", f"-I{ROOT / 'src'}",
str(FIXTURE / "test_client_acl_response.cpp"), "-o", str(binary)]
if sys.platform.startswith("linux"):
command[1:1] = ["-fsanitize=address,undefined", "-fno-sanitize-recover=all",
"-fno-pie", "-no-pie"]
built = subprocess.run(command, capture_output=True, text=True, timeout=60)
self.assertEqual(built.returncode, 0, built.stdout + built.stderr)
checked = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10)
self.assertEqual(checked.returncode, 0, checked.stdout + checked.stderr)
self.assertIn("ACL route and replay checks passed", checked.stdout)
decoded = 0
for line in checked.stdout.splitlines():
if not line.startswith("LEGACY:"):
continue
_, expected, body = line.split(":")
result = parse_acl(bytes.fromhex(body))
self.assertEqual(len(result), int(expected))
self.assertTrue(all(entry["perm"] == 3 for entry in result))
decoded += 1
self.assertGreaterEqual(decoded, 10)
def test_admission_result_and_default_capacity_wiring(self):
source = (ROOT / "examples/simple_repeater/MyMesh.cpp").read_text()
header = (ROOT / "examples/simple_repeater/MyMesh.h").read_text()
wrapper = extract_braced(source, "bool MyMesh::sendClientReply(")
self.assertIn("return sendClientReplyWithFallbackScope(", wrapper)
self.assertIn("bool sendClientReply(", header)
self.assertIn("size_t reply_capacity = MAX_PACKET_PAYLOAD - CIPHER_MAC_SIZE - (CIPHER_BLOCK_SIZE - 1)", header)
self.assertIn("if (len < 5) return;", source)
def test_same_clock_generates_login_and_binary_request_tags(self):
source = (ROOT / "src/helpers/BaseChatMesh.cpp").read_text()
for signature in ("int BaseChatMesh::sendLogin(",
"bool BaseChatMesh::allocateRequestTag("):
body = extract_braced(source, signature)
self.assertEqual(body.count("getRTCClock()->getCurrentTimeUnique()"), 1)
if __name__ == "__main__":
unittest.main()