summaryrefslogtreecommitdiffstats
path: root/nts_ntp_auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'nts_ntp_auth.c')
-rw-r--r--nts_ntp_auth.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/nts_ntp_auth.c b/nts_ntp_auth.c
new file mode 100644
index 0000000..ac0763e
--- /dev/null
+++ b/nts_ntp_auth.c
@@ -0,0 +1,183 @@
+/*
+ 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 nonce_length,
+ const unsigned char *plaintext, int plaintext_length,
+ int min_ef_length)
+{
+ int auth_length, ciphertext_length, assoc_length;
+ int nonce_padding, ciphertext_padding, additional_padding;
+ unsigned char *ciphertext, *body;
+ struct AuthHeader *header;
+
+ assert(sizeof (*header) == 4);
+
+ if (nonce_length <= 0 || plaintext_length < 0) {
+ DEBUG_LOG("Invalid nonce/plaintext length");
+ return 0;
+ }
+
+ assoc_length = info->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(NTS_MIN_UNPADDED_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;
+ 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)
+{
+ unsigned int siv_tag_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);
+
+ 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 (ef_body_length < sizeof (*header) +
+ NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_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;
+}