1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Miroslav Lichvar 2020
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 **********************************************************************
21
22 =======================================================================
23
24 Client NTS-NTP authentication
25 */
26
27 #include "config.h"
28
29 #include "sysincl.h"
30
31 #include "nts_ntp_client.h"
32
33 #include "conf.h"
34 #include "logging.h"
35 #include "memory.h"
36 #include "ntp.h"
37 #include "ntp_ext.h"
38 #include "ntp_sources.h"
39 #include "nts_ke_client.h"
40 #include "nts_ntp.h"
41 #include "nts_ntp_auth.h"
42 #include "sched.h"
43 #include "siv.h"
44 #include "util.h"
45
46 /* Maximum length of all cookies to avoid IP fragmentation */
47 #define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
48
49 /* Magic string of files containing keys and cookies */
50 #define DUMP_IDENTIFIER "NNC0\n"
51
52 struct NNC_Instance_Record {
53 /* Address of NTS-KE server */
54 IPSockAddr nts_address;
55 /* Hostname or IP address for certificate verification */
56 char *name;
57 /* ID of trusted certificates */
58 uint32_t cert_set;
59 /* Configured NTP port */
60 uint16_t default_ntp_port;
61 /* Address of NTP server (can be negotiated in NTS-KE) */
62 IPSockAddr ntp_address;
63
64 NKC_Instance nke;
65 SIV_Instance siv;
66
67 int nke_attempts;
68 double next_nke_attempt;
69 double last_nke_success;
70
71 NKE_Context context;
72 unsigned int context_id;
73 NKE_Cookie cookies[NTS_MAX_COOKIES];
74 int num_cookies;
75 int cookie_index;
76 int auth_ready;
77 int nak_response;
78 int ok_response;
79 unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
80 unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
81 };
82
83 /* ================================================== */
84
85 static void save_cookies(NNC_Instance inst);
86 static void load_cookies(NNC_Instance inst);
87
88 /* ================================================== */
89
90 static void
reset_instance(NNC_Instance inst)91 reset_instance(NNC_Instance inst)
92 {
93 if (inst->nke)
94 NKC_DestroyInstance(inst->nke);
95 inst->nke = NULL;
96 if (inst->siv)
97 SIV_DestroyInstance(inst->siv);
98 inst->siv = NULL;
99
100 inst->nke_attempts = 0;
101 inst->next_nke_attempt = 0.0;
102 inst->last_nke_success = 0.0;
103
104 memset(&inst->context, 0, sizeof (inst->context));
105 inst->context_id = 0;
106 memset(inst->cookies, 0, sizeof (inst->cookies));
107 inst->num_cookies = 0;
108 inst->cookie_index = 0;
109 inst->auth_ready = 0;
110 inst->nak_response = 0;
111 inst->ok_response = 1;
112 memset(inst->nonce, 0, sizeof (inst->nonce));
113 memset(inst->uniq_id, 0, sizeof (inst->uniq_id));
114 }
115
116 /* ================================================== */
117
118 NNC_Instance
NNC_CreateInstance(IPSockAddr * nts_address,const char * name,uint32_t cert_set,uint16_t ntp_port)119 NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, uint16_t ntp_port)
120 {
121 NNC_Instance inst;
122
123 inst = MallocNew(struct NNC_Instance_Record);
124
125 inst->nts_address = *nts_address;
126 inst->name = Strdup(name);
127 inst->cert_set = cert_set;
128 inst->default_ntp_port = ntp_port;
129 inst->ntp_address.ip_addr = nts_address->ip_addr;
130 inst->ntp_address.port = ntp_port;
131 inst->siv = NULL;
132 inst->nke = NULL;
133
134 reset_instance(inst);
135
136 /* Try to reload saved keys and cookies */
137 load_cookies(inst);
138
139 return inst;
140 }
141
142 /* ================================================== */
143
144 void
NNC_DestroyInstance(NNC_Instance inst)145 NNC_DestroyInstance(NNC_Instance inst)
146 {
147 save_cookies(inst);
148
149 reset_instance(inst);
150
151 Free(inst->name);
152 Free(inst);
153 }
154
155 /* ================================================== */
156
157 static int
check_cookies(NNC_Instance inst)158 check_cookies(NNC_Instance inst)
159 {
160 /* Force a new NTS-KE session if a NAK was received without a valid response,
161 or the keys encrypting the cookies need to be refreshed */
162 if (inst->num_cookies > 0 &&
163 ((inst->nak_response && !inst->ok_response) ||
164 SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
165 inst->num_cookies = 0;
166 DEBUG_LOG("Dropped cookies");
167 }
168
169 return inst->num_cookies > 0;
170 }
171
172 /* ================================================== */
173
174 static int
set_ntp_address(NNC_Instance inst,NTP_Remote_Address * negotiated_address)175 set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
176 {
177 NTP_Remote_Address old_address, new_address;
178
179 old_address = inst->ntp_address;
180 new_address = *negotiated_address;
181
182 if (new_address.ip_addr.family == IPADDR_UNSPEC)
183 new_address.ip_addr = inst->nts_address.ip_addr;
184 if (new_address.port == 0)
185 new_address.port = inst->default_ntp_port;
186
187 if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
188 old_address.port == new_address.port)
189 /* Nothing to do */
190 return 1;
191
192 if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) {
193 LOG(LOGS_ERR, "Could not change %s to negotiated address %s",
194 UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr));
195 return 0;
196 }
197
198 inst->ntp_address = new_address;
199
200 return 1;
201 }
202
203 /* ================================================== */
204
205 static void
update_next_nke_attempt(NNC_Instance inst,double now)206 update_next_nke_attempt(NNC_Instance inst, double now)
207 {
208 int factor, interval;
209
210 if (!inst->nke)
211 return;
212
213 factor = NKC_GetRetryFactor(inst->nke);
214 interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2);
215 inst->next_nke_attempt = now + UTI_Log2ToDouble(interval);
216 }
217
218 /* ================================================== */
219
220 static int
get_cookies(NNC_Instance inst)221 get_cookies(NNC_Instance inst)
222 {
223 NTP_Remote_Address ntp_address;
224 double now;
225 int got_data;
226
227 assert(inst->num_cookies == 0);
228
229 now = SCH_GetLastEventMonoTime();
230
231 /* Create and start a new NTS-KE session if not already present */
232 if (!inst->nke) {
233 if (now < inst->next_nke_attempt) {
234 DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
235 inst->next_nke_attempt - now);
236 return 0;
237 }
238
239 inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
240
241 inst->nke_attempts++;
242 update_next_nke_attempt(inst, now);
243
244 if (!NKC_Start(inst->nke))
245 return 0;
246 }
247
248 update_next_nke_attempt(inst, now);
249
250 /* Wait until the session stops */
251 if (NKC_IsActive(inst->nke))
252 return 0;
253
254 assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
255
256 /* Get the new keys, cookies and NTP address if the session was successful */
257 got_data = NKC_GetNtsData(inst->nke, &inst->context,
258 inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
259 &ntp_address);
260
261 NKC_DestroyInstance(inst->nke);
262 inst->nke = NULL;
263
264 if (!got_data)
265 return 0;
266
267 if (inst->siv)
268 SIV_DestroyInstance(inst->siv);
269 inst->siv = NULL;
270
271 inst->context_id++;
272
273 /* Force a new session if the NTP address is used by another source, with
274 an expectation that it will eventually get a non-conflicting address */
275 if (!set_ntp_address(inst, &ntp_address)) {
276 inst->num_cookies = 0;
277 return 0;
278 }
279
280 inst->last_nke_success = now;
281 inst->cookie_index = 0;
282
283 return 1;
284 }
285
286 /* ================================================== */
287
288 int
NNC_PrepareForAuth(NNC_Instance inst)289 NNC_PrepareForAuth(NNC_Instance inst)
290 {
291 inst->auth_ready = 0;
292
293 /* Prepare data for the next request and invalidate any responses to the
294 previous request */
295 UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
296 UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
297
298 /* Get new cookies if there are not any, or they are no longer usable */
299 if (!check_cookies(inst)) {
300 if (!get_cookies(inst))
301 return 0;
302 }
303
304 inst->nak_response = 0;
305
306 if (!inst->siv)
307 inst->siv = SIV_CreateInstance(inst->context.algorithm);
308
309 if (!inst->siv ||
310 !SIV_SetKey(inst->siv, inst->context.c2s.key, inst->context.c2s.length)) {
311 DEBUG_LOG("Could not set SIV key");
312 return 0;
313 }
314
315 inst->auth_ready = 1;
316
317 return 1;
318 }
319
320 /* ================================================== */
321
322 int
NNC_GenerateRequestAuth(NNC_Instance inst,NTP_Packet * packet,NTP_PacketInfo * info)323 NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
324 NTP_PacketInfo *info)
325 {
326 NKE_Cookie *cookie;
327 int i, req_cookies;
328 void *ef_body;
329
330 if (!inst->auth_ready)
331 return 0;
332
333 inst->auth_ready = 0;
334
335 if (inst->num_cookies <= 0 || !inst->siv)
336 return 0;
337
338 if (info->mode != MODE_CLIENT)
339 return 0;
340
341 cookie = &inst->cookies[inst->cookie_index];
342 inst->num_cookies--;
343 inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
344
345 req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies,
346 MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
347
348 if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
349 inst->uniq_id, sizeof (inst->uniq_id)))
350 return 0;
351
352 if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
353 cookie->cookie, cookie->length))
354 return 0;
355
356 for (i = 0; i < req_cookies - 1; i++) {
357 if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
358 cookie->length, &ef_body))
359 return 0;
360 memset(ef_body, 0, cookie->length);
361 }
362
363 if (!NNA_GenerateAuthEF(packet, info, inst->siv, inst->nonce, sizeof (inst->nonce),
364 (const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
365 return 0;
366
367 inst->ok_response = 0;
368
369 return 1;
370 }
371
372 /* ================================================== */
373
374 static int
parse_encrypted_efs(NNC_Instance inst,unsigned char * plaintext,int length)375 parse_encrypted_efs(NNC_Instance inst, unsigned char *plaintext, int length)
376 {
377 int ef_length, parsed;
378
379 for (parsed = 0; parsed < length; parsed += ef_length) {
380 if (!NEF_ParseSingleField(plaintext, length, parsed, &ef_length, NULL, NULL, NULL)) {
381 DEBUG_LOG("Could not parse encrypted EF");
382 return 0;
383 }
384 }
385
386 return 1;
387 }
388
389 /* ================================================== */
390
391 static int
extract_cookies(NNC_Instance inst,unsigned char * plaintext,int length)392 extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
393 {
394 int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved;
395 void *ef_body;
396
397 acceptable = saved = 0;
398
399 for (parsed = 0; parsed < length; parsed += ef_length) {
400 if (!NEF_ParseSingleField(plaintext, length, parsed,
401 &ef_length, &ef_type, &ef_body, &ef_body_length))
402 return 0;
403
404 if (ef_type != NTP_EF_NTS_COOKIE)
405 continue;
406
407 if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) {
408 DEBUG_LOG("Unexpected cookie length %d", ef_body_length);
409 continue;
410 }
411
412 acceptable++;
413
414 if (inst->num_cookies >= NTS_MAX_COOKIES)
415 continue;
416
417 index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
418 assert(index >= 0 && index < NTS_MAX_COOKIES);
419 assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
420
421 memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
422 inst->cookies[index].length = ef_body_length;
423 inst->num_cookies++;
424
425 saved++;
426 }
427
428 DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved);
429
430 return acceptable > 0;
431 }
432
433 /* ================================================== */
434
435 int
NNC_CheckResponseAuth(NNC_Instance inst,NTP_Packet * packet,NTP_PacketInfo * info)436 NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
437 NTP_PacketInfo *info)
438 {
439 int ef_type, ef_body_length, ef_length, parsed, plaintext_length;
440 int has_valid_uniq_id = 0, has_valid_auth = 0;
441 unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
442 void *ef_body;
443
444 if (info->ext_fields == 0 || info->mode != MODE_SERVER)
445 return 0;
446
447 /* Accept at most one response per request */
448 if (inst->ok_response || inst->auth_ready)
449 return 0;
450
451 if (!inst->siv ||
452 !SIV_SetKey(inst->siv, inst->context.s2c.key, inst->context.s2c.length)) {
453 DEBUG_LOG("Could not set SIV key");
454 return 0;
455 }
456
457 for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
458 if (!NEF_ParseField(packet, info->length, parsed,
459 &ef_length, &ef_type, &ef_body, &ef_body_length))
460 /* This is not expected as the packet already passed parsing */
461 return 0;
462
463 switch (ef_type) {
464 case NTP_EF_NTS_UNIQUE_IDENTIFIER:
465 if (ef_body_length != sizeof (inst->uniq_id) ||
466 memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) {
467 DEBUG_LOG("Invalid uniq id");
468 return 0;
469 }
470 has_valid_uniq_id = 1;
471 break;
472 case NTP_EF_NTS_COOKIE:
473 DEBUG_LOG("Unencrypted cookie");
474 break;
475 case NTP_EF_NTS_AUTH_AND_EEF:
476 if (parsed + ef_length != info->length) {
477 DEBUG_LOG("Auth not last EF");
478 return 0;
479 }
480
481 if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed,
482 plaintext, sizeof (plaintext), &plaintext_length))
483 return 0;
484
485 if (!parse_encrypted_efs(inst, plaintext, plaintext_length))
486 return 0;
487
488 has_valid_auth = 1;
489 break;
490 default:
491 break;
492 }
493 }
494
495 if (!has_valid_uniq_id || !has_valid_auth) {
496 if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM &&
497 ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) {
498 DEBUG_LOG("NTS NAK");
499 inst->nak_response = 1;
500 return 0;
501 }
502
503 DEBUG_LOG("Missing NTS EF");
504 return 0;
505 }
506
507 if (!extract_cookies(inst, plaintext, plaintext_length))
508 return 0;
509
510 inst->ok_response = 1;
511
512 /* At this point we know the client interoperates with the server. Allow a
513 new NTS-KE session to be started as soon as the cookies run out. */
514 inst->nke_attempts = 0;
515 inst->next_nke_attempt = 0.0;
516
517 return 1;
518 }
519
520 /* ================================================== */
521
522 void
NNC_ChangeAddress(NNC_Instance inst,IPAddr * address)523 NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
524 {
525 save_cookies(inst);
526
527 inst->nts_address.ip_addr = *address;
528 inst->ntp_address.ip_addr = *address;
529
530 reset_instance(inst);
531
532 DEBUG_LOG("NTS reset");
533
534 load_cookies(inst);
535 }
536
537 /* ================================================== */
538
539 static void
save_cookies(NNC_Instance inst)540 save_cookies(NNC_Instance inst)
541 {
542 char buf[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename;
543 struct timespec now;
544 double context_time;
545 FILE *f;
546 int i;
547
548 if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr))
549 return;
550
551 dump_dir = CNF_GetNtsDumpDir();
552 if (!dump_dir)
553 return;
554
555 filename = UTI_IPToString(&inst->nts_address.ip_addr);
556
557 f = UTI_OpenFile(dump_dir, filename, ".tmp", 'w', 0600);
558 if (!f)
559 return;
560
561 SCH_GetLastEventTime(&now, NULL, NULL);
562 context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
563 context_time += UTI_TimespecToDouble(&now);
564
565 if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
566 DUMP_IDENTIFIER, inst->name, context_time,
567 UTI_IPToString(&inst->ntp_address.ip_addr), inst->ntp_address.port,
568 inst->context_id, (int)inst->context.algorithm) < 0 ||
569 !UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
570 fprintf(f, "%s ", buf) < 0 ||
571 !UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
572 fprintf(f, "%s\n", buf) < 0)
573 goto error;
574
575 for (i = 0; i < inst->num_cookies; i++) {
576 if (!UTI_BytesToHex(inst->cookies[i].cookie, inst->cookies[i].length, buf, sizeof (buf)) ||
577 fprintf(f, "%s\n", buf) < 0)
578 goto error;
579 }
580
581 fclose(f);
582
583 if (!UTI_RenameTempFile(dump_dir, filename, ".tmp", ".nts"))
584 ;
585 return;
586
587 error:
588 DEBUG_LOG("Could not %s cookies for %s", "save", filename);
589 fclose(f);
590
591 if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
592 ;
593 }
594
595 /* ================================================== */
596
597 #define MAX_WORDS 4
598
599 static void
load_cookies(NNC_Instance inst)600 load_cookies(NNC_Instance inst)
601 {
602 char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
603 unsigned int context_id;
604 int i, algorithm, port;
605 double context_time;
606 struct timespec now;
607 IPSockAddr ntp_addr;
608 FILE *f;
609
610 dump_dir = CNF_GetNtsDumpDir();
611 if (!dump_dir)
612 return;
613
614 filename = UTI_IPToString(&inst->nts_address.ip_addr);
615
616 f = UTI_OpenFile(dump_dir, filename, ".nts", 'r', 0);
617 if (!f)
618 return;
619
620 /* Don't load this file again */
621 if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
622 ;
623
624 if (inst->siv)
625 SIV_DestroyInstance(inst->siv);
626 inst->siv = NULL;
627
628 if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
629 !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
630 strcmp(words[0], inst->name) != 0 ||
631 !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
632 sscanf(words[0], "%lf", &context_time) != 1 ||
633 !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
634 !UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
635 !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 ||
636 sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
637 goto error;
638
639 inst->context.algorithm = algorithm;
640 inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
641 sizeof (inst->context.s2c.key));
642 inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key,
643 sizeof (inst->context.c2s.key));
644
645 if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
646 inst->context.c2s.length != inst->context.s2c.length)
647 goto error;
648
649 for (i = 0; i < NTS_MAX_COOKIES && fgets(line, sizeof (line), f); i++) {
650 if (UTI_SplitString(line, words, MAX_WORDS) != 1)
651 goto error;
652
653 inst->cookies[i].length = UTI_HexToBytes(words[0], inst->cookies[i].cookie,
654 sizeof (inst->cookies[i].cookie));
655 if (inst->cookies[i].length == 0)
656 goto error;
657 }
658
659 inst->num_cookies = i;
660
661 ntp_addr.port = port;
662 if (!set_ntp_address(inst, &ntp_addr))
663 goto error;
664
665 SCH_GetLastEventTime(&now, NULL, NULL);
666 context_time -= UTI_TimespecToDouble(&now);
667 if (context_time > 0)
668 context_time = 0;
669 inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
670 inst->context_id = context_id;
671
672 fclose(f);
673
674 DEBUG_LOG("Loaded %d cookies for %s", i, filename);
675 return;
676
677 error:
678 DEBUG_LOG("Could not %s cookies for %s", "load", filename);
679 fclose(f);
680
681 memset(&inst->context, 0, sizeof (inst->context));
682 inst->num_cookies = 0;
683 }
684
685 /* ================================================== */
686
687 void
NNC_DumpData(NNC_Instance inst)688 NNC_DumpData(NNC_Instance inst)
689 {
690 save_cookies(inst);
691 }
692
693 /* ================================================== */
694
695 void
NNC_GetReport(NNC_Instance inst,RPT_AuthReport * report)696 NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
697 {
698 report->key_id = inst->context_id;
699 report->key_type = inst->context.algorithm;
700 report->key_length = 8 * inst->context.s2c.length;
701 report->ke_attempts = inst->nke_attempts;
702 if (report->key_length > 0)
703 report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success;
704 else
705 report->last_ke_ago = -1;
706 report->cookies = inst->num_cookies;
707 report->cookie_length = inst->num_cookies > 0 ? inst->cookies[inst->cookie_index].length : 0;
708 report->nak = inst->nak_response;
709 }
710