Home | History | Annotate | Line # | Download | only in libntp
      1 /*	$NetBSD: a_md5encrypt.c,v 1.13 2024/08/18 20:47:13 christos Exp $	*/
      2 
      3 /*
      4  *	digest support for NTP, MD5 and with OpenSSL more
      5  */
      6 #ifdef HAVE_CONFIG_H
      7 #include <config.h>
      8 #endif
      9 
     10 #include "ntp_fp.h"
     11 #include "ntp_string.h"
     12 #include "ntp_stdlib.h"
     13 #include "ntp.h"
     14 #include "isc/string.h"
     15 
     16 typedef struct {
     17 	const void *	buf;
     18 	size_t		len;
     19 } robuffT;
     20 
     21 typedef struct {
     22 	void *		buf;
     23 	size_t		len;
     24 } rwbuffT;
     25 
     26 #if defined(OPENSSL) && defined(ENABLE_CMAC)
     27 static size_t
     28 cmac_ctx_size(
     29 	CMAC_CTX *	ctx
     30 	)
     31 {
     32 	size_t mlen = 0;
     33 
     34 	if (ctx) {
     35 		EVP_CIPHER_CTX * 	cctx;
     36 		if (NULL != (cctx = CMAC_CTX_get0_cipher_ctx (ctx)))
     37 			mlen = EVP_CIPHER_CTX_block_size(cctx);
     38 	}
     39 	return mlen;
     40 }
     41 #endif	/* OPENSSL && ENABLE_CMAC */
     42 
     43 
     44 /*
     45  * Allocate and initialize a digest context.  As a speed optimization,
     46  * take an idea from ntpsec and cache the context to avoid malloc/free
     47  * overhead in time-critical paths.  ntpsec also caches the algorithms
     48  * with each key.
     49  * This is not thread-safe, but that is
     50  * not a problem at present.
     51  */
     52 static EVP_MD_CTX *
     53 get_md_ctx(
     54 	int		nid
     55 	)
     56 {
     57 #ifndef OPENSSL
     58 	static MD5_CTX	md5_ctx;
     59 
     60 	DEBUG_INSIST(NID_md5 == nid);
     61 	MD5Init(&md5_ctx);
     62 
     63 	return &md5_ctx;
     64 #else
     65 	if (!EVP_DigestInit(digest_ctx, EVP_get_digestbynid(nid))) {
     66 		msyslog(LOG_ERR, "%s init failed", OBJ_nid2sn(nid));
     67 		return NULL;
     68 	}
     69 
     70 	return digest_ctx;
     71 #endif	/* OPENSSL */
     72 }
     73 
     74 
     75 static size_t
     76 make_mac(
     77 	const rwbuffT *	digest,
     78 	int		ktype,
     79 	const robuffT *	key,
     80 	const robuffT *	msg
     81 	)
     82 {
     83 	/*
     84 	 * Compute digest of key concatenated with packet. Note: the
     85 	 * key type and digest type have been verified when the key
     86 	 * was created.
     87 	 */
     88 	size_t	retlen = 0;
     89 
     90 #ifdef OPENSSL
     91 
     92 	INIT_SSL();
     93 
     94 	/* Check if CMAC key type specific code required */
     95 #   ifdef ENABLE_CMAC
     96 	if (ktype == NID_cmac) {
     97 		CMAC_CTX *	ctx    = NULL;
     98 		void const *	keyptr = key->buf;
     99 		u_char		keybuf[AES_128_KEY_SIZE];
    100 
    101 		/* adjust key size (zero padded buffer) if necessary */
    102 		if (AES_128_KEY_SIZE > key->len) {
    103 			memcpy(keybuf, keyptr, key->len);
    104 			zero_mem((keybuf + key->len),
    105 				 (AES_128_KEY_SIZE - key->len));
    106 			keyptr = keybuf;
    107 		}
    108 
    109 		if (NULL == (ctx = CMAC_CTX_new())) {
    110 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s CTX new failed.", CMAC);
    111 			goto cmac_fail;
    112 		}
    113 		if (!CMAC_Init(ctx, keyptr, AES_128_KEY_SIZE, EVP_aes_128_cbc(), NULL)) {
    114 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Init failed.",    CMAC);
    115 			goto cmac_fail;
    116 		}
    117 		if (cmac_ctx_size(ctx) > digest->len) {
    118 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s buf too small.",  CMAC);
    119 			goto cmac_fail;
    120 		}
    121 		if (!CMAC_Update(ctx, msg->buf, msg->len)) {
    122 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Update failed.",  CMAC);
    123 			goto cmac_fail;
    124 		}
    125 		if (!CMAC_Final(ctx, digest->buf, &retlen)) {
    126 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Final failed.",   CMAC);
    127 			retlen = 0;
    128 		}
    129 	  cmac_fail:
    130 		if (ctx)
    131 			CMAC_CTX_free(ctx);
    132 	}
    133 	else
    134 #   endif /* ENABLE_CMAC */
    135 	{	/* generic MAC handling */
    136 		EVP_MD_CTX *	ctx;
    137 		u_int		uilen = 0;
    138 
    139 		ctx = get_md_ctx(ktype);
    140 		if (NULL == ctx) {
    141 			goto mac_fail;
    142 		}
    143 		if ((size_t)EVP_MD_CTX_size(ctx) > digest->len) {
    144 			msyslog(LOG_ERR, "MAC encrypt: MAC %s buf too small.",
    145 				OBJ_nid2sn(ktype));
    146 			goto mac_fail;
    147 		}
    148 		if (!EVP_DigestUpdate(ctx, key->buf, (u_int)key->len)) {
    149 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update key failed.",
    150 				OBJ_nid2sn(ktype));
    151 			goto mac_fail;
    152 		}
    153 		if (!EVP_DigestUpdate(ctx, msg->buf, (u_int)msg->len)) {
    154 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update data failed.",
    155 				OBJ_nid2sn(ktype));
    156 			goto mac_fail;
    157 		}
    158 		if (!EVP_DigestFinal(ctx, digest->buf, &uilen)) {
    159 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Final failed.",
    160 				OBJ_nid2sn(ktype));
    161 			uilen = 0;
    162 		}
    163 	  mac_fail:
    164 		retlen = (size_t)uilen;
    165 	}
    166 
    167 #else /* !OPENSSL follows */
    168 
    169 	if (NID_md5 == ktype) {
    170 		EVP_MD_CTX *	ctx;
    171 
    172 		ctx = get_md_ctx(ktype);
    173 		if (digest->len < MD5_LENGTH) {
    174 			msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small.");
    175 		} else {
    176 			MD5Init(ctx);
    177 			MD5Update(ctx, (const void *)key->buf, key->len);
    178 			MD5Update(ctx, (const void *)msg->buf, msg->len);
    179 			MD5Final(digest->buf, ctx);
    180 			retlen = MD5_LENGTH;
    181 		}
    182 	} else {
    183 		msyslog(LOG_ERR, "MAC encrypt: invalid key type %d", ktype);
    184 	}
    185 
    186 #endif /* !OPENSSL */
    187 
    188 	return retlen;
    189 }
    190 
    191 
    192 /*
    193  * MD5authencrypt - generate message digest
    194  *
    195  * Returns 0 on failure or length of MAC including key ID.
    196  */
    197 size_t
    198 MD5authencrypt(
    199 	int		type,	/* hash algorithm */
    200 	const u_char *	key,	/* key pointer */
    201 	size_t		klen,	/* key length */
    202 	u_int32 *	pkt,	/* packet pointer */
    203 	size_t		length	/* packet length */
    204 	)
    205 {
    206 	u_char	digest[EVP_MAX_MD_SIZE];
    207 	rwbuffT digb = { digest, sizeof(digest) };
    208 	robuffT keyb = { key, klen };
    209 	robuffT msgb = { pkt, length };
    210 	size_t	dlen;
    211 
    212 	dlen = make_mac(&digb, type, &keyb, &msgb);
    213 	if (0 == dlen) {
    214 		return 0;
    215 	}
    216 	memcpy((u_char *)pkt + length + KEY_MAC_LEN, digest,
    217 	       min(dlen, MAX_MDG_LEN));
    218 	return (dlen + KEY_MAC_LEN);
    219 }
    220 
    221 
    222 /*
    223  * MD5authdecrypt - verify MD5 message authenticator
    224  *
    225  * Returns one if digest valid, zero if invalid.
    226  */
    227 int
    228 MD5authdecrypt(
    229 	int		type,	/* hash algorithm */
    230 	const u_char *	key,	/* key pointer */
    231 	size_t		klen,	/* key length */
    232 	u_int32	*	pkt,	/* packet pointer */
    233 	size_t		length,	/* packet length */
    234 	size_t		size,	/* MAC size */
    235 	keyid_t		keyno   /* key id (for err log) */
    236 	)
    237 {
    238 	u_char	digest[EVP_MAX_MD_SIZE];
    239 	rwbuffT digb = { digest, sizeof(digest) };
    240 	robuffT keyb = { key, klen };
    241 	robuffT msgb = { pkt, length };
    242 	size_t	dlen = 0;
    243 
    244 	dlen = make_mac(&digb, type, &keyb, &msgb);
    245 	if (0 == dlen || size != dlen + KEY_MAC_LEN) {
    246 		msyslog(LOG_ERR,
    247 			"MAC decrypt: MAC length error: %u not %u for key %u",
    248 			(u_int)size, (u_int)(dlen + KEY_MAC_LEN), keyno);
    249 		return FALSE;
    250 	}
    251 	return !isc_tsmemcmp(digest,
    252 		 (u_char *)pkt + length + KEY_MAC_LEN, dlen);
    253 }
    254 
    255 /*
    256  * Calculate the reference id from the address. If it is an IPv4
    257  * address, use it as is. If it is an IPv6 address, do a md5 on
    258  * it and use the bottom 4 bytes.
    259  * The result is in network byte order for IPv4 addreseses.  For
    260  * IPv6, ntpd long differed in the hash calculated on big-endian
    261  * vs. little-endian because the first four bytes of the MD5 hash
    262  * were used as a u_int32 without any byte swapping.  This broke
    263  * the refid-based loop detection between mixed-endian systems.
    264  * In order to preserve behavior on the more-common little-endian
    265  * systems, the hash is now byte-swapped on big-endian systems to
    266  * match the little-endian hash.  This is ugly but it seems better
    267  * than changing the IPv6 refid calculation on the more-common
    268  * systems.
    269  * This is not thread safe, not a problem so far.
    270  */
    271 u_int32
    272 addr2refid(sockaddr_u *addr)
    273 {
    274 	static MD5_CTX	md5_ctx;
    275 	union u_tag {
    276 		u_char		digest[MD5_DIGEST_LENGTH];
    277 		u_int32		addr_refid;
    278 	} u;
    279 
    280 	if (IS_IPV4(addr)) {
    281 		return (NSRCADR(addr));
    282 	}
    283 	/* MD5 is not used for authentication here. */
    284 	MD5Init(&md5_ctx);
    285 	MD5Update(&md5_ctx, (void *)&SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr)));
    286 	MD5Final(u.digest, &md5_ctx);
    287 #ifdef WORDS_BIGENDIAN
    288 	u.addr_refid = BYTESWAP32(u.addr_refid);
    289 #endif
    290 	return u.addr_refid;
    291 }
    292