Coverage Report

Created: 2026-02-26 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntpsec/ntpd/nts_cookie.c
Line
Count
Source
1
/*
2
 * nts_cookie.c - Network Time Security (NTS) cookie processing
3
 * Copyright the NTPsec project contributors
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 *
6
 * Section references are to
7
 * https://tools.ietf.org/html/rfc8915
8
 *
9
 * This follows section 6, Suggested Format for NTS Cookies
10
 * It uses AEAD_AES_SIV_CMAC_256/384/512 from RFC 5297
11
 * The selection is done by the key length.
12
 *
13
 * We use the implementation in libaes_siv by Daniel Franke (Akamai)
14
 * There is a similar implementation in OpenSSL (or soon will be)
15
 * It has a slightly different API.  See libaes_siv/README.md
16
 *
17
 */
18
19
#include "config.h"
20
21
#include <sys/types.h>
22
#include <sys/stat.h>
23
#include <fcntl.h>
24
#include <stdbool.h>
25
#include <stdint.h>
26
#include <stdio.h>
27
#include <string.h>
28
#include <pthread.h>
29
#include <unistd.h>
30
31
#include <aes_siv.h>
32
33
#include "ntpd.h"
34
#include "ntp_stdlib.h"
35
#include "nts.h"
36
#include "nts2.h"
37
38
/* Cookie format:
39
 *  cookie is I,N,CMAC,C
40
 *    I Key index, see below
41
 *    N nonce
42
 *  C is encrypt(K, N, P)
43
 *  P is AEAD, C2S, S2C
44
 *  length of C2S and S2C depends upon AEAD
45
 *  CMAC is 16 bytes
46
 */
47
48
/* K and I should be preserved across boots, and rotated every day or so.
49
 * We need to support the old K/I for another day.
50
 *
51
 * If the file gets corrupted, blow it away and reboot.  It will get
52
 * recreated, we will start using new cookies, packets from clients
53
 * with old cookies will get dropped, and eventually clients will
54
 * run out of cookies and use NTS-KE to get new ones.
55
 *
56
 * It would be possible to run without a cookie file.  Nobody would
57
 * notice until the server was restarted.  Then there would be a flurry
58
 * of NTS-KE requests until all clients obtained new/working cookies.
59
 */
60
61
/* Encryption within cookies uses AEAD_AES_SIV_CMAC_nnn.  That's the
62
 * same family of algorithms as NTS uses on the wire.
63
 * The nnn is selected by the key length.
64
 *   32 => 256
65
 *   48 => 384
66
 *   64 => 512
67
 */
68
69
/* NTS_MAX_COOKIELEN:
70
 *   4 I
71
 *  16 N
72
 *  16 CMAC
73
 *   4 AEAD
74
 *  64 C2S    NTS_MAX_KEYLEN
75
 *  64 S2C    NTS_MAX_KEYLEN
76
 * ------
77
 * 168
78
 *
79
 * That's the max length for our cookies.
80
 * Round up a bit in case another implementation uses more.
81
 * #define is in include/nts.h
82
 */
83
84
/* cookies use same AEAD algorithms as wire */
85
/* This determines which algorithm we use.
86
 * Valid choices are 32, 48, and 64
87
 * making this a variable rather than #define
88
 * opens up the opportunity to pick one at run time.
89
 * The default (below) is 32/AEAD_AES_SIV_CMAC_256
90
 * You can change that by editing the keys file.
91
 */
92
int K_length = AEAD_AES_SIV_CMAC_256_KEYLEN;
93
time_t K_time = 0;  /* time K was created, 0 for none */
94
struct NTS_Key nts_keys[NTS_nKEYS];
95
int nts_nKeys = 0;
96
97
/* The mutex protects cookie_ctx
98
 * The NTS-KE servers can make cookies
99
 *   while the main NTP server thread is unpacking and making cookies.
100
 * If this becomes a bottleneck, we could use a cookie_ctx per thread. */
101
pthread_mutex_t cookie_lock = PTHREAD_MUTEX_INITIALIZER;
102
AES_SIV_CTX* cookie_ctx;
103
104
void nts_lock_cookielock(void);
105
void nts_unlock_cookielock(void);
106
107
// FIXME  AEAD_LENGTH
108
/* Associated data: aead (rounded up to 4) plus NONCE */
109
0
#define AD_LENGTH 20
110
0
#define AEAD_LENGTH 4
111
112
/* cookie_ctx needed for client side */
113
0
bool nts_cookie_init(void) {
114
0
  cookie_ctx = AES_SIV_CTX_new();
115
0
  if (NULL == cookie_ctx) {
116
0
    msyslog(LOG_ERR, "NTS: Can't init cookie_ctx");
117
0
    exit(1);
118
0
  }
119
0
  return true;
120
0
}
121
122
/* cookie key needed for server side */
123
0
bool nts_cookie_init2(void) {
124
0
  bool OK = true;
125
0
  if (!nts_read_cookie_keys()) {
126
    /* Can't read cookie file.  Make one */
127
0
    nts_make_cookie_key();
128
0
    K_time = time(NULL);
129
0
    nts_write_cookie_keys();
130
0
  }
131
0
  return OK;
132
0
}
133
134
/* Rotate key -- 24 hours after last rotation
135
 * That allows a cluster NTS-KE server to keep in sync
136
 * if we use ratchet rather than random.
137
 */
138
0
#define SecondsPerDay (24*60*60)
139
// Set this shorter for debugging
140
//  keys will timeout, packets will get dropped
141
//  after 8 lost packets, it should go through the NTS-KE dance again
142
// Just uncommenting the next line will generate a warning reminder.
143
// #define SecondsPerDay 3600
144
0
void nts_cookie_timer(void) {
145
0
  time_t now;
146
0
  if (0 == K_time) {
147
0
    return;
148
0
  }
149
0
  now = time(NULL);
150
0
  if (SecondsPerDay > (now-K_time)) {
151
0
    return;
152
0
  }
153
0
  nts_make_cookie_key();
154
  /* In case we were off for many days. */
155
0
  while (SecondsPerDay < (now-K_time)) {
156
0
    K_time += SecondsPerDay;
157
0
  }
158
0
  if (nts_write_cookie_keys() )
159
0
    msyslog(LOG_INFO, "NTS: Wrote new cookie file, %d keys.", nts_nKeys);
160
0
  else
161
0
    msyslog(LOG_INFO, "NTS: Trouble writing new cookie file.");
162
0
  return;
163
0
}
164
165
166
0
bool nts_read_cookie_keys(void) {
167
0
  const char *cookie_filename = NTS_COOKIE_KEY_FILE;
168
0
  FILE *in;
169
0
  unsigned long templ;
170
0
  if (NULL != ntsconfig.KI)
171
0
    cookie_filename = ntsconfig.KI;
172
0
  in = fopen(cookie_filename, "r");
173
0
  if (NULL == in) {
174
0
    char errbuf[100];
175
0
    if (ENOENT == errno)
176
0
      return false;   /* File doesn't exist */
177
0
    ntp_strerror_r(errno, errbuf, sizeof(errbuf));
178
0
    msyslog(LOG_ERR, "NTSs: can't read old cookie file: %s=>%s",
179
0
      cookie_filename, errbuf);
180
0
    exit(1);
181
0
  }
182
0
  if (1 != fscanf(in, "T: %lu\n", &templ)) {
183
0
    goto bail;
184
0
  }
185
0
  K_time = templ;
186
0
  if (1 != fscanf(in, "L: %d\n", &K_length)) {
187
0
    goto bail;
188
0
  }
189
0
  if ( !((32 == K_length) || (48 == K_length) || (64 == K_length))) {
190
0
    goto bail;
191
0
  }
192
0
  nts_nKeys = 0;
193
0
  for (int i=0; i<NTS_nKEYS; i++) {
194
0
    struct NTS_Key *key = &nts_keys[i];
195
0
    if (1 != fscanf(in, "I: %u\n", &key->I)) {
196
0
    if (0 < nts_nKeys) break;
197
0
    goto bail;
198
0
    }
199
0
    if (0 != fscanf(in, "K: ")) {
200
0
    goto bail;
201
0
    }
202
0
    for (int j=0; j< K_length; j++) {
203
0
    unsigned int temp;
204
0
    if (1 != fscanf(in, "%02x", &temp)) {
205
0
      goto bail;
206
0
    }
207
0
    key->K[j] = temp;
208
0
    }
209
0
    if (0 != fscanf(in, "\n")) {
210
0
    goto bail;
211
0
    }
212
0
    nts_nKeys = i+1;
213
0
  }
214
0
  fclose(in);
215
0
  msyslog(LOG_INFO, "NTS: Read cookie file, %d keys.", nts_nKeys);
216
0
  return true;
217
218
0
  bail:
219
0
  msyslog(LOG_ERR, "ERR: Error parsing cookie keys file");
220
0
  fclose(in);
221
0
  return false;
222
0
}
223
224
/* RFC 8915 describes a ratchet mode to make new keys
225
 * That's one way to implement a KE server for a cluster of NTP servers.
226
 * The KE server and the NTP servers stay in sync without communication
227
 * after a one-time copy of the cookie file from NTP server to KE server.
228
 * An alternative is to elect one system to generate new keys and
229
 * they copy the key file to other systems and have them load it.
230
 */
231
0
void nts_make_cookie_key(void) {
232
0
  if (nts_nKeys < NTS_nKEYS) nts_nKeys++;
233
0
  for (int i=nts_nKeys-1; i>0; i--) {
234
0
    nts_keys[i] = nts_keys[i-1];
235
0
  }
236
0
  ntp_RAND_priv_bytes(nts_keys[0].K, K_length);
237
0
  ntp_RAND_bytes((uint8_t *)&nts_keys[0].I, sizeof(nts_keys[0].I));
238
0
  return;
239
0
}
240
241
0
bool nts_write_cookie_keys(void) {
242
0
  const char *cookiefile = NTS_COOKIE_KEY_FILE;
243
0
  char tempfile[PATH_MAX];
244
0
  int fd;
245
0
  FILE *out;
246
0
  char errbuf[100];
247
0
  if (NULL != ntsconfig.KI)
248
0
    cookiefile = ntsconfig.KI;
249
0
  strlcpy(tempfile, cookiefile, sizeof(tempfile));
250
0
  strlcat(tempfile, "-tmp", sizeof(tempfile));
251
0
  fd = open(tempfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
252
0
  if (-1 == fd) {
253
0
    ntp_strerror_r(errno, errbuf, sizeof(errbuf));
254
0
    msyslog(LOG_ERR, "ERR: can't open %s: %s", tempfile, errbuf);
255
0
    return false;
256
0
  }
257
0
  out = fdopen(fd, "w");
258
0
  if (NULL == out) {
259
0
    ntp_strerror_r(errno, errbuf, sizeof(errbuf));
260
0
    msyslog(LOG_ERR, "ERR: can't fdopen %s: %s", tempfile, errbuf);
261
0
    close(fd);
262
0
    return false;
263
0
  }
264
265
0
  fprintf(out, "T: %lu\n", (unsigned long)K_time);
266
0
  fprintf(out, "L: %d\n", K_length);
267
0
  for (int i=0; i<nts_nKeys; i++) {
268
0
    struct NTS_Key *key = &nts_keys[i];
269
0
    fprintf(out, "I: %u\n", key->I);
270
0
    fprintf(out, "K: ");
271
0
      for (int j=0; j< K_length; j++) fprintf(out, "%02x", key->K[j]);
272
0
    fprintf(out, "\n");
273
0
    key++;
274
0
  }
275
0
  fclose(out);
276
0
        if (rename(tempfile, cookiefile)) {
277
0
      ntp_strerror_r(errno, errbuf, sizeof(errbuf));
278
0
            msyslog(LOG_WARNING,
279
0
                    "LOG: Unable to rename temp cookie file %s to %s, %s",
280
0
                    tempfile, cookiefile, errbuf);
281
0
      return false;
282
0
  }
283
0
  return true;
284
0
}
285
286
/* returns actual length */
287
int nts_make_cookie(uint8_t *cookie,
288
  uint16_t aead,
289
0
  uint8_t *c2s, uint8_t *s2c, int keylen) {
290
0
  uint8_t plaintext[NTS_MAX_COOKIELEN];
291
0
  uint8_t *nonce;
292
0
  int used, plainlength;
293
0
  bool ok;
294
0
  uint8_t * finger;
295
0
  uint32_t temp;  /* keep 4 byte alignment */
296
0
  size_t left;
297
298
0
  if (NULL == cookie_ctx)
299
0
    return 0;   /* We aren't initialized yet. */
300
301
0
  nts_cnt.cookie_make++;
302
303
0
  INSIST(keylen <= NTS_MAX_KEYLEN);
304
305
  /* collect plaintext
306
   * separate buffer avoids encrypt in place
307
   * but costs cache space
308
   */
309
0
  finger = plaintext;
310
0
  temp = aead;
311
0
  memcpy(finger, &temp, AEAD_LENGTH);
312
0
  finger += AEAD_LENGTH;
313
0
  memcpy(finger, c2s, keylen);
314
0
  finger += keylen;
315
0
  memcpy(finger, s2c, keylen);
316
0
  finger += keylen;
317
0
  plainlength = finger-plaintext;
318
319
  /* collect associated data */
320
0
  finger = cookie;
321
322
0
  memcpy(finger, &nts_keys[0].I, sizeof(nts_keys[0].I));
323
0
  finger += sizeof(nts_keys[0].I);
324
325
0
  nonce = finger;
326
0
  ntp_RAND_bytes(finger, NONCE_LENGTH);
327
0
  finger += NONCE_LENGTH;
328
329
0
  used = finger-cookie;
330
0
  left = NTS_MAX_COOKIELEN-used;
331
332
0
  nts_lock_cookielock();
333
334
0
  ok = AES_SIV_Encrypt(cookie_ctx,
335
0
           finger, &left,   /* left: in: max out length, out: length used */
336
0
           nts_keys[0].K, K_length,
337
0
           nonce, NONCE_LENGTH,
338
0
           plaintext, plainlength,
339
0
           cookie, AD_LENGTH);
340
341
0
  nts_unlock_cookielock();
342
343
0
  if (!ok) {
344
0
    msyslog(LOG_ERR, "NTS: nts_make_cookie - Error from AES_SIV_Encrypt");
345
    /* I don't think this should happen,
346
     * so crash rather than work incorrectly.
347
     * Hal, 2019-Feb-17
348
     * Similar code in ntp_extens
349
     */
350
0
    exit(1);
351
0
  }
352
353
0
  used += left;
354
0
  INSIST(used <= NTS_MAX_COOKIELEN);
355
356
0
  return used;
357
0
}
358
359
/* can't decrypt in place - that would trash the unauthenticated packet */
360
bool nts_unpack_cookie(uint8_t *cookie, int cookielen,
361
  uint16_t *aead,
362
2
  uint8_t *c2s, uint8_t *s2c, int *keylen) {
363
2
  uint8_t *finger;
364
2
  uint8_t plaintext[NTS_MAX_COOKIELEN];
365
2
  uint8_t *nonce;
366
2
  uint32_t temp;
367
2
  size_t plainlength;
368
2
  int cipherlength;
369
2
  bool ok;
370
2
  struct NTS_Key *key;
371
2
  int i;
372
373
2
  if (NULL == cookie_ctx)
374
2
    return false;  /* We aren't initialized yet. */
375
376
0
  if (0 == nts_nKeys) {
377
0
    nts_cnt.cookie_not_server++;
378
0
    return false;  /* We are not a NTS enabled server. */
379
0
  }
380
381
  /* We may get garbage from the net */
382
0
  if (cookielen > NTS_MAX_COOKIELEN)
383
0
    return false;
384
385
0
  finger = cookie;
386
0
  key = NULL;   /* squash uninitialized warning */
387
0
  for (i=0; i<nts_nKeys; i++) {
388
0
    key = &nts_keys[i];
389
0
    if (0 == memcmp(finger, &key->I, sizeof(key->I))) {
390
0
    break;
391
0
    }
392
0
  }
393
0
  nts_cnt.cookie_decode_total++;  /* total attempts, includes too old */
394
0
  if (nts_nKeys == i) {
395
0
    nts_cnt.cookie_decode_too_old++;
396
0
    return false;
397
0
        }
398
0
  if (0 == i) {
399
0
    nts_cnt.cookie_decode_current++;
400
0
  } else if (1 == i) {
401
0
    nts_cnt.cookie_decode_old++;
402
0
  } else if (2 == i) {
403
0
    nts_cnt.cookie_decode_old2++;
404
0
  } else {
405
0
    nts_cnt.cookie_decode_older++;
406
0
  }
407
#if 0
408
  if (1<i) {
409
    /* Hack for debugging */
410
    /* Beware: DoS possibility on a public server */
411
    msyslog(LOG_INFO, "NTS: Old cookie: %d days.", i);
412
  }
413
#endif
414
415
0
  finger += sizeof(key->I);
416
0
  nonce = finger;
417
0
  finger += NONCE_LENGTH;
418
419
  // require(AD_LENGTH==finger-cookie);
420
421
0
  cipherlength = cookielen - AD_LENGTH;
422
0
  plainlength = NTS_MAX_COOKIELEN;
423
424
0
  nts_lock_cookielock();
425
426
0
  ok = AES_SIV_Decrypt(cookie_ctx,
427
0
           plaintext, &plainlength,
428
0
           key->K, K_length,
429
0
           nonce, NONCE_LENGTH,
430
0
           finger, cipherlength,
431
0
           cookie, AD_LENGTH);
432
433
0
  nts_unlock_cookielock();
434
435
0
  if (!ok) {
436
0
    nts_cnt.cookie_decode_error++;
437
0
    return false;
438
0
  }
439
440
0
  *keylen = (plainlength-AEAD_LENGTH)/2;
441
0
  finger = plaintext;
442
0
  memcpy(&temp, finger, AEAD_LENGTH);
443
0
  *aead = temp;
444
0
  finger += AEAD_LENGTH;
445
0
  memcpy(c2s, finger, *keylen);
446
0
  finger += *keylen;
447
0
  memcpy(s2c, finger, *keylen);
448
0
  finger += *keylen;
449
450
0
  return true;
451
0
}
452
453
0
void nts_lock_cookielock(void) {
454
0
  int err = pthread_mutex_lock(&cookie_lock);
455
0
  if (0 != err) {
456
0
    msyslog(LOG_ERR, "ERR: Can't lock cookie_lock: %d", err);
457
0
    exit(2);
458
0
  }
459
0
}
460
461
0
void nts_unlock_cookielock(void) {
462
0
  int err = pthread_mutex_unlock(&cookie_lock);
463
0
  if (0 != err) {
464
    msyslog(LOG_ERR, "ERR: Can't unlock cookie_lock: %d", err);
465
0
    exit(2);
466
0
  }
467
0
}
468
469
/* end */