xref: /openbsd/usr.bin/snmp/usm.c (revision 09467b48)
1 /*	$OpenBSD: usm.c,v 1.5 2019/10/24 12:39:26 tb Exp $	*/
2 
3 /*
4  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/time.h>
20 
21 #include <openssl/evp.h>
22 #include <openssl/hmac.h>
23 
24 #include <ber.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <time.h>
28 
29 #include "smi.h"
30 #include "snmp.h"
31 #include "usm.h"
32 
33 #define USM_MAX_DIGESTLEN 48
34 #define USM_MAX_TIMEWINDOW 150
35 #define USM_SALTOFFSET 8
36 
37 struct usm_sec {
38 	struct snmp_sec snmp;
39 	char *user;
40 	size_t userlen;
41 	int engineidset;
42 	char *engineid;
43 	size_t engineidlen;
44 	enum usm_key_level authlevel;
45 	const EVP_MD *digest;
46 	char *authkey;
47 	enum usm_key_level privlevel;
48 	const EVP_CIPHER *cipher;
49 	char *privkey;
50 	int bootsset;
51 	uint32_t boots;
52 	int timeset;
53 	uint32_t time;
54 	struct timespec timecheck;
55 };
56 
57 struct usm_cookie {
58 	size_t digestoffset;
59 	long long salt;
60 	uint32_t boots;
61 	uint32_t time;
62 };
63 
64 static int usm_doinit(struct snmp_agent *);
65 static char *usm_genparams(struct snmp_agent *, size_t *, void **);
66 static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *);
67 static struct ber_element *usm_encpdu(struct snmp_agent *agent,
68     struct ber_element *pdu, void *cookie);
69 static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *,
70     char *, size_t, size_t *);
71 static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
72     size_t, uint8_t, void **);
73 struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *);
74 static void usm_digest_pos(void *, size_t);
75 static void usm_free(void *);
76 static char *usm_passwd2mkey(const EVP_MD *, const char *);
77 static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
78 static size_t usm_digestlen(const EVP_MD *);
79 
80 struct snmp_sec *
81 usm_init(const char *user, size_t userlen)
82 {
83 	struct snmp_sec *sec;
84 	struct usm_sec *usm;
85 
86 	if (user == NULL || user[0] == '\0') {
87 		errno = EINVAL;
88 		return NULL;
89 	}
90 
91 	if ((sec = malloc(sizeof(*sec))) == NULL)
92 		return NULL;
93 
94 	if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) {
95 		free(sec);
96 		return NULL;
97 	}
98 	if ((usm->user = malloc(userlen)) == NULL) {
99 		free(sec);
100 		free(usm);
101 		return NULL;
102 	}
103 	memcpy(usm->user, user, userlen);
104 	usm->userlen = userlen;
105 
106 	sec->model = SNMP_SEC_USM;
107 	sec->init = usm_doinit;
108 	sec->genparams = usm_genparams;
109 	sec->encpdu = usm_encpdu;
110 	sec->parseparams = usm_parseparams;
111 	sec->decpdu = usm_decpdu;
112 	sec->finalparams = usm_finalparams;
113 	sec->free = usm_free;
114 	sec->freecookie = free;
115 	sec->data = usm;
116 	return sec;
117 }
118 
119 static int
120 usm_doinit(struct snmp_agent *agent)
121 {
122 	struct ber_element *ber;
123 	struct usm_sec *usm = agent->v3->sec->data;
124 	int level;
125 	size_t userlen;
126 
127 	if (usm->engineidset && usm->bootsset && usm->timeset)
128 		return 0;
129 
130 	level = agent->v3->level;
131 	agent->v3->level = SNMP_MSGFLAG_REPORT;
132 	userlen = usm->userlen;
133 	usm->userlen = 0;
134 
135 	if ((ber = snmp_get(agent, NULL, 0)) == NULL) {
136 		agent->v3->level = level;
137 		usm->userlen = userlen;
138 		return -1;
139 	}
140 	ober_free_element(ber);
141 
142 	agent->v3->level = level;
143 	usm->userlen = userlen;
144 
145 	 /*
146 	  * Ugly hack for HP Laserjet:
147 	  * This device returns the engineid on probing, but only returns boots
148 	  * and time after a packet has been sent with full auth/enc.
149 	  */
150 	if (!usm->engineidset || !usm->bootsset || !usm->timeset) {
151 		if ((ber = snmp_get(agent, NULL, 0)) == NULL)
152 			return -1;
153 		ober_free_element(ber);
154 	}
155 	return 0;
156 }
157 
158 static char *
159 usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
160 {
161 	struct ber ber;
162 	struct ber_element *params, *digestelm;
163 	struct usm_sec *usm = agent->v3->sec->data;
164 	char digest[USM_MAX_DIGESTLEN];
165 	size_t digestlen = 0, saltlen = 0;
166 	char *secparams = NULL;
167 	ssize_t berlen = 0;
168 	struct usm_cookie *usmcookie;
169 	struct timespec now, timediff;
170 
171 	bzero(digest, sizeof(digest));
172 
173 	if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL)
174 		return NULL;
175 	*cookie = usmcookie;
176 
177 	arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt));
178 	if (usm->timeset) {
179 		if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
180 			free(usmcookie);
181 			return NULL;
182 		}
183 		timespecsub(&now, &(usm->timecheck), &timediff);
184 		usmcookie->time = usm->time + timediff.tv_sec;
185 	} else
186 		usmcookie->time = 0;
187 	usmcookie->boots = usm->boots;
188 
189 	if (agent->v3->level & SNMP_MSGFLAG_AUTH)
190 		digestlen = usm_digestlen(usm->digest);
191 	if (agent->v3->level & SNMP_MSGFLAG_PRIV)
192 	    saltlen = sizeof(usmcookie->salt);
193 
194 	if ((params = ober_printf_elements(NULL, "{xddxxx}", usm->engineid,
195 	    usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user,
196 	    usm->userlen, digest, digestlen, &(usmcookie->salt),
197 	    saltlen)) == NULL) {
198 		free(usmcookie);
199 		return NULL;
200 	}
201 
202 	if (ober_scanf_elements(params, "{SSSSe",  &digestelm) == -1) {
203 		ober_free_element(params);
204 		free(usmcookie);
205 		return NULL;
206 	}
207 
208 	ober_set_writecallback(digestelm, usm_digest_pos, usmcookie);
209 
210 	bzero(&ber, sizeof(ber));
211 	ober_set_application(&ber, smi_application);
212 	if (ober_write_elements(&ber, params) != -1)
213 	    berlen = ber_copy_writebuf(&ber, (void **)&secparams);
214 
215 	*len = berlen;
216 	ober_free_element(params);
217 	ober_free(&ber);
218 	return secparams;
219 }
220 
221 static struct ber_element *
222 usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie)
223 {
224 	struct usm_sec *usm = agent->v3->sec->data;
225 	struct usm_cookie *usmcookie = cookie;
226 	struct ber ber;
227 	struct ber_element *retpdu;
228 	char *serialpdu, *encpdu;
229 	ssize_t pdulen;
230 	size_t encpdulen;
231 
232 	bzero(&ber, sizeof(ber));
233 	ober_set_application(&ber, smi_application);
234 	pdulen = ober_write_elements(&ber, pdu);
235 	if (pdulen == -1)
236 		return NULL;
237 
238 	ober_get_writebuf(&ber, (void **)&serialpdu);
239 
240 	encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu,
241 	    pdulen, &encpdulen);
242 	ober_free(&ber);
243 	if (encpdu == NULL)
244 		return NULL;
245 
246 	retpdu = ober_add_nstring(NULL, encpdu, encpdulen);
247 	free(encpdu);
248 	return retpdu;
249 }
250 
251 static char *
252 usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key,
253     struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t *outlen)
254 {
255 	EVP_CIPHER_CTX ctx;
256 	size_t i;
257 	char iv[EVP_MAX_IV_LENGTH];
258 	char *salt = (char *)&(cookie->salt);
259 	char *outtext;
260 	int len, len2, bs;
261 	uint32_t ivv;
262 
263 	switch (EVP_CIPHER_type(cipher)) {
264 	case NID_des_cbc:
265 		/* RFC3414, chap 8.1.1.1. */
266 		for (i = 0; i < 8; i++)
267 			iv[i] = salt[i] ^ key[USM_SALTOFFSET + i];
268 		break;
269 	case NID_aes_128_cfb128:
270 		/* RFC3826, chap 3.1.2.1. */
271 		ivv = htobe32(cookie->boots);
272 		memcpy(iv, &ivv, sizeof(ivv));
273 		ivv = htobe32(cookie->time);
274 		memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
275 		memcpy(iv + 2 * sizeof(ivv), &(cookie->salt),
276 		    sizeof(cookie->salt));
277 		break;
278 	default:
279 		return NULL;
280 	}
281 
282 	bzero(&ctx, sizeof(ctx));
283 	if (!EVP_CipherInit(&ctx, cipher, key, iv, do_enc))
284 		return NULL;
285 
286 	EVP_CIPHER_CTX_set_padding(&ctx, do_enc);
287 
288 	bs = EVP_CIPHER_block_size(cipher);
289 	/* Maximum output size */
290 	*outlen = pdulen + (bs - (pdulen % bs));
291 
292 	if ((outtext = malloc(*outlen)) == NULL)
293 		return NULL;
294 
295 	if (EVP_CipherUpdate(&ctx, outtext, &len, serialpdu, pdulen) &&
296 	    EVP_CipherFinal_ex(&ctx, outtext + len, &len2))
297 		*outlen = len + len2;
298 	else {
299 		free(outtext);
300 		outtext = NULL;
301 	}
302 
303 	EVP_CIPHER_CTX_cleanup(&ctx);
304 
305 	return outtext;
306 }
307 
308 static int
309 usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
310     size_t secparamsoffset, void *cookie)
311 {
312 	struct usm_sec *usm = agent->v3->sec->data;
313 	struct usm_cookie *usmcookie = cookie;
314 	u_char digest[EVP_MAX_MD_SIZE];
315 
316 	if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0)
317 		return 0;
318 
319 	if (usm->authlevel != USM_KEY_LOCALIZED)
320 		return -1;
321 
322 	if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
323 	    buflen, digest, NULL) == NULL)
324 		return -1;
325 
326 	memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
327 	    usm_digestlen(usm->digest));
328 	return 0;
329 }
330 
331 static int
332 usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
333     off_t secparamsoffset, char *buf, size_t buflen, uint8_t level,
334     void **cookie)
335 {
336 	struct usm_sec *usm = agent->v3->sec->data;
337 	struct ber ber;
338 	struct ber_element *secparams;
339 	char *engineid, *user, *digest, *salt;
340 	size_t engineidlen, userlen, digestlen, saltlen;
341 	struct timespec now, timediff;
342 	off_t digestoffset;
343 	char exp_digest[EVP_MAX_MD_SIZE];
344 	struct usm_cookie *usmcookie;
345 
346 	bzero(&ber, sizeof(ber));
347 	bzero(exp_digest, sizeof(exp_digest));
348 
349 	ober_set_application(&ber, smi_application);
350 	ober_set_readbuf(&ber, buf, buflen);
351 	if ((secparams = ober_read_elements(&ber, NULL)) == NULL)
352 		return -1;
353 	ober_free(&ber);
354 
355 	if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL)
356 		goto fail;
357 	*cookie = usmcookie;
358 
359 	if (ober_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen,
360 	    &(usmcookie->boots), &(usmcookie->time), &user, &userlen,
361 	    &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1)
362 		goto fail;
363 	if (saltlen != sizeof(usmcookie->salt) && saltlen != 0)
364 		goto fail;
365 	memcpy(&(usmcookie->salt), salt, saltlen);
366 
367 	if (!usm->engineidset) {
368 		if (usm_setengineid(agent->v3->sec, engineid,
369 		    engineidlen) == -1)
370 			goto fail;
371 	} else {
372 		if (usm->engineidlen != engineidlen)
373 			goto fail;
374 		if (memcmp(usm->engineid, engineid, engineidlen) != 0)
375 			goto fail;
376 	}
377 
378 	if (!usm->bootsset) {
379 		usm->boots = usmcookie->boots;
380 		usm->bootsset = 1;
381 	} else {
382 		if (usmcookie->boots < usm->boots)
383 			goto fail;
384 		if (usmcookie->boots > usm->boots) {
385 			usm->bootsset = 0;
386 			usm->timeset = 0;
387 			usm_doinit(agent);
388 			goto fail;
389 		}
390 	}
391 
392 	if (!usm->timeset) {
393 		usm->time = usmcookie->time;
394 		if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1)
395 			goto fail;
396 		usm->timeset = 1;
397 	} else {
398 		if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
399 			goto fail;
400 		timespecsub(&now, &(usm->timecheck), &timediff);
401 		if (usmcookie->time <
402 		    usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW ||
403 		    usmcookie->time >
404 		    usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) {
405 			usm->bootsset = 0;
406 			usm->timeset = 0;
407 			usm_doinit(agent);
408 			goto fail;
409 		}
410 	}
411 	/*
412 	 * Don't assume these are set if both are zero.
413 	 * Ugly hack for HP Laserjet
414 	 */
415 	if (usm->boots == 0 && usm->time == 0) {
416 		usm->bootsset = 0;
417 		usm->timeset = 0;
418 	}
419 
420 	if (userlen != usm->userlen ||
421 	    memcmp(user, usm->user, userlen) != 0)
422 		goto fail;
423 
424 	if (level & SNMP_MSGFLAG_AUTH) {
425 		if (digestlen != usm_digestlen(usm->digest))
426 			goto fail;
427 	}
428 	if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) {
429 		bzero(packet + secparamsoffset + digestoffset, digestlen);
430 		if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet,
431 		    packetlen, exp_digest, NULL) == NULL)
432 			goto fail;
433 
434 		if (memcmp(exp_digest, digest, digestlen) != 0)
435 			goto fail;
436 	} else
437 		if (digestlen != 0)
438 			goto fail;
439 
440 	ober_free_element(secparams);
441 	return 0;
442 
443 fail:
444 	free(usmcookie);
445 	ober_free_element(secparams);
446 	return -1;
447 }
448 
449 struct ber_element *
450 usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie)
451 {
452 	struct usm_sec *usm = agent->v3->sec->data;
453 	struct usm_cookie *usmcookie = cookie;
454 	struct ber ber;
455 	struct ber_element *scopedpdu;
456 	char *rawpdu;
457 	size_t rawpdulen;
458 
459 	if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie,
460 	    encpdu, encpdulen, &rawpdulen)) == NULL)
461 		return NULL;
462 
463 	bzero(&ber, sizeof(ber));
464 	ober_set_application(&ber, smi_application);
465 	ober_set_readbuf(&ber, rawpdu, rawpdulen);
466 	scopedpdu = ober_read_elements(&ber, NULL);
467 	ober_free(&ber);
468 	free(rawpdu);
469 
470 	return scopedpdu;
471 }
472 
473 static void
474 usm_digest_pos(void *data, size_t offset)
475 {
476 	struct usm_cookie *usmcookie = data;
477 
478 	usmcookie->digestoffset = offset;
479 }
480 
481 static void
482 usm_free(void *data)
483 {
484 	struct usm_sec *usm = data;
485 
486 	free(usm->user);
487 	free(usm->authkey);
488 	free(usm->privkey);
489 	free(usm->engineid);
490 	free(usm);
491 }
492 
493 int
494 usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
495     size_t keylen, enum usm_key_level level)
496 {
497 	struct usm_sec *usm = sec->data;
498 	char *lkey;
499 
500 	/*
501 	 * We could transform a master key to a local key here if we already
502 	 * have usm_setengineid called. Sine snmpc.c is the only caller at
503 	 * the moment there's no need, since it always calls this function
504 	 * first.
505 	 */
506 	if (level == USM_KEY_PASSWORD) {
507 		if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL)
508 			return -1;
509 		level = USM_KEY_MASTER;
510 		keylen = EVP_MD_size(digest);
511 	} else {
512 		if (keylen != (size_t)EVP_MD_size(digest)) {
513 			errno = EINVAL;
514 			return -1;
515 		}
516 		if ((lkey = malloc(keylen)) == NULL)
517 			return -1;
518 		memcpy(lkey, key, keylen);
519 		usm->authkey = lkey;
520 	}
521 	usm->digest = digest;
522 	usm->authlevel = level;
523 	return 0;
524 }
525 
526 int
527 usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key,
528     size_t keylen, enum usm_key_level level)
529 {
530 	struct usm_sec *usm = sec->data;
531 	char *lkey;
532 
533 	if (usm->digest == NULL) {
534 		errno = EINVAL;
535 		return -1;
536 	}
537 
538 	/*
539 	 * We could transform a master key to a local key here if we already
540 	 * have usm_setengineid called. Sine snmpc.c is the only caller at
541 	 * the moment there's no need, since it always calls us first.
542 	 */
543 	if (level == USM_KEY_PASSWORD) {
544 		if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL)
545 			return -1;
546 		level = USM_KEY_MASTER;
547 		keylen = EVP_MD_size(usm->digest);
548 	} else {
549 		if (keylen != (size_t)EVP_MD_size(usm->digest)) {
550 			errno = EINVAL;
551 			return -1;
552 		}
553 		if ((lkey = malloc(keylen)) == NULL)
554 			return -1;
555 		memcpy(lkey, key, keylen);
556 		usm->privkey = lkey;
557 	}
558 	usm->cipher = cipher;
559 	usm->privlevel = level;
560 	return 0;
561 }
562 
563 int
564 usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
565 {
566 	struct usm_sec *usm = sec->data;
567 	char *mkey;
568 
569 	if (usm->engineid != NULL)
570 		free(usm->engineid);
571 	if ((usm->engineid = malloc(engineidlen)) == NULL)
572 		return -1;
573 	memcpy(usm->engineid, engineid, engineidlen);
574 	usm->engineidlen = engineidlen;
575 	usm->engineidset = 1;
576 
577 	if (usm->authlevel == USM_KEY_MASTER) {
578 		mkey = usm->authkey;
579 		if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
580 		    mkey)) == NULL) {
581 			usm->authkey = mkey;
582 			return -1;
583 		}
584 		free(mkey);
585 		usm->authlevel = USM_KEY_LOCALIZED;
586 	}
587 	if (usm->privlevel == USM_KEY_MASTER) {
588 		mkey = usm->privkey;
589 		if ((usm->privkey = usm_mkey2lkey(usm, usm->digest,
590 		    mkey)) == NULL) {
591 			usm->privkey = mkey;
592 			return -1;
593 		}
594 		free(mkey);
595 		usm->privlevel = USM_KEY_LOCALIZED;
596 	}
597 
598 	return 0;
599 }
600 
601 int
602 usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
603 {
604 	struct usm_sec *usm = sec->data;
605 
606 	if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1)
607 		return -1;
608 
609 	usm->boots = boots;
610 	usm->bootsset = 1;
611 	usm->time = time;
612 	usm->timeset = 1;
613 	return 0;
614 }
615 
616 static char *
617 usm_passwd2mkey(const EVP_MD *md, const char *passwd)
618 {
619 	EVP_MD_CTX ctx;
620 	int i, count;
621 	const u_char *pw;
622 	u_char *c;
623 	u_char keybuf[EVP_MAX_MD_SIZE];
624 	unsigned dlen;
625 	char *key;
626 
627 	bzero(&ctx, sizeof(ctx));
628 	EVP_DigestInit_ex(&ctx, md, NULL);
629 	pw = (const u_char *)passwd;
630 	for (count = 0; count < 1048576; count += 64) {
631 		c = keybuf;
632 		for (i = 0; i < 64; i++) {
633 			if (*pw == '\0')
634 				pw = (const u_char *)passwd;
635 			*c++ = *pw++;
636 		}
637 		EVP_DigestUpdate(&ctx, keybuf, 64);
638 	}
639 	EVP_DigestFinal_ex(&ctx, keybuf, &dlen);
640 	EVP_MD_CTX_cleanup(&ctx);
641 
642 	if ((key = malloc(dlen)) == NULL)
643 		return NULL;
644 	memcpy(key, keybuf, dlen);
645 	return key;
646 }
647 
648 static char *
649 usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
650 {
651 	EVP_MD_CTX ctx;
652 	u_char buf[EVP_MAX_MD_SIZE];
653 	u_char *lkey;
654 	unsigned lklen;
655 
656 	bzero(&ctx, sizeof(ctx));
657 	EVP_DigestInit_ex(&ctx, md, NULL);
658 
659 	EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
660 	EVP_DigestUpdate(&ctx, usm->engineid, usm->engineidlen);
661 	EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
662 
663 	EVP_DigestFinal_ex(&ctx, buf, &lklen);
664 	EVP_MD_CTX_cleanup(&ctx);
665 
666 	if ((lkey = malloc(lklen)) == NULL)
667 		return NULL;
668 	memcpy(lkey, buf, lklen);
669 	return lkey;
670 }
671 
672 static size_t
673 usm_digestlen(const EVP_MD *md)
674 {
675 	switch (EVP_MD_type(md)) {
676 	case NID_md5:
677 	case NID_sha1:
678 		return 12;
679 	case NID_sha224:
680 		return 16;
681 	case NID_sha256:
682 		return 24;
683 	case NID_sha384:
684 		return 32;
685 	case NID_sha512:
686 		return 48;
687 	default:
688 		return 0;
689 	}
690 }
691