5 #include <net-snmp/net-snmp-config.h>
12 #ifdef HAVE_NETINET_IN_H
13 #include <netinet/in.h>
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>
34 # include <openssl/hmac.h>
36 #ifdef USE_INTERNAL_MD5
37 #include <net-snmp/library/md5.h>
41 #include <net-snmp/library/scapi.h>
42 #include <net-snmp/library/keytools.h>
44 #include <net-snmp/library/transform_oids.h>
46 /*******************************************************************-o-******
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.
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)
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
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)
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
;
89 u_char buf
[USM_LENGTH_KU_HASHBLOCK
], *bufp
;
92 EVP_MD_CTX
*ctx
= malloc(sizeof(EVP_MD_CTX
));
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.
117 if (ISTRANSFORM(hashtype
, HMACMD5Auth
))
118 EVP_DigestInit(ctx
, EVP_md5());
119 else if (ISTRANSFORM(hashtype
, HMACSHA1Auth
))
120 EVP_DigestInit(ctx
, EVP_sha1());
123 return (SNMPERR_GENERR
);
127 #endif /* USE_OPENSSL */
131 for (i
= 0; i
< USM_LENGTH_KU_HASHBLOCK
; i
++) {
132 *bufp
++ = P
[pindex
++ % pplen
];
135 EVP_DigestUpdate(ctx
, buf
, USM_LENGTH_KU_HASHBLOCK
);
137 if (MDupdate(&MD
, buf
, USM_LENGTH_KU_HASHBLOCK
* 8)) {
138 rval
= SNMPERR_USM_ENCRYPTIONERROR
;
141 #endif /* USE_OPENSSL */
143 nbytes
-= USM_LENGTH_KU_HASHBLOCK
;
147 EVP_DigestFinal(ctx
, (unsigned char *) Ku
, (unsigned int *) kulen
);
152 if (MDupdate(&MD
, buf
, 0)) {
153 rval
= SNMPERR_USM_ENCRYPTIONERROR
;
156 *kulen
= sc_get_properlength(hashtype
, hashtype_len
);
157 MDget(&MD
, Ku
, *kulen
);
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 */
172 memset(buf
, 0, sizeof(buf
));
178 } /* end generate_Ku() */
181 _KEYTOOLS_NOT_AVAILABLE
182 #endif /* internal or openssl */
183 /*******************************************************************-o-******
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).
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
;
232 u_char buf
[SNMP_MAXBUF
];
233 #ifdef SNMP_TESTING_CODE
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.
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
);
284 } /* end generate_kul() */
287 _KEYTOOLS_NOT_AVAILABLE
288 #endif /* internal or openssl */
289 /*******************************************************************-o-******
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.
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
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
;
338 u_char
*tmpbuf
= NULL
;
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);
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
;
402 while ((int) (nbytes
++) < properlength
) {
403 *kcstring
++ ^= *newkey
++;
407 encode_keychange_quit
:
408 if (rval
!= SNMPERR_SUCCESS
)
409 memset(kcstring
, 0, *kcstring_len
);
414 } /* end encode_keychange() */
417 _KEYTOOLS_NOT_AVAILABLE
418 #endif /* internal or openssl */
419 /*******************************************************************-o-******
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.
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
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;
463 u_char
*bufp
, tmp_buf
[SNMP_MAXBUF
];
464 size_t tmp_buf_len
= SNMP_MAXBUF
;
465 u_char
*tmpbuf
= NULL
;
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
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);
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
;
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
);
527 } /* end decode_keychange() */
530 _KEYTOOLS_NOT_AVAILABLE
531 #endif /* internal or openssl */