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