00001 /* Portions of this file are subject to the following copyright(s). See 00002 * the Net-SNMP's COPYING file for more details and other copyrights 00003 * that may apply: 00004 */ 00005 /* 00006 * Portions of this file are copyrighted by: 00007 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. 00008 * Use is subject to license terms specified in the COPYING file 00009 * distributed with the Net-SNMP package. 00010 */ 00011 00012 /* 00013 * keytools.c 00014 */ 00015 00016 #include <net-snmp/net-snmp-config.h> 00017 00018 #include <stdio.h> 00019 #include <sys/types.h> 00020 #if HAVE_WINSOCK_H 00021 #include <winsock.h> 00022 #endif 00023 #ifdef HAVE_NETINET_IN_H 00024 #include <netinet/in.h> 00025 #endif 00026 #ifdef HAVE_STDLIB_H 00027 #include <stdlib.h> 00028 #endif 00029 #if HAVE_STRING_H 00030 #include <string.h> 00031 #else 00032 #include <strings.h> 00033 #endif 00034 00035 #if HAVE_DMALLOC_H 00036 #include <dmalloc.h> 00037 #endif 00038 00039 #include <net-snmp/types.h> 00040 #include <net-snmp/output_api.h> 00041 #include <net-snmp/utilities.h> 00042 00043 #include <net-snmp/library/snmp_api.h> 00044 #ifdef USE_OPENSSL 00045 # include <openssl/hmac.h> 00046 #else 00047 #ifdef USE_INTERNAL_MD5 00048 #include <net-snmp/library/md5.h> 00049 #endif 00050 #endif 00051 00052 #ifdef USE_PKCS 00053 #include <security/cryptoki.h> 00054 #endif 00055 00056 #include <net-snmp/library/scapi.h> 00057 #include <net-snmp/library/keytools.h> 00058 00059 #include <net-snmp/library/transform_oids.h> 00060 00061 /*******************************************************************-o-****** 00062 * generate_Ku 00063 * 00064 * Parameters: 00065 * *hashtype MIB OID for the transform type for hashing. 00066 * hashtype_len Length of OID value. 00067 * *P Pre-allocated bytes of passpharase. 00068 * pplen Length of passphrase. 00069 * *Ku Buffer to contain Ku. 00070 * *kulen Length of Ku buffer. 00071 * 00072 * Returns: 00073 * SNMPERR_SUCCESS Success. 00074 * SNMPERR_GENERR All errors. 00075 * 00076 * 00077 * Convert a passphrase into a master user key, Ku, according to the 00078 * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM) 00079 * as follows: 00080 * 00081 * Expand the passphrase to fill the passphrase buffer space, if necessary, 00082 * concatenation as many duplicates as possible of P to itself. If P is 00083 * larger than the buffer space, truncate it to fit. 00084 * 00085 * Then hash the result with the given hashtype transform. Return 00086 * the result as Ku. 00087 * 00088 * If successful, kulen contains the size of the hash written to Ku. 00089 * 00090 * NOTE Passphrases less than USM_LENGTH_P_MIN characters in length 00091 * cause an error to be returned. 00092 * (Punt this check to the cmdline apps? XXX) 00093 */ 00094 int 00095 generate_Ku(const oid * hashtype, u_int hashtype_len, 00096 u_char * P, size_t pplen, u_char * Ku, size_t * kulen) 00097 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) 00098 { 00099 int rval = SNMPERR_SUCCESS, 00100 nbytes = USM_LENGTH_EXPANDED_PASSPHRASE; 00101 00102 u_int i, pindex = 0; 00103 00104 u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp; 00105 00106 #ifdef USE_OPENSSL 00107 EVP_MD_CTX *ctx = malloc(sizeof(EVP_MD_CTX)); 00108 #else 00109 MDstruct MD; 00110 #endif 00111 /* 00112 * Sanity check. 00113 */ 00114 if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0) 00115 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00116 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00117 } 00118 00119 if (pplen < USM_LENGTH_P_MIN) { 00120 snmp_log(LOG_ERR, "Error: passphrase chosen is below the length " 00121 "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN); 00122 snmp_set_detail("The supplied password length is too short."); 00123 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00124 } 00125 00126 00127 /* 00128 * Setup for the transform type. 00129 */ 00130 #ifdef USE_OPENSSL 00131 00132 #ifndef DISABLE_MD5 00133 if (ISTRANSFORM(hashtype, HMACMD5Auth)) 00134 EVP_DigestInit(ctx, EVP_md5()); 00135 else 00136 #endif 00137 if (ISTRANSFORM(hashtype, HMACSHA1Auth)) 00138 EVP_DigestInit(ctx, EVP_sha1()); 00139 else { 00140 free(ctx); 00141 return (SNMPERR_GENERR); 00142 } 00143 #else 00144 MDbegin(&MD); 00145 #endif /* USE_OPENSSL */ 00146 00147 while (nbytes > 0) { 00148 bufp = buf; 00149 for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) { 00150 *bufp++ = P[pindex++ % pplen]; 00151 } 00152 #ifdef USE_OPENSSL 00153 EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK); 00154 #elif USE_INTERNAL_MD5 00155 if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) { 00156 rval = SNMPERR_USM_ENCRYPTIONERROR; 00157 goto md5_fin; 00158 } 00159 #endif /* USE_OPENSSL */ 00160 00161 nbytes -= USM_LENGTH_KU_HASHBLOCK; 00162 } 00163 00164 #ifdef USE_OPENSSL 00165 EVP_DigestFinal(ctx, (unsigned char *) Ku, (unsigned int *) kulen); 00166 /* 00167 * what about free() 00168 */ 00169 #elif USE_INTERNAL_MD5 00170 if (MDupdate(&MD, buf, 0)) { 00171 rval = SNMPERR_USM_ENCRYPTIONERROR; 00172 goto md5_fin; 00173 } 00174 *kulen = sc_get_properlength(hashtype, hashtype_len); 00175 MDget(&MD, Ku, *kulen); 00176 md5_fin: 00177 memset(&MD, 0, sizeof(MD)); 00178 #endif /* USE_INTERNAL_MD5 */ 00179 00180 00181 #ifdef SNMP_TESTING_CODE 00182 DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P)); 00183 for (i = 0; i < *kulen; i++) 00184 DEBUGMSG(("generate_Ku", "%02x", Ku[i])); 00185 DEBUGMSG(("generate_Ku", "\n")); 00186 #endif /* SNMP_TESTING_CODE */ 00187 00188 00189 generate_Ku_quit: 00190 memset(buf, 0, sizeof(buf)); 00191 #ifdef USE_OPENSSL 00192 free(ctx); 00193 #endif 00194 return rval; 00195 00196 } /* end generate_Ku() */ 00197 #elif USE_PKCS 00198 { 00199 int rval = SNMPERR_SUCCESS; 00200 00201 /* 00202 * Sanity check. 00203 */ 00204 if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0) 00205 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00206 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00207 } 00208 00209 if (pplen < USM_LENGTH_P_MIN) { 00210 snmp_log(LOG_ERR, "Error: passphrase chosen is below the length " 00211 "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN); 00212 snmp_set_detail("The supplied password length is too short."); 00213 QUITFUN(SNMPERR_GENERR, generate_Ku_quit); 00214 } 00215 00216 /* 00217 * Setup for the transform type. 00218 */ 00219 00220 #ifndef DISABLE_MD5 00221 if (ISTRANSFORM(hashtype, HMACMD5Auth)) 00222 return pkcs_generate_Ku(CKM_MD5, P, pplen, Ku, kulen); 00223 else 00224 #endif 00225 if (ISTRANSFORM(hashtype, HMACSHA1Auth)) 00226 return pkcs_generate_Ku(CKM_SHA_1, P, pplen, Ku, kulen); 00227 else { 00228 return (SNMPERR_GENERR); 00229 } 00230 00231 generate_Ku_quit: 00232 00233 return rval; 00234 } /* end generate_Ku() */ 00235 #else 00236 _KEYTOOLS_NOT_AVAILABLE 00237 #endif /* internal or openssl */ 00238 /*******************************************************************-o-****** 00239 * generate_kul 00240 * 00241 * Parameters: 00242 * *hashtype 00243 * hashtype_len 00244 * *engineID 00245 * engineID_len 00246 * *Ku Master key for a given user. 00247 * ku_len Length of Ku in bytes. 00248 * *Kul Localized key for a given user at engineID. 00249 * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT). 00250 * 00251 * Returns: 00252 * SNMPERR_SUCCESS Success. 00253 * SNMPERR_GENERR All errors. 00254 * 00255 * 00256 * Ku MUST be the proper length (currently fixed) for the given hashtype. 00257 * 00258 * Upon successful return, Kul contains the localized form of Ku at 00259 * engineID, and the length of the key is stored in kul_len. 00260 * 00261 * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and 00262 * originally documented in: 00263 * U. Blumenthal, N. C. Hien, B. Wijnen, 00264 * "Key Derivation for Network Management Applications", 00265 * IEEE Network Magazine, April/May issue, 1997. 00266 * 00267 * 00268 * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku). 00269 * 00270 * NOTE Localized keys for privacy transforms are generated via 00271 * the authentication transform held by the same usmUser. 00272 * 00273 * XXX An engineID of any length is accepted, even if larger than 00274 * what is spec'ed for the textual convention. 00275 */ 00276 int 00277 generate_kul(const oid * hashtype, u_int hashtype_len, 00278 u_char * engineID, size_t engineID_len, 00279 u_char * Ku, size_t ku_len, 00280 u_char * Kul, size_t * kul_len) 00281 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) || defined(USE_PKCS) 00282 { 00283 int rval = SNMPERR_SUCCESS; 00284 u_int nbytes = 0; 00285 size_t properlength; 00286 00287 u_char buf[SNMP_MAXBUF]; 00288 #ifdef SNMP_TESTING_CODE 00289 int i; 00290 #endif 00291 00292 00293 /* 00294 * Sanity check. 00295 */ 00296 if (!hashtype || !engineID || !Ku || !Kul || !kul_len 00297 || (engineID_len <= 0) || (ku_len <= 0) || (*kul_len <= 0) 00298 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00299 QUITFUN(SNMPERR_GENERR, generate_kul_quit); 00300 } 00301 00302 00303 properlength = sc_get_properlength(hashtype, hashtype_len); 00304 if (properlength == SNMPERR_GENERR) 00305 QUITFUN(SNMPERR_GENERR, generate_kul_quit); 00306 00307 00308 if (((int) *kul_len < properlength) || ((int) ku_len < properlength)) { 00309 QUITFUN(SNMPERR_GENERR, generate_kul_quit); 00310 } 00311 00312 /* 00313 * Concatenate Ku and engineID properly, then hash the result. 00314 * Store it in Kul. 00315 */ 00316 nbytes = 0; 00317 memcpy(buf, Ku, properlength); 00318 nbytes += properlength; 00319 memcpy(buf + nbytes, engineID, engineID_len); 00320 nbytes += engineID_len; 00321 memcpy(buf + nbytes, Ku, properlength); 00322 nbytes += properlength; 00323 00324 rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len); 00325 00326 #ifdef SNMP_TESTING_CODE 00327 DEBUGMSGTL(("generate_kul", "generating Kul (from Ku): ")); 00328 for (i = 0; i < *kul_len; i++) 00329 DEBUGMSG(("generate_kul", "%02x", Kul[i])); 00330 DEBUGMSG(("generate_kul", "keytools\n")); 00331 #endif /* SNMP_TESTING_CODE */ 00332 00333 QUITFUN(rval, generate_kul_quit); 00334 00335 00336 generate_kul_quit: 00337 return rval; 00338 00339 } /* end generate_kul() */ 00340 00341 #else 00342 _KEYTOOLS_NOT_AVAILABLE 00343 #endif /* internal or openssl */ 00344 /*******************************************************************-o-****** 00345 * encode_keychange 00346 * 00347 * Parameters: 00348 * *hashtype MIB OID for the hash transform type. 00349 * hashtype_len Length of the MIB OID hash transform type. 00350 * *oldkey Old key that is used to encodes the new key. 00351 * oldkey_len Length of oldkey in bytes. 00352 * *newkey New key that is encoded using the old key. 00353 * newkey_len Length of new key in bytes. 00354 * *kcstring Buffer to contain the KeyChange TC string. 00355 * *kcstring_len Length of kcstring buffer. 00356 * 00357 * Returns: 00358 * SNMPERR_SUCCESS Success. 00359 * SNMPERR_GENERR All errors. 00360 * 00361 * 00362 * Uses oldkey and acquired random bytes to encode newkey into kcstring 00363 * according to the rules of the KeyChange TC described in RFC 2274, Section 5. 00364 * 00365 * Upon successful return, *kcstring_len contains the length of the 00366 * encoded string. 00367 * 00368 * ASSUMES Old and new key are always equal to each other, although 00369 * this may be less than the transform type hash output 00370 * output length (eg, using KeyChange for a DESPriv key when 00371 * the user also uses SHA1Auth). This also implies that the 00372 * hash placed in the second 1/2 of the key change string 00373 * will be truncated before the XOR'ing when the hash output is 00374 * larger than that 1/2 of the key change string. 00375 * 00376 * *kcstring_len will be returned as exactly twice that same 00377 * length though the input buffer may be larger. 00378 * 00379 * XXX FIX: Does not handle varibable length keys. 00380 * XXX FIX: Does not handle keys larger than the hash algorithm used. 00381 */ 00382 int 00383 encode_keychange(const oid * hashtype, u_int hashtype_len, 00384 u_char * oldkey, size_t oldkey_len, 00385 u_char * newkey, size_t newkey_len, 00386 u_char * kcstring, size_t * kcstring_len) 00387 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) || defined(USE_PKCS) 00388 { 00389 int rval = SNMPERR_SUCCESS; 00390 size_t properlength; 00391 size_t nbytes = 0; 00392 00393 u_char *tmpbuf = NULL; 00394 00395 00396 /* 00397 * Sanity check. 00398 */ 00399 if (!hashtype || !oldkey || !newkey || !kcstring || !kcstring_len 00400 || (oldkey_len <= 0) || (newkey_len <= 0) || (*kcstring_len <= 0) 00401 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00402 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00403 } 00404 00405 /* 00406 * Setup for the transform type. 00407 */ 00408 properlength = sc_get_properlength(hashtype, hashtype_len); 00409 if (properlength == SNMPERR_GENERR) 00410 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00411 00412 if ((oldkey_len != newkey_len) || (*kcstring_len < (2 * oldkey_len))) { 00413 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00414 } 00415 00416 properlength = SNMP_MIN((int) oldkey_len, properlength); 00417 00418 /* 00419 * Use the old key and some random bytes to encode the new key 00420 * in the KeyChange TC format: 00421 * . Get random bytes (store in first half of kcstring), 00422 * . Hash (oldkey | random_bytes) (into second half of kcstring), 00423 * . XOR hash and newkey (into second half of kcstring). 00424 * 00425 * Getting the wrong number of random bytes is considered an error. 00426 */ 00427 nbytes = properlength; 00428 00429 #if defined(SNMP_TESTING_CODE) && defined(RANDOMZEROS) 00430 memset(kcstring, 0, nbytes); 00431 DEBUGMSG(("encode_keychange", 00432 "** Using all zero bits for \"random\" delta of )" 00433 "the keychange string! **\n")); 00434 #else /* !SNMP_TESTING_CODE */ 00435 rval = sc_random(kcstring, &nbytes); 00436 QUITFUN(rval, encode_keychange_quit); 00437 if ((int) nbytes != properlength) { 00438 QUITFUN(SNMPERR_GENERR, encode_keychange_quit); 00439 } 00440 #endif /* !SNMP_TESTING_CODE */ 00441 00442 tmpbuf = (u_char *) malloc(properlength * 2); 00443 if (tmpbuf) { 00444 memcpy(tmpbuf, oldkey, properlength); 00445 memcpy(tmpbuf + properlength, kcstring, properlength); 00446 00447 *kcstring_len -= properlength; 00448 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength * 2, 00449 kcstring + properlength, kcstring_len); 00450 00451 QUITFUN(rval, encode_keychange_quit); 00452 00453 *kcstring_len = (properlength * 2); 00454 00455 kcstring += properlength; 00456 nbytes = 0; 00457 while ((int) (nbytes++) < properlength) { 00458 *kcstring++ ^= *newkey++; 00459 } 00460 } 00461 00462 encode_keychange_quit: 00463 if (rval != SNMPERR_SUCCESS) 00464 memset(kcstring, 0, *kcstring_len); 00465 SNMP_FREE(tmpbuf); 00466 00467 return rval; 00468 00469 } /* end encode_keychange() */ 00470 00471 #else 00472 _KEYTOOLS_NOT_AVAILABLE 00473 #endif /* internal or openssl */ 00474 /*******************************************************************-o-****** 00475 * decode_keychange 00476 * 00477 * Parameters: 00478 * *hashtype MIB OID of the hash transform to use. 00479 * hashtype_len Length of the hash transform MIB OID. 00480 * *oldkey Old key that is used to encode the new key. 00481 * oldkey_len Length of oldkey in bytes. 00482 * *kcstring Encoded KeyString buffer containing the new key. 00483 * kcstring_len Length of kcstring in bytes. 00484 * *newkey Buffer to hold the extracted new key. 00485 * *newkey_len Length of newkey in bytes. 00486 * 00487 * Returns: 00488 * SNMPERR_SUCCESS Success. 00489 * SNMPERR_GENERR All errors. 00490 * 00491 * 00492 * Decodes a string of bits encoded according to the KeyChange TC described 00493 * in RFC 2274, Section 5. The new key is extracted from *kcstring with 00494 * the aid of the old key. 00495 * 00496 * Upon successful return, *newkey_len contains the length of the new key. 00497 * 00498 * 00499 * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer, 00500 * although this length may be less than the hash transform 00501 * output. Thus the new key length will be equal to the old 00502 * key length. 00503 */ 00504 /* 00505 * XXX: if the newkey is not long enough, it should be freed and remalloced 00506 */ 00507 int 00508 decode_keychange(const oid * hashtype, u_int hashtype_len, 00509 u_char * oldkey, size_t oldkey_len, 00510 u_char * kcstring, size_t kcstring_len, 00511 u_char * newkey, size_t * newkey_len) 00512 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) || defined(USE_PKCS) 00513 { 00514 int rval = SNMPERR_SUCCESS; 00515 size_t properlength = 0; 00516 u_int nbytes = 0; 00517 00518 u_char *bufp, tmp_buf[SNMP_MAXBUF]; 00519 size_t tmp_buf_len = SNMP_MAXBUF; 00520 u_char *tmpbuf = NULL; 00521 00522 00523 00524 /* 00525 * Sanity check. 00526 */ 00527 if (!hashtype || !oldkey || !kcstring || !newkey || !newkey_len 00528 || (oldkey_len <= 0) || (kcstring_len <= 0) || (*newkey_len <= 0) 00529 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) { 00530 QUITFUN(SNMPERR_GENERR, decode_keychange_quit); 00531 } 00532 00533 00534 /* 00535 * Setup for the transform type. 00536 */ 00537 properlength = sc_get_properlength(hashtype, hashtype_len); 00538 if (properlength == SNMPERR_GENERR) 00539 QUITFUN(SNMPERR_GENERR, decode_keychange_quit); 00540 00541 00542 if (((oldkey_len * 2) != kcstring_len) || (*newkey_len < oldkey_len)) { 00543 QUITFUN(SNMPERR_GENERR, decode_keychange_quit); 00544 } 00545 00546 properlength = oldkey_len; 00547 *newkey_len = properlength; 00548 00549 /* 00550 * Use the old key and the given KeyChange TC string to recover 00551 * the new key: 00552 * . Hash (oldkey | random_bytes) (into newkey), 00553 * . XOR hash and encoded (second) half of kcstring (into newkey). 00554 */ 00555 tmpbuf = (u_char *) malloc(properlength * 2); 00556 if (tmpbuf) { 00557 memcpy(tmpbuf, oldkey, properlength); 00558 memcpy(tmpbuf + properlength, kcstring, properlength); 00559 00560 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength * 2, 00561 tmp_buf, &tmp_buf_len); 00562 QUITFUN(rval, decode_keychange_quit); 00563 00564 memcpy(newkey, tmp_buf, properlength); 00565 bufp = kcstring + properlength; 00566 nbytes = 0; 00567 while ((int) (nbytes++) < properlength) { 00568 *newkey++ ^= *bufp++; 00569 } 00570 } 00571 00572 decode_keychange_quit: 00573 if (rval != SNMPERR_SUCCESS) { 00574 memset(newkey, 0, properlength); 00575 } 00576 memset(tmp_buf, 0, SNMP_MAXBUF); 00577 if (tmpbuf != NULL) 00578 SNMP_FREE(tmpbuf); 00579 00580 return rval; 00581 00582 } /* end decode_keychange() */ 00583 00584 #else 00585 _KEYTOOLS_NOT_AVAILABLE 00586 #endif /* internal or openssl */
1.3.9.1
Last modified: Thursday, 01-Mar-2007 16:20:06 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.