SNMP integration and GUI
[tomato.git] / release / src / router / snmp / snmplib / keytools.c
blob3badf756d9b126392034554198496596cfeb8fd5
1 /*
2 * keytools.c
3 */
5 #include <net-snmp/net-snmp-config.h>
7 #include <stdio.h>
8 #include <sys/types.h>
9 #if HAVE_WINSOCK_H
10 #include <winsock.h>
11 #endif
12 #ifdef HAVE_NETINET_IN_H
13 #include <netinet/in.h>
14 #endif
15 #ifdef HAVE_STDLIB_H
16 #include <stdlib.h>
17 #endif
18 #if HAVE_STRING_H
19 #include <string.h>
20 #else
21 #include <strings.h>
22 #endif
24 #if HAVE_DMALLOC_H
25 #include <dmalloc.h>
26 #endif
28 #include <net-snmp/types.h>
29 #include <net-snmp/output_api.h>
30 #include <net-snmp/utilities.h>
32 #include <net-snmp/library/snmp_api.h>
33 #ifdef USE_OPENSSL
34 # include <openssl/hmac.h>
35 #else
36 #ifdef USE_INTERNAL_MD5
37 #include <net-snmp/library/md5.h>
38 #endif
39 #endif
41 #include <net-snmp/library/scapi.h>
42 #include <net-snmp/library/keytools.h>
44 #include <net-snmp/library/transform_oids.h>
46 /*******************************************************************-o-******
47 * generate_Ku
49 * Parameters:
50 * *hashtype MIB OID for the transform type for hashing.
51 * hashtype_len Length of OID value.
52 * *P Pre-allocated bytes of passpharase.
53 * pplen Length of passphrase.
54 * *Ku Buffer to contain Ku.
55 * *kulen Length of Ku buffer.
57 * Returns:
58 * SNMPERR_SUCCESS Success.
59 * SNMPERR_GENERR All errors.
62 * Convert a passphrase into a master user key, Ku, according to the
63 * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM)
64 * as follows:
66 * Expand the passphrase to fill the passphrase buffer space, if necessary,
67 * concatenation as many duplicates as possible of P to itself. If P is
68 * larger than the buffer space, truncate it to fit.
70 * Then hash the result with the given hashtype transform. Return
71 * the result as Ku.
73 * If successful, kulen contains the size of the hash written to Ku.
75 * NOTE Passphrases less than USM_LENGTH_P_MIN characters in length
76 * cause an error to be returned.
77 * (Punt this check to the cmdline apps? XXX)
79 int
80 generate_Ku(const oid * hashtype, u_int hashtype_len,
81 u_char * P, size_t pplen, u_char * Ku, size_t * kulen)
82 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
84 int rval = SNMPERR_SUCCESS,
85 nbytes = USM_LENGTH_EXPANDED_PASSPHRASE;
87 u_int i, pindex = 0;
89 u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp;
91 #ifdef USE_OPENSSL
92 EVP_MD_CTX *ctx = malloc(sizeof(EVP_MD_CTX));
93 #else
94 MDstruct MD;
95 #endif
97 * Sanity check.
99 if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0)
100 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) {
101 QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
104 if (pplen < USM_LENGTH_P_MIN) {
105 snmp_log(LOG_ERR, "Error: passphrase chosen is below the length "
106 "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN);
107 snmp_set_detail("The supplied password length is too short.");
108 QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
113 * Setup for the transform type.
115 #ifdef USE_OPENSSL
117 if (ISTRANSFORM(hashtype, HMACMD5Auth))
118 EVP_DigestInit(ctx, EVP_md5());
119 else if (ISTRANSFORM(hashtype, HMACSHA1Auth))
120 EVP_DigestInit(ctx, EVP_sha1());
121 else {
122 free(ctx);
123 return (SNMPERR_GENERR);
125 #else
126 MDbegin(&MD);
127 #endif /* USE_OPENSSL */
129 while (nbytes > 0) {
130 bufp = buf;
131 for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
132 *bufp++ = P[pindex++ % pplen];
134 #ifdef USE_OPENSSL
135 EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK);
136 #else
137 if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) {
138 rval = SNMPERR_USM_ENCRYPTIONERROR;
139 goto md5_fin;
141 #endif /* USE_OPENSSL */
143 nbytes -= USM_LENGTH_KU_HASHBLOCK;
146 #ifdef USE_OPENSSL
147 EVP_DigestFinal(ctx, (unsigned char *) Ku, (unsigned int *) kulen);
149 * what about free()
151 #else
152 if (MDupdate(&MD, buf, 0)) {
153 rval = SNMPERR_USM_ENCRYPTIONERROR;
154 goto md5_fin;
156 *kulen = sc_get_properlength(hashtype, hashtype_len);
157 MDget(&MD, Ku, *kulen);
158 md5_fin:
159 memset(&MD, 0, sizeof(MD));
160 #endif /* USE_OPENSSL */
163 #ifdef SNMP_TESTING_CODE
164 DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P));
165 for (i = 0; i < *kulen; i++)
166 DEBUGMSG(("generate_Ku", "%02x", Ku[i]));
167 DEBUGMSG(("generate_Ku", "\n"));
168 #endif /* SNMP_TESTING_CODE */
171 generate_Ku_quit:
172 memset(buf, 0, sizeof(buf));
173 #ifdef USE_OPENSSL
174 free(ctx);
175 #endif
176 return rval;
178 } /* end generate_Ku() */
180 #else
181 _KEYTOOLS_NOT_AVAILABLE
182 #endif /* internal or openssl */
183 /*******************************************************************-o-******
184 * generate_kul
186 * Parameters:
187 * *hashtype
188 * hashtype_len
189 * *engineID
190 * engineID_len
191 * *Ku Master key for a given user.
192 * ku_len Length of Ku in bytes.
193 * *Kul Localized key for a given user at engineID.
194 * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT).
196 * Returns:
197 * SNMPERR_SUCCESS Success.
198 * SNMPERR_GENERR All errors.
201 * Ku MUST be the proper length (currently fixed) for the given hashtype.
203 * Upon successful return, Kul contains the localized form of Ku at
204 * engineID, and the length of the key is stored in kul_len.
206 * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and
207 * originally documented in:
208 * U. Blumenthal, N. C. Hien, B. Wijnen,
209 * "Key Derivation for Network Management Applications",
210 * IEEE Network Magazine, April/May issue, 1997.
213 * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku).
215 * NOTE Localized keys for privacy transforms are generated via
216 * the authentication transform held by the same usmUser.
218 * XXX An engineID of any length is accepted, even if larger than
219 * what is spec'ed for the textual convention.
222 generate_kul(const oid * hashtype, u_int hashtype_len,
223 u_char * engineID, size_t engineID_len,
224 u_char * Ku, size_t ku_len,
225 u_char * Kul, size_t * kul_len)
226 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5)
228 int rval = SNMPERR_SUCCESS;
229 u_int nbytes = 0;
230 size_t properlength;
232 u_char buf[SNMP_MAXBUF];
233 #ifdef SNMP_TESTING_CODE
234 int i;
235 #endif
239 * Sanity check.
241 if (!hashtype || !engineID || !Ku || !Kul || !kul_len
242 || (engineID_len <= 0) || (ku_len <= 0) || (*kul_len <= 0)
243 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) {
244 QUITFUN(SNMPERR_GENERR, generate_kul_quit);
248 properlength = sc_get_properlength(hashtype, hashtype_len);
249 if (properlength == SNMPERR_GENERR)
250 QUITFUN(SNMPERR_GENERR, generate_kul_quit);
253 if (((int) *kul_len < properlength) || ((int) ku_len < properlength)) {
254 QUITFUN(SNMPERR_GENERR, generate_kul_quit);
258 * Concatenate Ku and engineID properly, then hash the result.
259 * Store it in Kul.
261 nbytes = 0;
262 memcpy(buf, Ku, properlength);
263 nbytes += properlength;
264 memcpy(buf + nbytes, engineID, engineID_len);
265 nbytes += engineID_len;
266 memcpy(buf + nbytes, Ku, properlength);
267 nbytes += properlength;
269 rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len);
271 #ifdef SNMP_TESTING_CODE
272 DEBUGMSGTL(("generate_kul", "generating Kul (from Ku): "));
273 for (i = 0; i < *kul_len; i++)
274 DEBUGMSG(("generate_kul", "%02x", Kul[i]));
275 DEBUGMSG(("generate_kul", "keytools\n"));
276 #endif /* SNMP_TESTING_CODE */
278 QUITFUN(rval, generate_kul_quit);
281 generate_kul_quit:
282 return rval;
284 } /* end generate_kul() */
286 #else
287 _KEYTOOLS_NOT_AVAILABLE
288 #endif /* internal or openssl */
289 /*******************************************************************-o-******
290 * encode_keychange
292 * Parameters:
293 * *hashtype MIB OID for the hash transform type.
294 * hashtype_len Length of the MIB OID hash transform type.
295 * *oldkey Old key that is used to encodes the new key.
296 * oldkey_len Length of oldkey in bytes.
297 * *newkey New key that is encoded using the old key.
298 * newkey_len Length of new key in bytes.
299 * *kcstring Buffer to contain the KeyChange TC string.
300 * *kcstring_len Length of kcstring buffer.
302 * Returns:
303 * SNMPERR_SUCCESS Success.
304 * SNMPERR_GENERR All errors.
307 * Uses oldkey and acquired random bytes to encode newkey into kcstring
308 * according to the rules of the KeyChange TC described in RFC 2274, Section 5.
310 * Upon successful return, *kcstring_len contains the length of the
311 * encoded string.
313 * ASSUMES Old and new key are always equal to each other, although
314 * this may be less than the transform type hash output
315 * output length (eg, using KeyChange for a DESPriv key when
316 * the user also uses SHA1Auth). This also implies that the
317 * hash placed in the second 1/2 of the key change string
318 * will be truncated before the XOR'ing when the hash output is
319 * larger than that 1/2 of the key change string.
321 * *kcstring_len will be returned as exactly twice that same
322 * length though the input buffer may be larger.
324 * XXX FIX: Does not handle varibable length keys.
325 * XXX FIX: Does not handle keys larger than the hash algorithm used.
328 encode_keychange(const oid * hashtype, u_int hashtype_len,
329 u_char * oldkey, size_t oldkey_len,
330 u_char * newkey, size_t newkey_len,
331 u_char * kcstring, size_t * kcstring_len)
332 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5)
334 int rval = SNMPERR_SUCCESS;
335 size_t properlength;
336 size_t nbytes = 0;
338 u_char *tmpbuf = NULL;
342 * Sanity check.
344 if (!hashtype || !oldkey || !newkey || !kcstring || !kcstring_len
345 || (oldkey_len <= 0) || (newkey_len <= 0) || (*kcstring_len <= 0)
346 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) {
347 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
351 * Setup for the transform type.
353 properlength = sc_get_properlength(hashtype, hashtype_len);
354 if (properlength == SNMPERR_GENERR)
355 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
357 if ((oldkey_len != newkey_len) || (*kcstring_len < (2 * oldkey_len))) {
358 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
361 properlength = SNMP_MIN((int) oldkey_len, properlength);
364 * Use the old key and some random bytes to encode the new key
365 * in the KeyChange TC format:
366 * . Get random bytes (store in first half of kcstring),
367 * . Hash (oldkey | random_bytes) (into second half of kcstring),
368 * . XOR hash and newkey (into second half of kcstring).
370 * Getting the wrong number of random bytes is considered an error.
372 nbytes = properlength;
374 #if defined(SNMP_TESTING_CODE) && defined(RANDOMZEROS)
375 memset(kcstring, 0, nbytes);
376 DEBUGMSG(("encode_keychange",
377 "** Using all zero bits for \"random\" delta of )"
378 "the keychange string! **\n"));
379 #else /* !SNMP_TESTING_CODE */
380 rval = sc_random(kcstring, &nbytes);
381 QUITFUN(rval, encode_keychange_quit);
382 if ((int) nbytes != properlength) {
383 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
385 #endif /* !SNMP_TESTING_CODE */
387 tmpbuf = (u_char *) malloc(properlength * 2);
388 if (tmpbuf) {
389 memcpy(tmpbuf, oldkey, properlength);
390 memcpy(tmpbuf + properlength, kcstring, properlength);
392 *kcstring_len -= properlength;
393 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength * 2,
394 kcstring + properlength, kcstring_len);
396 QUITFUN(rval, encode_keychange_quit);
398 *kcstring_len = (properlength * 2);
400 kcstring += properlength;
401 nbytes = 0;
402 while ((int) (nbytes++) < properlength) {
403 *kcstring++ ^= *newkey++;
407 encode_keychange_quit:
408 if (rval != SNMPERR_SUCCESS)
409 memset(kcstring, 0, *kcstring_len);
410 SNMP_FREE(tmpbuf);
412 return rval;
414 } /* end encode_keychange() */
416 #else
417 _KEYTOOLS_NOT_AVAILABLE
418 #endif /* internal or openssl */
419 /*******************************************************************-o-******
420 * decode_keychange
422 * Parameters:
423 * *hashtype MIB OID of the hash transform to use.
424 * hashtype_len Length of the hash transform MIB OID.
425 * *oldkey Old key that is used to encode the new key.
426 * oldkey_len Length of oldkey in bytes.
427 * *kcstring Encoded KeyString buffer containing the new key.
428 * kcstring_len Length of kcstring in bytes.
429 * *newkey Buffer to hold the extracted new key.
430 * *newkey_len Length of newkey in bytes.
432 * Returns:
433 * SNMPERR_SUCCESS Success.
434 * SNMPERR_GENERR All errors.
437 * Decodes a string of bits encoded according to the KeyChange TC described
438 * in RFC 2274, Section 5. The new key is extracted from *kcstring with
439 * the aid of the old key.
441 * Upon successful return, *newkey_len contains the length of the new key.
444 * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer,
445 * although this length may be less than the hash transform
446 * output. Thus the new key length will be equal to the old
447 * key length.
450 * XXX: if the newkey is not long enough, it should be freed and remalloced
453 decode_keychange(const oid * hashtype, u_int hashtype_len,
454 u_char * oldkey, size_t oldkey_len,
455 u_char * kcstring, size_t kcstring_len,
456 u_char * newkey, size_t * newkey_len)
457 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5)
459 int rval = SNMPERR_SUCCESS;
460 size_t properlength = 0;
461 u_int nbytes = 0;
463 u_char *bufp, tmp_buf[SNMP_MAXBUF];
464 size_t tmp_buf_len = SNMP_MAXBUF;
465 u_char *tmpbuf = NULL;
470 * Sanity check.
472 if (!hashtype || !oldkey || !kcstring || !newkey || !newkey_len
473 || (oldkey_len <= 0) || (kcstring_len <= 0) || (*newkey_len <= 0)
474 || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) {
475 QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
480 * Setup for the transform type.
482 properlength = sc_get_properlength(hashtype, hashtype_len);
483 if (properlength == SNMPERR_GENERR)
484 QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
487 if (((oldkey_len * 2) != kcstring_len) || (*newkey_len < oldkey_len)) {
488 QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
491 properlength = oldkey_len;
492 *newkey_len = properlength;
495 * Use the old key and the given KeyChange TC string to recover
496 * the new key:
497 * . Hash (oldkey | random_bytes) (into newkey),
498 * . XOR hash and encoded (second) half of kcstring (into newkey).
500 tmpbuf = (u_char *) malloc(properlength * 2);
501 if (tmpbuf) {
502 memcpy(tmpbuf, oldkey, properlength);
503 memcpy(tmpbuf + properlength, kcstring, properlength);
505 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength * 2,
506 tmp_buf, &tmp_buf_len);
507 QUITFUN(rval, decode_keychange_quit);
509 memcpy(newkey, tmp_buf, properlength);
510 bufp = kcstring + properlength;
511 nbytes = 0;
512 while ((int) (nbytes++) < properlength) {
513 *newkey++ ^= *bufp++;
517 decode_keychange_quit:
518 if (rval != SNMPERR_SUCCESS) {
519 memset(newkey, 0, properlength);
521 memset(tmp_buf, 0, SNMP_MAXBUF);
522 if (tmpbuf != NULL)
523 SNMP_FREE(tmpbuf);
525 return rval;
527 } /* end decode_keychange() */
529 #else
530 _KEYTOOLS_NOT_AVAILABLE
531 #endif /* internal or openssl */