Roughtime
A no_std-capable Rust client for the Roughtime secure
time synchronization protocol, with pluggable crypto backends and an anti-rollback time floor.
Client-only: this crate queries and verifies Roughtime servers, but does not implement one.
Quickstart (hosted, default features)
With default features, server hostnames are resolved end-to-end-encrypted over DNSCrypt — no manual IP resolution required:
use ;
async
See examples/query_dnscrypt.rs (cargo run --example query_dnscrypt). If you'd rather resolve
hostnames yourself (e.g. via the plain OS resolver, or your own pinned IPs), build the
(RoughtimeServer, IpAddr) list by hand and call client.query(&resolved) instead — see
examples/query_tokio.rs.
Feature flags
Exactly one crypto backend must be selected:
| Feature | Backend |
|---|---|
rustcrypto |
Pure Rust ([ed25519-dalek] + [sha2]), no_std-capable |
aws-lc-rs (default) |
[aws-lc-rs] |
aws-lc-rs-fips |
aws-lc-rs built in FIPS mode |
Layered on top:
std(default) — enables the networking clients and the Linuxos-clockbackend.tokio-client(default) — an async, concurrent multi-server query client.blocking-client— a synchronousstd::net::UdpSocket-based client.os-clock— an opt-in LinuxSystemClockimplementation that can set the OS wall clock forward to the verified time floor.dnscrypt(default) — resolves well-known Roughtime server hostnames over DNSCrypt (authenticated, encrypted DNS) instead of the OS's plain, unauthenticated resolver, viaresolve_servers/resolve_servers_asyncandTokioClient/BlockingClient::query_well_known. This closes the one remaining unauthenticated hop before Roughtime's own Ed25519-verified response takes over.build-time-floor— opt-in, not indefault. See "The anti-rollback floor" below.
no_std / kernel usage
For a kernel or bootloader that supplies its own networking and randomness:
This gives you wire-format parsing, request building, and response verification, with no
std, networking, or OS-clock dependency — only a global allocator is required (see below).
use ;
let nonce = new;
let request = build_request;
// ... send `request` over your own UDP stack, receive `response_bytes` ...
let verified = verify_response?;
See examples/no_std.rs for a complete, runnable version of this
(cargo run --example no_std --no-default-features --features rustcrypto).
Requirement: a global allocator
This crate always links alloc (for Vec-backed message parsing), even in no_std builds.
no_std consumers must provide a global allocator.
The anti-rollback floor
No time reported by a Roughtime server (or the OS clock) is trusted below a floor: a machine cannot legitimately observe a time earlier than when this crate was built.
Two supported workflows:
- Default (reproducible builds): the floor is a static constant (
src/floor.rs,HARDCODED_FLOOR_SECS) with no dependency on the build machine's clock. Bump it in source as a normal, reviewable commit as time passes. This is required for bit-for-bit reproducible builds (e.g. SEV-SNP attestation measurement matching) — enablingbuild-time-floorwould make the build depend on wall-clock time and break reproducibility. - Opt-in
build-time-floor: the floor auto-ratchets forward to the build day, using the build machine's wall clock, at the cost of build reproducibility. If the build machine's clock is before the hardcoded floor, the build refuses to compile (this should only happen on a misconfigured build host). Suitable for non-attested hosted deployments that rebuild frequently.
Security notes
This is a client-only crate — it never holds Ed25519 private key material, only verifies server-supplied public keys and signatures. The one client-generated secret-like value is the 64-byte nonce, which is zeroized on drop. Raw response bytes and extracted public keys are intentionally not zeroized: they are bulk public-protocol data (broadcast in the clear by protocol design), and scrubbing them would cost real performance for no confidentiality benefit.
MSRV
Rust 1.89.
License
This project licensed under either the MIT License or the Apache License, Version 2.0 at your option.