diff options
Diffstat (limited to 'nts_ntp_auth.c')
-rw-r--r-- | nts_ntp_auth.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/nts_ntp_auth.c b/nts_ntp_auth.c new file mode 100644 index 0000000..b92c406 --- /dev/null +++ b/nts_ntp_auth.c @@ -0,0 +1,187 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Miroslav Lichvar 2020 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + ********************************************************************** + + ======================================================================= + + NTS Authenticator and Encrypted Extension Fields extension field + */ + +#include "config.h" + +#include "sysincl.h" + +#include "nts_ntp_auth.h" + +#include "logging.h" +#include "ntp_ext.h" +#include "nts_ntp.h" +#include "siv.h" +#include "util.h" + +struct AuthHeader { + uint16_t nonce_length; + uint16_t ciphertext_length; +}; + +/* ================================================== */ + +static int +get_padding_length(int length) +{ + return length % 4U ? 4 - length % 4U : 0; +} + +/* ================================================== */ + +static int +get_padded_length(int length) +{ + return length + get_padding_length(length); +} + +/* ================================================== */ + +int +NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, + const unsigned char *nonce, int max_nonce_length, + const unsigned char *plaintext, int plaintext_length, + int min_ef_length) +{ + int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length; + int nonce_padding, ciphertext_padding, additional_padding; + unsigned char *ciphertext, *body; + struct AuthHeader *header; + + assert(sizeof (*header) == 4); + + if (max_nonce_length <= 0 || plaintext_length < 0) { + DEBUG_LOG("Invalid nonce/plaintext length"); + return 0; + } + + assoc_length = info->length; + max_siv_nonce_length = SIV_GetMaxNonceLength(siv); + nonce_length = MIN(max_nonce_length, max_siv_nonce_length); + ciphertext_length = SIV_GetTagLength(siv) + plaintext_length; + nonce_padding = get_padding_length(nonce_length); + ciphertext_padding = get_padding_length(ciphertext_length); + min_ef_length = get_padded_length(min_ef_length); + + auth_length = sizeof (*header) + nonce_length + nonce_padding + + ciphertext_length + ciphertext_padding; + additional_padding = MAX(min_ef_length - auth_length - 4, 0); + additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) - + nonce_length - nonce_padding, additional_padding); + auth_length += additional_padding; + + if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length, + (void **)&header)) { + DEBUG_LOG("Could not add EF"); + return 0; + } + + header->nonce_length = htons(nonce_length); + header->ciphertext_length = htons(ciphertext_length); + + body = (unsigned char *)(header + 1); + ciphertext = body + nonce_length + nonce_padding; + + if ((unsigned char *)header + auth_length != + ciphertext + ciphertext_length + ciphertext_padding + additional_padding) + assert(0); + + memcpy(body, nonce, nonce_length); + memset(body + nonce_length, 0, nonce_padding); + + if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length, + plaintext, plaintext_length, ciphertext, ciphertext_length)) { + DEBUG_LOG("SIV encrypt failed"); + info->length = assoc_length; + info->ext_fields--; + return 0; + } + + memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding); + + return 1; +} + +/* ================================================== */ + +int +NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start, + unsigned char *plaintext, int buffer_length, int *plaintext_length) +{ + int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length; + unsigned char *nonce, *ciphertext; + int ef_type, ef_body_length; + void *ef_body; + struct AuthHeader *header; + + if (buffer_length < 0) + return 0; + + if (!NEF_ParseField(packet, info->length, ef_start, + NULL, &ef_type, &ef_body, &ef_body_length)) + return 0; + + if (ef_type != NTP_EF_NTS_AUTH_AND_EEF || ef_body_length < sizeof (*header)) + return 0; + + header = ef_body; + + nonce_length = ntohs(header->nonce_length); + ciphertext_length = ntohs(header->ciphertext_length); + + if (get_padded_length(nonce_length) + + get_padded_length(ciphertext_length) > ef_body_length) + return 0; + + nonce = (unsigned char *)(header + 1); + ciphertext = nonce + get_padded_length(nonce_length); + + max_siv_nonce_length = SIV_GetMaxNonceLength(siv); + siv_tag_length = SIV_GetTagLength(siv); + + if (nonce_length < 1 || + ciphertext_length < siv_tag_length || + ciphertext_length - siv_tag_length > buffer_length) { + DEBUG_LOG("Unexpected nonce/ciphertext length"); + return 0; + } + + if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) + + get_padded_length(ciphertext_length) > ef_body_length) { + DEBUG_LOG("Missing padding"); + return 0; + } + + *plaintext_length = ciphertext_length - siv_tag_length; + assert(*plaintext_length >= 0); + + if (!SIV_Decrypt(siv, nonce, nonce_length, packet, ef_start, + ciphertext, ciphertext_length, plaintext, *plaintext_length)) { + DEBUG_LOG("SIV decrypt failed"); + return 0; + } + + return 1; +} |