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   NTS Authenticator and Encrypted Extension Fields extension field
25   */
26 
27 #include "config.h"
28 
29 #include "sysincl.h"
30 
31 #include "nts_ntp_auth.h"
32 
33 #include "logging.h"
34 #include "ntp_ext.h"
35 #include "nts_ntp.h"
36 #include "siv.h"
37 #include "util.h"
38 
39 struct AuthHeader {
40   uint16_t nonce_length;
41   uint16_t ciphertext_length;
42 };
43 
44 /* ================================================== */
45 
46 static int
get_padding_length(int length)47 get_padding_length(int length)
48 {
49   return length % 4U ? 4 - length % 4U : 0;
50 }
51 
52 /* ================================================== */
53 
54 static int
get_padded_length(int length)55 get_padded_length(int length)
56 {
57   return length + get_padding_length(length);
58 }
59 
60 /* ================================================== */
61 
62 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)63 NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
64                    const unsigned char *nonce, int nonce_length,
65                    const unsigned char *plaintext, int plaintext_length,
66                    int min_ef_length)
67 {
68   int auth_length, ciphertext_length, assoc_length;
69   int nonce_padding, ciphertext_padding, additional_padding;
70   unsigned char *ciphertext, *body;
71   struct AuthHeader *header;
72 
73   assert(sizeof (*header) == 4);
74 
75   if (nonce_length <= 0 || plaintext_length < 0) {
76     DEBUG_LOG("Invalid nonce/plaintext length");
77     return 0;
78   }
79 
80   assoc_length = info->length;
81   ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
82   nonce_padding = get_padding_length(nonce_length);
83   ciphertext_padding = get_padding_length(ciphertext_length);
84   min_ef_length = get_padded_length(min_ef_length);
85 
86   auth_length = sizeof (*header) + nonce_length + nonce_padding +
87                 ciphertext_length + ciphertext_padding;
88   additional_padding = MAX(min_ef_length - auth_length - 4, 0);
89   additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
90                            additional_padding);
91   auth_length += additional_padding;
92 
93   if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
94                          (void **)&header)) {
95     DEBUG_LOG("Could not add EF");
96     return 0;
97   }
98 
99   header->nonce_length = htons(nonce_length);
100   header->ciphertext_length = htons(ciphertext_length);
101 
102   body = (unsigned char *)(header + 1);
103   ciphertext = body + nonce_length + nonce_padding;
104 
105   if ((unsigned char *)header + auth_length !=
106       ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
107     assert(0);
108 
109   memcpy(body, nonce, nonce_length);
110   memset(body + nonce_length, 0, nonce_padding);
111 
112   if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
113                    plaintext, plaintext_length, ciphertext, ciphertext_length)) {
114     DEBUG_LOG("SIV encrypt failed");
115     info->length = assoc_length;
116     return 0;
117   }
118 
119   memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding);
120 
121   return 1;
122 }
123 
124 /* ================================================== */
125 
126 int
NNA_DecryptAuthEF(NTP_Packet * packet,NTP_PacketInfo * info,SIV_Instance siv,int ef_start,unsigned char * plaintext,int buffer_length,int * plaintext_length)127 NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
128                   unsigned char *plaintext, int buffer_length, int *plaintext_length)
129 {
130   unsigned int siv_tag_length, nonce_length, ciphertext_length;
131   unsigned char *nonce, *ciphertext;
132   int ef_type, ef_body_length;
133   void *ef_body;
134   struct AuthHeader *header;
135 
136   if (buffer_length < 0)
137     return 0;
138 
139   if (!NEF_ParseField(packet, info->length, ef_start,
140                       NULL, &ef_type, &ef_body, &ef_body_length))
141     return 0;
142 
143   if (ef_type != NTP_EF_NTS_AUTH_AND_EEF || ef_body_length < sizeof (*header))
144     return 0;
145 
146   header = ef_body;
147 
148   nonce_length = ntohs(header->nonce_length);
149   ciphertext_length = ntohs(header->ciphertext_length);
150 
151   if (get_padded_length(nonce_length) +
152       get_padded_length(ciphertext_length) > ef_body_length)
153     return 0;
154 
155   nonce = (unsigned char *)(header + 1);
156   ciphertext = nonce + get_padded_length(nonce_length);
157 
158   siv_tag_length = SIV_GetTagLength(siv);
159 
160   if (nonce_length < 1 ||
161       ciphertext_length < siv_tag_length ||
162       ciphertext_length - siv_tag_length > buffer_length) {
163     DEBUG_LOG("Unexpected nonce/ciphertext length");
164     return 0;
165   }
166 
167   if (ef_body_length < sizeof (*header) +
168         NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
169     DEBUG_LOG("Missing padding");
170     return 0;
171   }
172 
173   *plaintext_length = ciphertext_length - siv_tag_length;
174   assert(*plaintext_length >= 0);
175 
176   if (!SIV_Decrypt(siv, nonce, nonce_length, packet, ef_start,
177                    ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
178     DEBUG_LOG("SIV decrypt failed");
179     return 0;
180   }
181 
182   return 1;
183 }
184