Mercurial > flash_v2
diff packages/net/snmp/lib/current/src/keytools.c @ 103:95f3e12a6327 ecos-sw-2000-06-23
Merge from eCos master repository on 2000-06-23-16:41:10-BST
| author | jlarmour |
|---|---|
| date | Fri, 23 Jun 2000 17:06:31 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/snmp/lib/current/src/keytools.c @@ -0,0 +1,636 @@ +//========================================================================== +// +// ./lib/current/src/keytools.c +// +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####UCDSNMPCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from the UCD-SNMP +// project, <http://ucd-snmp.ucdavis.edu/> from the University of +// California at Davis, which was originally based on the Carnegie Mellon +// University SNMP implementation. Portions of this software are therefore +// covered by the appropriate copyright disclaimers included herein. +// +// The release used was version 4.1.2 of May 2000. "ucd-snmp-4.1.2" +// ------------------------------------------- +// +//####UCDSNMPCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): hmt +// Contributors: hmt +// Date: 2000-05-30 +// Purpose: Port of UCD-SNMP distribution to eCos. +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +/******************************************************************** + Copyright 1989, 1991, 1992 by Carnegie Mellon University + + Derivative Work - +Copyright 1996, 1998, 1999, 2000 The Regents of the University of California + + All Rights Reserved + +Permission to use, copy, modify and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies and +that both that copyright notice and this permission notice appear in +supporting documentation, and that the name of CMU and The Regents of +the University of California not be used in advertising or publicity +pertaining to distribution of the software without specific written +permission. + +CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR +THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*********************************************************************/ +/* + * keytools.c + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/types.h> +#if HAVE_WINSOCK_H +#include <winsock.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include "asn1.h" +#include "snmp_api.h" +#ifdef USE_OPENSSL +# include <openssl/hmac.h> +#else +#ifdef USE_INTERNAL_MD5 +#include "md5.h" +#endif +#endif + +#include "scapi.h" +#include "keytools.h" +#include "tools.h" +#include "snmp_debug.h" +#include "snmp_logging.h" + +#include "transform_oids.h" + +/*******************************************************************-o-****** + * generate_Ku + * + * Parameters: + * *hashtype MIB OID for the transform type for hashing. + * hashtype_len Length of OID value. + * *P Pre-allocated bytes of passpharase. + * pplen Length of passphrase. + * *Ku Buffer to contain Ku. + * *kulen Length of Ku buffer. + * + * Returns: + * SNMPERR_SUCCESS Success. + * SNMPERR_GENERR All errors. + * + * + * Convert a passphrase into a master user key, Ku, according to the + * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM) + * as follows: + * + * Expand the passphrase to fill the passphrase buffer space, if necessary, + * concatenation as many duplicates as possible of P to itself. If P is + * larger than the buffer space, truncate it to fit. + * + * Then hash the result with the given hashtype transform. Return + * the result as Ku. + * + * If successful, kulen contains the size of the hash written to Ku. + * + * NOTE Passphrases less than USM_LENGTH_P_MIN characters in length + * cause an error to be returned. + * (Punt this check to the cmdline apps? XXX) + */ +int +generate_Ku( oid *hashtype, u_int hashtype_len, + u_char *P, size_t pplen, + u_char *Ku, size_t *kulen) +#if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) +{ + int rval = SNMPERR_SUCCESS, + nbytes = USM_LENGTH_EXPANDED_PASSPHRASE; + + u_int i, pindex = 0; + + u_char buf[USM_LENGTH_KU_HASHBLOCK], + *bufp; + +#ifdef USE_OPENSSL + EVP_MD_CTX *ctx = malloc(sizeof(EVP_MD_CTX)); +#else + MDstruct MD; +#endif + /* + * Sanity check. + */ + if ( !hashtype || !P || !Ku || !kulen + || (*kulen<=0) + || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) + { + QUITFUN(SNMPERR_GENERR, generate_Ku_quit); + } + + if (pplen < USM_LENGTH_P_MIN) { +#ifdef SNMP_TESTING_CODE + snmp_log(LOG_WARNING, "Warning: passphrase chosen is below the length requiremnts of the USM.\n"); +#else + snmp_set_detail("Password length too short."); + QUITFUN(SNMPERR_GENERR, generate_Ku_quit); +#endif + } + + + /* + * Setup for the transform type. + */ +#ifdef USE_OPENSSL + + if (ISTRANSFORM(hashtype, HMACMD5Auth)) + EVP_DigestInit(ctx, EVP_md5()); + else if (ISTRANSFORM(hashtype, HMACSHA1Auth)) + EVP_DigestInit(ctx, EVP_sha1()); + else { + free(ctx); + return (SNMPERR_GENERR); + } +#else + MDbegin(&MD); +#endif /* USE_OPENSSL */ + + while (nbytes > 0) { + bufp = buf; + for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) { + *bufp++ = P[pindex++ % pplen]; + } +#ifdef USE_OPENSSL + EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK); +#else + if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK*8)) { + rval = SNMPERR_USM_ENCRYPTIONERROR; + goto md5_fin; + } +#endif /* USE_OPENSSL */ + + nbytes -= USM_LENGTH_KU_HASHBLOCK; + } + +#ifdef USE_OPENSSL + EVP_DigestFinal(ctx, (unsigned char *) Ku, (unsigned int *) kulen); + /* what about free() */ +#else + if (MDupdate(&MD, buf, 0)) { + rval = SNMPERR_USM_ENCRYPTIONERROR; + goto md5_fin; + } + *kulen = sc_get_properlength(hashtype, hashtype_len); + MDget(&MD, Ku, *kulen); +md5_fin: + memset(&MD, 0, sizeof(MD)); +#endif /* USE_OPENSSL */ + + +#ifdef SNMP_TESTING_CODE + DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P)); + for(i=0; i < *kulen; i++) + DEBUGMSG(("generate_Ku", "%02x",Ku[i])); + DEBUGMSG(("generate_Ku","\n")); +#endif /* SNMP_TESTING_CODE */ + + +generate_Ku_quit: + memset(buf, 0, sizeof(buf)); +#ifdef USE_OPENSSL + free(ctx); +#endif + return rval; + +} /* end generate_Ku() */ + +#else +_KEYTOOLS_NOT_AVAILABLE +#endif /* internal or openssl */ + + + + +/*******************************************************************-o-****** + * generate_kul + * + * Parameters: + * *hashtype + * hashtype_len + * *engineID + * engineID_len + * *Ku Master key for a given user. + * ku_len Length of Ku in bytes. + * *Kul Localized key for a given user at engineID. + * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT). + * + * Returns: + * SNMPERR_SUCCESS Success. + * SNMPERR_GENERR All errors. + * + * + * Ku MUST be the proper length (currently fixed) for the given hashtype. + * + * Upon successful return, Kul contains the localized form of Ku at + * engineID, and the length of the key is stored in kul_len. + * + * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and + * originally documented in: + * U. Blumenthal, N. C. Hien, B. Wijnen, + * "Key Derivation for Network Management Applications", + * IEEE Network Magazine, April/May issue, 1997. + * + * + * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku). + * + * NOTE Localized keys for privacy transforms are generated via + * the authentication transform held by the same usmUser. + * + * XXX An engineID of any length is accepted, even if larger than + * what is spec'ed for the textual convention. + */ +int +generate_kul( oid *hashtype, u_int hashtype_len, + u_char *engineID, size_t engineID_len, + u_char *Ku, size_t ku_len, + u_char *Kul, size_t *kul_len) +#if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) +{ + int rval = SNMPERR_SUCCESS; + u_int nbytes = 0; + size_t properlength; + + u_char buf[SNMP_MAXBUF]; + void *context = NULL; +#ifdef SNMP_TESTING_CODE + int i; +#endif + + + /* + * Sanity check. + */ + if ( !hashtype || !engineID || !Ku || !Kul || !kul_len + || (engineID_len<=0) || (ku_len<=0) || (*kul_len<=0) + || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) + { + QUITFUN(SNMPERR_GENERR, generate_kul_quit); + } + + + properlength = sc_get_properlength(hashtype, hashtype_len); + if (properlength == SNMPERR_GENERR) + QUITFUN(SNMPERR_GENERR, generate_kul_quit); + + + if (((int)*kul_len < properlength) || ((int)ku_len < properlength) ) { + QUITFUN(SNMPERR_GENERR, generate_kul_quit); + } + + /* + * Concatenate Ku and engineID properly, then hash the result. + * Store it in Kul. + */ + nbytes = 0; + memcpy(buf, Ku, properlength); nbytes += properlength; + memcpy(buf+nbytes, engineID, engineID_len); nbytes += engineID_len; + memcpy(buf+nbytes, Ku, properlength); nbytes += properlength; + + rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len); + +#ifdef SNMP_TESTING_CODE + DEBUGMSGTL(("generate_kul", "generating Kul (from Ku): ")); + for(i=0; i < *kul_len; i++) + DEBUGMSG(("generate_kul", "%02x",Kul[i])); + DEBUGMSG(("generate_kul", "keytools\n")); +#endif /* SNMP_TESTING_CODE */ + + QUITFUN(rval, generate_kul_quit); + + +generate_kul_quit: + SNMP_FREE(context); + return rval; + +} /* end generate_kul() */ + +#else +_KEYTOOLS_NOT_AVAILABLE +#endif /* internal or openssl */ + + + + +/*******************************************************************-o-****** + * encode_keychange + * + * Parameters: + * *hashtype MIB OID for the hash transform type. + * hashtype_len Length of the MIB OID hash transform type. + * *oldkey Old key that is used to encodes the new key. + * oldkey_len Length of oldkey in bytes. + * *newkey New key that is encoded using the old key. + * newkey_len Length of new key in bytes. + * *kcstring Buffer to contain the KeyChange TC string. + * *kcstring_len Length of kcstring buffer. + * + * Returns: + * SNMPERR_SUCCESS Success. + * SNMPERR_GENERR All errors. + * + * + * Uses oldkey and acquired random bytes to encode newkey into kcstring + * according to the rules of the KeyChange TC described in RFC 2274, Section 5. + * + * Upon successful return, *kcstring_len contains the length of the + * encoded string. + * + * ASSUMES Old and new key are always equal to each other, although + * this may be less than the transform type hash output + * output length (eg, using KeyChange for a DESPriv key when + * the user also uses SHA1Auth). This also implies that the + * hash placed in the second 1/2 of the key change string + * will be truncated before the XOR'ing when the hash output is + * larger than that 1/2 of the key change string. + * + * *kcstring_len will be returned as exactly twice that same + * length though the input buffer may be larger. + * + * XXX FIX: Does not handle varibable length keys. + * XXX FIX: Does not handle keys larger than the hash algorithm used. + */ +int +encode_keychange( oid *hashtype, u_int hashtype_len, + u_char *oldkey, size_t oldkey_len, + u_char *newkey, size_t newkey_len, + u_char *kcstring, size_t *kcstring_len) +#if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) +{ + int rval = SNMPERR_SUCCESS; + size_t properlength; + size_t nbytes = 0; + + u_char *tmpbuf = NULL; + void *context = NULL; + + + /* + * Sanity check. + */ + if ( !hashtype || !oldkey || !newkey || !kcstring || !kcstring_len + || (oldkey_len<=0) || (newkey_len<=0) || (*kcstring_len<=0) + || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) + { + QUITFUN(SNMPERR_GENERR, encode_keychange_quit); + } + + /* + * Setup for the transform type. + */ + properlength = sc_get_properlength(hashtype, hashtype_len); + if (properlength == SNMPERR_GENERR) + QUITFUN(SNMPERR_GENERR, encode_keychange_quit); + + if ( (oldkey_len != newkey_len) || (*kcstring_len < (2*oldkey_len)) ) + { + QUITFUN(SNMPERR_GENERR, encode_keychange_quit); + } + + properlength = SNMP_MIN((int)oldkey_len, properlength); + + /* + * Use the old key and some random bytes to encode the new key + * in the KeyChange TC format: + * . Get random bytes (store in first half of kcstring), + * . Hash (oldkey | random_bytes) (into second half of kcstring), + * . XOR hash and newkey (into second half of kcstring). + * + * Getting the wrong number of random bytes is considered an error. + */ + nbytes = properlength; + +#if defined(SNMP_TESTING_CODE) && defined(RANDOMZEROS) + memset(kcstring, 0, nbytes); + DEBUGMSG(("encode_keychange", + "** Using all zero bits for \"random\" delta of )" + "the keychange string! **\n")); +#else /* !SNMP_TESTING_CODE */ + rval = sc_random(kcstring, &nbytes); + QUITFUN(rval, encode_keychange_quit); + if ((int)nbytes != properlength) { + QUITFUN(SNMPERR_GENERR, encode_keychange_quit); + } +#endif /* !SNMP_TESTING_CODE */ + + tmpbuf = (u_char *)malloc(properlength*2); + if (tmpbuf) { + memcpy(tmpbuf, oldkey, properlength); + memcpy(tmpbuf+properlength, kcstring, properlength); + + *kcstring_len -= properlength; + rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength*2, + kcstring+properlength, kcstring_len); + + QUITFUN(rval, encode_keychange_quit); + + *kcstring_len = (properlength*2); + + kcstring += properlength; + nbytes = 0; + while ((int)(nbytes++) < properlength) { + *kcstring++ = *kcstring ^ *newkey++; + } + } + +encode_keychange_quit: + if (rval != SNMPERR_SUCCESS) memset(kcstring, 0, *kcstring_len); + SNMP_FREE(tmpbuf); + SNMP_FREE(context); + + return rval; + +} /* end encode_keychange() */ + +#else +_KEYTOOLS_NOT_AVAILABLE +#endif /* internal or openssl */ + + + + +/*******************************************************************-o-****** + * decode_keychange + * + * Parameters: + * *hashtype MIB OID of the hash transform to use. + * hashtype_len Length of the hash transform MIB OID. + * *oldkey Old key that is used to encode the new key. + * oldkey_len Length of oldkey in bytes. + * *kcstring Encoded KeyString buffer containing the new key. + * kcstring_len Length of kcstring in bytes. + * *newkey Buffer to hold the extracted new key. + * *newkey_len Length of newkey in bytes. + * + * Returns: + * SNMPERR_SUCCESS Success. + * SNMPERR_GENERR All errors. + * + * + * Decodes a string of bits encoded according to the KeyChange TC described + * in RFC 2274, Section 5. The new key is extracted from *kcstring with + * the aid of the old key. + * + * Upon successful return, *newkey_len contains the length of the new key. + * + * + * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer, + * although this length may be less than the hash transform + * output. Thus the new key length will be equal to the old + * key length. + */ + +/* XXX: if the newkey is not long enough, it should be freed and remalloced */ +int +decode_keychange( oid *hashtype, u_int hashtype_len, + u_char *oldkey, size_t oldkey_len, + u_char *kcstring, size_t kcstring_len, + u_char *newkey, size_t *newkey_len) +#if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) +{ + int rval = SNMPERR_SUCCESS; + size_t properlength = 0; + u_int nbytes = 0; + + u_char *bufp, + tmp_buf[SNMP_MAXBUF]; + size_t tmp_buf_len = SNMP_MAXBUF; + void *context = NULL; + u_char *tmpbuf = NULL; + + + + /* + * Sanity check. + */ + if ( !hashtype || !oldkey || !kcstring || !newkey || !newkey_len + || (oldkey_len<=0) || (kcstring_len<=0) || (*newkey_len<=0) + || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) + { + QUITFUN(SNMPERR_GENERR, decode_keychange_quit); + } + + + /* + * Setup for the transform type. + */ + properlength = sc_get_properlength(hashtype, hashtype_len); + if (properlength == SNMPERR_GENERR) + QUITFUN(SNMPERR_GENERR, decode_keychange_quit); + + + if ( ((oldkey_len*2) != kcstring_len) || (*newkey_len < oldkey_len) ) + { + QUITFUN(SNMPERR_GENERR, decode_keychange_quit); + } + + properlength = oldkey_len; + *newkey_len = properlength; + + /* + * Use the old key and the given KeyChange TC string to recover + * the new key: + * . Hash (oldkey | random_bytes) (into newkey), + * . XOR hash and encoded (second) half of kcstring (into newkey). + */ + tmpbuf = (u_char *)malloc(properlength*2); + if (tmpbuf) { + memcpy(tmpbuf, oldkey, properlength); + memcpy(tmpbuf+properlength, kcstring, properlength); + + rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength*2, + tmp_buf, &tmp_buf_len); + QUITFUN(rval, decode_keychange_quit); + + memcpy(newkey, tmp_buf, properlength); + bufp = kcstring+properlength; + nbytes = 0; + while ((int)(nbytes++) < properlength) { + *newkey++ = *newkey ^ *bufp++; + } + } + +decode_keychange_quit: + if (rval != SNMPERR_SUCCESS) { + memset(newkey, 0, properlength); + } + memset(tmp_buf, 0, SNMP_MAXBUF); + SNMP_FREE(context); + if (tmpbuf != NULL) SNMP_FREE(tmpbuf); + + return rval; + +} /* end decode_keychange() */ + +#else +_KEYTOOLS_NOT_AVAILABLE +#endif /* internal or openssl */ +
