xref: /netbsd-src/external/bsd/ntp/dist/libntp/a_md5encrypt.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
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