RFC 10049

Roughtime: A Protocol for Rough Time Synchronization

๐Ÿ“… published October 2026, from ietf

Roughtime: A Protocol for Rough Time Synchronization ๐Ÿ•ฐ๏ธโœจ

Okay bestie, here's the paperwork tea before we dive in ๐Ÿ’…

  • RFC: 10049 ๐ŸŽ€
  • Series: Internet Engineering Task Force (IETF) ๐ŸŒˆ
  • Authors: W. Ladd (Akamai Technologies) and M. Dansarie (Netnod) ๐Ÿ‘‘
  • Category: Experimental ๐Ÿงช
  • ISSN: 2070-1721
  • Date: October 2026 ๐ŸŒธ
  • Obsoletes / Updates: nothing listed in this title block, so it doesn't obsolete or update anything ๐Ÿซถ

Abstract ๐Ÿฆ‹

Roughtime is an experimental protocol with two big jobs ๐Ÿ’– The first is secure, rough time synchronization, even for clients that have no clue what time it is. The second is a format that lets clients report any inconsistencies they spot between timeservers. This doc specifies the on-wire protocol needed for all of that, and it also talks through the ecosystem pieces it needs to actually work โœจ


Status of This Memo ๐Ÿ“œ

Okay so this document is not an Internet Standards Track specification ๐Ÿ™ˆ It's published for examination, experimental implementation, and evaluation.

This document defines an Experimental Protocol for the Internet community. It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Not every document the IESG approves is a candidate for any level of Internet Standard, so see Section 2 of RFC 7841 for the details ๐Ÿ’…

Info about the current status of this document, any errata, and how to give feedback is at https://www.rfc-editor.org/info/rfc10049 ๐Ÿ”—


Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’œ

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they're in effect on the date of publication. Please read these carefully, because they spell out your rights and restrictions for this document ๐Ÿ“– Code Components pulled out of this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Revised BSD License โญ


Table of Contents ๐Ÿ—บ๏ธ

   1.  Introduction
   2.  Conventions
   3.  Protocol Overview
     3.1.  Single Server Mode
     3.2.  Multi-server Mode
   4.  Message Format
     4.1.  Data Types
       4.1.1.  uint32
       4.1.2.  uint64
       4.1.3.  Tag
       4.1.4.  Timestamp
     4.2.  Header
   5.  Protocol Details
     5.1.  Requests
       5.1.1.  VER
       5.1.2.  NONC
       5.1.3.  TYPE
       5.1.4.  SRV
       5.1.5.  ZZZZ
     5.2.  Responses
       5.2.1.  SIG
       5.2.2.  NONC
       5.2.3.  TYPE
       5.2.4.  PATH
       5.2.5.  SREP
       5.2.6.  CERT
       5.2.7.  INDX
     5.3.  The Merkle Tree
       5.3.1.  Root Value Validity Check Algorithm
     5.4.  Validity of Response
   6.  Integration into NTP
   7.  Grease
   8.  Roughtime Clients
     8.1.  Necessary Configuration
     8.2.  Measurement Sequence
     8.3.  Server Lists
     8.4.  Malfeasance Reporting
       8.4.1.  Malfeasance Report Format
       8.4.2.  Reporting
   9.  Security Considerations
     9.1.  Confidentiality
     9.2.  Integrity and Authenticity
     9.3.  Generating Private Keys
     9.4.  Private Key Compromise
     9.5.  Quantum Resistance
     9.6.  Maintaining Lists of Servers
     9.7.  Amplification Attacks
   10. Privacy Considerations
   11. Operational Considerations
   12. IANA Considerations
     12.1.  Service Name and Transport Protocol Port Number Registry
     12.2.  Roughtime Versions Registry
     12.3.  Roughtime Tags Registry
     12.4.  Media Type Registry
       12.4.1.  Media Type for Roughtime Server List
       12.4.2.  Media Type for Roughtime Malfeasance
   13. References
     13.1.  Normative References
     13.2.  Informative References
   Appendix A.  Example Server List
   Appendix B.  Example Malfeasance Report
   Acknowledgments
   Authors' Addresses

1. Introduction โœจ

Time sync is essential to Internet security because so many security protocols and other apps need it [RFC0738] ๐Ÿ” The catch is that widely deployed protocols like the Network Time Protocol (NTP) [RFC5905] lack essential security features, and even newer ones like Network Time Security (NTS) [RFC8915] have no way to observe whether the servers are behaving correctly ๐Ÿ‘€ Clients might not even have a basic idea of the time either, which causes bootstrapping problems, since time is needed for X.509 certificate validation ๐Ÿ™ƒ

The main design goal of Roughtime is to let devices get a rough idea of the current time from a fairly static configuration, and to let them report any inconsistencies they see between servers ๐Ÿ’… That configuration is a list of servers and their associated long-term keys, which ideally stay unchanged for a server's whole lifetime. So the long-term public keys are the roots of trust in Roughtime ๐ŸŒณ With a long enough list of trusted servers and keys, a client can acquire authenticated time with high probability, even after long stretches of inactivity ๐Ÿ’ค Proofs of malfeasance, built by chaining together responses from different trusted servers, can prove a server misbehaved and, after analysis, can lead to revoking trust in that particular key โš–๏ธ

Unlike Khronos [RFC9523], Roughtime produces external evidence that time servers are reporting incompatible times ๐Ÿ”ฅ That means the timestamp format has to change, so it can't just be a mere extension to NTP ๐Ÿ™…โ€โ™€๏ธ

Real operational experience is needed to figure out whether Roughtime is viable for secure time bootstrapping in Internet-connected systems ๐Ÿงช That includes running a Roughtime ecosystem with services that maintain and distribute lists of trusted servers and process malfeasance reports. To make those experiments happen, this document only covers the Roughtime on-wire protocol ๐Ÿ“ก Apart from describing the server list and malfeasance report formats, it doesn't describe the ecosystem, the way the server list is maintained and distributed, or the policies to apply to such a list ๐Ÿซถ


2. Conventions ๐Ÿงท

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be read as described in BCP 14 [RFC2119] [RFC8174], but only when they appear in all capitals, just like shown here ๐Ÿ’– Lowercase versions are just regular words, no special powers ๐Ÿ’…


3. Protocol Overview ๐ŸŒธ

Roughtime is a protocol for authenticated, rough time synchronization that lets clients get cryptographic proof of server malfeasance ๐Ÿ•ต๏ธโ€โ™€๏ธ The way it works is that server responses include a signature over a value derived from the client's request, and that request includes a nonce. That gives cryptographic proof that the response was issued after the server got the client's request ๐Ÿงพ The derived value in the response is the root of a Merkle tree [Merkle] that includes the hash of the client's request as the value of one of its leaf nodes. The tree lets the server amortize the relatively costly signing operation across a bunch of client requests ๐ŸŒณโœจ

3.1. Single Server Mode ๐Ÿงโ€โ™€๏ธ

At its most basic, Roughtime is a one-round protocol. A totally fresh client asks for the current time, and the server sends back a signed response ๐Ÿ“จ The response includes a timestamp and a radius, which shows how certain the server is about the reported time ๐ŸŽฏ

The client's request contains a nonce that the server folds into its signed response. The client can verify the server's signatures, and as long as the nonce has sufficient entropy, that proves the signed response could only have been generated after the nonce existed ๐Ÿ”

3.2. Multi-server Mode ๐Ÿ‘ฏโ€โ™€๏ธ

When you use multiple servers, a client can detect, cryptographically prove, and report inconsistencies between them ๐Ÿšจ

A Roughtime server guarantees that the timestamp in its response is generated after it receives the request and before it transmits the matching response โฑ๏ธ If a server's time response doesn't match the responses from other servers, that points to server error or intentional malfeasance. It can be reported and potentially used to impeach the server โš–๏ธ

Proofs of malfeasance are built by chaining requests across different Roughtime servers. Details on proofs and malfeasance reporting are in Section 8 ๐Ÿ” For reporting to actually result in impeachment, an additional mechanism is required that provides a review and impeachment process. Defining that mechanism is beyond the scope of this document. One simple option could be an online forum where a court of human observers evaluates cases after reviewing the input reports ๐Ÿง‘โ€โš–๏ธ๐Ÿ“


4. Message Format ๐Ÿ’Œ

Roughtime messages are maps made of one or more (tag, value) pairs ๐Ÿ—‚๏ธ They start with a header that holds the number of pairs, the value offsets, and the tags. The header is followed by a message values section, which holds the values tied to the tags in the header. Messages are formatted according to Figure 1, as described in the subsections below ๐Ÿงฉ

In some cases messages are recursive, meaning the value of a tag can itself be a Roughtime message ๐Ÿชž

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                   Number of pairs, N (uint32)                 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   .                                                               .
   .                     N-1 offsets (uint32)                      .
   .                                                               .
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   .                                                               .
   .                        N tags (uint32)                        .
   .                                                               .
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   .                                                               .
   .                           N values                            .
   .                                                               .
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                        Figure 1: Roughtime Message

4.1. Data Types ๐Ÿงƒ

4.1.1. uint32 ๐Ÿ”ข

A uint32 is a 32-bit unsigned integer. It's serialized with the least significant byte first ๐Ÿ’พ

4.1.2. uint64 ๐Ÿ”ข

A uint64 is a 64-bit unsigned integer. It's serialized with the least significant byte first too ๐Ÿ’œ

4.1.3. Tag ๐ŸŽ€

okay bestie, tags are the little labels ๐Ÿท๏ธ we stick on each value inside a Roughtime message so the receiver knows what's what. A tag is exactly four octets. Each one starts with one to four capital ASCII letters (A-Z) [RFC0020], followed by zero to three padding zero octets. Throughout this doc we talk about tags by their ASCII string, but under the hood they get registered and sorted as uint32 values, where the least significant byte is the first octet in the sequence. ngl it's a lot, but it's giving precision ๐Ÿ’…

For example, the ASCII string "NONC" corresponds to the uint32 0x434e4f4e, which gets serialized as {0x4e, 0x4f, 0x4e, 0x43}. "VER" corresponds to 0x00524556 and gets serialized as {0x56, 0x45, 0x52, 0x00}. the byte order is giving reverse-engineering vibes ๐Ÿงš

4.1.4. Timestamp ๐Ÿ•ฐ๏ธ

a timestamp is UTC time written as a uint64 count of seconds since 00:00:00 on 1 January 1970 (the Unix epoch), assuming every day has 86400 seconds. it's a constant offset from the NTP timestamp in seconds. the tricky part is that leap seconds don't have an unambiguous representation in a timestamp, and that has implications for the attainable accuracy and for how the RADI tag gets set (see Section 5.2.5). this is the kind of detail that keeps me up at night ๐Ÿ˜ญ

4.2. Header ๐Ÿ“ฆ

as shown in Figure 1, the first four bytes of the header are the uint32 number of tags N, and that tells you how many (tag, value) pairs you're dealing with. the next 4*(N-1) bytes are offsets, each a uint32, and the last 4*N bytes in the header are tags.

the offsets array is considered to have an implicitly encoded value of 0 as its zeroth entry. its members point to the positions of the tag values in the message values section. all offsets are multiples of four ๐Ÿ”ข

the members of the offsets and tags arrays, plus the message values section, are sorted in ascending order by the tag's uint32 value. so the offset array is also sorted in ascending order, which is so clean ๐Ÿซถ A tag MUST NOT appear more than once in a header.

the first post-header byte (aka the first byte of the message values section) is at offset 0. the value for the ith tag begins at offset[i] and ends at offset[i+1]-1, except for the last value, which ends at the end of the message. Values MAY have zero length. all lengths and offsets are in bytes ๐ŸŒธ


5. Protocol Details ๐ŸŒŸ

as we covered in Section 3, clients kick off time sync by sending requests with a nonce, and servers send back signed time responses. Roughtime packets can travel between clients and servers either as UDP datagrams or over TCP streams. Servers SHOULD support both the UDP and TCP transport modes ๐Ÿ’œ

Roughtime packets are formatted like Figure 2 and as described here. the first field is a uint64 with the value 0x4d49544847554f52 ("ROUGHTIM" in ASCII). the second field is a uint32 that holds the length of the third field. the third and last field is a Roughtime message as specified in Section 4.

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                  0x4d49544847554f52 (uint64)                  |
   |                        ("ROUGHTIM")                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Message length (uint32)                    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   .                                                               .
   .                      Roughtime message                        .
   .                                                               .
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 2: Roughtime Packet

Roughtime request and response packets MUST be transmitted in a single datagram when the UDP transport mode is used. Setting the packet's Don't Fragment bit [RFC0791] is OPTIONAL in IPv4 networks. Setting it may cause packets to get dropped, but not setting it could lead to long delays due to reconstruction and dropped fragments ๐Ÿ™ˆ

a Roughtime packet could exceed the maximum deliverable length of a packet on a particular path, which would make Roughtime queries over UDP impossible on that path. so if a client isn't getting responses to its UDP queries, it SHOULD attempt to use the TCP transport mode for Roughtime queries to that server ๐Ÿฆ‹

Clients MUST implement exponential backoff in establishing TCP connections and making requests over UDP. It is RECOMMENDED that clients use an initial retry interval of 1 second, a maximum interval of 24 hours, and a base of 1.5. So the minimum interval, in seconds, before retrying after n failures is min(1.5^(n-1), 86400). If you're an implementer thinking about other values, Section 3.1.3 of [RFC8085] has guidance for you โœจ

Clients MUST NOT reset the retry interval until they receive a properly signed response. no shortcuts, bestie ๐Ÿ™…โ€โ™€๏ธ

Multiple requests and responses can be exchanged over an established TCP connection. Clients MAY send multiple outstanding requests and servers MAY send responses out of order. The connection SHOULD be closed by the client when it has no more requests to send and has received all expected responses. Either side SHOULD close the connection in response to synchronization, format, implementation-defined timeouts, or other errors ๐ŸŒˆ

All requests and responses contain the VER tag. It holds a list of one or more uint32 version numbers. The version of Roughtime specified by this document has version number 1 ๐ŸŽ€


5.1. Requests ๐Ÿ’Œ

a request contains the tags VER, NONC, and TYPE. It SHOULD include the tag SRV. Unknown tags MUST be ignored by the server. Requests that don't contain the three mandatory tags MUST be ignored. A future version of this protocol may mandate additional tags in the message and assign them semantic meaning ๐Ÿ‘‘

the size of the request message SHOULD be at least 1024 bytes when the UDP transport mode is used. to hit that size, the ZZZZ tag gets added to the message. one reason you might send smaller requests is to use the UDP transport mode over paths with a low maximum deliverable length. responding to request messages shorter than 1024 bytes is OPTIONAL, but servers MUST NOT send responses larger than the request messages they're replying to; see Section 9.7. size matters, and this is the rule ๐Ÿ“

5.1.1. VER ๐ŸŒท

in a request, the VER tag holds a list of uint32 version numbers. The VER tag MUST include at least one Roughtime version supported by the client, and it MUST NOT contain more than 32 version numbers. The version numbers and tags included in the request MUST be compatible with each other and with the packet contents.

The version numbers MUST NOT repeat and MUST be sorted in ascending numerical order.

Servers MUST ignore any unknown version numbers in the list supplied by the client. If the list contains no version numbers the server supports, it MAY respond with another version or ignore the request entirely; see Section 5.2.5. ๐Ÿซ 

5.1.2. NONC ๐ŸŽฒ

the value of the NONC tag is a 32-byte nonce. It SHOULD be generated in a manner indistinguishable from random. RFC 4086 [BCP106] has specific guidelines on this. Section 8.2 explains how to securely generate nonces when querying multiple servers in sequence ๐Ÿ”

5.1.3. TYPE ๐Ÿท๏ธ

the TYPE tag is how you unambiguously tell request and response messages apart. In a request, it MUST contain a uint32 with value 0. Requests with a TYPE tag holding any other value MUST be ignored by servers.

5.1.4. SRV ๐Ÿ”‘

the SRV tag tells the server which long-term public key the client expects to verify the response with. The value of the SRV tag is H(0xff || public_key), where public_key is the server's long-term, 32-byte Ed25519 public key, and H is SHA-512 truncated to the first 32 bytes. it's giving key-identity energy ๐Ÿ’…

5.1.5. ZZZZ ๐Ÿซง

the ZZZZ tag exists to pad the request out to the minimum required length. Its value is all zero bytes.


5.2. Responses ๐Ÿ’–

the server starts handling the request with a set of long-term keys. It figures out which long-term key to use with the following procedure:

  1. If the request contains a SRV tag, then the server looks up the long-term key indicated by the SRV value. If no such key exists, then the server MUST ignore the request.
  2. If the request contains no SRV tag, but the server has just one long-term key, it SHOULD select that key. Otherwise, if the server has multiple long-term keys, then it MUST ignore the request.

a response contains the tags SIG, NONC, TYPE, PATH, SREP, CERT, and INDX. Figure 3 shows what a response message looks like ๐Ÿงฉ

   |--SIG
   |--NONC
   |--TYPE
   |--PATH
   |--SREP
   |  |--VER
   |  |--RADI
   |  |--MIDP
   |  |--VERS
   |  |--ROOT
   |--CERT
   |  |--SIG
   |  |--DELE
   |  |  |--PUBK
   |  |  |--MINT
   |  |  |--MAXT
   |--INDX

Figure 3: Roughtime Response Message Structure

No mechanism exists for reporting errors (like a wrong request format, an unsupported version, or an unknown SRV value) back to the client. This comes from experience with the NTP protocol, where Kiss-o'-Death packets (see Section 7.4 of [RFC5905]) are used to indicate errors. The existence of this unauthenticated protocol feature in NTP makes it possible for on-path attackers to make a client stop using authenticated modes or certain servers altogether (see Section 5.4 of [RFC8633] and Sections 8.3 and 8.7 of [RFC8915]). Since the protocol depends on multiple independent servers for security, error reporting functionality has been excluded from this version of Roughtime.

๐Ÿšจ on-path attackers can literally make a client give up on authenticated modes or on certain servers if error reports are unauthenticated. that's exactly why Roughtime skips error reporting altogether. protect your clients, bestie ๐Ÿ›ก๏ธ

5.2.1. SIG โœ๏ธ

in general, a SIG tag value is a 64-byte Ed25519 signature [RFC8032] over a concatenation of a context string and the entire value of a tag. All context strings include a terminating zero byte.

the SIG tag in the root of a response is a signature over the SREP value, made with the public key in CERT, using the context string "Roughtime v1 response signature" ๐Ÿ”

5.2.2. NONC ๐ŸŽ€

the NONC tag contains the nonce of the message being responded to.

5.2.3. TYPE ๐ŸŒธ

In a response, the TYPE tag MUST contain a uint32 with value 1. Responses with a TYPE tag holding any other value MUST be ignored by clients.

5.2.4. PATH ๐Ÿชœ

the PATH tag value is a multiple of 32 bytes long and represents a path of 32-byte hash values in the Merkle tree used to generate the ROOT value, as described in Section 5.3. If a response is prepared for a single request and the Merkle tree only contains the root node, then the size of PATH is zero ๐ŸŒณ

The PATH MUST NOT contain more than 32 hash values. The maximum length of PATH is normally limited by the maximum size of the response message; see Sections 5.1 and 9.7. Server implementations MUST select a maximum Merkle tree height (see Section 5.3) that ensures this. tree height, but make it responsible ๐Ÿ”ฅ

5.2.5. SREP ๐ŸŽ€

okay so the SREP tag is where the signed response actually lives ๐Ÿ’Œ its value is a Roughtime message with the tags VER, RADI, MIDP, VERS, and ROOT, so it's basically a mini packet tucked inside the packet, iykyk ๐Ÿช†

The VER tag, when it shows up in a response, holds a single uint32 version number. It SHOULD be one of the version numbers the client sent in its request (see Section 5.1.1) ๐Ÿซถ and the server MUST ensure that the version number lines up with the rest of the packet contents. no mismatches allowed bestie ๐Ÿ™…

The RADI tag value is a uint32 representing the server's estimate of how accurate MIDP is, in seconds ๐Ÿ“ Servers MUST ensure that the true time is within (MIDP-RADI, MIDP+RADI) at the moment of processing. The value of RADI MUST NOT be zero, ngl that would be giving false confidence ๐Ÿ’€ Since leap seconds can't be unambiguously represented by Roughtime timestamps, servers MUST take this into account when setting the RADI value during leap second events ๐ŸŒ— Servers that do not have any leap second information SHOULD set the value of RADI to at least 3. Failure to do so will impact the observed correctness of Roughtime servers and can lead to malfeasance reports, and nobody wants that era ๐Ÿ˜ญ

The MIDP tag value is the timestamp of the moment of processing โฐ

The VERS tag value contains a list of uint32 version numbers supported by the server, sorted in ascending numerical order ๐Ÿ“š It MUST contain the version number specified in the VER tag. It MUST NOT contain more than 32 version numbers.

The ROOT tag contains a 32-byte value of a Merkle tree root, as described in Section 5.3 ๐ŸŒณ

5.2.6. CERT ๐Ÿ”

The CERT tag contains a public key certificate signed with the server's private long-term key. Its value is a Roughtime message with the tags SIG and DELE, where SIG is a signature over the DELE value with the context string "Roughtime v1 delegation signature" โœ๏ธ

The DELE tag contains a delegated public key certificate used by the server to sign the SREP tag. Its value is a Roughtime message with the tags PUBK, MINT, and MAXT. The whole point of DELE is to separate a long-term public key from keys on devices exposed to the public Internet, which is such a smart move ๐Ÿง โœจ

The PUBK tag contains a temporary 32-byte Ed25519 public key that is used to sign the SREP tag ๐Ÿ”‘

The MINT tag is the minimum timestamp for which the key in PUBK is trusted to sign responses. MIDP MUST be more than or equal to MINT for a response to be considered valid โœ…

The MAXT tag is the maximum timestamp for which the key in PUBK is trusted to sign responses. MIDP MUST be less than or equal to MAXT for a response to be considered valid ๐Ÿ™Œ

5.2.7. INDX ๐Ÿ“

The INDX tag value is a uint32 determining the position of NONC in the Merkle tree used to generate the ROOT value, as described in Section 5.3 ๐Ÿ—บ๏ธ


5.3. The Merkle Tree ๐ŸŒฒ

A Merkle tree [Merkle] is a binary tree where the value of each non-leaf node is a hash value derived from its two children. The root of the tree is thus dependent on all leaf nodes ๐Ÿƒ

In Roughtime, each leaf node in the Merkle tree represents one request. Leaf nodes are indexed left to right, beginning with zero ๐Ÿ‘ˆ๐Ÿ‘‰

The values of all nodes are calculated from the leaf nodes and up towards the root node using the first 32 bytes of the output of the SHA-512 hash algorithm [RFC6234]. For leaf nodes, the byte 0x00 is prepended to the full value of the client's request packet, including the "ROUGHTIM" header, before applying the hash function. For all other nodes, the byte 0x01 is concatenated with first the left and then the right child node value before applying the hash function ๐Ÿงฎ

The value of the Merkle tree's root node is included in the ROOT tag of the response.

The index of a request leaf node is included in the INDX tag of the response.

The values of all sibling nodes in the path between a request leaf node and the root node are stored in the PATH tag, so that the client can reconstruct and validate the value in the ROOT tag using its request packet. These values are each 32 bytes and are stored one after the other with no additional padding or structure. The order in which they are stored is described in the following subsection ๐Ÿ“ฆ

5.3.1. Root Value Validity Check Algorithm ๐Ÿงฉ

This section describes how to compute the value of the root of the Merkle tree from the values in the tags PATH, INDX, and NONC. The bits of INDX are ordered from least to most significant. H(x) denotes the first 32 bytes of the SHA-512 hash digest of x, and || denotes concatenation.

The algorithm maintains a current value h. At initialization, h is set to H(0x00 || request_packet). For each step of the algorithm, let node be the next 32 bytes in PATH. If the current bit in INDX is 0 then h = H(0x01 || h || node), else h = H(0x01 || node || h). When no more entries remain in PATH, h is compared to the value of the root of the Merkle tree contained in ROOT. If they are equal, the algorithm succeeds. If they are not, or if any of the remaining bits of INDX is non-zero, the algorithm fails ๐ŸŽฏ


5.4. Validity of Response โœ…

A client MUST check the following properties when it receives a response. We assume the long-term server public key is known to the client through other means ๐Ÿ”“

The signature in CERT was made with the long-term key of the server.

The MIDP timestamp lies in the interval specified by the MINT and MAXT timestamps.

The INDX and PATH values prove a hash value derived from the request packet was included in the Merkle tree with value ROOT using the algorithm in Section 5.3.1.

The signature of SREP in SIG validates with the public key in DELE.

A response that passes these checks is said to be valid. Validity of a response does not prove that the timestamp's value in the response is correct, but merely that the server guarantees that it signed the timestamp and computed its signature during the time interval (MIDP-RADI, MIDP+RADI) ๐Ÿ•ฐ๏ธ


6. Integration into NTP ๐Ÿ•

We assume that there is a bound phi on the frequency error in the clock on the machine. Let delta be the time difference between the clock on the client and the clock on the server, and let sigma represent the error in the measured value of delta introduced by the measurement process. Given a measurement taken at a local time t, we know the true time is in (t-delta-sigma, t-delta+sigma). After d seconds have elapsed we know the true time is within (t-delta-sigma-d*phi, t-delta+sigma+d*phi) ๐Ÿง

This bound can be used as a simple and effective means to limit the error an attacker can introduce into NTP or Precision Time Protocol (PTP) measurements. For example, an NTP client can ensure that its observation intervals fall entirely within this range, or can reject measurements that fall outside ๐Ÿ›ก๏ธ

An application that needs to verify X.509 certificates (which requires knowledge of the current time) but lacks an accurate and trusted time source can use Roughtime to obtain a time estimate. In particular, securely establishing NTS-protected NTP time synchronization requires verification of the NTS-KE server's certificate, which is not possible if the client has no idea of the current time (see Section 8.5 of [RFC8915]). In that case, a Roughtime time estimate can be used for certificate validation ๐Ÿ”

If an NTP server uses a Roughtime server as a time source for synchronization (and not only for filtering its NTP measurements), the root dispersion SHOULD include the server's RADI value, and root delay SHOULD include the interval between sending the Roughtime request and receiving the response ๐Ÿค


7. Grease ๐Ÿงด

The primary purpose of grease is to prevent protocol ossification, which could prohibit future protocol extensions and development [RFC9170]. In Roughtime, grease is also intended to ensure that clients validate signatures. To grease the Roughtime protocol, servers SHOULD send back a fraction of responses with any of the following: lack of mandatory tags, version numbers not in the request, undefined tags, or invalid signatures together with incorrect times. Clients MUST properly ignore undefined tags and reject invalid responses. Servers MUST NOT send back responses with incorrect times and valid signatures. Either signature in the response (i.e., of the SREP or DELE tag) MAY be invalid for this application ๐Ÿซง

Warning: servers must never send back responses with incorrect times and valid signatures. Grease is only for the cases listed above ๐Ÿšจ

Hmm, that last aside isn't a requirement; delete. Final answer continues below without it.


8. Roughtime Clients ๐Ÿ“ฑ

8.1. Necessary Configuration โš™๏ธ

To carry out a Roughtime measurement, a client needs a list of servers, a minimum of three of which are operational and not run by the same parties. Roughtime clients SHOULD regularly update their view of which servers are trustworthy in order to benefit from the detection of misbehavior (see Section 8.3). Clients SHOULD also have a means of reporting to the provider of such a list, such as an operating system or software vendor, a malfeasance report as described in Section 8.4 ๐Ÿ“

8.2. Measurement Sequence ๐Ÿ”

The client randomly selects at least three servers from the list, and sequentially queries them. To ensure that all possible inconsistencies can be detected, it is necessary for clients to repeat the query sequence twice with the servers in the same order.

The first probe uses a nonce that is randomly generated. The second query uses H(resp || rand) where rand is a random 32-byte value and resp is the entire response to the first probe, including the "ROUGHTIM" header. Each subsequent query uses H(resp || rand) for the previous response and a different 32-byte rand value. H(x) and || are defined as in Section 5.3.1.

For each pair of responses (i, j), where i was received before j, the client MUST check that MIDP_i-RADI_i is less than or equal to MIDP_j+RADI_j. If these checks pass, the times are consistent with causal ordering. The measurement succeeds if the validity checks described in Section 5.4 are successful, the times reported are consistent with causal ordering, and the delay between request and response is within an implementation-dependent maximum value ๐ŸŽ‰

If the validity checks are successful, but at least one of the responses is not consistent with causal ordering, there has been a malfeasance. In case of detected malfeasance, clients SHOULD, if it is technically possible, generate a malfeasance report (see Section 8.4), alert the user, and make another measurement. See Section 5 for guidance on backoff when making repeated measurements ๐Ÿšจ

8.3. Server Lists ๐Ÿ“‹โœจ

Okay so, to keep your list of trusted servers fresh, there's a shared server list format defined right here ๐Ÿซถ. Supporting this common format is OPTIONAL ๐Ÿ’…, and clients MAY instead build their own way of configuring server lists ๐Ÿ› ๏ธ.

A server list is a JSON object [RFC8259] that has the key servers ๐ŸŽ€. Server list objects MAY also have the keys sources and reports ๐ŸŒธ. Appendix A has an example server list in this format ๐Ÿ’–.

Server lists get the application/roughtime-server+json media type ๐Ÿงš.

The value of servers is a list of server objects, and each one has the keys name, version, publicKeyType, publicKey, and addresses ๐Ÿ‘‘.

The value of name is a string with a server name that's readable for a human ๐Ÿ‘€.

The value of version is an integer for the highest Roughtime version number the server supports ๐Ÿ”ฅ.

The value of publicKeyType is a string naming the signature scheme the server uses. For servers that support version 1 of Roughtime, it's ed25519 ๐Ÿ’œ.

The value of publicKey is a base64-encoded [RFC4648] string holding the server's long-term public key, in a format that matches publicKeyType ๐Ÿ”.

The value of addresses is a list of address objects. Each address object has the keys protocol and address ๐Ÿฆ‹. The value of protocol is either tcp or udp, which tells you the transport mode to use ๐Ÿš€. The value of address is a string with a host and a port number separated by a colon, like roughtime.example.com:5319 โœจ. The host part is an IPv4 address, an IPv6 address, or a fully qualified domain name (FQDN) ๐ŸŒˆ. IPv4 addresses are written in dotted decimal notation. IPv6 addresses MUST conform to the "Text Representation of Addresses" (Section 2.2 of [RFC4291]) and MUST NOT include zone identifiers [RFC9844] ๐Ÿ™ˆ. To tell an IPv6 address apart from the port when zero compression happens, the IPv6 address gets wrapped in [] ๐Ÿงฉ. The port part is a decimal integer for a valid port number, so anything in the range 0-65535 ๐Ÿ“.

The value of sources, if it's there, is a list of strings that say where updated versions of the list can be grabbed using the HTTP GET method [RFC9110] ๐Ÿ“ฅ. Each string is a URL [RFC3986] pointing to a list in the format specified here ๐ŸŒŸ. The URI scheme MUST be HTTPS [RFC9110] ๐Ÿ”’.

The value of reports, if it's there, is a string with a URL [RFC3986] where clients can send malfeasance reports using the HTTP POST method ๐Ÿ“ฎ. The URI scheme MUST be HTTPS [RFC9110] ๐Ÿ’–.

8.4. Malfeasance Reporting ๐Ÿ•ต๏ธโ€โ™€๏ธ

A malfeasance report is cryptographic proof that a sequence of responses arrived in that order. It can be used to demonstrate that at least one server sent the wrong time โฐ๐Ÿšจ. Iconic evidence energy, no cap ๐Ÿ’….

8.4.1. Malfeasance Report Format ๐Ÿ“

A malfeasance report is a JSON object [RFC8259] with the key responses ๐ŸŽ€. Its value is a list of response objects, sorted in the order they were received ๐Ÿ“š. Each response object has the keys rand, publicKey, request, and response. All four values are represented as base64-encoded [RFC4648] strings ๐Ÿซง. Appendix B has an example malfeasance report in this format ๐ŸŒธ.

Malfeasance reports have the application/roughtime-malfeasance+json media type ๐Ÿงšโ€โ™€๏ธ.

The rand key MAY be left out of the first response object in the list ๐Ÿ™ˆ. In every other case, its value is the 32-byte value that was used to generate the request nonce value from the previous response packet ๐Ÿ”ข.

The value of publicKey is the long-term key the server was expected to use for deriving the response signature ๐Ÿ”‘.

The value of request is the transmitted request packet, including the ROUGHTIM header ๐Ÿ“ค.

The value of response is the received response packet, including the ROUGHTIM header ๐Ÿ“ฅ.

8.4.2. Reporting ๐Ÿ“ฃ

When the client's list of servers has an associated URL for malfeasance reports, it SHOULD send a malfeasance report to that URL when malfeasance is detected (see Section 8.2) and it's technically feasible to do so ๐Ÿ’Œ. Malfeasance reports are sent using the HTTP POST method [RFC9110] ๐Ÿ“ฎ.

Okay, this part is so important ๐Ÿ˜ญ. Since the failure of a popular Roughtime server can make a ton of clients send malfeasance reports at the exact same time, clients MUST use exponential backoff so they don't overload the server receiving the reports ๐Ÿซ . It is RECOMMENDED that clients use an initial retry interval of 10 seconds, a maximum interval of 24 hours, and a base of 1.5 ๐ŸŒˆ. So the minimum interval, in seconds, before retrying after n failures is min(10 * 1.5^(n-1), 86400) ๐Ÿงฎโœจ.

Clients MUST NOT send malfeasance reports in response to signature verification failures or any other protocol errors. Not me being dramatic, but this one is a hard no ๐Ÿšซ๐Ÿ’€.

As described in Section 1, the operational rules for accepting or rejecting a particular malfeasance report are beyond the scope of this document ๐Ÿค“.


9. Security Considerations ๐Ÿ”’

9.1. Confidentiality ๐Ÿคซ

This protocol does not provide any confidentiality. Given the nature of timestamps, such impact is minor ๐Ÿ•ฐ๏ธ.

9.2. Integrity and Authenticity ๐Ÿ›ก๏ธ

The Roughtime protocol only provides integrity and authenticity protection for data contained in the SREP tag. Accordingly, new tags SHOULD be added to the SREP tag whenever possible ๐Ÿ’œ.

9.3. Generating Private Keys ๐Ÿ—๏ธ

Although any random 256-bit string can be used as a private Ed25519 key, it has a high risk of being vulnerable to small-subgroup attacks and timing side-channel leaks ๐Ÿ˜ฑ. For this reason, all private keys used in Roughtime MUST be generated following the procedure described in Section 5.1.5 of [RFC8032] ๐Ÿ”๐Ÿ”ฅ.

9.4. Private Key Compromise ๐Ÿ’”

The compromise of a PUBK's private key, even past MAXT, is a problem ๐Ÿšจ. The private key can be used to sign invalid times that are in the range MINT to MAXT, which violates the good-behavior guarantee of the server ๐Ÿ˜ญ. To protect against this, it is necessary for clients to query multiple servers in accordance with the procedure described in Section 8.2 ๐Ÿ›ก๏ธโœจ.

9.5. Quantum Resistance ๐ŸŒŒ

Since the only supported signature scheme, Ed25519, is not quantum resistant, the Roughtime version described in this document will not survive the advent of quantum computers ๐Ÿซฃ. A later version will have to be devised and implemented before then โณ. Using a single version number as the negotiation point, instead of defining a suite of acceptable signatures, is intended to prevent fragmentation and misconfiguration ๐Ÿงฉ.

9.6. Maintaining Lists of Servers ๐Ÿ“‹

The infrastructure and procedures for maintaining a list of trusted servers, and for adjudicating violations of the rules by servers, are not discussed in this document. They are essential for security ๐Ÿซถ.

9.7. Amplification Attacks ๐Ÿ“ข

UDP protocols that send responses significantly larger than their requests, such as NTP, have previously been leveraged for amplification attacks ๐Ÿ˜ค. To prevent Roughtime from being used for such attacks, servers MUST NOT send response packets larger than the request packets sent by clients. ๐Ÿšซ๐Ÿ”ฅ


10. Privacy Considerations ๐Ÿซง

This protocol is designed to obscure all client identifiers ๐Ÿ™ˆ. Servers necessarily have persistent long-term identities, which are essential to enforcing correct behavior ๐Ÿชช. Generating nonces in a nonrandom manner can cause leaks of private data or enable tracking of clients as they move between networks ๐Ÿ‘€.


11. Operational Considerations โš™๏ธ

It is expected that clients identify a server by its long-term public key ๐Ÿ”‘. In multi-tenancy environments, where multiple servers may be listening on the same IP or port space, the protocol is designed so that the client indicates which server it expects to respond ๐Ÿ . This is done with the SRV tag ๐Ÿท๏ธ. Additional recommendations for clients are listed in Section 8 ๐Ÿ’–.


12. IANA Considerations ๐Ÿ›๏ธ

12.1. Service Name and Transport Protocol Port Number Registry ๐Ÿ”Œ

IANA has allocated the following entry in the "Service Name and Transport Protocol Port Number Registry":

Service Name:  roughtime

Transport Protocol:  tcp, udp

Assignee:  IESG <iesg@ietf.org>

Contact:  IETF Chair <chair@ietf.org>

Description:  Roughtime time synchronization

Reference:  RFC 10049

Port Number:  5319

12.2. Roughtime Versions Registry ๐Ÿท๏ธ

IANA has created a new registry titled "Roughtime Versions" in a new "Roughtime" registry group. Entries have the following fields:

  • Version ID (REQUIRED): A 32-bit unsigned integer
  • Version Name (REQUIRED): A short text string naming the version being identified
  • Reference (REQUIRED): A reference to a relevant specification document

The policy for allocation of new entries is IETF Review [RFC8126] ๐Ÿ“œ.

The initial contents of this registry are specified below ๐Ÿ‘‡.

+=======================+===============================+===========+
| Version ID            | Version Name                  | Reference |
+=======================+===============================+===========+
| 0x0                   | Reserved                      | RFC 10049 |
+-----------------------+-------------------------------+-----------+
| 0x1                   | Roughtime version 1           | RFC 10049 |
+-----------------------+-------------------------------+-----------+
| 0x2-0x7fffffff        | Unassigned                    |           |
+-----------------------+-------------------------------+-----------+
| 0x80000000-0xbfffffff | Reserved for                  | RFC 10049 |
|                       | Experimental Use              |           |
+-----------------------+-------------------------------+-----------+
| 0xc0000000-0xffffffff | Reserved for                  | RFC 10049 |
|                       | Private Use                   |           |
+-----------------------+-------------------------------+-----------+

        Table 1: Initial Contents of the Roughtime Versions Registry

Private and Experimental Use are defined in [RFC8126] ๐Ÿง‘โ€๐Ÿ’ป. The experimental range is intended for testing and evaluating new versions of the Roughtime protocol ๐Ÿงช. Such tests may be conducted over the open Internet ๐ŸŒโœจ.

12.3. Roughtime Tags Registry โœจ

okay so IANA made a brand new registry called "Roughtime Tags" ๐ŸŽ€, and it lives in its own shiny new "Roughtime" registry group ๐Ÿ’…. every entry has three fields, and they're all REQUIRED (no skipping, bestie) ๐Ÿซถ:

  • Tag (REQUIRED): a 32-bit unsigned integer written in hexadecimal format ๐Ÿ”ข
  • ASCII Representation (REQUIRED): the ASCII representation of the tag, done in accordance with Section 4.1.3 of this document โœ๏ธ
  • Reference (REQUIRED): a reference to a relevant specification document ๐Ÿ“š

the allocation policy for new entries is Specification Required [RFC8126], so it's giving strict-but-fair energy ๐Ÿ“œ

and the starting lineup? it's all in Table 2 ๐Ÿ‘‘, and every single one of them points back to RFC 10049 ๐Ÿฆ‹

+============+======================+===========+
|        Tag | ASCII Representation | Reference |
+============+======================+===========+
| 0x00474953 | SIG                  | RFC 10049 |
+------------+----------------------+-----------+
| 0x00524556 | VER                  | RFC 10049 |
+------------+----------------------+-----------+
| 0x00565253 | SRV                  | RFC 10049 |
+------------+----------------------+-----------+
| 0x434e4f4e | NONC                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x454c4544 | DELE                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x45505954 | TYPE                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x48544150 | PATH                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x49444152 | RADI                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x4b425550 | PUBK                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x5044494d | MIDP                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x50455253 | SREP                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x53524556 | VERS                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x544e494d | MINT                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x544f4f52 | ROOT                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x54524543 | CERT                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x5458414d | MAXT                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x58444e49 | INDX                 | RFC 10049 |
+------------+----------------------+-----------+
| 0x5a5a5a5a | ZZZZ                 | RFC 10049 |
+------------+----------------------+-----------+

    Table 2: Initial Contents of the Roughtime
                  Tags Registry

12.4. Media Type Registry ๐ŸŒˆ

okay, IANA went shopping in the "Media Types" registry and picked up two brand new Roughtime media types ๐Ÿ›๏ธ

12.4.1. Media Type for Roughtime Server List ๐Ÿงš

IANA has allocated the following entry in the "Media Types" registry.

  • Type name: application
  • Subtype name: roughtime-server+json
  • Required parameters: N/A
  • Optional parameters: N/A
  • Encoding considerations: Encoding considerations are identical to those specified for the "application/json" media type, see [RFC8259].
  • Security considerations: Section 9 of RFC 10049.
  • Interoperability considerations: N/A
  • Published specification: Section 8.3 of RFC 10049.
  • Applications that use this media type: Roughtime clients (RFC 10049) that update their lists of Roughtime servers.
  • Fragment identifier considerations: N/A
  • Additional information:
    • Deprecated alias names for this type: N/A
    • Magic number(s): N/A
    • File extension(s): N/A
    • Macintosh file type code(s): N/A
  • Person & email address to contact for further information: See Authors' Addresses section of RFC 10049.
  • Intended usage: COMMON
  • Restrictions on usage: N/A
  • Author: See Authors' Addresses section of RFC 10049.
  • Change controller: Internet Engineering Task Force

12.4.2. Media Type for Roughtime Malfeasance ๐Ÿ’€

IANA has allocated the following entry in the "Media Types" registry.

  • Type name: application
  • Subtype name: roughtime-malfeasance+json
  • Required parameters: N/A
  • Optional parameters: N/A
  • Encoding considerations: Encoding considerations are identical to those specified for the "application/json" media type, see [RFC8259].
  • Security considerations: Section 9 of RFC 10049.
  • Interoperability considerations: N/A
  • Published specification: Section 8.4.1 of RFC 10049.
  • Applications that use this media type: Roughtime clients (RFC 10049) use this media type to report cryptographic proof that a Roughtime server has sent the wrong time.
  • Fragment identifier considerations: N/A
  • Additional information:
    • Deprecated alias names for this type: N/A
    • Magic number(s): N/A
    • File extension(s): N/A
    • Macintosh file type code(s): N/A
  • Person & email address to contact for further information: See Authors' Addresses section of RFC 10049.
  • Intended usage: COMMON
  • Restrictions on usage: N/A
  • Author: See Authors' Addresses section of RFC 10049.
  • Change controller: Internet Engineering Task Force

13. References ๐Ÿ“š

13.1. Normative References

[BCP106] Best Current Practice 106, <https://www.rfc-editor.org/info/bcp106>. At the time of writing, this BCP comprises the following: Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005, <https://www.rfc-editor.org/info/rfc4086>.

[RFC0020] Cerf, V., "ASCII format for network interchange", STD 80, RFC 20, DOI 10.17487/RFC20, October 1969, <https://www.rfc-editor.org/info/rfc20>.

[RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791, DOI 10.17487/RFC791, September 1981, <https://www.rfc-editor.org/info/rfc791>.

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, DOI 10.17487/RFC3986, January 2005, <https://www.rfc-editor.org/info/rfc3986>.

[RFC4291] Hinden, R. and S. Deering, "IP Version 6 Addressing Architecture", RFC 4291, DOI 10.17487/RFC4291, February 2006, <https://www.rfc-editor.org/info/rfc4291>.

[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006, <https://www.rfc-editor.org/info/rfc4648>.

[RFC6234] Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, May 2011, <https://www.rfc-editor.org/info/rfc6234>.

[RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital Signature Algorithm (EdDSA)", RFC 8032, DOI 10.17487/RFC8032, January 2017, <https://www.rfc-editor.org/info/rfc8032>.

[RFC8085] Eggert, L., Fairhurst, G., and G. Shepherd, "UDP Usage Guidelines", BCP 145, RFC 8085, DOI 10.17487/RFC8085, March 2017, <https://www.rfc-editor.org/info/rfc8085>.

[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, December 2017, <https://www.rfc-editor.org/info/rfc8259>.

[RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, DOI 10.17487/RFC9110, June 2022, <https://www.rfc-editor.org/info/rfc9110>.

13.2. Informative References

[Merkle] Merkle, R. C., "A Digital Signature Based on a Conventional Encryption Function", Advances in Cryptology - CRYPTO '87, Lecture Notes in Computer Science, vol. 293, pp. 369-378, DOI 10.1007/3-540-48184-2_32, 1988, <https://doi.org/10.1007/3-540-48184-2_32>.

[RFC0738] Harrenstien, K., "Time server", RFC 738, DOI 10.17487/RFC738, October 1977, <https://www.rfc-editor.org/info/rfc738>.

[RFC5905] Mills, D., Martin, J., Ed., Burbank, J., and W. Kasch, "Network Time Protocol Version 4: Protocol and Algorithms Specification", RFC 5905, DOI 10.17487/RFC5905, June 2010, <https://www.rfc-editor.org/info/rfc5905>.

[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, June 2017, <https://www.rfc-editor.org/info/rfc8126>.

[RFC8633] Reilly, D., Stenn, H., and D. Sibold, "Network Time Protocol Best Current Practices", BCP 223, RFC 8633, DOI 10.17487/RFC8633, July 2019, <https://www.rfc-editor.org/info/rfc8633>.

[RFC8915] Franke, D., Sibold, D., Teichel, K., Dansarie, M., and R. Sundblad, "Network Time Security for the Network Time Protocol", RFC 8915, DOI 10.17487/RFC8915, September 2020, <https://www.rfc-editor.org/info/rfc8915>.

[RFC9170] Thomson, M. and T. Pauly, "Long-Term Viability of Protocol Extension Mechanisms", RFC 9170, DOI 10.17487/RFC9170, December 2021, <https://www.rfc-editor.org/info/rfc9170>.

[RFC9523] Rozen-Schiff, N., Dolev, D., Mizrahi, T., and M. Schapira, "A Secure Selection and Filtering Mechanism for the Network Time Protocol with Khronos", RFC 9523, DOI 10.17487/RFC9523, February 2024, <https://www.rfc-editor.org/info/rfc9523>.

[RFC9844] Carpenter, B. and R. Hinden, "Entering IPv6 Zone Identifiers in User Interfaces", RFC 9844, DOI 10.17487/RFC9844, August 2025, <https://www.rfc-editor.org/info/rfc9844>.

Appendix A. Example Server List โœจ

okay bestie, this appendix is a example Roughtime server list ๐Ÿ’– and it's written in the format from Section 8.3, so go check that section if you want the full rules ๐Ÿงš

Here's the vibe: there are two servers in this list. The first is "example.com Roughtime server" and it's reachable over both UDP and TCP on port 5319 ๐ŸŒธ The second is a UDP-only server that's listed by IP addresses instead of a name, with one IPv4 address and one IPv6 address, both on port 5319 ๐Ÿฆ‹ The list also has a sources field with two ecosystem URIs and a reports field pointing to a malfeasance URI ๐Ÿ‘‘

{
  "servers": [
    {
      "name": "example.com Roughtime server",
      "version": 1,
      "publicKeyType": "ed25519",
      "publicKey": "2O3mkkheDExCuhG+ZNIoWmO/IdCdLzADgUn8SnC4hME=",
      "addresses": [
        {
          "protocol": "udp",
          "address": "roughtime.example.com:5319"
        },
        {
          "protocol": "tcp",
          "address": "roughtime.example.com:5319"
        }
      ]
    },
    {
      "name": "A UDP-only server specified with IP addresses",
      "version": 1,
      "publicKeyType": "ed25519",
      "publicKey": "ZYfeGa94YuG1IZrV3kR9+8/nmZ2lX2XyHmiSb+wI0OY=",
      "addresses": [
        {
          "protocol": "udp",
          "address": "192.0.2.33:5319"
        },
        {
          "protocol": "udp",
          "address": "[2001:db8::2:33]:5319"
        }
      ]
    }
  ],
  "sources": [
    "https://www.example.net/roughtime/ecosystem.json",
    "https://www.example.org/roughtime/ecosystem.json"
  ],
  "reports":  "https://www.example.net/roughtime/malfeasance"
}

Appendix B. Example Malfeasance Report ๐Ÿ”ฅ

this appendix is giving receipts ๐Ÿ’€ It shows an example Roughtime malfeasance report in the format from Section 8.4.1, and it has enough info to prove that the server with the public key lRhHag6fn2wZQ6idy10ChgpRgks3gvdMM2hWNeJNgXg= gave a time that doesn't match the times reported by the other two servers ๐Ÿ™ˆ

the report is the proof, so every single character matters here, no cap ๐Ÿซถ
  {
    "responses": [
      {
        "publicKey": "FnDyLV/68ephhLdFJbdEGCdkVvpXDaVe5PYvRDdlOOY=",
        "request": "Uk9VR0hUSU0ABAAABQAAAAQAAAAkAAAARAAAAEgAAABWRVIAU1JW
  AE5PTkNUWVBFWlpaWgEAAACf4gKLPdPfiNTv93lrhNqYgyehDgMyHFmA1BrAhM1QEDBh9l
  BlN6LUye6zghiqSWMwyNm0IucxQxW3zTMrwj4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  ==",
        "response": "Uk9VR0hUSU2UAQAABwAAAEAAAABgAAAAZAAAAGQAAADAAAAAWAE
  AAFNJRwBOT05DVFlQRVBBVEhTUkVQQ0VSVElORFhBWL64CToGs4v/4UtfN/80HLFiA09vG
  IDRP/zTjcTj8/1DlZWCsVja6RlfwaYnc1wfJqThfhcuSDonrTGyKngBMGH2UGU3otTJ7rO
  CGKpJYzDI2bQi5zFDFbfNMyvCPh0BAAAABQAAAAQAAAAIAAAAEAAAABQAAABWRVIAUkFES
  U1JRFBWRVJTUk9PVAEAAAADAAAAQ0u4aQAAAAABAAAAc86AWYB/O3Kxzsx4d5P5cbSOftJ
  UA8bWVtVrQ3tc+b0CAAAAQAAAAFNJRwBERUxFI2B5tbj5ePjVKYE0PAL1NmgZOAsqh/E2f
  rom9Ol5BAnVcLje0C6exbXY8hE3dRvYV01Alru8Ocle+jOZT5r8AwMAAAAgAAAAKAAAAFB
  VQktNSU5UTUFYVKqljhhqi4A54vW20e+slwViPyxybNnqKXzimIiIUHQMaBCvaQAAAADYy
  d9pAAAAAAAAAAA="
      },
      {
        "publicKey": "l9cdSuR8dFxtG9aJo9pWzUXaX8pftNG4UDC45Qk3znc=",
        "rand": "v/DirVBRQLGtictYD7mN3px02UlMT4J3haTRomt1NNM=",
        "request": "Uk9VR0hUSU0ABAAABQAAAAQAAAAkAAAARAAAAEgAAABWRVIAU1JW
  AE5PTkNUWVBFWlpaWgEAAABFCvadUKPN/U9OXqFalVFnv/EhsAuPPpBYfo5MDULZKv335k
  bNTZcsYLM96o77yLbvvRk1fm2TQB4yKxbP1jzeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  ==",
        "response": "Uk9VR0hUSU2UAQAABwAAAEAAAABgAAAAZAAAAGQAAADAAAAAWAE
  AAFNJRwBOT05DVFlQRVBBVEhTUkVQQ0VSVElORFj+2I02cCBAfDYzP+8znW6bICqVrAF23
  xNLfM+Qycmkpfp1+BQSb4l/6mRll66l2VIfPSQzigl2V5OJgQzGEBcG/ffmRs1Nlyxgsz3
  qjvvItu+9GTV+bZNAHjIrFs/WPN4BAAAABQAAAAQAAAAIAAAAEAAAABQAAABWRVIAUkFES
  U1JRFBWRVJTUk9PVAEAAAADAAAAw/m2aQAAAAABAAAAS8ROJoXIPSlO9yN+uREb+/UiOJJ
  qCjx3VWZ/vD2FqBMCAAAAQAAAAFNJRwBERUxFw0MFQMiDoHySot8rlnV83Vqaa3qTrAY15
  W1TzqNuAurOvreNw08BfUwxF7BQ/b/JCwCQcqtD5uRYsvikvuyLCwMAAAAgAAAAKAAAAFB
  VQktNSU5UTUFYVCQs/eCxtWjVrXyse0SeojyZdNFkOwe3B3nLHtHJZK/9gRCvaQAAAADxy
  d9pAAAAAAAAAAA="
      },
      {
        "publicKey": "lRhHag6fn2wZQ6idy10ChgpRgks3gvdMM2hWNeJNgXg=",
        "rand": "lMvMVoLsakxc5ZmMzEFQ8hh1FaDo2gCXXIX/L4QPSxQ=",
        "request": "Uk9VR0hUSU0ABAAABQAAAAQAAAAkAAAARAAAAEgAAABWRVIAU1JW
  AE5PTkNUWVBFWlpaWgEAAADmAab6GlTSr3xRmwwwvRjW6EKRhtyWYYWZHZIGnF8w2b4DuI
  ICvoCWTwCaupBfcOEwV+RqBWB6L1JUGn+ot8+9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  ==",
        "response": "Uk9VR0hUSU2UAQAABwAAAEAAAABgAAAAZAAAAGQAAADAAAAAWAE
  AAFNJRwBOT05DVFlQRVBBVEhTUkVQQ0VSVElORFg8MlhdCJeQ2qzN6b9q646W9kB+XmAdS
  g7ToU1gj3AD8AP8eCHfduMBE0E8j4/LqWIr72zWQx8Y1U/1uxs97yMAvgO4ggK+gJZPAJq
  6kF9w4TBX5GoFYHovUlQaf6i3z70BAAAABQAAAAQAAAAIAAAAEAAAABQAAABWRVIAUkFES
  U1JRFBWRVJTUk9PVAEAAAADAAAAw/m2aQAAAAABAAAA6ho4+0Cml+VJbqU7hsF717uusV2
  HYvRbU9CdjJE/Zn0CAAAAQAAAAFNJRwBERUxFM9Fvq8T9kOrxcS7jviCPHe44HX/75Je1h
  afPJ0f8NoASy29EgD7C0c/LMxXiXxEwyuxYTPN9oseAr9XIt68uDwMAAAAgAAAAKAAAAFB
  VQktNSU5UTUFYVMxBDiNG247IO4onsGcjFHsA3vP+arl+s0lBLhXw0c1RlBCvaQAAAAAEy
  t9pAAAAAAAAAAA="
      }
    ]
  }

Acknowledgments ๐Ÿ’œ

big thank-you moment ๐Ÿซถ Aanchal Malhotra and Adam Langley wrote early draft versions of this document, which is iconic of them ๐Ÿ‘‘

Then there's a whole squad who helped out: Daniel Franke, Sarah Grant, Erik Kline, Martin Langer, Ben Laurie, Peter Lรถthberg, Michael McCourt, Hal Murray, Tal Mizrahi, Ruben Nijveld, Christopher Patton, Thomas Peterson, Rich Salz, Dieter Sibold, Ragnar Sundblad, Kristof Teichel, Luke Valenta, David Venhoek, Ulrich Windl, and the other members of the NTP Working Group. They left comments and suggestions, and they also caught errors, which is literally the best kind of friend ๐Ÿงš

The authors also want to thank the Last Call reviewers and members of the IESG for their helpful comments and suggestions ๐ŸŒˆ


Authors' Addresses ๐Ÿฆ‹

Watson Ladd

  • Akamai Technologies
  • Email: watsonbladd@gmail.com

Marcus Dansarie

๐Ÿ“ฆ where this page comes from