From c8e57f43503c52d3b250490db64cde4e500099aa Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Thu, 13 Oct 2022 15:35:53 +0200 Subject: [PATCH] nts: change ntskeys format to support different algorithms Specify the AEAD ID for each key saved in the ntskeys file instead of one ID for all keys. Keep support for loading files in the old format. This will allow servers to save their keys after upgrading to a new version with AES-128-GCM-SIV support before the loaded AES-SIV-CMAC-256 keys are rotated out. If an unsupported key is found, don't load any keys. Also, change the severity of the error message from debug to error. --- nts_ke_server.c | 81 ++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/nts_ke_server.c b/nts_ke_server.c index 2fd576d..3731079 100644 --- a/nts_ke_server.c +++ b/nts_ke_server.c @@ -54,7 +54,8 @@ #define FUTURE_KEYS 1 #define DUMP_FILENAME "ntskeys" -#define DUMP_IDENTIFIER "NKS0\n" +#define DUMP_IDENTIFIER "NKS1\n" +#define OLD_DUMP_IDENTIFIER "NKS0\n" #define INVALID_SOCK_FD (-7) @@ -519,7 +520,7 @@ generate_key(int index) update_key_siv(key, algorithm); - DEBUG_LOG("Generated key %"PRIX32" (%d)", key->id, (int)key->siv_algorithm); + DEBUG_LOG("Generated key %08"PRIX32" (%d)", key->id, (int)key->siv_algorithm); last_server_key_ts = SCH_GetLastEventMonoTime(); } @@ -547,20 +548,19 @@ save_keys(void) if (!f) return; - key_length = SIV_GetKeyLength(server_keys[0].siv_algorithm); last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts; - if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, (int)server_keys[0].siv_algorithm, - last_key_age) < 0) + if (fprintf(f, "%s%.1f\n", DUMP_IDENTIFIER, last_key_age) < 0) goto error; for (i = 0; i < MAX_SERVER_KEYS; i++) { index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS; + key_length = SIV_GetKeyLength(server_keys[index].siv_algorithm); if (key_length > sizeof (server_keys[index].key) || - server_keys[index].siv_algorithm != server_keys[0].siv_algorithm || !UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) || - fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0) + fprintf(f, "%08"PRIX32" %s %d\n", server_keys[index].id, buf, + (int)server_keys[index].siv_algorithm) < 0) goto error; } @@ -575,7 +575,7 @@ save_keys(void) return; error: - DEBUG_LOG("Could not %s server keys", "save"); + LOG(LOGS_ERR, "Could not %s server NTS keys", "save"); fclose(f); if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL)) @@ -584,17 +584,16 @@ error: /* ================================================== */ -#define MAX_WORDS 2 +#define MAX_WORDS 3 static int load_keys(void) { + int i, index, key_length, algorithm = 0, old_ver; char *dump_dir, line[1024], *words[MAX_WORDS]; - unsigned char key[SIV_MAX_KEY_LENGTH]; - int i, index, key_length, algorithm; + ServerKey new_keys[MAX_SERVER_KEYS]; double key_age; FILE *f; - uint32_t id; dump_dir = CNF_GetNtsDumpDir(); if (!dump_dir) @@ -604,42 +603,56 @@ load_keys(void) if (!f) return 0; - if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || - !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || - sscanf(words[0], "%d", &algorithm) != 1 || SIV_GetKeyLength(algorithm) <= 0 || - sscanf(words[1], "%lf", &key_age) != 1) + if (!fgets(line, sizeof (line), f) || + (strcmp(line, DUMP_IDENTIFIER) != 0 && strcmp(line, OLD_DUMP_IDENTIFIER) != 0)) goto error; - key_length = SIV_GetKeyLength(algorithm); - last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0); + old_ver = strcmp(line, DUMP_IDENTIFIER) != 0; + + if (!fgets(line, sizeof (line), f) || + UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 1) || + (old_ver && sscanf(words[0], "%d", &algorithm) != 1) || + sscanf(words[old_ver ? 1 : 0], "%lf", &key_age) != 1) + goto error; for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) { - if (UTI_SplitString(line, words, MAX_WORDS) != 2 || - sscanf(words[0], "%"PRIX32, &id) != 1) + if (UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 3) || + sscanf(words[0], "%"PRIX32, &new_keys[i].id) != 1 || + (!old_ver && sscanf(words[2], "%d", &algorithm) != 1)) goto error; - if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length) + new_keys[i].siv_algorithm = algorithm; + key_length = SIV_GetKeyLength(algorithm); + + if ((i > 0 && (new_keys[i].id - new_keys[i - 1].id) % MAX_SERVER_KEYS != 1) || + key_length <= 0 || + UTI_HexToBytes(words[1], new_keys[i].key, sizeof (new_keys[i].key)) != key_length) goto error; - - index = id % MAX_SERVER_KEYS; - - server_keys[index].id = id; - assert(sizeof (server_keys[index].key) == sizeof (key)); - memcpy(server_keys[index].key, key, key_length); - - update_key_siv(&server_keys[index], algorithm); - - DEBUG_LOG("Loaded key %"PRIX32" (%d)", id, (int)algorithm); - - current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS; } + if (i < MAX_SERVER_KEYS) + goto error; + + for (i = 0; i < MAX_SERVER_KEYS; i++) { + index = new_keys[i].id % MAX_SERVER_KEYS; + server_keys[index].id = new_keys[i].id; + memcpy(server_keys[index].key, new_keys[i].key, sizeof (server_keys[index].key)); + + update_key_siv(&server_keys[index], new_keys[i].siv_algorithm); + + DEBUG_LOG("Loaded key %08"PRIX32" (%d)", + server_keys[index].id, (int)server_keys[index].siv_algorithm); + } + + current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS; + last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0); + fclose(f); return 1; error: - DEBUG_LOG("Could not %s server keys", "load"); + LOG(LOGS_ERR, "Could not %s server NTS keys", "load"); fclose(f); return 0;