1/*	$NetBSD: a_md5encrypt.c,v 1.12 2022/10/09 21:41:03 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 "ntp_md5.h"	/* provides OpenSSL digest API */
15#include "isc/string.h"
16
17typedef struct {
18	const void *	buf;
19	size_t		len;
20} robuffT;
21
22typedef struct {
23	void *		buf;
24	size_t		len;
25} rwbuffT;
26
27#if defined(OPENSSL) && defined(ENABLE_CMAC)
28static size_t
29cmac_ctx_size(
30	CMAC_CTX *	ctx)
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
43static size_t
44make_mac(
45	const rwbuffT *	digest,
46	int		ktype,
47	const robuffT *	key,
48	const robuffT *	msg)
49{
50	/*
51	 * Compute digest of key concatenated with packet. Note: the
52	 * key type and digest type have been verified when the key
53	 * was created.
54	 */
55	size_t	retlen = 0;
56
57#ifdef OPENSSL
58
59	INIT_SSL();
60
61	/* Check if CMAC key type specific code required */
62#   ifdef ENABLE_CMAC
63	if (ktype == NID_cmac) {
64		CMAC_CTX *	ctx    = NULL;
65		void const *	keyptr = key->buf;
66		u_char		keybuf[AES_128_KEY_SIZE];
67
68		/* adjust key size (zero padded buffer) if necessary */
69		if (AES_128_KEY_SIZE > key->len) {
70			memcpy(keybuf, keyptr, key->len);
71			memset((keybuf + key->len), 0,
72			       (AES_128_KEY_SIZE - key->len));
73			keyptr = keybuf;
74		}
75
76		if (NULL == (ctx = CMAC_CTX_new())) {
77			msyslog(LOG_ERR, "MAC encrypt: CMAC %s CTX new failed.", CMAC);
78			goto cmac_fail;
79		}
80		if (!CMAC_Init(ctx, keyptr, AES_128_KEY_SIZE, EVP_aes_128_cbc(), NULL)) {
81			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Init failed.",    CMAC);
82			goto cmac_fail;
83		}
84		if (cmac_ctx_size(ctx) > digest->len) {
85			msyslog(LOG_ERR, "MAC encrypt: CMAC %s buf too small.",  CMAC);
86			goto cmac_fail;
87		}
88		if (!CMAC_Update(ctx, msg->buf, msg->len)) {
89			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Update failed.",  CMAC);
90			goto cmac_fail;
91		}
92		if (!CMAC_Final(ctx, digest->buf, &retlen)) {
93			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Final failed.",   CMAC);
94			retlen = 0;
95		}
96	  cmac_fail:
97		if (ctx)
98			CMAC_CTX_free(ctx);
99	}
100	else
101#   endif /*ENABLE_CMAC*/
102	{	/* generic MAC handling */
103		EVP_MD_CTX *	ctx   = EVP_MD_CTX_new();
104		u_int		uilen = 0;
105
106		if ( ! ctx) {
107			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest CTX new failed.",
108				OBJ_nid2sn(ktype));
109			goto mac_fail;
110		}
111
112           #ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
113		/* make sure MD5 is allowd */
114		EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
115           #endif
116		/* [Bug 3457] DON'T use plain EVP_DigestInit! It would
117		 * kill the flags! */
118		if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(ktype), NULL)) {
119			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Init failed.",
120				OBJ_nid2sn(ktype));
121			goto mac_fail;
122		}
123		if ((size_t)EVP_MD_CTX_size(ctx) > digest->len) {
124			msyslog(LOG_ERR, "MAC encrypt: MAC %s buf too small.",
125				OBJ_nid2sn(ktype));
126			goto mac_fail;
127		}
128		if (!EVP_DigestUpdate(ctx, key->buf, (u_int)key->len)) {
129			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update key failed.",
130				OBJ_nid2sn(ktype));
131			goto mac_fail;
132		}
133		if (!EVP_DigestUpdate(ctx, msg->buf, (u_int)msg->len)) {
134			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update data failed.",
135				OBJ_nid2sn(ktype));
136			goto mac_fail;
137		}
138		if (!EVP_DigestFinal(ctx, digest->buf, &uilen)) {
139			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Final failed.",
140				OBJ_nid2sn(ktype));
141			uilen = 0;
142		}
143	  mac_fail:
144		retlen = (size_t)uilen;
145
146		if (ctx)
147			EVP_MD_CTX_free(ctx);
148	}
149
150#else /* !OPENSSL follows */
151
152	if (ktype == NID_md5)
153	{
154		EVP_MD_CTX *	ctx   = EVP_MD_CTX_new();
155		u_int		uilen = 0;
156
157		if (digest->len < 16) {
158			msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small.");
159		}
160		else if ( ! ctx) {
161			msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 Digest CTX new failed.");
162		}
163		else {
164			EVP_DigestInit(ctx, EVP_get_digestbynid(ktype));
165			EVP_DigestUpdate(ctx, key->buf, key->len);
166			EVP_DigestUpdate(ctx, msg->buf, msg->len);
167			EVP_DigestFinal(ctx, digest->buf, &uilen);
168		}
169		if (ctx)
170			EVP_MD_CTX_free(ctx);
171		retlen = (size_t)uilen;
172	}
173	else
174	{
175		msyslog(LOG_ERR, "MAC encrypt: invalid key type %d"  , ktype);
176	}
177
178#endif /* !OPENSSL */
179
180	return retlen;
181}
182
183
184/*
185 * MD5authencrypt - generate message digest
186 *
187 * Returns length of MAC including key ID and digest.
188 */
189size_t
190MD5authencrypt(
191	int		type,	/* hash algorithm */
192	const u_char *	key,	/* key pointer */
193	size_t		klen,	/* key length */
194	u_int32 *	pkt,	/* packet pointer */
195	size_t		length	/* packet length */
196	)
197{
198	u_char	digest[EVP_MAX_MD_SIZE];
199	rwbuffT digb = { digest, sizeof(digest) };
200	robuffT keyb = { key, klen };
201	robuffT msgb = { pkt, length };
202	size_t	dlen = 0;
203
204	dlen = make_mac(&digb, type, &keyb, &msgb);
205	/* If the MAC is longer than the MAX then truncate it. */
206	if (dlen > MAX_MDG_LEN)
207		dlen = MAX_MDG_LEN;
208	memcpy((u_char *)pkt + length + KEY_MAC_LEN, digest, dlen);
209	return (dlen + KEY_MAC_LEN);
210}
211
212
213/*
214 * MD5authdecrypt - verify MD5 message authenticator
215 *
216 * Returns one if digest valid, zero if invalid.
217 */
218int
219MD5authdecrypt(
220	int		type,	/* hash algorithm */
221	const u_char *	key,	/* key pointer */
222	size_t		klen,	/* key length */
223	u_int32	*	pkt,	/* packet pointer */
224	size_t		length,	/* packet length */
225	size_t		size	/* MAC size */
226	)
227{
228	u_char	digest[EVP_MAX_MD_SIZE];
229	rwbuffT digb = { digest, sizeof(digest) };
230	robuffT keyb = { key, klen };
231	robuffT msgb = { pkt, length };
232	size_t	dlen = 0;
233
234	dlen = make_mac(&digb, type, &keyb, &msgb);
235
236	/* If the MAC is longer than the MAX then truncate it. */
237	if (dlen > MAX_MDG_LEN)
238		dlen = MAX_MDG_LEN;
239	if (size != (size_t)dlen + KEY_MAC_LEN) {
240		msyslog(LOG_ERR,
241		    "MAC decrypt: MAC length error");
242		return (0);
243	}
244	return !isc_tsmemcmp(digest,
245		 (u_char *)pkt + length + KEY_MAC_LEN, dlen);
246}
247
248/*
249 * Calculate the reference id from the address. If it is an IPv4
250 * address, use it as is. If it is an IPv6 address, do a md5 on
251 * it and use the bottom 4 bytes.
252 * The result is in network byte order.
253 */
254u_int32
255addr2refid(sockaddr_u *addr)
256{
257	u_char		digest[EVP_MAX_MD_SIZE];
258	u_int32		addr_refid;
259	EVP_MD_CTX	*ctx;
260	u_int		len;
261
262	if (IS_IPV4(addr))
263		return (NSRCADR(addr));
264
265	INIT_SSL();
266
267	ctx = EVP_MD_CTX_new();
268#   ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
269	/* MD5 is not used as a crypto hash here. */
270	EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
271#   endif
272	/* [Bug 3457] DON'T use plain EVP_DigestInit! It would kill the
273	 * flags! */
274	if (!EVP_DigestInit_ex(ctx, EVP_md5(), NULL)) {
275		msyslog(LOG_ERR,
276		    "MD5 init failed");
277		EVP_MD_CTX_free(ctx);	/* pedantic... but safe */
278		exit(1);
279	}
280
281	EVP_DigestUpdate(ctx, (u_char *)PSOCK_ADDR6(addr),
282	    sizeof(struct in6_addr));
283	EVP_DigestFinal(ctx, digest, &len);
284	EVP_MD_CTX_free(ctx);
285	memcpy(&addr_refid, digest, sizeof(addr_refid));
286	return (addr_refid);
287}
288