/* * @(#) $Id: rsvp_key.c,v 4.7 1998/08/18 17:31:24 lindell Exp $ */ /************************ rsvp_key.c ******************************** * * * Routines to compute and verify INTEGRITY objects * * * * * *********************************************************************/ /**************************************************************************** RSVPD -- ReSerVation Protocol Daemon USC Information Sciences Institute Marina del Rey, California Current Version: Bob Braden, June 1996. Copyright (c) 1996 by the University of Southern California All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation in source and binary forms for any purpose and without fee is hereby granted, provided that both the above copyright notice and this permission notice appear in all copies, and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed in part by the University of Southern California, Information Sciences Institute. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Other copyrights might apply to parts of this software and are so noted when applicable. ********************************************************************/ /* This code adapted from an experimental implementation of * INTEGRITY by Prakash Jayaraman of USC. */ /* CAVEAT : * For MD5 * Block Length (B) = 64B * Digest Length (L) = 16B * * For HMAC, the authors recommend a minimum key length of L (the digest * length of the corresponding hash algorithm used) and allow keys to be * of variable length. Key lengths less than L are strongly discouraged * as they would decrese the security strength of the function. * * This code assumes a key length of 16B (see load_key()). */ #include #include "rsa_hmac_md5.h" /* External declarations */ int tolower(int); /* Forward declarations */ void incr_key_assoc_seqno(KEY_ASSOC *kap); void set_integrity(struct packet *, int); void fin_integrity(struct packet *); int check_integrity(struct packet *); KEY_ASSOC *get_send_key(int); KEY_ASSOC *get_recv_key(INTEGRITY *, int); KEY_ASSOC *load_key(char *, char *); int hexcnv2(u_char *, char *); INTEGRITY *extract_integrity(struct packet *pkt, INTEGRITY *intp); int check_seqno(INTEGRITY *intp, KEY_ASSOC *kap); /* Global variables */ INTEGRITY Auth_Obj; KEY_ASSOC key_assoc_table[KEY_TABLE_SIZE]; int Key_Assoc_Max; #ifdef SECURITY /* * incr_key_assoc_seqno(): Increment sequence number to send. */ void incr_key_assoc_seqno(KEY_ASSOC *kap) { struct timeval tv; gettimeofday(&tv, NULL); if (kap->kas_seqno[0] == tv.tv_sec) kap->kas_seqno[1]++; /* Increment message counter */ else { /* Time advanced, reset message countr */ kap->kas_seqno[0] = tv.tv_sec; kap->kas_seqno[1] = 0; } } /* * set_integrity() * Initialize INTEGRITY object, if one is required, for * sending packet through specified vif. */ void set_integrity(struct packet *pkt, int vif) { INTEGRITY *intgp; KEY_ASSOC *kap; /* Look up key for specified vif. If none is found, * return without creating INTEGRITY object. */ kap = get_send_key(vif); if (!kap) return; /* Create and initialize INTEGRITY object */ intgp = pkt->pkt_map->rsvp_integrity = &Auth_Obj; Init_Object(intgp, INTEGRITY, INTEGRITY_MD5); memset(intgp->intgr_keyid, 0, KEYID_LENG); memcpy(intgp->intgr_keyid, kap->kas_keyid, KEYID_LENG); /* Increment sequence number for this INTEGRITY * associations. */ incr_key_assoc_seqno(kap); intgp->intgr_seqno[0] = kap->kas_seqno[0]; intgp->intgr_seqno[1] = kap->kas_seqno[1]; memset(intgp->intgr_digest, 0, MD5_LENG); memcpy(intgp->intgr_digest, kap->kas_key, MD5_LENG); } /* * fin_integrity() * Compute crypto digest over given packet and complete * the INTEGRITY object. */ void fin_integrity(struct packet *pkt) { INTEGRITY *intgp = pkt->pkt_map->rsvp_integrity; unsigned char key[MD5_LENG]; unsigned char digest[MD5_LENG]; assert(intgp); /* Extract Key stored in the digest field (length is MD5_LENG). * [The key is stored with the packet and not read in again in * fin_integrity() cause the key might have been deleted between * calls to set and fin. If this assumption is false then the two * functions can be simplified and we might do all key related * stuff here] * Zero out digest field. * Compute HMAC-MD5 keyed-hash * Store final value back into digest field. */ memcpy(key, intgp->intgr_digest, MD5_LENG); memset(intgp->intgr_digest, 0, MD5_LENG); hmac_md5((POINTER) pkt->pkt_data, pkt->pkt_len, key, MD5_LENG, digest); memcpy(intgp->intgr_digest, digest, MD5_LENG); log(LOG_DEBUG, 0, "INTEGRITY fin: Digest\n"); MDPrint(digest); log(LOG_DEBUG, 0, "\n"); } /* check_integrity * Check integrity object. * * Input * pkt : raw input received from the network (in NET byte order). * * Output * 1 If No Integrity Object Present * 0 No Error * <0 (Error Code) if Check Fails */ int check_integrity(struct packet *pkt) { unsigned char *received_digest; unsigned char computed_digest[MD5_LENG]; KEY_ASSOC *kap; INTEGRITY integrity, *intp; int i; pkt->pkt_data->rsvp_cksum = 0; /* Clear checksum field */ if (!(intp = extract_integrity(pkt, &integrity))) /* Copy intgrty obj */ return 1; /* No integrity obj found */ received_digest = integrity.intgr_digest; memset(intp->intgr_digest, 0, MD5_LENG); /* Clear digest field */ for (i = 0; (kap = get_recv_key(&integrity, i)); i++) { hmac_md5((POINTER) pkt->pkt_data, pkt->pkt_len, kap->kas_key, MD5_LENG, computed_digest); log(LOG_DEBUG, 0, "INTEGRITY chk: SeqNoOld, SeqNoNew, DigestIn, DigestCompute\n"); log(LOG_DEBUG, 0, "%u-%u\n%u-%u\n", kap->kas_seqno[0], kap->kas_seqno[1], integrity.intgr_seqno[0], integrity.intgr_seqno[1]); MDPrint(received_digest); log(LOG_DEBUG, 0, "\n"); MDPrint(computed_digest); log(LOG_DEBUG, 0, "\n"); /* Check that recomputed digest matches what arrived. */ if (!(memcmp(received_digest, computed_digest, MD5_LENG))) if(check_seqno(&integrity, kap)) /* Valid sequence number */ break; else return PKT_ERR_REPLAY; } if (!kap) if (i == 0) return PKT_ERR_NOINTASS; /* No matching key */ else return PKT_ERR_INTEGRITY; /* No satisfactory key */ return 0; /* OK */ } /* Return pointer to first entry of the Key Assocation table for * given send interface, or NULL. */ KEY_ASSOC * get_send_key(int vif) { KEY_ASSOC *kap; int i; kap = &key_assoc_table[0]; for (i = 0; i < Key_Assoc_Max; i++, kap++) if (kap->kas_keylen && (kap->kas_if == vif)) break; if (i == Key_Assoc_Max) return(NULL); return(kap); } /* Return pointer to ith receive entry of the Key Assocation table * for given keyid, or NULL. */ KEY_ASSOC * get_recv_key(INTEGRITY *intp, int index) { KEY_ASSOC *kap; int i; if (Obj_Class(intp) != class_INTEGRITY) return(NULL); kap = &key_assoc_table[0]; for (i= 0; i < Key_Assoc_Max; i++, kap++) { if (!kap->kas_keylen) continue; if (kap->kas_if >= 0) continue; if (memcmp(kap->kas_keyid, intp->intgr_keyid, KEYID_LENG)) continue; if (!(index--)) /* Find ith key */ break; } if (i == Key_Assoc_Max) return(NULL); return(kap); } KEY_ASSOC * load_key(char *keyidstr, char *keystr) { KEY_ASSOC *kap; u_char ascikey[2*MD5_LENG], ascikeyid[2*KEYID_LENG], *cp; int i; kap = &key_assoc_table[0]; for (i = 0; i < Key_Assoc_Max; i++, kap++) if (kap->kas_keylen == 0) break; if (i == Key_Assoc_Max) { if (i == KEY_TABLE_SIZE) { log(LOG_ERR, 0, "Too many key associations\n"); return(NULL); } Key_Assoc_Max++; } if (strlen(keyidstr) > 2*KEYID_LENG) { log(LOG_ERR, 0, "Key ID too long\n"); return(NULL); } /* * Convert hex key id string */ memset(kap->kas_keyid, 0, KEYID_LENG); memset(ascikeyid, '0', 2*KEYID_LENG); strncpy((char *) (cp = ascikeyid), keyidstr, strlen(keyidstr)); for (i = 0; i < KEYID_LENG; i++) { if (!hexcnv2(&kap->kas_keyid[i], (char *) cp)) { log(LOG_ERR, 0, "Bad hex char: %c\n", *cp); exit(1); } cp += 2; } if (strlen(keystr) > 2*MD5_LENG) { log(LOG_ERR, 0, "Send key too long\n"); return(NULL); } /* * Convert hex key string */ memset(kap->kas_key, 0, MD5_LENG); memset(ascikey, '0', 2*MD5_LENG); strncpy((char *) (cp = ascikey), keystr, strlen(keystr)); for (i = 0; i < MD5_LENG; i++) { if (!hexcnv2(&kap->kas_key[i], (char *) cp)) { log(LOG_ERR, 0, "Bad hex char: %c\n", *cp); exit(1); } cp += 2; } kap->kas_keylen = MD5_LENG; kap->kas_seqno[0] = kap->kas_seqno[1] = 0xffffffff; /* Initial SeqNo */ return(kap); /* OK */ } char hextab[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; int hexcnv2(u_char *rp, char *cp) { char *xp = strchr(hextab, tolower(*cp++)); char *yp = strchr(hextab, tolower(*cp)); if (!xp || !yp) return 0; *rp = ((xp-hextab)<<4) + (yp-hextab); return 1; } /* extract_integrity * Extract the integrity object from the raw packet, converting the * fields from NET to HOST byte order before storing them. Needed to * extract information about Seq # before integrity check can be * performed. * * Input * pkt : raw input received from the network (in NET byte order). * intp : pointer to object where integrity fields will be stored. * * Output * pointer to integrity object in packet (if integrity present) * NULL (else) */ INTEGRITY * extract_integrity(struct packet *pkt, INTEGRITY *intp) { common_header *hdrp = pkt->pkt_data; char *end_of_data = (char *) pkt->pkt_data + pkt->pkt_len; Object_header *objp, *next; objp = (Object_header *) (hdrp + 1); while (objp < (Object_header *) end_of_data) { if (Obj_Class(objp) == class_INTEGRITY) { memcpy(intp, objp, sizeof(INTEGRITY)); #if BYTE_ORDER == LITTLE_ENDIAN ntoh_object((Object_header *) intp); #endif return ((INTEGRITY *) objp); } NTOH16(Obj_Length(objp)); /* Find length */ next = Next_Object(objp); NTOH16(Obj_Length(objp)); /* Restore byte order */ objp = next; } return NULL; /* Integrity Object not found */ } /* check_seqno * Check the sequence number in the integrity object for replays. * * Input * intp : pointer to integrity object. * kap : pointer to key association object. * * Output * 1 (if sequence number valid) * 0 (else) * * Note * The initial seq no is always accepted (no handshake for now). * Safe if the real sender is actively generating RSVP messages, * as it would then bump the stored sequence number field. * * No tolerance to out of order packets yet. * * A valid sequence number updates the sequence number field in the * key association. */ int check_seqno(INTEGRITY *intp, KEY_ASSOC *kap) { #define new intp->intgr_seqno #define old kap->kas_seqno /* Previous field not invalid */ if (!((old[0] == 0xffffffff) && (old[1] == 0xffffffff))) if (LT(new[0], old[0]) && ((old[0] - new[0]) < UINT_MAX/2)) return 0; else if ((new[0] == old[0]) && (LT(new[1], old[1]))) return 0; else if (LT(old[0], new[0]) && ((new[0] - old[0]) > UINT_MAX/2)) return 0; /* Valid sequence number, update stored value */ old[0] = new[0]; old[1] = new[1]; return 1; } #endif /* SECURITY */