2 * Copyright (c) 2010 The FreeBSD Foundation
4 * This software was developed by Shteryana Sotirova Shopova under
5 * sponsorship from the FreeBSD Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/types.h>
31 #include <sys/socket.h>
38 #elif defined(HAVE_INTTYPES_H)
44 #include <netinet/in.h>
47 #include <openssl/evp.h>
54 #define SNMP_PRIV_AES_IV_SIZ 16
55 #define SNMP_EXTENDED_KEY_SIZ 64
56 #define SNMP_AUTH_KEY_LOOPCNT 1048576
57 #define SNMP_AUTH_BUF_SIZE 72
61 static const uint8_t ipad = 0x36;
62 static const uint8_t opad = 0x5c;
65 snmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx,
66 const EVP_MD **dtype, uint32_t *keylen)
68 if (user->auth_proto == SNMP_AUTH_HMAC_MD5) {
70 *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
71 } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) {
73 *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
74 } else if (user->auth_proto == SNMP_AUTH_NOAUTH)
77 snmp_error("unknown authentication option - %d",
82 if (EVP_DigestInit(ctx, *dtype) != 1)
89 snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest)
91 uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ];
92 uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ];
93 uint32_t i, keylen, olen;
98 ctx = EVP_MD_CTX_new();
100 return (SNMP_CODE_FAILED);
101 err = snmp_digest_init(&pdu->user, ctx, &dtype, &keylen);
103 EVP_MD_CTX_free(ctx);
105 return (SNMP_CODE_BADDIGEST);
107 return (SNMP_CODE_OK);
109 memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest));
110 memcpy(extkey, pdu->user.auth_key, keylen);
111 memset(extkey + keylen, 0, sizeof(extkey) - keylen);
113 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) {
114 key1[i] = extkey[i] ^ ipad;
115 key2[i] = extkey[i] ^ opad;
118 if (EVP_DigestUpdate(ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 ||
119 EVP_DigestUpdate(ctx, pdu->outer_ptr, pdu->outer_len) != 1 ||
120 EVP_DigestFinal(ctx, md, &olen) != 1)
123 if (EVP_DigestInit(ctx, dtype) != 1 ||
124 EVP_DigestUpdate(ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 ||
125 EVP_DigestUpdate(ctx, md, olen) != 1 ||
126 EVP_DigestFinal(ctx, md, &olen) != 1)
129 if (olen < SNMP_USM_AUTH_SIZE) {
130 snmp_error("bad digest size - %d", olen);
131 EVP_MD_CTX_free(ctx);
132 return (SNMP_CODE_BADDIGEST);
135 memcpy(digest, md, SNMP_USM_AUTH_SIZE);
136 EVP_MD_CTX_free(ctx);
137 return (SNMP_CODE_OK);
140 EVP_MD_CTX_free(ctx);
141 return (SNMP_CODE_BADDIGEST);
145 snmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len,
146 const EVP_CIPHER **ctype, uint8_t *piv)
151 if (pdu->user.priv_proto == SNMP_PRIV_DES) {
154 *ctype = EVP_des_cbc();
155 memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
156 for (i = 0; i < 8; i++)
157 piv[i] = piv[i] ^ pdu->user.priv_key[8 + i];
158 } else if (pdu->user.priv_proto == SNMP_PRIV_AES) {
159 *ctype = EVP_aes_128_cfb128();
160 netint = htonl(pdu->engine.engine_boots);
161 memcpy(piv, &netint, sizeof(netint));
162 piv += sizeof(netint);
163 netint = htonl(pdu->engine.engine_time);
164 memcpy(piv, &netint, sizeof(netint));
165 piv += sizeof(netint);
166 memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
167 } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV)
170 snmp_error("unknown privacy option - %d", pdu->user.priv_proto);
178 snmp_pdu_encrypt(const struct snmp_pdu *pdu)
181 uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
182 const EVP_CIPHER *ctype;
185 err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
187 return (SNMP_CODE_EDECRYPT);
189 return (SNMP_CODE_OK);
191 ctx = EVP_CIPHER_CTX_new();
193 return (SNMP_CODE_FAILED);
194 if (EVP_EncryptInit(ctx, ctype, pdu->user.priv_key, iv) != 1)
197 if (EVP_EncryptUpdate(ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
198 pdu->scoped_len) != 1 ||
199 EVP_EncryptFinal(ctx, pdu->scoped_ptr + olen, &olen) != 1)
202 EVP_CIPHER_CTX_free(ctx);
203 return (SNMP_CODE_OK);
206 EVP_CIPHER_CTX_free(ctx);
207 return (SNMP_CODE_FAILED);
211 snmp_pdu_decrypt(const struct snmp_pdu *pdu)
214 uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
215 const EVP_CIPHER *ctype;
218 err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
220 return (SNMP_CODE_EDECRYPT);
222 return (SNMP_CODE_OK);
224 ctx = EVP_CIPHER_CTX_new();
226 return (SNMP_CODE_FAILED);
227 if (EVP_DecryptInit(ctx, ctype, pdu->user.priv_key, iv) != 1 ||
228 EVP_CIPHER_CTX_set_padding(ctx, 0) != 1)
231 if (EVP_DecryptUpdate(ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
232 pdu->scoped_len) != 1 ||
233 EVP_DecryptFinal(ctx, pdu->scoped_ptr + olen, &olen) != 1)
236 EVP_CIPHER_CTX_free(ctx);
237 return (SNMP_CODE_OK);
240 EVP_CIPHER_CTX_free(ctx);
241 return (SNMP_CODE_EDECRYPT);
244 /* [RFC 3414] - A.2. Password to Key Algorithm */
246 snmp_passwd_to_keys(struct snmp_user *user, char *passwd)
248 int err, loop, i, pwdlen;
249 uint32_t keylen, olen;
252 uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
254 if (passwd == NULL || user == NULL)
255 return (SNMP_CODE_FAILED);
257 ctx = EVP_MD_CTX_new();
259 return (SNMP_CODE_FAILED);
261 err = snmp_digest_init(user, ctx, &dtype, &keylen);
263 EVP_MD_CTX_free(ctx);
265 return (SNMP_CODE_BADDIGEST);
267 return (SNMP_CODE_OK);
269 memset(user->auth_key, 0, sizeof(user->auth_key));
270 pwdlen = strlen(passwd);
272 for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) {
273 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++)
274 authbuf[i] = passwd[(loop + i) % pwdlen];
275 if (EVP_DigestUpdate(ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1)
279 if (EVP_DigestFinal(ctx, user->auth_key, &olen) != 1)
282 EVP_MD_CTX_free(ctx);
283 return (SNMP_CODE_OK);
286 EVP_MD_CTX_free(ctx);
287 return (SNMP_CODE_BADDIGEST);
290 /* [RFC 3414] - 2.6. Key Localization Algorithm */
292 snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen)
295 uint32_t keylen, olen;
298 uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
300 if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ)
301 return (SNMP_CODE_FAILED);
303 ctx = EVP_MD_CTX_new();
305 return (SNMP_CODE_FAILED);
307 memset(user->priv_key, 0, sizeof(user->priv_key));
308 memset(authbuf, 0, sizeof(authbuf));
310 err = snmp_digest_init(user, ctx, &dtype, &keylen);
312 EVP_MD_CTX_free(ctx);
314 return (SNMP_CODE_BADDIGEST);
316 return (SNMP_CODE_OK);
318 memcpy(authbuf, user->auth_key, keylen);
319 memcpy(authbuf + keylen, eid, elen);
320 memcpy(authbuf + keylen + elen, user->auth_key, keylen);
322 if (EVP_DigestUpdate(ctx, authbuf, 2 * keylen + elen) != 1 ||
323 EVP_DigestFinal(ctx, user->auth_key, &olen) != 1) {
324 EVP_MD_CTX_free(ctx);
325 return (SNMP_CODE_BADDIGEST);
327 EVP_MD_CTX_free(ctx);
329 if (user->priv_proto != SNMP_PRIV_NOPRIV)
330 memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key));
332 return (SNMP_CODE_OK);
336 snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange)
338 int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4];
339 uint32_t i, keylen, olen;
343 ctx = EVP_MD_CTX_new();
345 return (SNMP_CODE_FAILED);
347 err = snmp_digest_init(user, ctx, &dtype, &keylen);
349 EVP_MD_CTX_free(ctx);
351 return (SNMP_CODE_BADDIGEST);
353 return (SNMP_CODE_OK);
355 for (i = 0; i < keylen / 4; i++)
356 rvalue[i] = random();
358 memcpy(keychange, user->auth_key, keylen);
359 memcpy(keychange + keylen, rvalue, keylen);
361 if (EVP_DigestUpdate(ctx, keychange, 2 * keylen) != 1 ||
362 EVP_DigestFinal(ctx, keychange, &olen) != 1) {
363 EVP_MD_CTX_free(ctx);
364 return (SNMP_CODE_BADDIGEST);
367 EVP_MD_CTX_free(ctx);
368 return (SNMP_CODE_OK);
371 #else /* !HAVE_LIBCRYPTO */
374 snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused)
376 if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
377 return (SNMP_CODE_BADSECLEVEL);
380 return (SNMP_CODE_OK);
384 snmp_pdu_encrypt(const struct snmp_pdu *pdu)
386 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
387 return (SNMP_CODE_BADSECLEVEL);
389 return (SNMP_CODE_OK);
393 snmp_pdu_decrypt(const struct snmp_pdu *pdu)
395 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
396 return (SNMP_CODE_BADSECLEVEL);
398 return (SNMP_CODE_OK);
402 snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused)
404 if (user->auth_proto == SNMP_AUTH_NOAUTH &&
405 user->priv_proto == SNMP_PRIV_NOPRIV)
406 return (SNMP_CODE_OK);
408 errno = EPROTONOSUPPORT;
410 return (SNMP_CODE_FAILED);
414 snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused,
415 uint32_t elen __unused)
417 if (user->auth_proto == SNMP_AUTH_NOAUTH &&
418 user->priv_proto == SNMP_PRIV_NOPRIV)
419 return (SNMP_CODE_OK);
421 errno = EPROTONOSUPPORT;
423 return (SNMP_CODE_FAILED);
427 snmp_calc_keychange(struct snmp_user *user __unused,
428 uint8_t *keychange __unused)
430 errno = EPROTONOSUPPORT;
431 return (SNMP_CODE_FAILED);
434 #endif /* HAVE_LIBCRYPTO */