Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Local Certificate Authority (CA) and TLS Proxying

Crate: kinetic-daemon Stage: 9 Reading time: 30 minutes Depends on: kinetic-core (Constants like NETWORK_ID and TLD_SUFFIX)


What Is This?

The ca.rs file provides the cryptographic infrastructure for the Kinetic Daemon to act as a localized Certificate Authority (CA). It handles the generation of a root certificate, stores its sensitive private key, and injects that certificate into the operating system’s native trust store.

Beyond initialization, it provides the runtime machinery to dynamically generate “leaf” certificates on the fly. Whenever the user’s browser attempts to navigate to a .kin domain (or whichever custom TLD the network is configured to use), this module generates a valid, sound TLS certificate specifically for that domain, signed by the local root CA. It also provides an in-memory caching layer to ensure we do not waste CPU cycles re-generating these certificates for every single network request.

In the simplest terms, this module is the engine that allows the user’s web browser to show a secure, trusted green padlock when browsing the decentralized Kinetic network, without throwing aggressive security warnings that would ruin the user experience. It turns a chaotic, peer-to-peer network into something that feels as safe and standardized as traditional cloud computing to the end user.


Why Kinetic Needs This

To understand why this module is so critical to the Kinetic architecture, we must examine the strict security model of modern web browsers.

Modern web browsers (Chrome, Firefox, Safari) mandate HTTPS for almost all modern web APIs. If you attempt to serve a web application over plain HTTP, the browser will restrict its capabilities. Crucial features like:

  • Service Workers (required for offline support and caching)
  • The WebCrypto API
  • Secure Cookies
  • Modern clipboard access
  • Geolocation API will simply fail to execute. For Kinetic to provide a seamless, modern web experience where decentralized apps behave exactly like traditional cloud-hosted web apps, the Kinetic Daemon must serve its content over HTTPS.

However, the domains on the Kinetic network (e.g., app.kin) do not exist in the global ICANN DNS system. Because they are localized, virtual domains, traditional Certificate Authorities like Let’s Encrypt, DigiCert, or Cloudflare will never issue a TLS certificate for them. They cannot verify domain ownership over a domain that only exists inside Kinetic’s peer-to-peer overlay.

If the Kinetic Daemon attempted to serve standard self-signed certificates for every domain independently, the browser would present a massive, unskippable “Your connection is not private” error screen to the user for every single app. This is an unacceptable user experience, as it trains users to click through and ignore severe security warnings entirely.

The solution is to perform a benign, authorized Man-In-The-Middle (MITM) operation directly on the user’s own machine. The ca.rs module orchestrates this through a multi-step process:

  1. The Local Root: When the Kinetic Daemon starts for the very first time, it generates a brand new, privileged Root Certificate Authority. This CA exists locally on the user’s machine and its private key never leaves the hard drive.
  2. The Trust Injection: The daemon then prompts the user’s operating system to implicitly trust this new CA. Once the OS trusts it, the browser (which inherits trust from the OS) will automatically trust any certificate that bears the cryptographic signature of this specific Root CA.
  3. The Leaf Forgery: When the user types https://search.kin into their browser, the browser connects to the local Kinetic Daemon proxy. The daemon intercepts the TLS ClientHello, instantly uses the Root CA to forge a valid TLS “leaf” certificate specifically for search.kin, and hands it back to the browser.
  4. The Verification: The browser verifies the leaf certificate, traces its signature back to the trusted local Root CA, and establishes a secure TLS connection. The user sees a standard secure connection, and Kinetic can safely decrypt, route, and serve the decentralized content.

Without this module, the entire premise of using a standard web browser as a client for the Kinetic network collapses. It acts as the diplomatic bridge between Kinetic’s decentralized backend and the unforgiving, strict security model of the traditional web.

Furthermore, we cannot simply generate one giant wildcard certificate (like *.kin) because users might configure custom network IDs, complex nested subdomains, or arbitrary local routes. On-the-fly, dynamic generation is the only architecture that matches the browser’s expectations for any arbitrary request.


How It Works

The lifecycle of the CA system in Kinetic involves several complex, distinct mechanisms. It handles everything from preventing race conditions during parallel initialization to OS-specific security integrations and performance-critical caching.

1. Bootstrapping and Atomic File Locking

-> See: kinetic-daemon/src/ca.rs — Lines 44 to 71

When the daemon starts, it calls load_or_create_root_ca. Because a user might accidentally launch the daemon multiple times simultaneously, or a background OS service might race with a manual terminal launch, we must guarantee that two processes do not attempt to generate the Root CA at the same time. If they did, they would overwrite each other’s cryptographic keys, irrevocably breaking the trust chain and leaving the OS trusting a ghost certificate.

This race condition is solved using a primitive but effective file lock (.ca.lock). The code attempts to open this file with create_new(true). In Rust, create_new(true) translates directly to an atomic OS-level syscall (like O_CREAT | O_EXCL on Unix). This means the operating system itself guarantees that only one process can successfully create the file.

If we had instead written if !file.exists() { create_file() }, we would have introduced a classic TOCTOU (Time-Of-Check to Time-Of-Use) vulnerability. Two separate OS threads (or processes) could check exists() at the exact same nanosecond, both see false, and then both proceed to blindly create and overwrite the file. By using the atomic create_new(true), the OS kernel steps in at the lowest level and guarantees mutual exclusion. Only the absolute first thread will succeed, and the second will instantly fail.

If the syscall succeeds, the process holds the lock. If it fails with an AlreadyExists error, it enters a graceful retry loop, waiting 50 milliseconds between attempts. If it fails 100 times sequentially (indicating 5 seconds have passed), it makes an architectural assumption: the lock is “stale” (likely left behind by a previous daemon crash that failed to clean up). In this scenario, it forces a deletion of the lock file to recover. This ensures the daemon never permanently deadlocks on a subsequent startup.

2. The Immediately Invoked Closure Expression (IICE) Pattern

-> See: kinetic-daemon/src/ca.rs — Lines 72 to 202

Notice how the core logic of load_or_create_root_ca is wrapped inside an Immediately Invoked Closure Expression (IICE):

#![allow(unused)]
fn main() {
// An IICE used for cleanup emulation let result = (|| -> Result<(RootCa, bool), CaError> {
    // ... complex logic with many ? operators
})(); let _ = std::fs::remove_file(&lock_path); return result;
}

Why is this necessary? When holding a lock file, it is absolute paramount that the lock is deleted when the function exits. However, the internal logic uses the ? operator extensively for error handling. If we didn’t use a closure, an early return triggered by ? would bypass the lock deletion code at the end of the function, leaving a stale lock on disk.

Unlike languages like Go that have a defer keyword, Rust typically handles cleanup via the Drop trait on specific objects. However, creating a custom struct just to implement Drop for deleting a single file is overly verbose. By wrapping the logic in a closure, we can capture the Result of all the internal operations, unconditionally run std::fs::remove_file(&lock_path) afterward, and then return the captured result. This elegantly ensures clean state.

3. Root CA Generation and Basic Constraints

-> See: kinetic-daemon/src/ca.rs — Lines 107 to 132

If the lock is acquired and no existing CA is found on disk, the module leverages the rcgen crate to forge a brand new Root CA. This certificate is configured to expire in exactly 730 days (2 years).

A critical detail is how the CA is constrained. The code uses: IsCa::Ca(BasicConstraints::Constrained(0)) What does Constrained(0) mean? In X.509 terminology, this defines the maximum depth of the certificate chain. By setting it to 0, we are stating that this Root CA can sign leaf certificates, but it cannot sign other intermediate Certificate Authorities. If a hacker stole the key and tried to spin up a subordinate CA, browsers would reject it. If this were Constrained(1), the leaf could theoretically act as an intermediate CA itself, which is a massive security vulnerability we want to avoid.

Furthermore, the daemon injects NameConstraints into the root certificate. It restricts this CA so it is only allowed to sign certificates for the kin subtree and the specific TLD_SUFFIX defined in kinetic_core. By baking NameConstraints directly into the certificate’s X.509 extensions, modern browsers will outright reject any attempt to use this specific CA for standard web domains (like google.com). This limits the “blast radius” of a potential private key compromise exclusively to the Kinetic network itself.

It’s also worth noting why rcgen is used here instead of a traditional library like openssl. openssl requires complex C bindings and system dependencies (like libssl-dev), which makes cross-compiling the Kinetic daemon for Windows or macOS an absolute nightmare. rcgen relies on pure Rust/Assembly backends (like ring), meaning the daemon can be statically compiled and remains instantly portable across all architectures without users installing C libraries.

4. Private Key Storage and OS Keychain Integration

-> See: kinetic-daemon/src/ca.rs — Lines 137 to 184

The Root CA’s private key is the single most sensitive piece of cryptographic material the daemon holds. Storing it as raw plaintext on the filesystem is dangerous, as any script with read access could steal it.

The code attempts a progressive, fault-tolerant fallback strategy for storage: First, it uses the keyring crate to inject the private key directly into the Operating System’s native secure enclave (macOS Keychain, Windows Credential Manager, or Linux Secret Service via D-Bus). If this operation succeeds, the key is managed by the OS, encrypted at rest, and never written directly to the Kinetic configuration directory.

However, the OS keychain is frequently unavailable (especially on headless Linux servers or minimal CI environments). If the keychain injection fails, the daemon falls back to raw disk storage but locks down the file permissions. Using conditional compilation attributes (#[cfg(unix)]), the code imports std::os::unix::fs::OpenOptionsExt to enforce a file mode of 0o600. This means the file is readable and writable only by the user owner; group members and guests have zero access. On Windows (#[cfg(windows)]), Unix permissions do not exist. Therefore, it achieves the identical security posture by spawning a subprocess to invoke the icacls command-line utility. It strips all inherited permissions (/inheritance:r) and grants full control (/grant:r) solely to the current $USERNAME.

5. Injecting Trust into the Operating System

-> See: kinetic-daemon/src/ca.rs — Lines 210 to 265

Generating a perfect CA is useless if the host OS refuses to trust it. The trust_root_ca function contains platform-specific, conditionally compiled logic to force this trust.

  • Windows: It executes certutil -addstore -user root. certutil is a native Microsoft binary designed to manipulate the Windows certificate store.
  • macOS: It executes security add-trusted-cert wrapped inside an osascript (AppleScript) command. This AppleScript wrapper is vital because it guarantees the user is presented with the native macOS GUI prompt (requiring Touch ID or an administrator password) to authorize the modification of the restricted System Keychain.
  • Linux: It uses pkexec (Polkit) to escalate privileges, copies the certificate into /usr/local/share/ca-certificates/, and triggers the update-ca-certificates system utility to rebuild the OS trust bundle.

Because these commands fundamentally alter system security and require elevated privileges, they will trigger GUI prompts for the user. If the user clicks “Cancel”, denies the prompt, or if the environment is headless without Polkit, the subprocess will fail. The Rust code matches on the status and logs a warning but does not panic. The daemon will continue to run normally, but the user will face browser security warnings when they attempt to connect.

6. On-the-Fly Leaf Certificate Forgery

-> See: kinetic-daemon/src/ca.rs — Lines 270 to 315

When the daemon’s internal proxy receives an incoming TLS connection intended for app.kin, it must immediately present a valid certificate exclusively for app.kin. The generate_leaf_cert function handles this synchronous generation.

It initializes a new CertificateParams struct targeted precisely at the requested domain. This leaf certificate is configured to be valid for only 30 days. A deliberately short lifespan minimizes risk; if a leaf certificate is somehow cached or extracted, it expires rapidly, forcing a fresh rotation. It then signs this new leaf certificate using the Root CA’s private key.

The final hurdle is format conversion. The rcgen crate outputs certificates and keys in PEM format (Privacy Enhanced Mail, which is Base64 encoded text strings surrounded by -----BEGIN CERTIFICATE----- headers). However, the Kinetic Daemon uses rustls for its TLS termination, and rustls requires DER format (Distinguished Encoding Rules, which is a raw binary format). Rustls requires DER because it is a systems-level networking library optimized for extreme speed, avoiding slow text parsing in the critical path of the TLS handshake.

The function uses rustls_pemfile to parse the PEM strings back into binary CertificateDer arrays, builds the full certificate chain (Leaf -> Root), and constructs the final ServerConfig. Crucially, it calls .with_no_client_auth(). Client authentication (mTLS) is a feature where the server asks the browser to prove its identity using a certificate. Since standard web browsers do not have Kinetic identity certs installed in their client stores, asking for one would cause the browser to abort the connection. We disable it to prevent handshake failures.

7. The LRU Certificate Cache and Concurrency

-> See: kinetic-daemon/src/ca.rs — Lines 318 to 374

Generating RSA or ECDSA key pairs and executing cryptographic signatures requires measurable CPU time. Doing this synchronously for every single network request (every image, every script, every CSS file) requested by the browser would cripple the proxy’s throughput and cause massive latency spikes.

To mitigate this, the LeafCertCache struct implements a bounded LRU (Least Recently Used) caching mechanism. It maps string domains to a tuple containing (Arc<ServerConfig>, Instant).

When a request arrives, get_or_create queries the cache. If the domain exists and was generated less than 1 hour ago (3600 seconds, determined via Instant::now()), it instantly returns a clone of the Arc.

Why use Arc (Atomic Reference Counted pointer) here? The daemon proxy is concurrent, processing hundreds of async connections simultaneously. A ServerConfig contains the complex private key and the entire certificate chain. Cloning the whole structure for every single TCP connection would consume significant memory and CPU. By wrapping it in an Arc, multiple asynchronous tasks can safely share a pointer to the exact same immutable TLS configuration in memory, requiring only a nanosecond atomic integer increment to borrow it.

If the cache hits its maximum capacity (hardcoded to 256 entries), it performs a linear scan using .iter().min_by_key(...) to locate the absolute oldest entry based on its Instant creation timestamp. It evicts this oldest entry before inserting the new one. This ensures the daemon’s memory footprint remains bounded and predictable, even if a user browses thousands of unique .kin domains in a single session.

8. Fuzzing and Test Resiliency

-> See: kinetic-daemon/src/ca.rs — Lines 376 to 477

The module includes extensive #[cfg(test)] blocks to verify the stability of the CA infrastructure. It tests cache eviction, lock file recovery, and disk reads. More importantly, it uses the proptest crate for fuzzing the leaf certificate generation (test_fuzz_leaf_cert_generation).

Because the requested domain comes from the user’s browser, a malicious user might attempt to request a certificate for an absurdly long, malformed, or character-injected domain string (e.g., passing SQL or bash injections into the CommonName field). The proptest feeds chaotic strings (up to 255 characters) into the generate_leaf_cert function to ensure that even when rcgen fails to parse the invalid DNS name, the function returns a clean CaError::Rcgen rather than panicking and crashing the entire daemon. This is a vital security measure against Denial of Service (DoS) attacks on the proxy.


Key Pieces

CaError

-> See: kinetic-daemon/src/ca.rs — Lines 14 to 26 An enumeration capturing all possible failure states within the CA pipeline. It seamlessly wraps standard IO errors, rcgen cryptographic parsing/generation errors, and rustls configuration errors. It utilizes the thiserror crate macro #[from] to automatically derive the From trait, allowing the codebase to use the ? operator to bubble up vastly different error types into this single, unified CaError type.

RootCa

-> See: kinetic-daemon/src/ca.rs — Lines 28 to 36 A straightforward struct holding a tri-state representation of the Root Certificate Authority. It retains the raw PEM string (required for writing to disk and injecting into the OS), the rcgen::KeyPair (required for signing new leaf operations), and the parsed rcgen::Certificate (the public half of the identity). Grouping these prevents the costly need to constantly re-parse the PEM strings into cryptographic objects.

load_or_create_root_ca

-> See: kinetic-daemon/src/ca.rs — Lines 44 to 202 The massive orchestration function executed during the daemon’s boot sequence. It is responsible for managing the atomic .ca.lock file, reading existing certificates from the filesystem, attempting OS keychain retrieval, generating new keys if none exist, enforcing critical DNS name constraints, handling granular disk permission fallbacks, and triggering the OS trust injection subroutines. It returns the RootCa and a boolean flag indicating whether this was a fresh generation (true) or a successful load from disk (false).

trust_root_ca

-> See: kinetic-daemon/src/ca.rs — Lines 210 to 265 A platform-dependent function wrapped in #[cfg(...)] conditional compilation blocks. Note the #[cfg(not(test))] on line 210 — this intentionally disables the function during unit tests to prevent automated test runners from spamming the developer’s OS with permission GUI prompts. It shells out to certutil, security, or pkexec depending on the active compilation target.

generate_leaf_cert

-> See: kinetic-daemon/src/ca.rs — Lines 270 to 315 The core cryptographic engine for the dynamic proxy. It accepts an arbitrary string domain (like “search.kin”) and a reference to the RootCa. It returns a fully configured rustls::ServerConfig ready to be attached to a TCP listener. It handles the intricate, error-prone dance of converting rcgen PEM outputs into the strict, binary CertificateDer arrays required by the rustls ecosystem.

LeafCertCache

-> See: kinetic-daemon/src/ca.rs — Lines 318 to 374 A critical bounding mechanism designed to protect CPU and memory resources. By utilizing Instant::now() rather than system time, it guarantees that its 1-hour expiration logic is immune to NTP clock skews or the user manually changing their system timezone.


How This Connects to the Rest of Kinetic

  • kinetic-core: This entire module is anchored by constants imported from the core crate, specifically kinetic_core::constants::NETWORK_ID and TLD_SUFFIX. These foundational constants dictate the names of the files generated on disk ({network_id}.cert.pem), the organizational name embedded deep within the X.509 certificates, and most importantly, the NameConstraints that prevent the CA from being globally exploited. CROSS-CRATE: NETWORK_ID and TLD_SUFFIX — defined and explained in docs/learn/core/01_overview.md.
  • The Proxy Server (kinetic-daemon): The entire ca.rs file exists solely to serve the daemon’s HTTPS proxy module. The LeafCertCache is held continuously in memory by the proxy’s main routing loop, and get_or_create is invoked every single time a new TLS ClientHello handshake is initiated by the user’s browser.

Quick Reference

  • Root CA Lifespan: 730 days (2 years) from the exact moment of generation.
  • Leaf Cert Lifespan: 30 days.
  • Cache Eviction Policy: 1 hour (3600 seconds) based on monotonic Instant.
  • Maximum Cache Size: 256 unique domains in memory simultaneously.
  • Lock File Retry Logic: 100 attempts, with a 50ms sleep per attempt (totaling a 5-second wait before forced recovery).
  • Key Storage Hierarchy: OS Keychain (Primary) -> 0o600 restricted disk file (Unix Fallback) -> icacls restricted disk file (Windows Fallback).
  • Trust Injection Commands: certutil (Windows), security via osascript (macOS), pkexec (Linux).

Open Questions / Things to Revisit

  • Linux Trust Pathing Constraints: The trust_root_ca function hardcodes /usr/local/share/ca-certificates/ for Linux OS injection. While this works beautifully on Debian/Ubuntu derivatives, distributions like Arch Linux and Fedora use different paths for their system trust stores (/etc/ca-certificates/trust-source/anchors/ and /etc/pki/ca-trust/source/anchors/ respectively). This command will silently fail on those distributions, leaving users with persistent browser warnings despite a successful daemon launch.
  • Cache Eviction Algorithmic Complexity: When the LeafCertCache hits its 256 entry limit, it uses .iter().min_by_key(...) to find the oldest entry to evict. This performs an $O(N)$ linear scan over the entire HashMap. While $N=256$ is trivially small and executes in microseconds, implementing a proper doubly-linked LruCache struct would reduce this operation to true $O(1)$ constant time, which is architecturally cleaner for a high-throughput network proxy.
  • Lock File Edge Case Race Condition: The lock file retry loop removes the .ca.lock file if it hits 100 retries (5 seconds). If the user’s machine is under extreme IO load and a legitimate daemon startup takes longer than 5 seconds to generate the intensive RSA/ECDSA keys, a secondary daemon instance might assume the lock is stale and delete it out from under the first instance, causing a severe race condition during key disk writes.
  • Firefox NSS Store Limitation: Firefox natively maintains its own internal certificate store (NSS) and often ignores the OS-level trust store on macOS and Linux. The current trust_root_ca logic only injects into the OS store, meaning Firefox users may still see “Unknown Issuer” warnings even if the OS injection reports success. This is a known limitation of local proxy development and might require a separate certutil interaction targeted specifically at Firefox’s cert9.db in the future.
  • Keyring Fallback Silencing: The keyring crate might silently fail if the user is connected via SSH without active D-Bus session access, leading to a fallback to the disk storage method without the user ever realizing they lost the hardware-backed security of the OS keychain. We should probably log a more aggressive warning when the primary storage engine fails.
  • Hardcoded Cryptographic Algorithms: The rcgen crate defaults to whatever its backend (like ring or aws-lc-rs) considers the safest modern default, which is typically ED25519 or ECDSA P-256. While this is vastly superior in speed and security to legacy RSA-2048, older enterprise proxy software or legacy browsers might fail to parse elliptic curve root CAs. There is no configuration flag exposed yet in kinetic-daemon to force RSA generation for compatibility.