snmpcrypto.c revision 216299
156693Sjhb/*-
256693Sjhb * Copyright (c) 2010 The FreeBSD Foundation
356693Sjhb * All rights reserved.
456693Sjhb *
556693Sjhb * This software was developed by Shteryana Sotirova Shopova under
656693Sjhb * sponsorship from the FreeBSD Foundation.
756693Sjhb *
856693Sjhb * Redistribution and use in source and binary forms, with or without
956693Sjhb * modification, are permitted provided that the following conditions
1056693Sjhb * are met:
1156693Sjhb * 1. Redistributions of source code must retain the above copyright
1256693Sjhb *    notice, this list of conditions and the following disclaimer.
1356693Sjhb * 2. Redistributions in binary form must reproduce the above copyright
1456693Sjhb *    notice, this list of conditions and the following disclaimer in the
1556693Sjhb *    documentation and/or other materials provided with the distribution.
1656693Sjhb *
1756693Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1856693Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1956693Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2056693Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2156693Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2256693Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2356693Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2456693Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2556693Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2656693Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2756693Sjhb * SUCH DAMAGE.
2856693Sjhb *
2956693Sjhb * $FreeBSD: head/contrib/bsnmp/lib/snmpcrypto.c 216299 2010-12-08 15:52:06Z syrinx $
3056693Sjhb */
3158713Sjhb#include <sys/types.h>
3256693Sjhb#include <sys/socket.h>
3356693Sjhb#include <stdio.h>
3456693Sjhb#include <stdlib.h>
3556693Sjhb#include <stddef.h>
3656693Sjhb#include <stdarg.h>
3756693Sjhb#ifdef HAVE_STDINT_H
3858713Sjhb#include <stdint.h>
3956693Sjhb#elif defined(HAVE_INTTYPES_H)
4056693Sjhb#include <inttypes.h>
4158713Sjhb#endif
4258713Sjhb#include <string.h>
4358713Sjhb#include <ctype.h>
4458713Sjhb#include <errno.h>
4558713Sjhb#include <netinet/in.h>
4658713Sjhb
4758713Sjhb#ifdef HAVE_LIBCRYPTO
4858713Sjhb#include <openssl/evp.h>
4958713Sjhb#endif
5056693Sjhb
5156693Sjhb#include "asn1.h"
5256693Sjhb#include "snmp.h"
5356693Sjhb#include "snmppriv.h"
5456693Sjhb
5556693Sjhb#define	SNMP_PRIV_AES_IV_SIZ		16
5656693Sjhb#define	SNMP_EXTENDED_KEY_SIZ		64
5756693Sjhb#define	SNMP_AUTH_KEY_LOOPCNT		1048576
5856693Sjhb#define	SNMP_AUTH_BUF_SIZE		72
5956693Sjhb
6056693Sjhbstatic const uint8_t ipad = 0x36;
6156693Sjhbstatic const uint8_t opad = 0x5c;
6256693Sjhb
6356693Sjhb#ifdef HAVE_LIBCRYPTO
6456693Sjhb
6556693Sjhbstatic int32_t
6658713Sjhbsnmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx,
6758713Sjhb    const EVP_MD **dtype, uint32_t *keylen)
6856693Sjhb{
6956693Sjhb	if (user->auth_proto == SNMP_AUTH_HMAC_MD5) {
7056693Sjhb		*dtype = EVP_md5();
7156693Sjhb		*keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
7256693Sjhb	} else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) {
7356693Sjhb		*dtype = EVP_sha1();
7456693Sjhb		*keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
7558713Sjhb	} else if (user->auth_proto == SNMP_AUTH_NOAUTH)
7656693Sjhb		return (0);
7756693Sjhb	else {
7858713Sjhb		snmp_error("unknown authentication option - %d",
7956693Sjhb		    user->auth_proto);
8056693Sjhb		return (-1);
8158713Sjhb	}
8258713Sjhb
8358713Sjhb	if (EVP_DigestInit(ctx, *dtype) != 1)
8456693Sjhb		return (-1);
8556693Sjhb
8656693Sjhb	return (1);
8756693Sjhb}
8858713Sjhb
8956693Sjhbenum snmp_code
9058713Sjhbsnmp_pdu_calc_digest(struct asn_buf *b, const struct snmp_pdu *pdu,
9156693Sjhb    uint8_t *digest)
9256693Sjhb{
9356693Sjhb	uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ];
9456693Sjhb	uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ];
9556693Sjhb	uint32_t i, keylen, olen;
9656693Sjhb	int32_t err;
9756693Sjhb	const EVP_MD *dtype;
9858713Sjhb	EVP_MD_CTX ctx;
9958713Sjhb
10058713Sjhb	err = snmp_digest_init(&pdu->user, &ctx, &dtype, &keylen);
10156693Sjhb	if (err < 0)
10256693Sjhb		return (SNMP_CODE_BADDIGEST);
10356693Sjhb	else if (err == 0)
10456693Sjhb		return (SNMP_CODE_OK);
10556693Sjhb
10656693Sjhb	memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest));
10756693Sjhb	memcpy(extkey, pdu->user.auth_key, keylen);
10856693Sjhb	memset(extkey + keylen, 0, sizeof(extkey) - keylen);
10956693Sjhb
11058713Sjhb	for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) {
11158713Sjhb		key1[i] = extkey[i] ^ ipad;
11258713Sjhb		key2[i] = extkey[i] ^ opad;
11358713Sjhb	}
11458713Sjhb
11558713Sjhb	if (EVP_DigestUpdate(&ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 ||
11656693Sjhb	    EVP_DigestUpdate(&ctx, pdu->outer_ptr, pdu->outer_len) != 1 ||
11756693Sjhb	    EVP_DigestFinal(&ctx, md, &olen) != 1)
11856693Sjhb		goto failed;
11958713Sjhb
12056693Sjhb	if (EVP_DigestInit(&ctx, dtype) != 1 ||
12156693Sjhb	    EVP_DigestUpdate(&ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 ||
12258713Sjhb	    EVP_DigestUpdate(&ctx, md, olen) != 1 ||
12358713Sjhb	    EVP_DigestFinal(&ctx, md, &olen) != 1)
12456693Sjhb		goto failed;
12556693Sjhb
12656693Sjhb	if (olen < SNMP_USM_AUTH_SIZE) {
12756693Sjhb		snmp_error("bad digest size - %d", olen);
12858713Sjhb		EVP_MD_CTX_cleanup(&ctx);
12958713Sjhb		return (SNMP_CODE_BADDIGEST);
13058713Sjhb	}
13158713Sjhb
13258713Sjhb	memcpy(digest, md, SNMP_USM_AUTH_SIZE);
13358713Sjhb	EVP_MD_CTX_cleanup(&ctx);
13458713Sjhb	return (SNMP_CODE_OK);
13558713Sjhb
13658713Sjhbfailed:
13758713Sjhb	EVP_MD_CTX_cleanup(&ctx);
13858713Sjhb	return (SNMP_CODE_BADDIGEST);
13958713Sjhb}
14058713Sjhb
14158713Sjhbstatic int32_t
14258713Sjhbsnmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len,
14356693Sjhb    EVP_CIPHER_CTX *ctx, const EVP_CIPHER **ctype, uint8_t *piv)
14456693Sjhb{
14556693Sjhb	int i;
14656693Sjhb	uint32_t netint;
14758713Sjhb
14856693Sjhb	if (pdu->user.priv_proto == SNMP_PRIV_DES) {
14956693Sjhb		if (len  % 8 != 0)
15056693Sjhb			return (-1);
15156693Sjhb		*ctype = EVP_des_cbc();
15256693Sjhb		memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
15358713Sjhb		for (i = 0; i < 8; i++)
15456693Sjhb			piv[i] = piv[i] ^ pdu->user.priv_key[8 + i];
15556693Sjhb	} else if (pdu->user.priv_proto == SNMP_PRIV_AES) {
15656693Sjhb		*ctype = EVP_aes_128_cfb128();
15758713Sjhb		netint = htonl(pdu->engine.engine_boots);
15856693Sjhb		memcpy(piv, &netint, sizeof(netint));
15956693Sjhb		piv += sizeof(netint);
16056693Sjhb		netint = htonl(pdu->engine.engine_time);
16156693Sjhb		memcpy(piv, &netint, sizeof(netint));
16258713Sjhb		piv += sizeof(netint);
16358713Sjhb		memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
16458713Sjhb	} else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV)
16556693Sjhb		return (0);
16656693Sjhb	else {
16756693Sjhb		snmp_error("unknown privacy option - %d", pdu->user.priv_proto);
16856693Sjhb		return (-1);
16956693Sjhb	}
17056693Sjhb
17156693Sjhb	return (1);
17256693Sjhb}
17356693Sjhb
17456693Sjhbenum snmp_code
17556693Sjhbsnmp_pdu_encrypt(struct asn_buf *b, const struct snmp_pdu *pdu)
17656693Sjhb{
17756693Sjhb	int32_t err, olen;
17856693Sjhb	uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
17958713Sjhb	const EVP_CIPHER *ctype;
18058713Sjhb	EVP_CIPHER_CTX ctx;
18158713Sjhb
18258713Sjhb	err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctx, &ctype, iv);
18358713Sjhb	if (err < 0)
18458713Sjhb		return (SNMP_CODE_EDECRYPT);
18556693Sjhb	else if (err == 0)
18656693Sjhb		return (SNMP_CODE_OK);
18758713Sjhb
18856693Sjhb	if (EVP_EncryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1)
18956693Sjhb		return (SNMP_CODE_FAILED);
19056693Sjhb
19156693Sjhb	if (EVP_EncryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
19256693Sjhb	    pdu->scoped_len) != 1 ||
19356693Sjhb	    EVP_EncryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) {
19456693Sjhb		EVP_CIPHER_CTX_cleanup(&ctx);
19556693Sjhb		return (SNMP_CODE_FAILED);
19656693Sjhb	}
19756693Sjhb
19856693Sjhb	EVP_CIPHER_CTX_cleanup(&ctx);
19956693Sjhb	return (SNMP_CODE_OK);
20056693Sjhb}
20156693Sjhb
20256693Sjhbenum snmp_code
20356693Sjhbsnmp_pdu_decrypt(struct asn_buf *b, const struct snmp_pdu *pdu)
20456693Sjhb{
20556693Sjhb	int32_t err, olen;
20656693Sjhb	uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
20756693Sjhb	const EVP_CIPHER *ctype;
20856693Sjhb	EVP_CIPHER_CTX ctx;
20956693Sjhb
21056693Sjhb	err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctx, &ctype, iv);
21156693Sjhb	if (err < 0)
21256693Sjhb		return (SNMP_CODE_EDECRYPT);
21356693Sjhb	else if (err == 0)
21456693Sjhb		return (SNMP_CODE_OK);
21556693Sjhb
21656693Sjhb	if (EVP_DecryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1 ||
21756693Sjhb	    EVP_CIPHER_CTX_set_padding(&ctx, 0) != 1)
21856693Sjhb		return (SNMP_CODE_EDECRYPT);
21956693Sjhb
22056693Sjhb	if (EVP_DecryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
22156693Sjhb	    pdu->scoped_len) != 1 ||
22256693Sjhb	    EVP_DecryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) {
22358713Sjhb		EVP_CIPHER_CTX_cleanup(&ctx);
22456693Sjhb		return (SNMP_CODE_EDECRYPT);
22556693Sjhb	}
22656693Sjhb
22756693Sjhb	EVP_CIPHER_CTX_cleanup(&ctx);
22856693Sjhb	return (SNMP_CODE_OK);
22956693Sjhb}
23056693Sjhb
23156693Sjhb/* [RFC 3414] - A.2. Password to Key Algorithm */
23256693Sjhbenum snmp_code
23356693Sjhbsnmp_passwd_to_keys(struct snmp_user *user, char *passwd)
23456693Sjhb{
23558713Sjhb	int err, loop, i, pwdlen;
23658713Sjhb	uint32_t  keylen, olen;
23756693Sjhb	const EVP_MD *dtype;
23856693Sjhb	EVP_MD_CTX ctx;
23956693Sjhb	uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
24056693Sjhb
24156693Sjhb	if (passwd == NULL || user == NULL)
24256693Sjhb		return (SNMP_CODE_FAILED);
24356693Sjhb
24456693Sjhb	err = snmp_digest_init(user, &ctx, &dtype, &keylen);
24556693Sjhb	if (err < 0)
24656693Sjhb		return (SNMP_CODE_BADDIGEST);
24756693Sjhb	else if (err == 0)
24856693Sjhb		return (SNMP_CODE_OK);
24956693Sjhb
25056693Sjhb	memset(user->auth_key, 0, sizeof(user->auth_key));
25156693Sjhb	pwdlen = strlen(passwd);
25256693Sjhb
25356693Sjhb	for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) {
25456693Sjhb		for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++)
25556693Sjhb			authbuf[i] = passwd[(loop + i) % pwdlen];
25656693Sjhb		if (EVP_DigestUpdate(&ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1)
25758713Sjhb			goto failed;
25856693Sjhb	}
25956693Sjhb
26056693Sjhb	if (EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1)
26156693Sjhb		goto failed;
26256693Sjhb
26356693Sjhb	EVP_MD_CTX_cleanup(&ctx);
26456693Sjhb	return (SNMP_CODE_OK);
265
266failed:
267	EVP_MD_CTX_cleanup(&ctx);
268	return (SNMP_CODE_BADDIGEST);
269}
270
271/* [RFC 3414] - 2.6. Key Localization Algorithm */
272enum snmp_code
273snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen)
274{
275	int err;
276	uint32_t  keylen, olen;
277	const EVP_MD *dtype;
278	EVP_MD_CTX ctx;
279	uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
280
281	if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ)
282		return (SNMP_CODE_FAILED);
283
284	memset(user->priv_key, 0, sizeof(user->priv_key));
285	memset(authbuf, 0, sizeof(authbuf));
286
287	err = snmp_digest_init(user, &ctx, &dtype, &keylen);
288	if (err < 0)
289		return (SNMP_CODE_BADDIGEST);
290	else if (err == 0)
291		return (SNMP_CODE_OK);
292
293	memcpy(authbuf, user->auth_key, keylen);
294	memcpy(authbuf + keylen, eid, elen);
295	memcpy(authbuf + keylen + elen, user->auth_key, keylen);
296
297	if (EVP_DigestUpdate(&ctx, authbuf, 2 * keylen + elen) != 1 ||
298	    EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) {
299		EVP_MD_CTX_cleanup(&ctx);
300		return (SNMP_CODE_BADDIGEST);
301	}
302	EVP_MD_CTX_cleanup(&ctx);
303
304	if (user->priv_proto != SNMP_PRIV_NOPRIV)
305		memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key));
306
307	return (SNMP_CODE_OK);
308}
309
310enum snmp_code
311snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange)
312{
313	int32_t i, err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4];
314	uint32_t  keylen, olen;
315	const EVP_MD *dtype;
316	EVP_MD_CTX ctx;
317
318	err = snmp_digest_init(user, &ctx, &dtype, &keylen);
319	if (err < 0)
320		return (SNMP_CODE_BADDIGEST);
321	else if (err == 0)
322		return (SNMP_CODE_OK);
323
324	for (i = 0; i < keylen / 4; i++)
325		rvalue[i] = random();
326
327	memcpy(keychange, user->auth_key, keylen);
328	memcpy(keychange + keylen, rvalue, keylen);
329
330	if (EVP_DigestUpdate(&ctx, keychange, 2 * keylen) != 1 ||
331	    EVP_DigestFinal(&ctx, keychange, &olen) != 1) {
332		EVP_MD_CTX_cleanup(&ctx);
333		return (SNMP_CODE_BADDIGEST);
334	}
335
336	EVP_MD_CTX_cleanup(&ctx);
337	return (SNMP_CODE_OK);
338}
339
340#else /* !HAVE_LIBCRYPTO */
341
342enum snmp_code
343snmp_pdu_calc_digest(struct asn_buf *b __unused, const struct snmp_pdu *pdu,
344    uint8_t *digest __unused)
345{
346	if  (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
347		return (SNMP_CODE_BADSECLEVEL);
348
349
350	return (SNMP_CODE_OK);
351}
352
353enum snmp_code
354snmp_pdu_encrypt(struct asn_buf *b __unused, const struct snmp_pdu *pdu)
355{
356	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
357		return (SNMP_CODE_BADSECLEVEL);
358
359	return (SNMP_CODE_OK);
360}
361
362enum snmp_code
363snmp_pdu_decrypt(struct asn_buf *b __unused, const struct snmp_pdu *pdu)
364{
365	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
366		return (SNMP_CODE_BADSECLEVEL);
367
368	return (SNMP_CODE_OK);
369}
370
371int
372snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused)
373{
374	if (user->auth_proto == SNMP_AUTH_NOAUTH &&
375	    user->priv_proto == SNMP_PRIV_NOPRIV)
376		return (SNMP_CODE_OK);
377
378	errno = EPROTONOSUPPORT;
379
380	return (SNMP_CODE_FAILED);
381}
382
383int
384snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused,
385    uint32_t elen __unused)
386{
387	if (user->auth_proto == SNMP_AUTH_NOAUTH &&
388	    user->priv_proto == SNMP_PRIV_NOPRIV)
389		return (SNMP_CODE_OK);
390
391	errno = EPROTONOSUPPORT;
392
393	return (SNMP_CODE_FAILED);
394}
395
396enum snmp_code
397snmp_calc_keychange(struct snmp_user *user __unused,
398    uint8_t *keychange __unused)
399{
400	errno = EPROTONOSUPPORT;
401	return (SNMP_CODE_FAILED);
402}
403
404#endif /* HAVE_LIBCRYPTO */
405