xref: /netbsd-src/external/bsd/ntp/dist/sntp/crypto.c (revision c42dbd0ed2e61fe6eda8590caa852ccf34719964)
1 /*	$NetBSD: crypto.c,v 1.19 2022/10/09 21:41:04 christos Exp $	*/
2 
3 /*
4  * HMS: we need to test:
5  * - OpenSSL versions, if we are building with them
6  * - our versions
7  *
8  * We may need to test with(out) OPENSSL separately.
9  */
10 
11 #include <config.h>
12 #include "crypto.h"
13 #include <ctype.h>
14 #include "isc/string.h"
15 #include "ntp_md5.h"
16 
17 #ifndef EVP_MAX_MD_SIZE
18 # define EVP_MAX_MD_SIZE 32
19 #endif
20 
21 struct key *key_ptr;
22 size_t key_cnt = 0;
23 
24 typedef struct key Key_T;
25 
26 static u_int
27 compute_mac(
28 	u_char		digest[EVP_MAX_MD_SIZE],
29 	char const *	macname,
30 	void const *	pkt_data,
31 	u_int		pkt_size,
32 	void const *	key_data,
33 	u_int		key_size
34 	)
35 {
36 	u_int		len  = 0;
37 #if defined(OPENSSL) && defined(ENABLE_CMAC)
38 	size_t		slen = 0;
39 #endif
40 	int		key_type;
41 
42 	INIT_SSL();
43 	key_type = keytype_from_text(macname, NULL);
44 
45 #if defined(OPENSSL) && defined(ENABLE_CMAC)
46 	/* Check if CMAC key type specific code required */
47 	if (key_type == NID_cmac) {
48 		CMAC_CTX *	ctx    = NULL;
49 		u_char		keybuf[AES_128_KEY_SIZE];
50 
51 		/* adjust key size (zero padded buffer) if necessary */
52 		if (AES_128_KEY_SIZE > key_size) {
53 			memcpy(keybuf, key_data, key_size);
54 			memset((keybuf + key_size), 0,
55 			       (AES_128_KEY_SIZE - key_size));
56 			key_data = keybuf;
57 		}
58 
59 		if (!(ctx = CMAC_CTX_new())) {
60 			msyslog(LOG_ERR, "make_mac: CMAC %s CTX new failed.",   CMAC);
61 		}
62 		else if (!CMAC_Init(ctx, key_data, AES_128_KEY_SIZE,
63 				    EVP_aes_128_cbc(), NULL)) {
64 			msyslog(LOG_ERR, "make_mac: CMAC %s Init failed.",      CMAC);
65 		}
66 		else if (!CMAC_Update(ctx, pkt_data, (size_t)pkt_size)) {
67 			msyslog(LOG_ERR, "make_mac: CMAC %s Update failed.",    CMAC);
68 		}
69 		else if (!CMAC_Final(ctx, digest, &slen)) {
70 			msyslog(LOG_ERR, "make_mac: CMAC %s Final failed.",     CMAC);
71 			slen = 0;
72 		}
73 		len = (u_int)slen;
74 
75 		if (ctx)
76 			CMAC_CTX_free(ctx);
77 		/* Test our AES-128-CMAC implementation */
78 
79 	} else	/* MD5 MAC handling */
80 #endif
81 	{
82 		EVP_MD_CTX *	ctx;
83 
84 		if (!(ctx = EVP_MD_CTX_new())) {
85 			msyslog(LOG_ERR, "make_mac: MAC %s Digest CTX new failed.",
86 				macname);
87 			goto mac_fail;
88 		}
89 #ifdef OPENSSL	/* OpenSSL 1 supports return codes 0 fail, 1 okay */
90 #	    ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
91 		EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
92 #	    endif
93 		/* [Bug 3457] DON'T use plain EVP_DigestInit! It would
94 		 *  kill the flags! */
95 		if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(key_type), NULL)) {
96 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Init failed.",
97 				macname);
98 			goto mac_fail;
99 		}
100 		if (!EVP_DigestUpdate(ctx, key_data, key_size)) {
101 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Update key failed.",
102 				macname);
103 			goto mac_fail;
104 		}
105 		if (!EVP_DigestUpdate(ctx, pkt_data, pkt_size)) {
106 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Update data failed.",
107 				macname);
108 			goto mac_fail;
109 		}
110 		if (!EVP_DigestFinal(ctx, digest, &len)) {
111 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Final failed.",
112 				macname);
113 			len = 0;
114 		}
115 #else /* !OPENSSL */
116 		EVP_DigestInit(ctx, EVP_get_digestbynid(key_type));
117 		EVP_DigestUpdate(ctx, key_data, key_size);
118 		EVP_DigestUpdate(ctx, pkt_data, pkt_size);
119 		EVP_DigestFinal(ctx, digest, &len);
120 #endif
121 	  mac_fail:
122 		EVP_MD_CTX_free(ctx);
123 	}
124 
125 	return len;
126 }
127 
128 int
129 make_mac(
130 	const void *	pkt_data,
131 	int		pkt_size,
132 	int		mac_size,
133 	Key_T const *	cmp_key,
134 	void * 		digest
135 	)
136 {
137 	u_int		len;
138 	u_char		dbuf[EVP_MAX_MD_SIZE];
139 
140 	if (cmp_key->key_len > 64 || mac_size <= 0)
141 		return 0;
142 	if (pkt_size % 4 != 0)
143 		return 0;
144 
145 	len = compute_mac(dbuf, cmp_key->typen,
146 			  pkt_data, (u_int)pkt_size,
147 			  cmp_key->key_seq, (u_int)cmp_key->key_len);
148 
149 
150 	if (len) {
151 		if (len > (u_int)mac_size)
152 			len = (u_int)mac_size;
153 		memcpy(digest, dbuf, len);
154 	}
155 	return (int)len;
156 }
157 
158 
159 /* Generates a md5 digest of the key specified in keyid concatenated with the
160  * ntp packet (exluding the MAC) and compares this digest to the digest in
161  * the packet's MAC. If they're equal this function returns 1 (packet is
162  * authentic) or else 0 (not authentic).
163  */
164 int
165 auth_md5(
166 	void const *	pkt_data,
167 	int 		pkt_size,
168 	int		mac_size,
169 	Key_T const *	cmp_key
170 	)
171 {
172 	u_int		len       = 0;
173 	u_char const *	pkt_ptr   = pkt_data;
174 	u_char		dbuf[EVP_MAX_MD_SIZE];
175 
176 	if (mac_size <= 0 || (size_t)mac_size > sizeof(dbuf))
177 		return FALSE;
178 
179 	len = compute_mac(dbuf, cmp_key->typen,
180 			  pkt_ptr, (u_int)pkt_size,
181 			  cmp_key->key_seq, (u_int)cmp_key->key_len);
182 
183 	pkt_ptr += pkt_size + 4;
184 	if (len > (u_int)mac_size)
185 		len = (u_int)mac_size;
186 
187 	/* isc_tsmemcmp will be better when its easy to link with.  sntp
188 	 * is a 1-shot program, so snooping for timing attacks is
189 	 * Harder.
190 	 */
191 	return ((u_int)mac_size == len) && !memcmp(dbuf, pkt_ptr, len);
192 }
193 
194 static int
195 hex_val(
196 	unsigned char x
197 	)
198 {
199 	int val;
200 
201 	if ('0' <= x && x <= '9')
202 		val = x - '0';
203 	else if ('a' <= x && x <= 'f')
204 		val = x - 'a' + 0xa;
205 	else if ('A' <= x && x <= 'F')
206 		val = x - 'A' + 0xA;
207 	else
208 		val = -1;
209 
210 	return val;
211 }
212 
213 /* Load keys from the specified keyfile into the key structures.
214  * Returns -1 if the reading failed, otherwise it returns the
215  * number of keys it read
216  */
217 int
218 auth_init(
219 	const char *keyfile,
220 	struct key **keys
221 	)
222 {
223 	FILE *keyf = fopen(keyfile, "r");
224 	struct key *prev = NULL;
225 	int scan_cnt, line_cnt = 1;
226 	char kbuf[200];
227 	char keystring[129];
228 
229 	/* HMS: Is it OK to do this later, after we know we have a key file? */
230 	INIT_SSL();
231 
232 	if (keyf == NULL) {
233 		if (debug)
234 			printf("sntp auth_init: Couldn't open key file %s for reading!\n", keyfile);
235 		return -1;
236 	}
237 	if (feof(keyf)) {
238 		if (debug)
239 			printf("sntp auth_init: Key file %s is empty!\n", keyfile);
240 		fclose(keyf);
241 		return -1;
242 	}
243 	key_cnt = 0;
244 	while (!feof(keyf)) {
245 		char * octothorpe;
246 		struct key *act;
247 		int goodline = 0;
248 
249 		if (NULL == fgets(kbuf, sizeof(kbuf), keyf))
250 			continue;
251 
252 		kbuf[sizeof(kbuf) - 1] = '\0';
253 		octothorpe = strchr(kbuf, '#');
254 		if (octothorpe)
255 			*octothorpe = '\0';
256 		act = emalloc(sizeof(*act));
257 		/* keep width 15 = sizeof struct key.typen - 1 synced */
258 		scan_cnt = sscanf(kbuf, "%d %15s %128s",
259 					&act->key_id, act->typen, keystring);
260 		if (scan_cnt == 3) {
261 			int len = strlen(keystring);
262 			goodline = 1;	/* assume best for now */
263 			if (len <= 20) {
264 				act->key_len = len;
265 				memcpy(act->key_seq, keystring, len + 1);
266 			} else if ((len & 1) != 0) {
267 				goodline = 0; /* it's bad */
268 			} else {
269 				int j;
270 				act->key_len = len >> 1;
271 				for (j = 0; j < len; j+=2) {
272 					int val;
273 					val = (hex_val(keystring[j]) << 4) |
274 					       hex_val(keystring[j+1]);
275 					if (val < 0) {
276 						goodline = 0; /* it's bad */
277 						break;
278 					}
279 					act->key_seq[j>>1] = (char)val;
280 				}
281 			}
282 			act->typei = keytype_from_text(act->typen, NULL);
283 			if (0 == act->typei) {
284 				printf("%s: line %d: key %d, %s not supported - ignoring\n",
285 					keyfile, line_cnt,
286 					act->key_id, act->typen);
287 				goodline = 0; /* it's bad */
288 			}
289 		}
290 		if (goodline) {
291 			act->next = NULL;
292 			if (NULL == prev)
293 				*keys = act;
294 			else
295 				prev->next = act;
296 			prev = act;
297 			key_cnt++;
298 		} else {
299 			if (debug) {
300 				printf("auth_init: scanf %d items, skipping line %d.",
301 					scan_cnt, line_cnt);
302 			}
303 			free(act);
304 		}
305 		line_cnt++;
306 	}
307 	fclose(keyf);
308 
309 	key_ptr = *keys;
310 	return key_cnt;
311 }
312 
313 /* Looks for the key with keyid key_id and sets the d_key pointer to the
314  * address of the key. If no matching key is found the pointer is not touched.
315  */
316 void
317 get_key(
318 	int key_id,
319 	struct key **d_key
320 	)
321 {
322 	struct key *itr_key;
323 
324 	if (key_cnt == 0)
325 		return;
326 	for (itr_key = key_ptr; itr_key; itr_key = itr_key->next) {
327 		if (itr_key->key_id == key_id) {
328 			*d_key = itr_key;
329 			break;
330 		}
331 	}
332 	return;
333 }
334