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