Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Main Page | Modules | Data Structures | File List | Data Fields | Related Pages | Examples

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 
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 */

Generated on Fri Dec 30 13:47:45 2005 for net-snmp by  doxygen 1.3.9.1

Valid CSS!


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.