2 * Copyright (c) 2010 The FreeBSD Foundation
5 * This software was developed by Shteryana Sotirova Shopova under
6 * sponsorship from the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
32 #include <sys/socket.h>
39 #elif defined(HAVE_INTTYPES_H)
45 #include <netinet/in.h>
48 #include <openssl/evp.h>
55 #define SNMP_PRIV_AES_IV_SIZ 16
56 #define SNMP_EXTENDED_KEY_SIZ 64
57 #define SNMP_AUTH_KEY_LOOPCNT 1048576
58 #define SNMP_AUTH_BUF_SIZE 72
62 static const uint8_t ipad = 0x36;
63 static const uint8_t opad = 0x5c;
66 snmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx,
67 const EVP_MD **dtype, uint32_t *keylen)
69 if (user->auth_proto == SNMP_AUTH_HMAC_MD5) {
71 *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
72 } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) {
74 *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
75 } else if (user->auth_proto == SNMP_AUTH_NOAUTH)
78 snmp_error("unknown authentication option - %d",
83 if (EVP_DigestInit(ctx, *dtype) != 1)
90 snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest)
92 uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ];
93 uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ];
94 uint32_t i, keylen, olen;
99 ctx = EVP_MD_CTX_new();
101 return (SNMP_CODE_FAILED);
102 err = snmp_digest_init(&pdu->user, ctx, &dtype, &keylen);
104 EVP_MD_CTX_free(ctx);
106 return (SNMP_CODE_BADDIGEST);
108 return (SNMP_CODE_OK);
110 memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest));
111 memcpy(extkey, pdu->user.auth_key, keylen);
112 memset(extkey + keylen, 0, sizeof(extkey) - keylen);
114 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) {
115 key1[i] = extkey[i] ^ ipad;
116 key2[i] = extkey[i] ^ opad;
119 if (EVP_DigestUpdate(ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 ||
120 EVP_DigestUpdate(ctx, pdu->outer_ptr, pdu->outer_len) != 1 ||
121 EVP_DigestFinal(ctx, md, &olen) != 1)
124 if (EVP_DigestInit(ctx, dtype) != 1 ||
125 EVP_DigestUpdate(ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 ||
126 EVP_DigestUpdate(ctx, md, olen) != 1 ||
127 EVP_DigestFinal(ctx, md, &olen) != 1)
130 if (olen < SNMP_USM_AUTH_SIZE) {
131 snmp_error("bad digest size - %d", olen);
132 EVP_MD_CTX_free(ctx);
133 return (SNMP_CODE_BADDIGEST);
136 memcpy(digest, md, SNMP_USM_AUTH_SIZE);
137 EVP_MD_CTX_free(ctx);
138 return (SNMP_CODE_OK);
141 EVP_MD_CTX_free(ctx);
142 return (SNMP_CODE_BADDIGEST);
146 snmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len,
147 const EVP_CIPHER **ctype, uint8_t *piv)
152 if (pdu->user.priv_proto == SNMP_PRIV_DES) {
155 *ctype = EVP_des_cbc();
156 memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
157 for (i = 0; i < 8; i++)
158 piv[i] = piv[i] ^ pdu->user.priv_key[8 + i];
159 } else if (pdu->user.priv_proto == SNMP_PRIV_AES) {
160 *ctype = EVP_aes_128_cfb128();
161 netint = htonl(pdu->engine.engine_boots);
162 memcpy(piv, &netint, sizeof(netint));
163 piv += sizeof(netint);
164 netint = htonl(pdu->engine.engine_time);
165 memcpy(piv, &netint, sizeof(netint));
166 piv += sizeof(netint);
167 memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
168 } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV)
171 snmp_error("unknown privacy option - %d", pdu->user.priv_proto);
179 snmp_pdu_encrypt(const struct snmp_pdu *pdu)
182 uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
183 const EVP_CIPHER *ctype;
186 err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
188 return (SNMP_CODE_EDECRYPT);
190 return (SNMP_CODE_OK);
192 ctx = EVP_CIPHER_CTX_new();
194 return (SNMP_CODE_FAILED);
195 if (EVP_EncryptInit(ctx, ctype, pdu->user.priv_key, iv) != 1)
198 if (EVP_EncryptUpdate(ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
199 pdu->scoped_len) != 1 ||
200 EVP_EncryptFinal(ctx, pdu->scoped_ptr + olen, &olen) != 1)
203 EVP_CIPHER_CTX_free(ctx);
204 return (SNMP_CODE_OK);
207 EVP_CIPHER_CTX_free(ctx);
208 return (SNMP_CODE_FAILED);
212 snmp_pdu_decrypt(const struct snmp_pdu *pdu)
215 uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
216 const EVP_CIPHER *ctype;
219 err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
221 return (SNMP_CODE_EDECRYPT);
223 return (SNMP_CODE_OK);
225 ctx = EVP_CIPHER_CTX_new();
227 return (SNMP_CODE_FAILED);
228 if (EVP_DecryptInit(ctx, ctype, pdu->user.priv_key, iv) != 1 ||
229 EVP_CIPHER_CTX_set_padding(ctx, 0) != 1)
232 if (EVP_DecryptUpdate(ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
233 pdu->scoped_len) != 1 ||
234 EVP_DecryptFinal(ctx, pdu->scoped_ptr + olen, &olen) != 1)
237 EVP_CIPHER_CTX_free(ctx);
238 return (SNMP_CODE_OK);
241 EVP_CIPHER_CTX_free(ctx);
242 return (SNMP_CODE_EDECRYPT);
245 /* [RFC 3414] - A.2. Password to Key Algorithm */
247 snmp_passwd_to_keys(struct snmp_user *user, char *passwd)
249 int err, loop, i, pwdlen;
250 uint32_t keylen, olen;
253 uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
255 if (passwd == NULL || user == NULL)
256 return (SNMP_CODE_FAILED);
258 ctx = EVP_MD_CTX_new();
260 return (SNMP_CODE_FAILED);
262 err = snmp_digest_init(user, ctx, &dtype, &keylen);
264 EVP_MD_CTX_free(ctx);
266 return (SNMP_CODE_BADDIGEST);
268 return (SNMP_CODE_OK);
270 memset(user->auth_key, 0, sizeof(user->auth_key));
271 pwdlen = strlen(passwd);
273 for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) {
274 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++)
275 authbuf[i] = passwd[(loop + i) % pwdlen];
276 if (EVP_DigestUpdate(ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1)
280 if (EVP_DigestFinal(ctx, user->auth_key, &olen) != 1)
283 EVP_MD_CTX_free(ctx);
284 return (SNMP_CODE_OK);
287 EVP_MD_CTX_free(ctx);
288 return (SNMP_CODE_BADDIGEST);
291 /* [RFC 3414] - 2.6. Key Localization Algorithm */
293 snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen)
296 uint32_t keylen, olen;
299 uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
301 if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ)
302 return (SNMP_CODE_FAILED);
304 ctx = EVP_MD_CTX_new();
306 return (SNMP_CODE_FAILED);
308 memset(user->priv_key, 0, sizeof(user->priv_key));
309 memset(authbuf, 0, sizeof(authbuf));
311 err = snmp_digest_init(user, ctx, &dtype, &keylen);
313 EVP_MD_CTX_free(ctx);
315 return (SNMP_CODE_BADDIGEST);
317 return (SNMP_CODE_OK);
319 memcpy(authbuf, user->auth_key, keylen);
320 memcpy(authbuf + keylen, eid, elen);
321 memcpy(authbuf + keylen + elen, user->auth_key, keylen);
323 if (EVP_DigestUpdate(ctx, authbuf, 2 * keylen + elen) != 1 ||
324 EVP_DigestFinal(ctx, user->auth_key, &olen) != 1) {
325 EVP_MD_CTX_free(ctx);
326 return (SNMP_CODE_BADDIGEST);
328 EVP_MD_CTX_free(ctx);
330 if (user->priv_proto != SNMP_PRIV_NOPRIV)
331 memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key));
333 return (SNMP_CODE_OK);
337 snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange)
339 int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4];
340 uint32_t i, keylen, olen;
344 ctx = EVP_MD_CTX_new();
346 return (SNMP_CODE_FAILED);
348 err = snmp_digest_init(user, ctx, &dtype, &keylen);
350 EVP_MD_CTX_free(ctx);
352 return (SNMP_CODE_BADDIGEST);
354 return (SNMP_CODE_OK);
356 for (i = 0; i < keylen / 4; i++)
357 rvalue[i] = random();
359 memcpy(keychange, user->auth_key, keylen);
360 memcpy(keychange + keylen, rvalue, keylen);
362 if (EVP_DigestUpdate(ctx, keychange, 2 * keylen) != 1 ||
363 EVP_DigestFinal(ctx, keychange, &olen) != 1) {
364 EVP_MD_CTX_free(ctx);
365 return (SNMP_CODE_BADDIGEST);
368 EVP_MD_CTX_free(ctx);
369 return (SNMP_CODE_OK);
372 #else /* !HAVE_LIBCRYPTO */
375 snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused)
377 if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
378 return (SNMP_CODE_BADSECLEVEL);
381 return (SNMP_CODE_OK);
385 snmp_pdu_encrypt(const struct snmp_pdu *pdu)
387 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
388 return (SNMP_CODE_BADSECLEVEL);
390 return (SNMP_CODE_OK);
394 snmp_pdu_decrypt(const struct snmp_pdu *pdu)
396 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
397 return (SNMP_CODE_BADSECLEVEL);
399 return (SNMP_CODE_OK);
403 snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused)
405 if (user->auth_proto == SNMP_AUTH_NOAUTH &&
406 user->priv_proto == SNMP_PRIV_NOPRIV)
407 return (SNMP_CODE_OK);
409 errno = EPROTONOSUPPORT;
411 return (SNMP_CODE_FAILED);
415 snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused,
416 uint32_t elen __unused)
418 if (user->auth_proto == SNMP_AUTH_NOAUTH &&
419 user->priv_proto == SNMP_PRIV_NOPRIV)
420 return (SNMP_CODE_OK);
422 errno = EPROTONOSUPPORT;
424 return (SNMP_CODE_FAILED);
428 snmp_calc_keychange(struct snmp_user *user __unused,
429 uint8_t *keychange __unused)
431 errno = EPROTONOSUPPORT;
432 return (SNMP_CODE_FAILED);
435 #endif /* HAVE_LIBCRYPTO */