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