Subject: [Ntp] NTPv4 Server/Port Negotiation records in NTS

From: Christer Weinigel <[email-masked]>
Date: Mon, 4 Feb 2019 16:55:47 +0100

Hello all,

sorry for jumping on the NTS discussions this late, but I have recently
started working  for Netnod here in Sweden and have just written yet
another proof of concept implementation of NTS:

     https://github.com/Netnod/nts-poc-python

While doing this I've found two issues regarding the NTPv4 Server/Port
Negotiation record in NTS.  The first one regarding the syntax of a
host name, is something i believe must be changed, the second one,
allowing multiple NTPv4 Server Negotiation records is something I
hope can be changed.  And sorry for the long email, but I wanted
to cover all the bases and show that I have considered  different
ways to solve the issues.

First some background.  Netnod has developed a NTP implementation which
runs in a FPGA.  This implementation runs on a Xilinx VC709 board and
can serve NTP at wire speed on 4 ports which each run at 10GBit/s.

     https://github.com/Netnod/FPGA_NTP_SERVER

A distant goal is to implement UDP timestamping part of NTS in the
same FPGA.  To do this, we will have to run NTSKE on a PC separate from
the FPGA and will need to use the NTPv4 Server Negotiation records.
While writing my proof of concept I have found a few things in the draft
that I would like to change.

1) Parsing of a host name in the NTPv4 Server Negotiation record

The specification looks like this:

     4.1.7.  NTPv4 Server Negotiation

     The NTPv4 Server Negotiation record has a Record Type number of 6.
     Its body consists of an ASCII-encoded [ANSI.X3-4.1986] string
     conforming to the syntax of the Host subcomponent of a URI
     ([RFC3986]).

RFC3986 has this to say about the host subcomponent of a URI:

     https://tools.ietf.org/html/rfc3986

     host        = IP-literal / IPv4address / reg-name

     reg-name    = *( unreserved / pct-encoded / sub-delims )

     unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"

     sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
                   / "*" / "+" / "," / ";" / "="

that is, letters outside of unreserved/sub-delims should be %-encoded.
This means that the following NTPv4 Server Negotiation records could
be used for "räksmörgås.se":

     79.99.1.121
     [2a02:750:7::817]
     xn--rksmrgs-5wao1o.se
     r%E4ksm%F6rg%E5s.se
     r%C3%A4ksm%C3%B6rg%C3%A5s.se

The last two encodings are a bit troublesome though. RFC3986
specifies no definite character encoding, which means that depending
on the server it could be ISO-8859-1 "r%E4ksm%F6rg%E5s.se", UTF-8
"r%C3%A4ksm%C3%B6rg%C3%A5s.se", or some other encoding. The client
will have to guess which encoding is used, convert that to Unicode,
and finally translate the Unicode string to an IDNA encoded string
"xn--rksmrgs-5wao1o.se" which can be handed over to a DNS resolver.
The client may also have to handle normalization/denormalization of
Unicode which can be painful.  I consider this a bug in the
specification.

Suggestion: Change the NTPv4 Server Negotiation record specification
so that it is self contained and no longer refers to RFC3986 and only
allows ASCII characters in hostnames.  IDNA-encoding must be used
for characters outside of ASCII:

     An IPv4 address using the dotted decimal notation, an IPv6 address
     using the "Text Representation of Addresses" described in
     [RFC4291] enclosed in brackets "[" IPv6-adress "]", or an ASCII
     string with a host name consisting of A-LABELS separated by
     periods (".").  The A-LABEL syntax is specified in [IDNA2008].

the only actual change in functionality, compared to the existing
draft, will be that %-encoding is no longer allowed and the
uncertainty regarding character encoding is removed. With this
change, the following NTPv4 Server Negotiation records could be used
for "räksmörgås.se":

     79.99.1.121
     [2a02:750:7::817]
     xn--rksmrgs-5wao1o.se

Advantage: Removes complexity from the client.  The client can almost
hand over the record directly to a resolver (i.e. getaddrinfo on Unix).
See (2) for a further issue though.

Disadvantage: None that I can see.

I believe that this is something that must be changed in the
specification.

2) Even with the suggested fix for (1), the string contained in a
NTPv4 Server Negotiation record can not be handed over to getaddrinfo
and needs a bit of parsing in the client.  The URI specification in
RFC3986 requires IPv6 addresses to be enclosed in brackets which
getaddrinfo is not able to parse.  It's not hard for a client to parse
the string and remove the brackets but it is an extra step.

Suggestion: Change the specification so that brackets are not needed
nor allowed for IPv6 addresses:

     An IPv4 address using the dotted decimal notation, an IPv6 address
     using the "Text Representation of Addresses" described in
     [RFC4291], or an ASCII string with a host name consisting of
     A-LABELS separated by periods (".").  The A-LABEL syntax is
     specified in [IDNA2008].

Advantage: Simplifies the client and allows the contens of a NTPv4
Server Negotiation record to be passed directly to getaddrinfo.

Disadvantage: Incompatible with the NTPv4 Server Negotiation record
format suggested in (4a).

If the suggestion in (4a) is rejected I'd suggest doing this to
simplify the client logic.

Note that a client ought to do a bit of sanity checking on the host
name anyway just to be on the safe side.  At a minimum it should
check that the record only contains [-.A-Za-z0-9] and that it is
a maximum of 253 characters long.

3) Multiple NTPv4 Server Negotiation records are forbidden

Since our FPGA_NTP_SERVER speaks both IPv4 and IPv6 we would like to
be able to return both an IPv4 and IPv6 address to the NTS/UDP server
in the response from the NTSKE server.  A client would ususally use
the first address it can handle.  A client which only speaks IPv4
would use the first IPv4 address.  A client which speaks both IPv4 and
IPv6 might prefer IPv6 with a fallback to IPv4 if it ends up on
network without IPv6 connectivity.

Actually, Netnod's use case would need the NTSKE server to be able to
return a total of 8 addresses since our FPGA implementation has 4
ports with an IPv4 and an IPv6 addresses for each port. But we are a
bit special in our requirements.

Unfortunately multiple records are explicitly forbidden by draft-15.

     4.1.7.  NTPv4 Server Negotiation

     ... Servers MUST NOT send more than one record of this type.

     ... The client MUST NOT send more than one record of this type.

I would like to remove this restriction if possible, and allow
multiple NTPv4 Server Negotiation records.

Consequences for a server: A server can limit itself to sending only
one NTPv4 Server Negotiation record and can ignore any such records it
has received.  There will be no complexity gain unless the server
needs it.

Consequences for a client: A client will have to be slightly more
complex, with an emphasis on slightly.  It should not be that hard for
a client to handle multiple records, it can just use the first record
that contains a valid address that the client can handle.  That is, a
client which only supports IPv4 should ignore IPv6 addresses (starting
with "["), a client which only supports IPv6 should ignore IPv4
addresses (matching IPv4 dotted notation), a client which supports
both can use the first record.  If a host name is found the client
will need to resolve the name using DNS just as before.

Advantages: A server can return both IPv4 and IPv6 addresses, and can
also return multiple NTP/UDP servers.

Disadvantages: Slightly more complex logic in a client.

If the restriction of only one NTPv4 Server Negotiation record is kept,
it's still possible to solve some of these issues by having using
a host name with a DNS record with multiple A/AAAA records.  But it
is an extra step.  Also much too many labs at small and large companies
don't have working DNS.  It would be nice if everyone had working DNS
but not everyone does.

I have not been able to read all of the discussions on NTS, but I
believe that one argument against allowing multiple NTPv4 Server
Negotiation records was a possible Sybil attack.  As long as a host
name is allowed in the NTPv4 Server Negotiation Record, the same
Sybil attack would be possible with multiple A/AAAA records in DNS.

A Sybil attack should not be an issue if the standard adds a
paragraph on how clients should treat multiple records, e.g.
something like this.

Note, when a NTSKE server returns multiple IP addresses for the
NTS/UDP servers, either through multiple NTPv4 Server Negotiation
records, or by returning a host name which resolves to multiple A/AAAA
records in DNS, the addresses SHOULD be considered as alternate
addresses for the same server, not as independent servers.  The
intended use of multiple records, is for a single server which uses
multiple transports such as IPv4 and IPv6 or when a server which has
multiple  IP addresses for load balancing and/or failover purposes.  A
client should normally select one of the addresses from the response and
keep using it forever.  A client can save the list of addresses and
switch to a different address as a fallback in case that currently used
address becomes unreachable, for example if the client is using an IPv6
address and loses IPv6 connectivity or if one of the ports on the
server stop working.

4) If multiple records are allowed, it will be a bit unclear which
NTPv4 Port Negotation record corresponds to each NTPv4 Server
Negotiation record.

Suggestion: drop the NTPv4 Port Negotation record from the standard
move the port number into the NTPv4 Server Negotiation record.

4a) Encode the port number with a colon similar to how it is done in a
URI.  For example:

     79.99.1.121:123
     [2a02:750:7::817]:123
     xn--rksmrgs-5wao1o.se:123

Note that it will not be possible to implement the suggestion in (2)
with this suggestion since an address such as 2a02:750:7::817:123 is
ambiguous and will probably be interpreted as an IPv6 address ending
with ":817:123".  The brackets are needed to disambiguate this case.

Advantage: Nice and human readable.  Easy to put the literal string in
a config file.  Extensible to other addressing schemes.

Disadvantage: Requires a bit of parsing on the client side to extract
the host and port parts.

4b) Encode the port number in the two first bytes of the record and
the server in the remaining space:

        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |             Port              |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                               |
       |   Server padded with up to 3  |
       |   octets of 0.                |
       |                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Advantage: The port number uses a binary encoding and can be read
directly from the field without any parsing.  If the suggestion in (2)
is implemented the server part can be passed directly to getaddrinfo
to resolve the server address.

Disadvantage: This will limit the port number to 16 bits, which is not
a problem for IPv4 or IPv6.  Do we have to care about some
hypothetical future protocol that would require more bits in the port
number?  (Note that if this is a proble, we already have this problem
with the existing NTPv4 Port Negotiation record).

4c) Make the encoding explicit by adding a protocol identifier at the
start of the NTPv4 Server Negotiation record:

        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       | Procotol (numeric values TBD) |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                               |
       |  Protocol specific encoding   |
       |  of server address            |
       |                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

for example, an IPv4 address would be encoded like this:

        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       | Proto=IPv4 (numeric value TBD)|
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |    Port (2 octets)            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |   4 octets of IPv4 address    |
       |                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

an IPv6 like this:

        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       | Proto=IPv6 (numeric value TBD)|
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |    Port (2 octets)            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |   16 octets of IPv6 address   |
       |                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

a host name which should be looked up via DNS:

        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       | Proto=DNS (numeric value TBD) |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |    Port (2 octets)            |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |   N octets of A-LABEL padded  |
       |    with up to 3 zero octets   |
       |                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

some future protocol which uses a 32 byte UUID to identify the server:

        0                   1
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       | Proto=Future (value TBD)      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |    32 bytes UIID              |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Advantage: Everything is explicit.  No parsing needed, IPv4 and IPv6
addresses can be used to fill in a sockaddr_in or sockaddr_in6
structure directly.

Disadvantage: I feel this is a bit too complex and overdesigned.  We'd
have to decide on a numbering scheme for the protocols. (Could we
reuse some existing list of protocols?)

4d) Keep the NTPv4 Server Negotiation record and NTPv4 Port
Negotiation record separate, but define an order for the records.

For example the following wording could work:

     The port specified in NTPv4 Port Negotiation record applies to all
     NTPv4 Server Negotiation records that follow it.  If no NTPv4 Port
     Negotiation records are provided, the client should default to the
     NTS port (currently the same as for NTP, 123).  If no NTPv4 Server
     Negotiation records are provided the client should fall back to
     using the same IP address as the NTSKE server.

and some Python-like pseudocode to select the first NTPv4 Server
Negotiation record that the client can use:

     server = None
     port = 123   # default port

     for record in ntske_session.records:
         if record.type == NTPv4_Port_Negotiation:
             port = unpack_big_endian_16bits(record.body)
         elif record.type == NTPv4_Server_Negotiation:
             if is_ipv4_address(record.body):
                 if ipv4_supported:
                     server = (parse_ipv4_address(record.body), port)
                     break
             elif is_ipv6_address(record.body):
                 if ipv6_supported:
                     server = (parse_ipv6_address(record.body), port)
                     break
             else:
                 tmp = resolve_address(record.body)
                 if tmp:
                     server = (tmp, port)
                     break

     if server is None:
         servers = ( ntske_host, port )

Advantage: Fairly easy to implement.

Disavantage: Slightly more complex logic than if the port number is
part of the NTPv4 Server Negotiation record.

4e) Ignore the problem.  All servers must use the same port, only
allow one NTPv4 Port Negotiation record.

Advantage: Easy to implement.

Disavantage: Not flexible.  But realistically very few people
will need multiple NTS/UDP servers at different ports. The servers
will need to share master keys, and will probably use the same NTS/UDP
implementation, so there is probably no reasonable reason to why they
should use different ports.

Right now I'm leaning towards implementing suggestion (1), (2) and
(4d), mostly because they involve as few changes to the draft as
possible.  Otherwise implementing (1) (2) and (4b), or (1) and (4a)
would be my preferred choices.  The only one I feel is really important
though is to fix the issue in (1).

What do you think?

Regards,
   Christer

---

From: "Ulrich Windl" <[email-masked]>
Date: Tue, 05 Feb 2019 09:08:39 +0100

>>> Christer Weinigel <[email-masked]> schrieb am 04.02.2019 um 16:55 in
Nachricht <[email-masked]>:

[...]
>      sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"

Actually I'm for the brackets: In the past it would be OK, but many years ago
the ystarted to add numeric domain named in DNS; one I think to remember right
now is "163.com". Another would be "35.Red-79-147-160.staticIP.rima-tde.net" or
"200-207-163-77.dsl.telesp.net.br".

> _______________________________________________
> ntp mailing list
> [email-masked] 
> https://www.ietf.org/mailman/listinfo/ntp

---

From: Martin Langer <[email-masked]>
Date: Fri, 08 Feb 2019 17:01:16 +0100

hello together,

As Christer already knows, I have noticed many of these points during 
the implementation
too and I guess a simplification of the URI makes sense.

On the subject of the server negotiation:
If the KE server supplies multiple time servers, it is currently 
necessary to ensure that these
servers support the cookies. This means that all servers must use the 
same MasterKey.

best regards,
Martin

-- 
Martin Langer, M.Eng.
Ostfalia Hochschule für angewandte Wissenschaften
- Hochschule Braunschweig/Wolfenbüttel
University of Applied Sciences

Labor Datentechnik, Labor Design Digitaler Systeme
Fakultät Elektrotechnik
Salzdahlumer Straße 46/48
38302 Wolfenbüttel
Germany

Tel. : +49 5331 939 43370
Web  : https://www.ostfalia.de/cms/de/pws/bermbach/mitarbeiter/martin-langer

---

From: Marcus Dansarie <[email-masked]>
Date: Sun, 10 Feb 2019 14:11:25 +0100

> servers support the cookies. This means that all servers must use the

No, not as I see it. This is first of all an implementation issue and
there's nothing in the draft that requires this.

Most NTS-KE servers will send a NTPv4 Server Negotiation record with the
initial cookies. This record indicates which NTP server the cookies will
work with. It is entirely possible for an NTS-KE server to handle
several master keys. This will, for example, be necessary in pool
configurations where there is no trust between operators of the NTP
servers in the pool.

In some configurations, such as the anycast configuration used by
ntp.se, this will require special handling, but that's up to the
operator(s) of the servers and software implementors to handle.

In a more extreme case, the actual data format of the cookies sent by an
NTS-KE server may be different for cookies meant for different NTP servers.

Kind regards,
Marcus

---

From: Hal Murray <[email-masked]>
Date: Sun, 10 Feb 2019 14:49:46 -0800

[email-masked] said:
> initial cookies. This record indicates which NTP server the cookies will work
> with. It is entirely possible for an NTS-KE server to handle several master
> keys.

A NTS-KE server can handle cookies from several NTP servers, but the protocol 
doesn't have any way to specify which cookies go with which IP Address.

I think the current approach for allowing the KE-server to return multiple IP 
Addresses covers the IPv4 vs IPv6 case.

It might cover the load sharing case where several servers are run by the same 
organization.  (From an admin standpoint, it may not be a good idea to have 
all your NTP servers using the same key.)  We can already cover that case by 
having the NTS-KE server pick the target NTP server rather than the NTS-KE 
client.

For something like the pool, I see two possibilities.

One is for the NTP server to do all the work, for example do the DNS lookup on 
pool.example.com to get several IP Addresses, do a reverse DNS lookup on each 
one to get the DNS name (which will be tied to the certificate), then do a 
normal NTS-KE with each name.

The other is to modify the NTS-KE protocol to process multiple servers.  I 
think this requires a new record type for the client to say how many servers 
it wants, and another record type for the server to say 
end-of-data-for-this-server.

-- 
These are my opinions.  I hate spam.

---

From: Christer Weinigel <[email-masked]>
Date: Mon, 11 Feb 2019 11:07:28 +0100

> Addresses covers the IPv4 vs IPv6 case.

There is no way for a NTSKE server to return multiple IP-addresses right 
now.  NTS draft 15 explicitly forbids multiple NTPv4 Server Negotiation 
records:

Currently the only way to have multiple IP-addresses for the NTPv4/UDP 
servers, is to place a host name in the record and then have multiple 
A/AAAA records in DNS.

> having the NTS-KE server pick the target NTP server rather than the NTS-KE

It seems to me that the common use case for multiple NTPv4 Server 
Negotiation records would be to be able to return both an IPv4 and IPv6 
address for the NTPv4/UDP server and not have to rely on DNS for that.

A more uncommon use case is a server like Netnod's FPGA implementation 
where one NTPv4/UDP server has four interfaces which are used for load 
balancing.

We might just decide that needing two IP-addresses for NTPv4/UDP is so 
uncommon that those who want to use both IPv4 and IPv6, or who want to 
have a NTPv4 server with multiple interfaces, will have to use DNS for 
that.  Unfortunately my experience from working in hardware labs is that 
DNS is often a luxury that is not available.  Management will happily 
spend $100k on a new spectrum analyzer, but getting the outsourced IT 
department to configure a DNS server is almost an impossibility.

So even though Netnod can manage with the restriction of only one NTPv4 
Server Negotiation record in the NTSKE protocol by using multiple A/AAAA 
records in DNS, I would be much happier if the restriction is removed.

> doesn't have any way to specify which cookies go with which IP Address.

Right now NTSKE server can only supply one IP address or host name.  If 
a host name is used and resolves to multiple IP addresses in DNS all 
those NTPv4/UDP servers must accept the same key.

I'd say that it is a good idea to not introduce multiple sets of keys 
and multiple servers which use different keys.  To do so would make the 
protocol too complex.  One NTSKE session should only return IP-addresses 
and cookies for one "logical" NTPv4/UDP server which uses one key.

> For something like the pool, I see two possibilities.

I believe that pools ought to be solved outside of the NTSKE protocol.

> normal NTS-KE with each name.

One way to do this would be to simply use a different host name for the 
NTSKE servers.  Use "se-nts.pool.ntp.org" instead of "se.pool.ntp.org". 
If one of the NTSKE servers in the pool is "sto1-nts.ntp.se" it would 
then return a NTPv4 Server Negotiation record for one of the NTPv4/UDP 
servers it knows the keys for, e.g. "sto1.ntp.se" which then resolves to 
the IPv4 and IPv6 addresses for the four ports the FPGA in Stockholm.

In addition to that one could add a SRV record on "se.pool.ntp.org" that 
points to a NTSKE server.  That is:

_nts._tcp.se.pool.ntp.org. 86400 IN SRV 0 5 443 se-nts.pool.ntp.org

se-nts.pool.ntp.org.        86400     IN      A       192.168.1.27
se-nts.pool.ntp.org.        86400     IN      A       172.16.10.37

I'm no DNS expert, but I think this ought to work.  The idea is that a 
user can just enter "se.pool.ntp.org" in the configuration field for 
"ntp servers" and a NTS aware client can then find the NTSKE server 
using the SRV record.

> end-of-data-for-this-server.

As I wrote, I don't think this is necessary since it can be solved 
outside of the NTSKE protocol.

Regards,
   Christer