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