/* 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. */ /* * scapi.c * */ #include #include #if HAVE_WINSOCK_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #if TIME_WITH_SYS_TIME # ifdef WIN32 # include # else # include # endif # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_NETINET_IN_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #ifdef USE_INTERNAL_MD5 #include #endif #include #include #include #include #include #include #include #include #ifdef USE_OPENSSL #include #include #include #include #ifdef HAVE_AES #include #endif #ifndef DISABLE_DES #ifdef STRUCT_DES_KS_STRUCT_HAS_WEAK_KEY /* these are older names for newer structures that exist in openssl .9.7 */ #define DES_key_schedule des_key_schedule #define DES_cblock des_cblock #define DES_key_sched des_key_sched #define DES_ncbc_encrypt des_ncbc_encrypt #define DES_cbc_encrypt des_cbc_encrypt #define OLD_DES #endif #endif #endif /* HAVE_OPENSSL */ #ifdef USE_PKCS #include #endif #ifdef QUITFUN #undef QUITFUN #define QUITFUN(e, l) \ if (e != SNMPERR_SUCCESS) { \ rval = SNMPERR_SC_GENERAL_FAILURE; \ goto l ; \ } #endif /* * sc_get_properlength(oid *hashtype, u_int hashtype_len): * * Given a hashing type ("hashtype" and its length hashtype_len), return * the length of the hash result. * * Returns either the length or SNMPERR_GENERR for an unknown hashing type. */ int sc_get_properlength(const oid * hashtype, u_int hashtype_len) { DEBUGTRACE; /* * Determine transform type hash length. */ #ifndef DISABLE_MD5 if (ISTRANSFORM(hashtype, HMACMD5Auth)) { return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACMD5); } else #endif if (ISTRANSFORM(hashtype, HMACSHA1Auth)) { return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACSHA1); } return SNMPERR_GENERR; } int sc_get_proper_priv_length(const oid * privtype, u_int privtype_len) { int properlength = 0; #ifndef DISABLE_DES if (ISTRANSFORM(privtype, DESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); } #endif #ifdef HAVE_AES if (ISTRANSFORM(privtype, AESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES); } #endif return properlength; } /*******************************************************************-o-****** * sc_init * * Returns: * SNMPERR_SUCCESS Success. */ int sc_init(void) { int rval = SNMPERR_SUCCESS; #ifndef USE_OPENSSL #ifdef USE_INTERNAL_MD5 struct timeval tv; DEBUGTRACE; gettimeofday(&tv, (struct timezone *) 0); srandom(tv.tv_sec ^ tv.tv_usec); #elif USE_PKCS DEBUGTRACE; rval = pkcs_init(); #else rval = SNMPERR_SC_NOT_CONFIGURED; #endif /* USE_INTERNAL_MD5 */ /* * XXX ogud: The only reason to do anything here with openssl is to * * XXX ogud: seed random number generator */ #endif /* ifndef USE_OPENSSL */ return rval; } /* end sc_init() */ /*******************************************************************-o-****** * sc_random * * Parameters: * *buf Pre-allocated buffer. * *buflen Size of buffer. * * Returns: * SNMPERR_SUCCESS Success. */ int sc_random(u_char * buf, size_t * buflen) #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) || defined(USE_PKCS) { int rval = SNMPERR_SUCCESS; #ifdef USE_INTERNAL_MD5 int i; int rndval; u_char *ucp = buf; #endif DEBUGTRACE; #ifdef USE_OPENSSL RAND_bytes(buf, *buflen); /* will never fail */ #elif USE_PKCS /* USE_PKCS */ pkcs_random(buf, *buflen); #else /* USE_INTERNAL_MD5 */ /* * fill the buffer with random integers. Note that random() * is defined in config.h and may not be truly the random() * system call if something better existed */ rval = *buflen - *buflen % sizeof(rndval); for (i = 0; i < rval; i += sizeof(rndval)) { rndval = random(); memcpy(ucp, &rndval, sizeof(rndval)); ucp += sizeof(rndval); } rndval = random(); memcpy(ucp, &rndval, *buflen % sizeof(rndval)); rval = SNMPERR_SUCCESS; #endif /* USE_OPENSSL */ return rval; } /* end sc_random() */ #else _SCAPI_NOT_CONFIGURED #endif /* */ /*******************************************************************-o-****** * sc_generate_keyed_hash * * Parameters: * authtype Type of authentication transform. * authtypelen * *key Pointer to key (Kul) to use in keyed hash. * keylen Length of key in bytes. * *message Pointer to the message to hash. * msglen Length of the message. * *MAC Will be returned with allocated bytes containg hash. * *maclen Length of the hash buffer in bytes; also indicates * whether the MAC should be truncated. * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR All errs * * * A hash of the first msglen bytes of message using a keyed hash defined * by authtype is created and stored in MAC. MAC is ASSUMED to be a buffer * of at least maclen bytes. If the length of the hash is greater than * maclen, it is truncated to fit the buffer. If the length of the hash is * less than maclen, maclen set to the number of hash bytes generated. * * ASSUMED that the number of hash bits is a multiple of 8. */ int sc_generate_keyed_hash(const oid * authtype, size_t authtypelen, u_char * key, u_int keylen, u_char * message, u_int msglen, u_char * MAC, size_t * maclen) #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) || defined(USE_PKCS) { int rval = SNMPERR_SUCCESS; int properlength; u_char buf[SNMP_MAXBUF_SMALL]; #if defined(USE_OPENSSL) || defined(USE_PKCS) size_t buf_len = sizeof(buf); #endif DEBUGTRACE; #ifdef SNMP_TESTING_CODE { int i; DEBUGMSG(("sc_generate_keyed_hash", "sc_generate_keyed_hash(): key=0x")); for (i = 0; i < keylen; i++) DEBUGMSG(("sc_generate_keyed_hash", "%02x", key[i] & 0xff)); DEBUGMSG(("sc_generate_keyed_hash", " (%d)\n", keylen)); } #endif /* SNMP_TESTING_CODE */ /* * Sanity check. */ if (!authtype || !key || !message || !MAC || !maclen || (keylen <= 0) || (msglen <= 0) || (*maclen <= 0) || (authtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } properlength = sc_get_properlength(authtype, authtypelen); if (properlength == SNMPERR_GENERR) return properlength; if (((int) keylen < properlength)) { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } #ifdef USE_OPENSSL /* * Determine transform type. */ #ifndef DISABLE_MD5 if (ISTRANSFORM(authtype, HMACMD5Auth)) HMAC(EVP_md5(), key, keylen, message, msglen, buf, &buf_len); else #endif if (ISTRANSFORM(authtype, HMACSHA1Auth)) HMAC(EVP_sha1(), key, keylen, message, msglen, buf, &buf_len); else { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } if ((int)buf_len != properlength) { QUITFUN(rval, sc_generate_keyed_hash_quit); } if ((int)*maclen > buf_len) *maclen = buf_len; memcpy(MAC, buf, *maclen); #elif USE_PKCS /* USE_PKCS */ #ifndef DISABLE_MD5 if (ISTRANSFORM(authtype, HMACMD5Auth)) { if (pkcs_sign(CKM_MD5_HMAC,key, keylen, message, msglen, buf, &buf_len) != SNMPERR_SUCCESS) { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } } else #endif if (ISTRANSFORM(authtype, HMACSHA1Auth)) { if (pkcs_sign(CKM_SHA_1_HMAC,key, keylen, message, msglen, buf, &buf_len) != SNMPERR_SUCCESS) { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } } else { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } if (buf_len != properlength) { QUITFUN(rval, sc_generate_keyed_hash_quit); } if (*maclen > buf_len) *maclen = buf_len; memcpy(MAC, buf, *maclen); #else /* USE_INTERNAL_MD5 */ if ((int) *maclen > properlength) *maclen = properlength; if (MDsign(message, msglen, MAC, *maclen, key, keylen)) { rval = SNMPERR_GENERR; goto sc_generate_keyed_hash_quit; } #endif /* USE_OPENSSL */ #ifdef SNMP_TESTING_CODE { char *s; int len = binary_to_hex(MAC, *maclen, &s); DEBUGMSGTL(("scapi", "Full v3 message hash: %s\n", s)); SNMP_ZERO(s, len); SNMP_FREE(s); } #endif /* SNMP_TESTING_CODE */ sc_generate_keyed_hash_quit: SNMP_ZERO(buf, SNMP_MAXBUF_SMALL); return rval; } /* end sc_generate_keyed_hash() */ #else _SCAPI_NOT_CONFIGURED #endif /* */ /* * sc_hash(): a generic wrapper around whatever hashing package we are using. * * IN: * hashtype - oid pointer to a hash type * hashtypelen - length of oid pointer * buf - u_char buffer to be hashed * buf_len - integer length of buf data * MAC_len - length of the passed MAC buffer size. * * OUT: * MAC - pre-malloced space to store hash output. * MAC_len - length of MAC output to the MAC buffer. * * Returns: * SNMPERR_SUCCESS Success. * SNMP_SC_GENERAL_FAILURE Any error. */ int sc_hash(const oid * hashtype, size_t hashtypelen, u_char * buf, size_t buf_len, u_char * MAC, size_t * MAC_len) #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) || defined(USE_PKCS) { #if defined(USE_OPENSSL) || defined(USE_PKCS) int rval = SNMPERR_SUCCESS; #endif #ifdef USE_OPENSSL const EVP_MD *hashfn; EVP_MD_CTX ctx, *cptr; #endif DEBUGTRACE; if (hashtype == NULL || hashtypelen < 0 || buf == NULL || buf_len < 0 || MAC == NULL || MAC_len == NULL || (int) (*MAC_len) < sc_get_properlength(hashtype, hashtypelen)) return (SNMPERR_GENERR); #ifdef USE_OPENSSL /* * Determine transform type. */ #ifndef DISABLE_MD5 if (ISTRANSFORM(hashtype, HMACMD5Auth)) { hashfn = (const EVP_MD *) EVP_md5(); } else #endif if (ISTRANSFORM(hashtype, HMACSHA1Auth)) { hashfn = (const EVP_MD *) EVP_sha1(); } else { return (SNMPERR_GENERR); } /** initialize the pointer */ memset(&ctx, 0, sizeof(ctx)); cptr = &ctx; #if defined(OLD_DES) EVP_DigestInit(cptr, hashfn); #else /* !OLD_DES */ /* this is needed if the runtime library is different than the compiled library since the openssl versions are very different. */ if (SSLeay() < 0x907000) { /* the old version of the struct was bigger and thus more memory is needed. should be 152, but we use 256 for safety. */ cptr = malloc(256); EVP_DigestInit(cptr, hashfn); } else { EVP_MD_CTX_init(cptr); EVP_DigestInit(cptr, hashfn); } #endif /** pass the data */ EVP_DigestUpdate(cptr, buf, buf_len); /** do the final pass */ #if defined(OLD_DES) EVP_DigestFinal(cptr, MAC, MAC_len); #else /* !OLD_DES */ if (SSLeay() < 0x907000) { EVP_DigestFinal(cptr, MAC, MAC_len); free(cptr); } else { EVP_DigestFinal_ex(cptr, MAC, MAC_len); EVP_MD_CTX_cleanup(cptr); } #endif /* OLD_DES */ return (rval); #elif USE_PKCS /* USE_PKCS */ #ifndef DISABLE_MD5 if (ISTRANSFORM(hashtype, HMACMD5Auth)) { rval = pkcs_digest(CKM_MD5, buf, buf_len, MAC, MAC_len); } else #endif if (ISTRANSFORM(hashtype, HMACSHA1Auth)) { rval = pkcs_digest(CKM_SHA_1, buf, buf_len, MAC, MAC_len); } else { return (SNMPERR_GENERR); } return (rval); #else /* USE_INTERNAL_MD5 */ if (MDchecksum(buf, buf_len, MAC, *MAC_len)) { return SNMPERR_GENERR; } if (*MAC_len > 16) *MAC_len = 16; return SNMPERR_SUCCESS; #endif /* USE_OPENSSL */ } #else /* !defined(USE_OPENSSL) && !defined(USE_INTERNAL_MD5) */ _SCAPI_NOT_CONFIGURED #endif /* !defined(USE_OPENSSL) && !defined(USE_INTERNAL_MD5) */ /*******************************************************************-o-****** * sc_check_keyed_hash * * Parameters: * authtype Transform type of authentication hash. * *key Key bits in a string of bytes. * keylen Length of key in bytes. * *message Message for which to check the hash. * msglen Length of message. * *MAC Given hash. * maclen Length of given hash; indicates truncation if it is * shorter than the normal size of output for * given hash transform. * Returns: * SNMPERR_SUCCESS Success. * SNMP_SC_GENERAL_FAILURE Any error * * * Check the hash given in MAC against the hash of message. If the length * of MAC is less than the length of the transform hash output, only maclen * bytes are compared. The length of MAC cannot be greater than the * length of the hash transform output. */ int sc_check_keyed_hash(const oid * authtype, size_t authtypelen, u_char * key, u_int keylen, u_char * message, u_int msglen, u_char * MAC, u_int maclen) #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) || defined(USE_PKCS) { int rval = SNMPERR_SUCCESS; size_t buf_len = SNMP_MAXBUF_SMALL; u_char buf[SNMP_MAXBUF_SMALL]; DEBUGTRACE; #ifdef SNMP_TESTING_CODE { int i; DEBUGMSG(("scapi", "sc_check_keyed_hash(): key=0x")); for (i = 0; i < keylen; i++) DEBUGMSG(("scapi", "%02x", key[i] & 0xff)); DEBUGMSG(("scapi", " (%d)\n", keylen)); } #endif /* SNMP_TESTING_CODE */ /* * Sanity check. */ if (!authtype || !key || !message || !MAC || (keylen <= 0) || (msglen <= 0) || (maclen <= 0) || (authtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); } /* * Generate a full hash of the message, then compare * the result with the given MAC which may shorter than * the full hash length. */ rval = sc_generate_keyed_hash(authtype, authtypelen, key, keylen, message, msglen, buf, &buf_len); QUITFUN(rval, sc_check_keyed_hash_quit); if (maclen > msglen) { QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); } else if (memcmp(buf, MAC, maclen) != 0) { QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit); } sc_check_keyed_hash_quit: SNMP_ZERO(buf, SNMP_MAXBUF_SMALL); return rval; } /* end sc_check_keyed_hash() */ #else _SCAPI_NOT_CONFIGURED #endif /* USE_INTERNAL_MD5 */ /*******************************************************************-o-****** * sc_encrypt * * Parameters: * privtype Type of privacy cryptographic transform. * *key Key bits for crypting. * keylen Length of key (buffer) in bytes. * *iv IV bits for crypting. * ivlen Length of iv (buffer) in bytes. * *plaintext Plaintext to crypt. * ptlen Length of plaintext. * *ciphertext Ciphertext to crypt. * *ctlen Length of ciphertext. * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_SC_NOT_CONFIGURED Encryption is not supported. * SNMPERR_SC_GENERAL_FAILURE Any other error * * * Encrypt plaintext into ciphertext using key and iv. * * ctlen contains actual number of crypted bytes in ciphertext upon * successful return. */ int sc_encrypt(const oid * privtype, size_t privtypelen, u_char * key, u_int keylen, u_char * iv, u_int ivlen, u_char * plaintext, u_int ptlen, u_char * ciphertext, size_t * ctlen) #if defined(USE_OPENSSL) { int rval = SNMPERR_SUCCESS; u_int properlength = 0, properlength_iv = 0; u_char pad_block[128]; /* bigger than anything I need */ u_char my_iv[128]; /* ditto */ int pad, plast, pad_size = 0; int have_trans; #ifndef DISABLE_DES #ifdef OLD_DES DES_key_schedule key_sch; #else DES_key_schedule key_sched_store; DES_key_schedule *key_sch = &key_sched_store; #endif DES_cblock key_struct; #endif #ifdef HAVE_AES AES_KEY aes_key; int new_ivlen = 0; #endif DEBUGTRACE; /* * Sanity check. */ #if !defined(SCAPI_AUTHPRIV) snmp_log(LOG_ERR, "Encryption support not enabled.\n"); return SNMPERR_SC_NOT_CONFIGURED; #endif if (!privtype || !key || !iv || !plaintext || !ciphertext || !ctlen || (keylen <= 0) || (ivlen <= 0) || (ptlen <= 0) || (*ctlen <= 0) || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } else if (ptlen > *ctlen) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } #ifdef SNMP_TESTING_CODE { size_t buf_len = 128, out_len = 0; u_char *buf = (u_char *) malloc(buf_len); if (buf != NULL) { if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, iv, ivlen)) { DEBUGMSGTL(("scapi", "encrypt: IV: %s/", buf)); } else { DEBUGMSGTL(("scapi", "encrypt: IV: %s [TRUNCATED]/", buf)); } out_len = 0; if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, key, keylen)) { DEBUGMSG(("scapi", "%s\n", buf)); } else { DEBUGMSG(("scapi", "%s [TRUNCATED]\n", buf)); } out_len = 0; if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, plaintext, 16)) { DEBUGMSGTL(("scapi", "encrypt: string: %s\n", buf)); } else { DEBUGMSGTL(("scapi", "encrypt: string: %s [TRUNCATED]\n", buf)); } free(buf); } else { DEBUGMSGTL(("scapi", "encrypt: malloc fail for debug output\n")); } } #endif /* SNMP_TESTING_CODE */ /* * Determine privacy transform. */ have_trans = 0; #ifndef DISABLE_DES if (ISTRANSFORM(privtype, DESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); pad_size = properlength; have_trans = 1; } #endif #ifdef HAVE_AES if (ISTRANSFORM(privtype, AESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES); properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_AES_IV); have_trans = 1; } #endif if (!have_trans) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } if ((keylen < properlength) || (ivlen < properlength_iv)) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } memset(my_iv, 0, sizeof(my_iv)); #ifndef DISABLE_DES if (ISTRANSFORM(privtype, DESPriv)) { /* * now calculate the padding needed */ pad = pad_size - (ptlen % pad_size); plast = (int) ptlen - (pad_size - pad); if (pad == pad_size) pad = 0; if (ptlen + pad > *ctlen) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); /* not enough space */ } if (pad > 0) { /* copy data into pad block if needed */ memcpy(pad_block, plaintext + plast, pad_size - pad); memset(&pad_block[pad_size - pad], pad, pad); /* filling in padblock */ } memcpy(key_struct, key, sizeof(key_struct)); (void) DES_key_sched(&key_struct, key_sch); memcpy(my_iv, iv, ivlen); /* * encrypt the data */ DES_ncbc_encrypt(plaintext, ciphertext, plast, key_sch, (DES_cblock *) my_iv, DES_ENCRYPT); if (pad > 0) { /* * then encrypt the pad block */ DES_ncbc_encrypt(pad_block, ciphertext + plast, pad_size, key_sch, (DES_cblock *) my_iv, DES_ENCRYPT); *ctlen = plast + pad_size; } else { *ctlen = plast; } } #endif #ifdef HAVE_AES if (ISTRANSFORM(privtype, AESPriv)) { (void) AES_set_encrypt_key(key, properlength*8, &aes_key); memcpy(my_iv, iv, ivlen); /* * encrypt the data */ AES_cfb128_encrypt(plaintext, ciphertext, ptlen, &aes_key, my_iv, &new_ivlen, AES_ENCRYPT); *ctlen = ptlen; } #endif sc_encrypt_quit: /* * clear memory just in case */ memset(my_iv, 0, sizeof(my_iv)); memset(pad_block, 0, sizeof(pad_block)); #ifndef DISABLE_DES memset(key_struct, 0, sizeof(key_struct)); #ifdef OLD_DES memset(&key_sch, 0, sizeof(key_sch)); #else memset(&key_sched_store, 0, sizeof(key_sched_store)); #endif #endif #ifdef HAVE_AES memset(&aes_key,0,sizeof(aes_key)); #endif return rval; } /* end sc_encrypt() */ #elif defined(USE_PKCS) { int rval = SNMPERR_SUCCESS; u_int properlength, properlength_iv; u_char pkcs_des_key[8]; DEBUGTRACE; /* * Sanity check. */ #if !defined(SCAPI_AUTHPRIV) snmp_log(LOG_ERR, "Encryption support not enabled.\n"); return SNMPERR_SC_NOT_CONFIGURED; #endif if (!privtype || !key || !iv || !plaintext || !ciphertext || !ctlen || (keylen <= 0) || (ivlen <= 0) || (ptlen <= 0) || (*ctlen <= 0) || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } else if (ptlen > *ctlen) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } /* * Determine privacy transform. */ if (ISTRANSFORM(privtype, DESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); } else { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } if ((keylen < properlength) || (ivlen < properlength_iv)) { QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); } if (ISTRANSFORM(privtype, DESPriv)) { memset(pkcs_des_key, 0, sizeof(pkcs_des_key)); memcpy(pkcs_des_key, key, sizeof(pkcs_des_key)); rval = pkcs_encrpyt(CKM_DES_CBC, pkcs_des_key, sizeof(pkcs_des_key), iv, ivlen, plaintext, ptlen, ciphertext, ctlen); } sc_encrypt_quit: return rval; } #else { # if USE_INTERNAL_MD5 { snmp_log(LOG_ERR, "Encryption support not enabled.\n"); DEBUGMSGTL(("scapi", "Encrypt function not defined.\n")); return SNMPERR_SC_GENERAL_FAILURE; } # else _SCAPI_NOT_CONFIGURED # endif /* USE_INTERNAL_MD5 */ } #endif /* */ /*******************************************************************-o-****** * sc_decrypt * * Parameters: * privtype * *key * keylen * *iv * ivlen * *ciphertext * ctlen * *plaintext * *ptlen * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_SC_NOT_CONFIGURED Encryption is not supported. * SNMPERR_SC_GENERAL_FAILURE Any other error * * * Decrypt ciphertext into plaintext using key and iv. * * ptlen contains actual number of plaintext bytes in plaintext upon * successful return. */ int sc_decrypt(const oid * privtype, size_t privtypelen, u_char * key, u_int keylen, u_char * iv, u_int ivlen, u_char * ciphertext, u_int ctlen, u_char * plaintext, size_t * ptlen) #ifdef USE_OPENSSL { int rval = SNMPERR_SUCCESS; u_char my_iv[128]; #ifndef DISABLE_DES #ifdef OLD_DES DES_key_schedule key_sch; #else DES_key_schedule key_sched_store; DES_key_schedule *key_sch = &key_sched_store; #endif DES_cblock key_struct; #endif u_int properlength = 0, properlength_iv = 0; int have_transform; #ifdef HAVE_AES int new_ivlen = 0; AES_KEY aes_key; #endif DEBUGTRACE; if (!privtype || !key || !iv || !plaintext || !ciphertext || !ptlen || (ctlen <= 0) || (*ptlen <= 0) || (*ptlen < ctlen) || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); } #ifdef SNMP_TESTING_CODE { size_t buf_len = 128, out_len = 0; u_char *buf = (u_char *) malloc(buf_len); if (buf != NULL) { if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, iv, ivlen)) { DEBUGMSGTL(("scapi", "decrypt: IV: %s/", buf)); } else { DEBUGMSGTL(("scapi", "decrypt: IV: %s [TRUNCATED]/", buf)); } out_len = 0; if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1, key, keylen)) { DEBUGMSG(("scapi", "%s\n", buf)); } else { DEBUGMSG(("scapi", "%s\n", buf)); } free(buf); } else { DEBUGMSGTL(("scapi", "decrypt: malloc fail for debug output\n")); } } #endif /* SNMP_TESTING_CODE */ /* * Determine privacy transform. */ have_transform = 0; #ifndef DISABLE_DES if (ISTRANSFORM(privtype, DESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); have_transform = 1; } #endif #ifdef HAVE_AES if (ISTRANSFORM(privtype, AESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES); properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_AES_IV); have_transform = 1; } #endif if (!have_transform) { QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); } if ((keylen < properlength) || (ivlen < properlength_iv)) { QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); } memset(my_iv, 0, sizeof(my_iv)); #ifndef DISABLE_DES if (ISTRANSFORM(privtype, DESPriv)) { memcpy(key_struct, key, sizeof(key_struct)); (void) DES_key_sched(&key_struct, key_sch); memcpy(my_iv, iv, ivlen); DES_cbc_encrypt(ciphertext, plaintext, ctlen, key_sch, (DES_cblock *) my_iv, DES_DECRYPT); *ptlen = ctlen; } #endif #ifdef HAVE_AES if (ISTRANSFORM(privtype, AESPriv)) { (void) AES_set_encrypt_key(key, properlength*8, &aes_key); memcpy(my_iv, iv, ivlen); /* * encrypt the data */ AES_cfb128_encrypt(ciphertext, plaintext, ctlen, &aes_key, my_iv, &new_ivlen, AES_DECRYPT); *ptlen = ctlen; } #endif /* * exit cond */ sc_decrypt_quit: #ifndef DISABLE_DES #ifdef OLD_DES memset(&key_sch, 0, sizeof(key_sch)); #else memset(&key_sched_store, 0, sizeof(key_sched_store)); #endif memset(key_struct, 0, sizeof(key_struct)); #endif memset(my_iv, 0, sizeof(my_iv)); return rval; } /* USE OPEN_SSL */ #elif USE_PKCS /* USE PKCS */ { int rval = SNMPERR_SUCCESS; u_int properlength, properlength_iv; u_char pkcs_des_key[8]; DEBUGTRACE; if (!privtype || !key || !iv || !plaintext || !ciphertext || !ptlen || (ctlen <= 0) || (*ptlen <= 0) || (*ptlen < ctlen) || (privtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); } /* * Determine privacy transform. */ if (ISTRANSFORM(privtype, DESPriv)) { properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES); properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); } else { QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); } if ((keylen < properlength) || (ivlen < properlength_iv)) { QUITFUN(SNMPERR_GENERR, sc_decrypt_quit); } if (ISTRANSFORM(privtype, DESPriv)) { memset(pkcs_des_key, 0, sizeof(pkcs_des_key)); memcpy(pkcs_des_key, key, sizeof(pkcs_des_key)); rval = pkcs_decrpyt(CKM_DES_CBC, pkcs_des_key, sizeof(pkcs_des_key), iv, ivlen, ciphertext, ctlen, plaintext, ptlen); *ptlen = ctlen; } sc_decrypt_quit: return rval; } /* USE PKCS */ #else { #if !defined(SCAPI_AUTHPRIV) snmp_log(LOG_ERR, "Encryption support not enabled.\n"); return SNMPERR_SC_NOT_CONFIGURED; #else # if USE_INTERNAL_MD5 { DEBUGMSGTL(("scapi", "Decryption function not defined.\n")); return SNMPERR_SC_GENERAL_FAILURE; } # else _SCAPI_NOT_CONFIGURED # endif /* USE_INTERNAL_MD5 */ #endif /* */ } #endif /* USE_OPENSSL */