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