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