//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // QUICServerCredentials{selfSigned = true} from a cold cache, in several // processes at once. The pair used to be made by the openssl CLI straight // into the fixed /tmp/crafter-network-quic-cert/{key,cert}.pem, guarded by an // in-process mutex only: test binaries started together each found no pair, // each ran openssl over the same two files, and one could load another run's // key with its own certificate, failing in ConfigurationLoadCredential. The // directory was shared by every user of the machine, and without the openssl // CLI there was no certificate at all. // // Six processes, forked before any thread or msquic exists, are released at // once; each starts two threads that create a selfSigned listener together, // against an empty cache (XDG_CACHE_HOME pointed at a fresh directory) and // with no openssl on PATH, and connects to both. Every listener must serve, // and every process must report the same certificate: one made the pair and // the rest read it. Then: // - the directory is the user's alone (0700) and so is the key (0600); // - a process started later serves the same certificate, so a browser's // pinned hash survives a restart; // - a mismatched pair left on disk, what the race used to leave behind, is // replaced by the next process rather than loaded. // // The parent never starts msquic or a thread, so forking stays safe: every // listener lives in a child, which answers with one line through a pipe. #include #include #include #include #include #include import Crafter.Network; import Crafter.Thread; import std; using namespace Crafter; namespace { int failures = 0; void Check(bool ok, std::string_view what) { std::println("{} {}", ok ? "PASS" : "FAIL", what); if (!ok) ++failures; } const std::string alpn = "crafter/test-certificate"; std::string Hex(const std::array& bytes) { std::string text; for (std::uint8_t byte : bytes) text += std::format("{:02x}", byte); return text; } // One child: wait for the byte on `go`, start `listeners` selfSigned // listeners together on threads of their own, reach each with a client, and // write "OK " or "FAIL " to // `report`. [[noreturn]] void Child(int go, int report, int listeners) { // A child that hangs dies and so answers nothing, which the parent // counts as a failure, inside the runner's 60 s. alarm(40); char byte = 0; while (read(go, &byte, 1) < 0 && errno == EINTR) {} close(go); std::string line; try { ThreadPool::Start(); std::latch together(listeners); std::vector problems(static_cast(listeners)); std::vector threads; for (int i = 0; i < listeners; ++i) { threads.emplace_back([&, i] { try { together.arrive_and_wait(); QUICServerCredentials serverCreds; serverCreds.selfSigned = true; ListenerQUIC listener("127.0.0.1", 0, alpn, serverCreds, [](ClientQUIC* peer) { peer->OnStream([](QUICStream stream) { try { std::vector got = stream.RecieveUntilCloseSync(); stream.SendSync(got.data(), static_cast(got.size()), true); } catch (...) {} }); }); listener.ListenAsyncAsync(); QUICClientCredentials clientCreds; clientCreds.insecureNoServerValidation = true; ClientQUIC client("127.0.0.1", listener.Port(), alpn, clientCreds); QUICStream stream = client.OpenStream(); stream.SendSync("hi", 2, true); std::vector echoed = stream.RecieveUntilCloseSync(); if (std::string(echoed.begin(), echoed.end()) != "hi") problems[static_cast(i)] = "no echo"; } catch (const std::exception& e) { problems[static_cast(i)] = e.what(); } }); } for (std::thread& thread : threads) thread.join(); std::string problem; for (const std::string& p : problems) if (!p.empty()) problem = p; if (problem.empty()) { const std::string path = GetSelfSignedCertificatePath(); line = std::format("OK {} {}", Hex(ComputeCertificateHashSHA256(path)), path); } else { line = "FAIL " + problem; } } catch (const std::exception& e) { line = std::string("FAIL ") + e.what(); } line += '\n'; for (std::size_t written = 0; written < line.size();) { const ssize_t put = write(report, line.data() + written, line.size() - written); if (put < 0) { if (errno == EINTR) continue; break; } written += static_cast(put); } // The accepted peers are still open: skip msquic's static teardown, // which would wait for their idle timeout. std::_Exit(0); } // Forks `count` children, releases them together, and returns their lines, // "FAIL no answer" for one that died first. std::vector RunChildren(int count, int listeners) { int go[2]; if (pipe(go) != 0) return std::vector(static_cast(count), "FAIL pipe"); std::vector> children; for (int i = 0; i < count; ++i) { int report[2]; if (pipe(report) != 0) break; const pid_t pid = fork(); if (pid == 0) { close(go[1]); close(report[0]); for (const auto& [sibling, siblingReport] : children) close(siblingReport); Child(go[0], report[1], listeners); } close(report[1]); children.emplace_back(pid, report[0]); } close(go[0]); const std::string release(children.size(), 'x'); (void)!write(go[1], release.data(), release.size()); close(go[1]); std::vector lines; for (const auto& [pid, report] : children) { std::string line; char buffer[512]; for (;;) { const ssize_t got = read(report, buffer, sizeof(buffer)); if (got == 0) break; if (got < 0) { if (errno == EINTR) continue; break; } line.append(buffer, static_cast(got)); } close(report); int status = 0; while (waitpid(pid, &status, 0) < 0 && errno == EINTR) {} if (!line.empty() && line.back() == '\n') line.pop_back(); lines.push_back(line.empty() ? "FAIL no answer" : line); } return lines; } bool Ok(const std::string& line) { return line.starts_with("OK "); } // The hash in an "OK " line. std::string HashOf(const std::string& line) { return Ok(line) ? line.substr(3, 64) : std::string(); } // The path in an "OK " line. std::string PathOf(const std::string& line) { return Ok(line) && line.size() > 68 ? line.substr(68) : std::string(); } } // namespace int main() { // The watchdog, without a thread: SIGALRM's default action ends the // process, which the runner counts as a failure. alarm(55); char scratch[] = "/tmp/crafter-network-certificate-XXXXXX"; if (mkdtemp(scratch) == nullptr) { std::println("FAIL mkdtemp: {}", std::strerror(errno)); return 1; } const std::filesystem::path cache = scratch; const std::filesystem::path directory = cache / "crafter-network" / "quic-cert"; setenv("XDG_CACHE_HOME", scratch, 1); // No openssl to fall back on, wherever it is installed. setenv("PATH", (cache / "no-programs-here").c_str(), 1); // ── Six processes, two listeners each, from cold ───────────────────────── const std::vector cold = RunChildren(6, 2); for (const std::string& line : cold) std::println(" {}", line); Check(std::ranges::all_of(cold, Ok), "twelve listeners in six processes started together from an empty cache, with no openssl CLI, were all served"); std::set hashes; for (const std::string& line : cold) hashes.insert(HashOf(line)); Check(hashes.size() == 1 && !hashes.begin()->empty(), "and all six processes serve one certificate: one made it, the rest read it"); const std::string first = HashOf(cold.front()); Check(PathOf(cold.front()) == (directory / "cert.pem").string(), std::format("it lives under XDG_CACHE_HOME ({})", PathOf(cold.front()))); // ── The directory is the user's alone ──────────────────────────────────── struct stat info {}; Check(stat(directory.c_str(), &info) == 0 && (info.st_mode & 0777) == 0700 && info.st_uid == geteuid(), "the certificate directory is the user's, mode 0700"); Check(stat((directory / "key.pem").c_str(), &info) == 0 && (info.st_mode & 077) == 0, "the key is readable by the user alone"); bool leftovers = false; std::error_code error; for (const auto& entry : std::filesystem::directory_iterator(directory, error)) { leftovers = leftovers || entry.path().extension() == ".new"; } Check(!leftovers, "no temporary file is left behind"); // ── A restart keeps the certificate ────────────────────────────────────── const std::vector restart = RunChildren(1, 1); std::println(" {}", restart.front()); Check(Ok(restart.front()) && HashOf(restart.front()) == first, "a process started later serves the same certificate, so a pinned hash survives a restart"); // ── A mismatched pair is replaced, not loaded ──────────────────────────── // Another valid P-256 key, of the TLS partition's certificate: what a // torn replacement leaves on disk. { std::ofstream key(directory / "key.pem", std::ios::binary | std::ios::trunc); key << GetSelfSignedCertificatePem().privateKey; } const std::vector mismatched = RunChildren(1, 1); std::println(" {}", mismatched.front()); Check(Ok(mismatched.front()) && HashOf(mismatched.front()) != first, "a mismatched pair left on disk is replaced by a new one, which serves"); try { Check(Hex(ComputeCertificateHashSHA256((directory / "cert.pem").string())) == HashOf(mismatched.front()), "and the new certificate is the one on disk"); } catch (const std::exception& e) { Check(false, std::format("hashing the certificate on disk threw: {}", e.what())); } std::filesystem::remove_all(cache, error); std::println("{}", failures == 0 ? "all checks passed" : "check(s) failed"); std::fflush(stdout); return failures == 0 ? 0 : 1; }