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