A high-level NTS (Network Time Security) client library for Rust with a self-contained RFC 8915 implementation.
This library provides a simple, safe, and ergonomic API for querying time from NTS-secured NTP servers. It handles the complexity of NTS key exchange and authenticated time synchronization, making it easy to integrate secure time synchronization into your applications.
- Secure: Full NTS (Network Time Security) support for authenticated time queries
- Certificate Diagnostics: TLS certificate information capture for security auditing and diagnostics
- TLS Debugging: optional
tls-keylogfeature for Wireshark traffic analysis - Simple API: Easy-to-use client interface with sensible defaults
- Async: Built on Tokio for efficient async I/O
- Configurable: Flexible configuration options for advanced use cases
- Self-contained: NTS-KE and NTS-protected NTP are implemented directly in this crate
Add to your Cargo.toml:
[dependencies]
rkik-nts = "1"
tokio = { version = "1", features = ["full"] }Basic usage:
use rkik_nts::{NtsClient, NtsClientConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client configuration
let config = NtsClientConfig::new("time.cloudflare.com");
// Create and connect the client
let mut client = NtsClient::new(config);
client.connect().await?;
// Query the current time
let time = client.get_time().await?;
println!("Network time: {:?}", time.network_time);
println!("Offset (ms): {} ms", time.offset_signed());
println!("Authenticated: {}", time.authenticated);
Ok(())
}cargo run --example simple_client --features tracing-subscribercargo run --example nts_end_to_end --features tracing-subscriberAccess TLS certificate information from the NTS-KE handshake:
use rkik_nts::{NtsClient, NtsClientConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = NtsClientConfig::new("time.cloudflare.com");
let mut client = NtsClient::new(config);
client.connect().await?;
// Access certificate information
if let Some(ke_result) = client.nts_ke_info() {
if let Some(cert) = &ke_result.certificate {
println!("Certificate Subject: {}", cert.subject);
println!("Certificate Issuer: {}", cert.issuer);
println!("Valid from: {} to {}", cert.valid_from, cert.valid_until);
println!("SHA-256 Fingerprint: {}", cert.fingerprint_sha256);
println!("Self-signed: {}", cert.is_self_signed);
}
}
Ok(())
}Run the certificate example:
cargo run --example test_certificate --features tracing-subscriberuse rkik_nts::{NtsClient, NtsClientConfig};
use std::time::Duration;
let config = NtsClientConfig::new("time.cloudflare.com")
.with_port(4460)
.with_timeout(Duration::from_secs(5))
.with_max_retries(3);
let mut client = NtsClient::new(config);
client.connect().await?;
let time = client.get_time().await?;max_retries controls how many additional authenticated query attempts are made
after transport or packet-validation failures before get_time() returns an error.
See the examples/ directory for more detailed examples.
For debugging and network analysis, you can capture TLS session keys for Wireshark decryption.
This is disabled by default and requires the tls-keylog feature:
# Set environment variable to enable keylog
export SSLKEYLOGFILE=/tmp/sslkeylog.txt
# Run your application or example
cargo run --example test_certificate --features "tracing-subscriber tls-keylog"
# Use the keylog file in Wireshark:
# Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filenameDo not enable this in production. The exported TLS secrets also expose the derived NTS keys for that session.
Here are some public NTS servers you can use for testing:
time.cloudflare.com- Cloudflarents.ntp.se- Netnod (Sweden)ntppool1.time.nl- NLnet Labs (Netherlands)time.txryan.com- Tanner Ryannts.ntp.org.au- Australian NTP Poolptbtime1.ptb.de- PTB (Germany, public service availability not guaranteed)
The current network test suite is validated against time.cloudflare.com and nts.ntp.se.
PTB servers are exercised opportunistically because PTB explicitly states that uninterrupted public availability is not guaranteed.
This library was initially designed for seamless integration with rkik, but is now mainly meant to be used as a standalone NTS client library. The API is intentionally kept simple and focused on authenticated time acquisition.
The library is structured into several modules:
client: High-level NTS client implementationconfig: Configuration types and builderserror: Error types and result aliasesnts_ke: NTS Key Exchange protocol implementationtypes: Common types (TimeSnapshot, NtsKeResult, etc.)
Network Time Security (NTS) is a security extension for NTP that provides:
- Authentication: Cryptographic verification that time data comes from the expected server
- Encryption: Protection of time synchronization traffic
- Resistance to replay attacks: Each query uses unique authentication cookies
The protocol works in two phases:
- NTS-KE (Key Exchange): TLS connection to exchange keys and cookies
- NTP with NTS: UDP-based time queries using the negotiated keys
This library handles both phases transparently.
- Rust 1.70 or later
- Tokio runtime
# Build the library
cargo build
# Run tests
cargo test
# Run examples
cargo run --example simple_client --features tracing-subscriber
# Build documentation
cargo doc --openSee CONTRIBUTING.md for development guidelines.
Contributions are welcome! Please see CONTRIBUTING.md for details.