snmpcrypto.c revision 276677
1204076Spjd/*- 2204076Spjd * Copyright (c) 2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Shteryana Sotirova Shopova under 6204076Spjd * sponsorship from the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd * 29204076Spjd * $FreeBSD: stable/10/contrib/bsnmp/lib/snmpcrypto.c 276677 2015-01-05 00:12:47Z ngie $ 30204076Spjd */ 31204076Spjd#include <sys/types.h> 32204076Spjd#include <sys/socket.h> 33204076Spjd#include <stdio.h> 34204076Spjd#include <stdlib.h> 35204076Spjd#include <stddef.h> 36204076Spjd#include <stdarg.h> 37204076Spjd#ifdef HAVE_STDINT_H 38204076Spjd#include <stdint.h> 39204076Spjd#elif defined(HAVE_INTTYPES_H) 40204076Spjd#include <inttypes.h> 41204076Spjd#endif 42204076Spjd#include <string.h> 43204076Spjd#include <ctype.h> 44204076Spjd#include <errno.h> 45204076Spjd#include <netinet/in.h> 46204076Spjd 47204076Spjd#ifdef HAVE_LIBCRYPTO 48204076Spjd#include <openssl/evp.h> 49204076Spjd#endif 50204076Spjd 51204076Spjd#include "asn1.h" 52204076Spjd#include "snmp.h" 53204076Spjd#include "snmppriv.h" 54204076Spjd 55204076Spjd#define SNMP_PRIV_AES_IV_SIZ 16 56204076Spjd#define SNMP_EXTENDED_KEY_SIZ 64 57204076Spjd#define SNMP_AUTH_KEY_LOOPCNT 1048576 58204076Spjd#define SNMP_AUTH_BUF_SIZE 72 59204076Spjd 60204076Spjdstatic const uint8_t ipad = 0x36; 61204076Spjdstatic const uint8_t opad = 0x5c; 62204076Spjd 63204076Spjd#ifdef HAVE_LIBCRYPTO 64204076Spjd 65204076Spjdstatic int32_t 66204076Spjdsnmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx, 67204076Spjd const EVP_MD **dtype, uint32_t *keylen) 68204076Spjd{ 69204076Spjd if (user->auth_proto == SNMP_AUTH_HMAC_MD5) { 70204076Spjd *dtype = EVP_md5(); 71204076Spjd *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; 72204076Spjd } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) { 73204076Spjd *dtype = EVP_sha1(); 74204076Spjd *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; 75204076Spjd } else if (user->auth_proto == SNMP_AUTH_NOAUTH) 76204076Spjd return (0); 77204076Spjd else { 78204076Spjd snmp_error("unknown authentication option - %d", 79204076Spjd user->auth_proto); 80204076Spjd return (-1); 81204076Spjd } 82204076Spjd 83204076Spjd if (EVP_DigestInit(ctx, *dtype) != 1) 84204076Spjd return (-1); 85204076Spjd 86204076Spjd return (1); 87204076Spjd} 88204076Spjd 89204076Spjdenum snmp_code 90204076Spjdsnmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest) 91204076Spjd{ 92204076Spjd uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ]; 93204076Spjd uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ]; 94204076Spjd uint32_t i, keylen, olen; 95204076Spjd int32_t err; 96204076Spjd const EVP_MD *dtype; 97204076Spjd EVP_MD_CTX ctx; 98204076Spjd 99209179Spjd err = snmp_digest_init(&pdu->user, &ctx, &dtype, &keylen); 100204076Spjd if (err < 0) 101204076Spjd return (SNMP_CODE_BADDIGEST); 102204076Spjd else if (err == 0) 103204076Spjd return (SNMP_CODE_OK); 104204076Spjd 105204076Spjd memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest)); 106204076Spjd memcpy(extkey, pdu->user.auth_key, keylen); 107204076Spjd memset(extkey + keylen, 0, sizeof(extkey) - keylen); 108204076Spjd 109204076Spjd for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) { 110204076Spjd key1[i] = extkey[i] ^ ipad; 111204076Spjd key2[i] = extkey[i] ^ opad; 112204076Spjd } 113204076Spjd 114204076Spjd if (EVP_DigestUpdate(&ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 || 115204076Spjd EVP_DigestUpdate(&ctx, pdu->outer_ptr, pdu->outer_len) != 1 || 116204076Spjd EVP_DigestFinal(&ctx, md, &olen) != 1) 117204076Spjd goto failed; 118204076Spjd 119204076Spjd if (EVP_DigestInit(&ctx, dtype) != 1 || 120204076Spjd EVP_DigestUpdate(&ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 || 121207343Spjd EVP_DigestUpdate(&ctx, md, olen) != 1 || 122204076Spjd EVP_DigestFinal(&ctx, md, &olen) != 1) 123204076Spjd goto failed; 124204076Spjd 125204076Spjd if (olen < SNMP_USM_AUTH_SIZE) { 126204076Spjd snmp_error("bad digest size - %d", olen); 127204076Spjd EVP_MD_CTX_cleanup(&ctx); 128204076Spjd return (SNMP_CODE_BADDIGEST); 129204076Spjd } 130204076Spjd 131204076Spjd memcpy(digest, md, SNMP_USM_AUTH_SIZE); 132204076Spjd EVP_MD_CTX_cleanup(&ctx); 133204076Spjd return (SNMP_CODE_OK); 134204076Spjd 135204076Spjdfailed: 136204076Spjd EVP_MD_CTX_cleanup(&ctx); 137204076Spjd return (SNMP_CODE_BADDIGEST); 138204076Spjd} 139204076Spjd 140204076Spjdstatic int32_t 141204076Spjdsnmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len, 142204076Spjd const EVP_CIPHER **ctype, uint8_t *piv) 143204076Spjd{ 144204076Spjd int i; 145204076Spjd uint32_t netint; 146204076Spjd 147204076Spjd if (pdu->user.priv_proto == SNMP_PRIV_DES) { 148204076Spjd if (len % 8 != 0) 149204076Spjd return (-1); 150204076Spjd *ctype = EVP_des_cbc(); 151204076Spjd memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); 152204076Spjd for (i = 0; i < 8; i++) 153204076Spjd piv[i] = piv[i] ^ pdu->user.priv_key[8 + i]; 154204076Spjd } else if (pdu->user.priv_proto == SNMP_PRIV_AES) { 155204076Spjd *ctype = EVP_aes_128_cfb128(); 156204076Spjd netint = htonl(pdu->engine.engine_boots); 157204076Spjd memcpy(piv, &netint, sizeof(netint)); 158209179Spjd piv += sizeof(netint); 159204076Spjd netint = htonl(pdu->engine.engine_time); 160204076Spjd memcpy(piv, &netint, sizeof(netint)); 161204076Spjd piv += sizeof(netint); 162204076Spjd memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); 163204076Spjd } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV) 164204076Spjd return (0); 165204076Spjd else { 166204076Spjd snmp_error("unknown privacy option - %d", pdu->user.priv_proto); 167204076Spjd return (-1); 168204076Spjd } 169204076Spjd 170204076Spjd return (1); 171204076Spjd} 172204076Spjd 173204076Spjdenum snmp_code 174204076Spjdsnmp_pdu_encrypt(const struct snmp_pdu *pdu) 175204076Spjd{ 176204076Spjd int32_t err, olen; 177209179Spjd uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; 178204076Spjd const EVP_CIPHER *ctype; 179204076Spjd EVP_CIPHER_CTX ctx; 180204076Spjd 181204076Spjd err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv); 182204076Spjd if (err < 0) 183204076Spjd return (SNMP_CODE_EDECRYPT); 184204076Spjd else if (err == 0) 185204076Spjd return (SNMP_CODE_OK); 186209179Spjd 187209179Spjd if (EVP_EncryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1) 188204076Spjd return (SNMP_CODE_FAILED); 189204076Spjd 190204076Spjd if (EVP_EncryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, 191204076Spjd pdu->scoped_len) != 1 || 192204076Spjd EVP_EncryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { 193204076Spjd EVP_CIPHER_CTX_cleanup(&ctx); 194204076Spjd return (SNMP_CODE_FAILED); 195204076Spjd } 196204076Spjd 197204076Spjd EVP_CIPHER_CTX_cleanup(&ctx); 198204076Spjd return (SNMP_CODE_OK); 199204076Spjd} 200204076Spjd 201204076Spjdenum snmp_code 202204076Spjdsnmp_pdu_decrypt(const struct snmp_pdu *pdu) 203204076Spjd{ 204204076Spjd int32_t err, olen; 205204076Spjd uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; 206204076Spjd const EVP_CIPHER *ctype; 207209179Spjd EVP_CIPHER_CTX ctx; 208204076Spjd 209204076Spjd err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv); 210204076Spjd if (err < 0) 211204076Spjd return (SNMP_CODE_EDECRYPT); 212204076Spjd else if (err == 0) 213204076Spjd return (SNMP_CODE_OK); 214204076Spjd 215204076Spjd if (EVP_DecryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1 || 216204076Spjd EVP_CIPHER_CTX_set_padding(&ctx, 0) != 1) 217204076Spjd return (SNMP_CODE_EDECRYPT); 218204076Spjd 219209179Spjd if (EVP_DecryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, 220204076Spjd pdu->scoped_len) != 1 || 221209179Spjd EVP_DecryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { 222209179Spjd EVP_CIPHER_CTX_cleanup(&ctx); 223204076Spjd return (SNMP_CODE_EDECRYPT); 224204076Spjd } 225209179Spjd 226204076Spjd EVP_CIPHER_CTX_cleanup(&ctx); 227 return (SNMP_CODE_OK); 228} 229 230/* [RFC 3414] - A.2. Password to Key Algorithm */ 231enum snmp_code 232snmp_passwd_to_keys(struct snmp_user *user, char *passwd) 233{ 234 int err, loop, i, pwdlen; 235 uint32_t keylen, olen; 236 const EVP_MD *dtype; 237 EVP_MD_CTX ctx; 238 uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; 239 240 if (passwd == NULL || user == NULL) 241 return (SNMP_CODE_FAILED); 242 243 err = snmp_digest_init(user, &ctx, &dtype, &keylen); 244 if (err < 0) 245 return (SNMP_CODE_BADDIGEST); 246 else if (err == 0) 247 return (SNMP_CODE_OK); 248 249 memset(user->auth_key, 0, sizeof(user->auth_key)); 250 pwdlen = strlen(passwd); 251 252 for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) { 253 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) 254 authbuf[i] = passwd[(loop + i) % pwdlen]; 255 if (EVP_DigestUpdate(&ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1) 256 goto failed; 257 } 258 259 if (EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) 260 goto failed; 261 262 EVP_MD_CTX_cleanup(&ctx); 263 return (SNMP_CODE_OK); 264 265failed: 266 EVP_MD_CTX_cleanup(&ctx); 267 return (SNMP_CODE_BADDIGEST); 268} 269 270/* [RFC 3414] - 2.6. Key Localization Algorithm */ 271enum snmp_code 272snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen) 273{ 274 int err; 275 uint32_t keylen, olen; 276 const EVP_MD *dtype; 277 EVP_MD_CTX ctx; 278 uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; 279 280 if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ) 281 return (SNMP_CODE_FAILED); 282 283 memset(user->priv_key, 0, sizeof(user->priv_key)); 284 memset(authbuf, 0, sizeof(authbuf)); 285 286 err = snmp_digest_init(user, &ctx, &dtype, &keylen); 287 if (err < 0) 288 return (SNMP_CODE_BADDIGEST); 289 else if (err == 0) 290 return (SNMP_CODE_OK); 291 292 memcpy(authbuf, user->auth_key, keylen); 293 memcpy(authbuf + keylen, eid, elen); 294 memcpy(authbuf + keylen + elen, user->auth_key, keylen); 295 296 if (EVP_DigestUpdate(&ctx, authbuf, 2 * keylen + elen) != 1 || 297 EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) { 298 EVP_MD_CTX_cleanup(&ctx); 299 return (SNMP_CODE_BADDIGEST); 300 } 301 EVP_MD_CTX_cleanup(&ctx); 302 303 if (user->priv_proto != SNMP_PRIV_NOPRIV) 304 memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key)); 305 306 return (SNMP_CODE_OK); 307} 308 309enum snmp_code 310snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange) 311{ 312 int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4]; 313 uint32_t i, keylen, olen; 314 const EVP_MD *dtype; 315 EVP_MD_CTX ctx; 316 317 err = snmp_digest_init(user, &ctx, &dtype, &keylen); 318 if (err < 0) 319 return (SNMP_CODE_BADDIGEST); 320 else if (err == 0) 321 return (SNMP_CODE_OK); 322 323 for (i = 0; i < keylen / 4; i++) 324 rvalue[i] = random(); 325 326 memcpy(keychange, user->auth_key, keylen); 327 memcpy(keychange + keylen, rvalue, keylen); 328 329 if (EVP_DigestUpdate(&ctx, keychange, 2 * keylen) != 1 || 330 EVP_DigestFinal(&ctx, keychange, &olen) != 1) { 331 EVP_MD_CTX_cleanup(&ctx); 332 return (SNMP_CODE_BADDIGEST); 333 } 334 335 EVP_MD_CTX_cleanup(&ctx); 336 return (SNMP_CODE_OK); 337} 338 339#else /* !HAVE_LIBCRYPTO */ 340 341enum snmp_code 342snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused) 343{ 344 if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH) 345 return (SNMP_CODE_BADSECLEVEL); 346 347 348 return (SNMP_CODE_OK); 349} 350 351enum snmp_code 352snmp_pdu_encrypt(const struct snmp_pdu *pdu) 353{ 354 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) 355 return (SNMP_CODE_BADSECLEVEL); 356 357 return (SNMP_CODE_OK); 358} 359 360enum snmp_code 361snmp_pdu_decrypt(const struct snmp_pdu *pdu) 362{ 363 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) 364 return (SNMP_CODE_BADSECLEVEL); 365 366 return (SNMP_CODE_OK); 367} 368 369enum snmp_code 370snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused) 371{ 372 if (user->auth_proto == SNMP_AUTH_NOAUTH && 373 user->priv_proto == SNMP_PRIV_NOPRIV) 374 return (SNMP_CODE_OK); 375 376 errno = EPROTONOSUPPORT; 377 378 return (SNMP_CODE_FAILED); 379} 380 381enum snmp_code 382snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused, 383 uint32_t elen __unused) 384{ 385 if (user->auth_proto == SNMP_AUTH_NOAUTH && 386 user->priv_proto == SNMP_PRIV_NOPRIV) 387 return (SNMP_CODE_OK); 388 389 errno = EPROTONOSUPPORT; 390 391 return (SNMP_CODE_FAILED); 392} 393 394enum snmp_code 395snmp_calc_keychange(struct snmp_user *user __unused, 396 uint8_t *keychange __unused) 397{ 398 errno = EPROTONOSUPPORT; 399 return (SNMP_CODE_FAILED); 400} 401 402#endif /* HAVE_LIBCRYPTO */ 403