1 //==========================================================================
3 // ./lib/current/src/keytools.c
6 //==========================================================================
7 //####ECOSGPLCOPYRIGHTBEGIN####
8 // -------------------------------------------
9 // This file is part of eCos, the Embedded Configurable Operating System.
10 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // eCos is free software; you can redistribute it and/or modify it under
13 // the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 2 or (at your option) any later version.
16 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
17 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 // You should have received a copy of the GNU General Public License along
22 // with eCos; if not, write to the Free Software Foundation, Inc.,
23 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 // As a special exception, if other files instantiate templates or use macros
26 // or inline functions from this file, or you compile this file and link it
27 // with other works to produce a work based on this file, this file does not
28 // by itself cause the resulting work to be covered by the GNU General Public
29 // License. However the source code for this file must still be made available
30 // in accordance with section (3) of the GNU General Public License.
32 // This exception does not invalidate any other reasons why a work based on
33 // this file might be covered by the GNU General Public License.
35 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
36 // at http://sources.redhat.com/ecos/ecos-license/
37 // -------------------------------------------
38 //####ECOSGPLCOPYRIGHTEND####
39 //####UCDSNMPCOPYRIGHTBEGIN####
41 // -------------------------------------------
43 // Portions of this software may have been derived from the UCD-SNMP
44 // project, <http://ucd-snmp.ucdavis.edu/> from the University of
45 // California at Davis, which was originally based on the Carnegie Mellon
46 // University SNMP implementation. Portions of this software are therefore
47 // covered by the appropriate copyright disclaimers included herein.
49 // The release used was version 4.1.2 of May 2000. "ucd-snmp-4.1.2"
50 // -------------------------------------------
52 //####UCDSNMPCOPYRIGHTEND####
53 //==========================================================================
54 //#####DESCRIPTIONBEGIN####
59 // Purpose: Port of UCD-SNMP distribution to eCos.
63 //####DESCRIPTIONEND####
65 //==========================================================================
66 /********************************************************************
67 Copyright 1989, 1991, 1992 by Carnegie Mellon University
70 Copyright 1996, 1998, 1999, 2000 The Regents of the University of California
74 Permission to use, copy, modify and distribute this software and its
75 documentation for any purpose and without fee is hereby granted,
76 provided that the above copyright notice appears in all copies and
77 that both that copyright notice and this permission notice appear in
78 supporting documentation, and that the name of CMU and The Regents of
79 the University of California not be used in advertising or publicity
80 pertaining to distribution of the software without specific written
83 CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL
84 WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
85 WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR
86 THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL,
87 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
88 FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
89 CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
90 CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
91 *********************************************************************/
98 #ifdef CYGPKG_SNMPAGENT_V3_SUPPORT
100 #include <sys/types.h>
104 #ifdef HAVE_NETINET_IN_H
105 #include <netinet/in.h>
121 #include "snmp_api.h"
123 # include <openssl/hmac.h>
125 #ifdef USE_INTERNAL_MD5
131 #include "keytools.h"
133 #include "snmp_debug.h"
134 #include "snmp_logging.h"
136 #include "transform_oids.h"
138 /*******************************************************************-o-******
142 * *hashtype MIB OID for the transform type for hashing.
143 * hashtype_len Length of OID value.
144 * *P Pre-allocated bytes of passpharase.
145 * pplen Length of passphrase.
146 * *Ku Buffer to contain Ku.
147 * *kulen Length of Ku buffer.
150 * SNMPERR_SUCCESS Success.
151 * SNMPERR_GENERR All errors.
154 * Convert a passphrase into a master user key, Ku, according to the
155 * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM)
158 * Expand the passphrase to fill the passphrase buffer space, if necessary,
159 * concatenation as many duplicates as possible of P to itself. If P is
160 * larger than the buffer space, truncate it to fit.
162 * Then hash the result with the given hashtype transform. Return
165 * If successful, kulen contains the size of the hash written to Ku.
167 * NOTE Passphrases less than USM_LENGTH_P_MIN characters in length
168 * cause an error to be returned.
169 * (Punt this check to the cmdline apps? XXX)
172 generate_Ku( oid *hashtype, u_int hashtype_len,
173 u_char *P, size_t pplen,
174 u_char *Ku, size_t *kulen)
175 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
177 int rval = SNMPERR_SUCCESS,
178 nbytes = USM_LENGTH_EXPANDED_PASSPHRASE;
182 u_char buf[USM_LENGTH_KU_HASHBLOCK],
186 EVP_MD_CTX *ctx = malloc(sizeof(EVP_MD_CTX));
193 if ( !hashtype || !P || !Ku || !kulen
195 || (hashtype_len != USM_LENGTH_OID_TRANSFORM) )
197 QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
200 if (pplen < USM_LENGTH_P_MIN) {
201 #ifdef SNMP_TESTING_CODE
202 snmp_log(LOG_WARNING, "Warning: passphrase chosen is below the length requiremnts of the USM.\n");
204 snmp_set_detail("Password length too short.");
205 QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
211 * Setup for the transform type.
215 if (ISTRANSFORM(hashtype, HMACMD5Auth))
216 EVP_DigestInit(ctx, EVP_md5());
217 else if (ISTRANSFORM(hashtype, HMACSHA1Auth))
218 EVP_DigestInit(ctx, EVP_sha1());
221 return (SNMPERR_GENERR);
225 #endif /* USE_OPENSSL */
229 for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
230 *bufp++ = P[pindex++ % pplen];
233 EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK);
235 if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK*8)) {
236 rval = SNMPERR_USM_ENCRYPTIONERROR;
239 #endif /* USE_OPENSSL */
241 nbytes -= USM_LENGTH_KU_HASHBLOCK;
245 EVP_DigestFinal(ctx, (unsigned char *) Ku, (unsigned int *) kulen);
246 /* what about free() */
248 if (MDupdate(&MD, buf, 0)) {
249 rval = SNMPERR_USM_ENCRYPTIONERROR;
252 *kulen = sc_get_properlength(hashtype, hashtype_len);
253 MDget(&MD, Ku, *kulen);
255 memset(&MD, 0, sizeof(MD));
256 #endif /* USE_OPENSSL */
259 #ifdef SNMP_TESTING_CODE
260 DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P));
261 for(i=0; i < *kulen; i++)
262 DEBUGMSG(("generate_Ku", "%02x",Ku[i]));
263 DEBUGMSG(("generate_Ku","\n"));
264 #endif /* SNMP_TESTING_CODE */
268 memset(buf, 0, sizeof(buf));
274 } /* end generate_Ku() */
277 _KEYTOOLS_NOT_AVAILABLE
278 #endif /* internal or openssl */
283 /*******************************************************************-o-******
291 * *Ku Master key for a given user.
292 * ku_len Length of Ku in bytes.
293 * *Kul Localized key for a given user at engineID.
294 * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT).
297 * SNMPERR_SUCCESS Success.
298 * SNMPERR_GENERR All errors.
301 * Ku MUST be the proper length (currently fixed) for the given hashtype.
303 * Upon successful return, Kul contains the localized form of Ku at
304 * engineID, and the length of the key is stored in kul_len.
306 * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and
307 * originally documented in:
308 * U. Blumenthal, N. C. Hien, B. Wijnen,
309 * "Key Derivation for Network Management Applications",
310 * IEEE Network Magazine, April/May issue, 1997.
313 * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku).
315 * NOTE Localized keys for privacy transforms are generated via
316 * the authentication transform held by the same usmUser.
318 * XXX An engineID of any length is accepted, even if larger than
319 * what is spec'ed for the textual convention.
322 generate_kul( oid *hashtype, u_int hashtype_len,
323 u_char *engineID, size_t engineID_len,
324 u_char *Ku, size_t ku_len,
325 u_char *Kul, size_t *kul_len)
326 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5)
328 int rval = SNMPERR_SUCCESS;
332 u_char buf[SNMP_MAXBUF];
333 void *context = NULL;
334 #ifdef SNMP_TESTING_CODE
342 if ( !hashtype || !engineID || !Ku || !Kul || !kul_len
343 || (engineID_len<=0) || (ku_len<=0) || (*kul_len<=0)
344 || (hashtype_len != USM_LENGTH_OID_TRANSFORM) )
346 QUITFUN(SNMPERR_GENERR, generate_kul_quit);
350 properlength = sc_get_properlength(hashtype, hashtype_len);
351 if (properlength == SNMPERR_GENERR)
352 QUITFUN(SNMPERR_GENERR, generate_kul_quit);
355 if (((int)*kul_len < properlength) || ((int)ku_len < properlength) ) {
356 QUITFUN(SNMPERR_GENERR, generate_kul_quit);
360 * Concatenate Ku and engineID properly, then hash the result.
364 memcpy(buf, Ku, properlength); nbytes += properlength;
365 memcpy(buf+nbytes, engineID, engineID_len); nbytes += engineID_len;
366 memcpy(buf+nbytes, Ku, properlength); nbytes += properlength;
368 rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len);
370 #ifdef SNMP_TESTING_CODE
371 DEBUGMSGTL(("generate_kul", "generating Kul (from Ku): "));
372 for(i=0; i < *kul_len; i++)
373 DEBUGMSG(("generate_kul", "%02x",Kul[i]));
374 DEBUGMSG(("generate_kul", "keytools\n"));
375 #endif /* SNMP_TESTING_CODE */
377 QUITFUN(rval, generate_kul_quit);
384 } /* end generate_kul() */
387 _KEYTOOLS_NOT_AVAILABLE
388 #endif /* internal or openssl */
393 /*******************************************************************-o-******
397 * *hashtype MIB OID for the hash transform type.
398 * hashtype_len Length of the MIB OID hash transform type.
399 * *oldkey Old key that is used to encodes the new key.
400 * oldkey_len Length of oldkey in bytes.
401 * *newkey New key that is encoded using the old key.
402 * newkey_len Length of new key in bytes.
403 * *kcstring Buffer to contain the KeyChange TC string.
404 * *kcstring_len Length of kcstring buffer.
407 * SNMPERR_SUCCESS Success.
408 * SNMPERR_GENERR All errors.
411 * Uses oldkey and acquired random bytes to encode newkey into kcstring
412 * according to the rules of the KeyChange TC described in RFC 2274, Section 5.
414 * Upon successful return, *kcstring_len contains the length of the
417 * ASSUMES Old and new key are always equal to each other, although
418 * this may be less than the transform type hash output
419 * output length (eg, using KeyChange for a DESPriv key when
420 * the user also uses SHA1Auth). This also implies that the
421 * hash placed in the second 1/2 of the key change string
422 * will be truncated before the XOR'ing when the hash output is
423 * larger than that 1/2 of the key change string.
425 * *kcstring_len will be returned as exactly twice that same
426 * length though the input buffer may be larger.
428 * XXX FIX: Does not handle varibable length keys.
429 * XXX FIX: Does not handle keys larger than the hash algorithm used.
432 encode_keychange( oid *hashtype, u_int hashtype_len,
433 u_char *oldkey, size_t oldkey_len,
434 u_char *newkey, size_t newkey_len,
435 u_char *kcstring, size_t *kcstring_len)
436 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5)
438 int rval = SNMPERR_SUCCESS;
442 u_char *tmpbuf = NULL;
443 void *context = NULL;
449 if ( !hashtype || !oldkey || !newkey || !kcstring || !kcstring_len
450 || (oldkey_len<=0) || (newkey_len<=0) || (*kcstring_len<=0)
451 || (hashtype_len != USM_LENGTH_OID_TRANSFORM) )
453 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
457 * Setup for the transform type.
459 properlength = sc_get_properlength(hashtype, hashtype_len);
460 if (properlength == SNMPERR_GENERR)
461 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
463 if ( (oldkey_len != newkey_len) || (*kcstring_len < (2*oldkey_len)) )
465 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
468 properlength = SNMP_MIN((int)oldkey_len, properlength);
471 * Use the old key and some random bytes to encode the new key
472 * in the KeyChange TC format:
473 * . Get random bytes (store in first half of kcstring),
474 * . Hash (oldkey | random_bytes) (into second half of kcstring),
475 * . XOR hash and newkey (into second half of kcstring).
477 * Getting the wrong number of random bytes is considered an error.
479 nbytes = properlength;
481 #if defined(SNMP_TESTING_CODE) && defined(RANDOMZEROS)
482 memset(kcstring, 0, nbytes);
483 DEBUGMSG(("encode_keychange",
484 "** Using all zero bits for \"random\" delta of )"
485 "the keychange string! **\n"));
486 #else /* !SNMP_TESTING_CODE */
487 rval = sc_random(kcstring, &nbytes);
488 QUITFUN(rval, encode_keychange_quit);
489 if ((int)nbytes != properlength) {
490 QUITFUN(SNMPERR_GENERR, encode_keychange_quit);
492 #endif /* !SNMP_TESTING_CODE */
494 tmpbuf = (u_char *)malloc(properlength*2);
496 memcpy(tmpbuf, oldkey, properlength);
497 memcpy(tmpbuf+properlength, kcstring, properlength);
499 *kcstring_len -= properlength;
500 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength*2,
501 kcstring+properlength, kcstring_len);
503 QUITFUN(rval, encode_keychange_quit);
505 *kcstring_len = (properlength*2);
507 kcstring += properlength;
509 while ((int)(nbytes++) < properlength) {
510 u_char kcs = *kcstring;
511 *kcstring++ = kcs ^ *newkey++;
515 encode_keychange_quit:
516 if (rval != SNMPERR_SUCCESS) memset(kcstring, 0, *kcstring_len);
522 } /* end encode_keychange() */
525 _KEYTOOLS_NOT_AVAILABLE
526 #endif /* internal or openssl */
531 /*******************************************************************-o-******
535 * *hashtype MIB OID of the hash transform to use.
536 * hashtype_len Length of the hash transform MIB OID.
537 * *oldkey Old key that is used to encode the new key.
538 * oldkey_len Length of oldkey in bytes.
539 * *kcstring Encoded KeyString buffer containing the new key.
540 * kcstring_len Length of kcstring in bytes.
541 * *newkey Buffer to hold the extracted new key.
542 * *newkey_len Length of newkey in bytes.
545 * SNMPERR_SUCCESS Success.
546 * SNMPERR_GENERR All errors.
549 * Decodes a string of bits encoded according to the KeyChange TC described
550 * in RFC 2274, Section 5. The new key is extracted from *kcstring with
551 * the aid of the old key.
553 * Upon successful return, *newkey_len contains the length of the new key.
556 * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer,
557 * although this length may be less than the hash transform
558 * output. Thus the new key length will be equal to the old
562 /* XXX: if the newkey is not long enough, it should be freed and remalloced */
564 decode_keychange( oid *hashtype, u_int hashtype_len,
565 u_char *oldkey, size_t oldkey_len,
566 u_char *kcstring, size_t kcstring_len,
567 u_char *newkey, size_t *newkey_len)
568 #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5)
570 int rval = SNMPERR_SUCCESS;
571 size_t properlength = 0;
575 tmp_buf[SNMP_MAXBUF];
576 size_t tmp_buf_len = SNMP_MAXBUF;
577 void *context = NULL;
578 u_char *tmpbuf = NULL;
585 if ( !hashtype || !oldkey || !kcstring || !newkey || !newkey_len
586 || (oldkey_len<=0) || (kcstring_len<=0) || (*newkey_len<=0)
587 || (hashtype_len != USM_LENGTH_OID_TRANSFORM) )
589 QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
594 * Setup for the transform type.
596 properlength = sc_get_properlength(hashtype, hashtype_len);
597 if (properlength == SNMPERR_GENERR)
598 QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
601 if ( ((oldkey_len*2) != kcstring_len) || (*newkey_len < oldkey_len) )
603 QUITFUN(SNMPERR_GENERR, decode_keychange_quit);
606 properlength = oldkey_len;
607 *newkey_len = properlength;
610 * Use the old key and the given KeyChange TC string to recover
612 * . Hash (oldkey | random_bytes) (into newkey),
613 * . XOR hash and encoded (second) half of kcstring (into newkey).
615 tmpbuf = (u_char *)malloc(properlength*2);
617 memcpy(tmpbuf, oldkey, properlength);
618 memcpy(tmpbuf+properlength, kcstring, properlength);
620 rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength*2,
621 tmp_buf, &tmp_buf_len);
622 QUITFUN(rval, decode_keychange_quit);
624 memcpy(newkey, tmp_buf, properlength);
625 bufp = kcstring+properlength;
627 while ((int)(nbytes++) < properlength) {
629 *newkey++ = nk ^ *bufp++;
633 decode_keychange_quit:
634 if (rval != SNMPERR_SUCCESS) {
635 memset(newkey, 0, properlength);
637 memset(tmp_buf, 0, SNMP_MAXBUF);
639 if (tmpbuf != NULL) SNMP_FREE(tmpbuf);
643 } /* end decode_keychange() */
646 _KEYTOOLS_NOT_AVAILABLE
647 #endif /* internal or openssl */
649 #endif /* CYGPKG_SNMPAGENT_V3_SUPPORT */