xref: /freebsd-src/contrib/ntp/libntp/a_md5encrypt.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
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 typedef struct {
16 	const void *	buf;
17 	size_t		len;
18 } robuffT;
19 
20 typedef struct {
21 	void *		buf;
22 	size_t		len;
23 } rwbuffT;
24 
25 #if defined(OPENSSL) && defined(ENABLE_CMAC)
26 static size_t
27 cmac_ctx_size(
28 	CMAC_CTX *	ctx)
29 {
30 	size_t mlen = 0;
31 
32 	if (ctx) {
33 		EVP_CIPHER_CTX * 	cctx;
34 		if (NULL != (cctx = CMAC_CTX_get0_cipher_ctx (ctx)))
35 			mlen = EVP_CIPHER_CTX_block_size(cctx);
36 	}
37 	return mlen;
38 }
39 #endif /*OPENSSL && ENABLE_CMAC*/
40 
41 static size_t
42 make_mac(
43 	const rwbuffT *	digest,
44 	int		ktype,
45 	const robuffT *	key,
46 	const robuffT *	msg)
47 {
48 	/*
49 	 * Compute digest of key concatenated with packet. Note: the
50 	 * key type and digest type have been verified when the key
51 	 * was created.
52 	 */
53 	size_t	retlen = 0;
54 
55 #ifdef OPENSSL
56 
57 	INIT_SSL();
58 
59 	/* Check if CMAC key type specific code required */
60 #   ifdef ENABLE_CMAC
61 	if (ktype == NID_cmac) {
62 		CMAC_CTX *	ctx    = NULL;
63 		void const *	keyptr = key->buf;
64 		u_char		keybuf[AES_128_KEY_SIZE];
65 
66 		/* adjust key size (zero padded buffer) if necessary */
67 		if (AES_128_KEY_SIZE > key->len) {
68 			memcpy(keybuf, keyptr, key->len);
69 			memset((keybuf + key->len), 0,
70 			       (AES_128_KEY_SIZE - key->len));
71 			keyptr = keybuf;
72 		}
73 
74 		if (NULL == (ctx = CMAC_CTX_new())) {
75 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s CTX new failed.", CMAC);
76 			goto cmac_fail;
77 		}
78 		if (!CMAC_Init(ctx, keyptr, AES_128_KEY_SIZE, EVP_aes_128_cbc(), NULL)) {
79 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Init failed.",    CMAC);
80 			goto cmac_fail;
81 		}
82 		if (cmac_ctx_size(ctx) > digest->len) {
83 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s buf too small.",  CMAC);
84 			goto cmac_fail;
85 		}
86 		if (!CMAC_Update(ctx, msg->buf, msg->len)) {
87 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Update failed.",  CMAC);
88 			goto cmac_fail;
89 		}
90 		if (!CMAC_Final(ctx, digest->buf, &retlen)) {
91 			msyslog(LOG_ERR, "MAC encrypt: CMAC %s Final failed.",   CMAC);
92 			retlen = 0;
93 		}
94 	  cmac_fail:
95 		if (ctx)
96 			CMAC_CTX_free(ctx);
97 	}
98 	else
99 #   endif /*ENABLE_CMAC*/
100 	{	/* generic MAC handling */
101 		EVP_MD_CTX *	ctx   = EVP_MD_CTX_new();
102 		u_int		uilen = 0;
103 
104 		if ( ! ctx) {
105 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest CTX new failed.",
106 				OBJ_nid2sn(ktype));
107 			goto mac_fail;
108 		}
109 
110            #ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
111 		/* make sure MD5 is allowd */
112 		EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
113            #endif
114 		/* [Bug 3457] DON'T use plain EVP_DigestInit! It would
115 		 * kill the flags! */
116 		if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(ktype), NULL)) {
117 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Init failed.",
118 				OBJ_nid2sn(ktype));
119 			goto mac_fail;
120 		}
121 		if ((size_t)EVP_MD_CTX_size(ctx) > digest->len) {
122 			msyslog(LOG_ERR, "MAC encrypt: MAC %s buf too small.",
123 				OBJ_nid2sn(ktype));
124 			goto mac_fail;
125 		}
126 		if (!EVP_DigestUpdate(ctx, key->buf, (u_int)key->len)) {
127 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update key failed.",
128 				OBJ_nid2sn(ktype));
129 			goto mac_fail;
130 		}
131 		if (!EVP_DigestUpdate(ctx, msg->buf, (u_int)msg->len)) {
132 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update data failed.",
133 				OBJ_nid2sn(ktype));
134 			goto mac_fail;
135 		}
136 		if (!EVP_DigestFinal(ctx, digest->buf, &uilen)) {
137 			msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Final failed.",
138 				OBJ_nid2sn(ktype));
139 			uilen = 0;
140 		}
141 	  mac_fail:
142 		retlen = (size_t)uilen;
143 
144 		if (ctx)
145 			EVP_MD_CTX_free(ctx);
146 	}
147 
148 #else /* !OPENSSL follows */
149 
150 	if (ktype == NID_md5)
151 	{
152 		EVP_MD_CTX *	ctx   = EVP_MD_CTX_new();
153 		u_int		uilen = 0;
154 
155 		if (digest->len < 16) {
156 			msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small.");
157 		}
158 		else if ( ! ctx) {
159 			msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 Digest CTX new failed.");
160 		}
161 		else if (!EVP_DigestInit(ctx, EVP_get_digestbynid(ktype))) {
162 			msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 Digest INIT failed.");
163 		}
164 		else {
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  */
189 size_t
190 MD5authencrypt(
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  */
218 int
219 MD5authdecrypt(
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 	keyid_t		keyno   /* key id (for err log) */
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: len=%zu key=%d",
243 			size, keyno);
244 		return (0);
245 	}
246 	return !isc_tsmemcmp(digest,
247 		 (u_char *)pkt + length + KEY_MAC_LEN, dlen);
248 }
249 
250 /*
251  * Calculate the reference id from the address. If it is an IPv4
252  * address, use it as is. If it is an IPv6 address, do a md5 on
253  * it and use the bottom 4 bytes.
254  * The result is in network byte order.
255  */
256 u_int32
257 addr2refid(sockaddr_u *addr)
258 {
259 	u_char		digest[EVP_MAX_MD_SIZE];
260 	u_int32		addr_refid;
261 	EVP_MD_CTX	*ctx;
262 	u_int		len;
263 
264 	if (IS_IPV4(addr))
265 		return (NSRCADR(addr));
266 
267 	INIT_SSL();
268 
269 	ctx = EVP_MD_CTX_new();
270 #   ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
271 	/* MD5 is not used as a crypto hash here. */
272 	EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
273 #   endif
274 	/* [Bug 3457] DON'T use plain EVP_DigestInit! It would kill the
275 	 * flags! */
276 	if (!EVP_DigestInit_ex(ctx, EVP_md5(), NULL)) {
277 		msyslog(LOG_ERR,
278 		    "MD5 init failed");
279 		EVP_MD_CTX_free(ctx);	/* pedantic... but safe */
280 		exit(1);
281 	}
282 
283 	EVP_DigestUpdate(ctx, (u_char *)PSOCK_ADDR6(addr),
284 	    sizeof(struct in6_addr));
285 	EVP_DigestFinal(ctx, digest, &len);
286 	EVP_MD_CTX_free(ctx);
287 	memcpy(&addr_refid, digest, sizeof(addr_refid));
288 	return (addr_refid);
289 }
290