summaryrefslogtreecommitdiff
path: root/snmplib/keytools.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/keytools.c')
-rw-r--r--snmplib/keytools.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/snmplib/keytools.c b/snmplib/keytools.c
new file mode 100644
index 0000000..1589889
--- /dev/null
+++ b/snmplib/keytools.c
@@ -0,0 +1,596 @@
+/* Portions of this file are subject to the following copyright(s). See
+ * the Net-SNMP's COPYING file for more details and other copyrights
+ * that may apply:
+ */
+/*
+ * Portions of this file are copyrighted by:
+ * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+
+/*
+ * keytools.c
+ */
+
+#include <net-snmp/net-snmp-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 <net-snmp/types.h>
+#include <net-snmp/output_api.h>
+#include <net-snmp/utilities.h>
+
+#include <net-snmp/library/snmp_api.h>
+#ifdef NETSNMP_USE_OPENSSL
+# include <openssl/hmac.h>
+#else
+#ifdef NETSNMP_USE_INTERNAL_MD5
+#include <net-snmp/library/md5.h>
+#endif
+#endif
+
+#ifdef NETSNMP_USE_PKCS11
+#include <security/cryptoki.h>
+#endif
+
+#include <net-snmp/library/scapi.h>
+#include <net-snmp/library/keytools.h>
+
+#include <net-snmp/library/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(const oid * hashtype, u_int hashtype_len,
+ u_char * P, size_t pplen, u_char * Ku, size_t * kulen)
+#if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_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 NETSNMP_USE_OPENSSL
+ EVP_MD_CTX *ctx = (EVP_MD_CTX *)malloc(sizeof(EVP_MD_CTX));
+ unsigned int tmp_len;
+#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) {
+ snmp_log(LOG_ERR, "Error: passphrase chosen is below the length "
+ "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN);
+ snmp_set_detail("The supplied password length is too short.");
+ QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
+ }
+
+
+ /*
+ * Setup for the transform type.
+ */
+#ifdef NETSNMP_USE_OPENSSL
+
+#ifndef NETSNMP_DISABLE_MD5
+ if (ISTRANSFORM(hashtype, HMACMD5Auth))
+ EVP_DigestInit(ctx, EVP_md5());
+ else
+#endif
+ if (ISTRANSFORM(hashtype, HMACSHA1Auth))
+ EVP_DigestInit(ctx, EVP_sha1());
+ else {
+ free(ctx);
+ return (SNMPERR_GENERR);
+ }
+#else
+ MDbegin(&MD);
+#endif /* NETSNMP_USE_OPENSSL */
+
+ while (nbytes > 0) {
+ bufp = buf;
+ for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
+ *bufp++ = P[pindex++ % pplen];
+ }
+#ifdef NETSNMP_USE_OPENSSL
+ EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK);
+#elif NETSNMP_USE_INTERNAL_MD5
+ if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) {
+ rval = SNMPERR_USM_ENCRYPTIONERROR;
+ goto md5_fin;
+ }
+#endif /* NETSNMP_USE_OPENSSL */
+
+ nbytes -= USM_LENGTH_KU_HASHBLOCK;
+ }
+
+#ifdef NETSNMP_USE_OPENSSL
+ tmp_len = *kulen;
+ EVP_DigestFinal(ctx, (unsigned char *) Ku, &tmp_len);
+ *kulen = tmp_len;
+ /*
+ * what about free()
+ */
+#elif NETSNMP_USE_INTERNAL_MD5
+ 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 /* NETSNMP_USE_INTERNAL_MD5 */
+
+
+#ifdef NETSNMP_ENABLE_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 /* NETSNMP_ENABLE_TESTING_CODE */
+
+
+ generate_Ku_quit:
+ memset(buf, 0, sizeof(buf));
+#ifdef NETSNMP_USE_OPENSSL
+ free(ctx);
+#endif
+ return rval;
+
+} /* end generate_Ku() */
+#elif NETSNMP_USE_PKCS11
+{
+ int rval = SNMPERR_SUCCESS;
+
+ /*
+ * 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) {
+ snmp_log(LOG_ERR, "Error: passphrase chosen is below the length "
+ "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN);
+ snmp_set_detail("The supplied password length is too short.");
+ QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
+ }
+
+ /*
+ * Setup for the transform type.
+ */
+
+#ifndef NETSNMP_DISABLE_MD5
+ if (ISTRANSFORM(hashtype, HMACMD5Auth))
+ return pkcs_generate_Ku(CKM_MD5, P, pplen, Ku, kulen);
+ else
+#endif
+ if (ISTRANSFORM(hashtype, HMACSHA1Auth))
+ return pkcs_generate_Ku(CKM_SHA_1, P, pplen, Ku, kulen);
+ else {
+ return (SNMPERR_GENERR);
+ }
+
+ generate_Ku_quit:
+
+ 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(const 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(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11)
+{
+ int rval = SNMPERR_SUCCESS;
+ u_int nbytes = 0;
+ size_t properlength;
+ int iproperlength;
+
+ u_char buf[SNMP_MAXBUF];
+#ifdef NETSNMP_ENABLE_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);
+ }
+
+
+ iproperlength = sc_get_properlength(hashtype, hashtype_len);
+ if (iproperlength == SNMPERR_GENERR)
+ QUITFUN(SNMPERR_GENERR, generate_kul_quit);
+
+ properlength = (size_t) iproperlength;
+
+ 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 NETSNMP_ENABLE_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 /* NETSNMP_ENABLE_TESTING_CODE */
+
+ QUITFUN(rval, generate_kul_quit);
+
+
+ generate_kul_quit:
+ 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(const 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(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11)
+{
+ int rval = SNMPERR_SUCCESS;
+ size_t properlength;
+ size_t nbytes = 0;
+
+ u_char *tmpbuf = NULL;
+
+
+ /*
+ * Sanity check.
+ */
+ if (!kcstring || !kcstring_len)
+ return SNMPERR_GENERR;
+
+ 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(NETSNMP_ENABLE_TESTING_CODE) && defined(RANDOMZEROS)
+ memset(kcstring, 0, nbytes);
+ DEBUGMSG(("encode_keychange",
+ "** Using all zero bits for \"random\" delta of )"
+ "the keychange string! **\n"));
+#else /* !NETSNMP_ENABLE_TESTING_CODE */
+ rval = sc_random(kcstring, &nbytes);
+ QUITFUN(rval, encode_keychange_quit);
+ if ((int) nbytes != properlength) {
+ QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
+ }
+#endif /* !NETSNMP_ENABLE_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++ ^= *newkey++;
+ }
+ }
+
+ encode_keychange_quit:
+ if (rval != SNMPERR_SUCCESS)
+ memset(kcstring, 0, *kcstring_len);
+ SNMP_FREE(tmpbuf);
+
+ 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(const 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(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_PKCS11)
+{
+ int rval = SNMPERR_SUCCESS;
+ size_t properlength = 0;
+ int iproperlength = 0;
+ u_int nbytes = 0;
+
+ u_char *bufp, tmp_buf[SNMP_MAXBUF];
+ size_t tmp_buf_len = SNMP_MAXBUF;
+ 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.
+ */
+ iproperlength = sc_get_properlength(hashtype, hashtype_len);
+ if (iproperlength == SNMPERR_GENERR)
+ QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
+
+ properlength = (size_t) iproperlength;
+
+ 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++ ^= *bufp++;
+ }
+ }
+
+ decode_keychange_quit:
+ if (rval != SNMPERR_SUCCESS) {
+ memset(newkey, 0, properlength);
+ }
+ memset(tmp_buf, 0, SNMP_MAXBUF);
+ if (tmpbuf != NULL)
+ SNMP_FREE(tmpbuf);
+
+ return rval;
+
+} /* end decode_keychange() */
+
+#else
+_KEYTOOLS_NOT_AVAILABLE
+#endif /* internal or openssl */