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