/* * Copyright (c) 1997 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notices and this permission notice appear in * all copies of the software and related documentation. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /*----------------------------------------------------------------------------+ | | | Package: krypto | | Author: Eugene Jhong | | | +----------------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include #endif #include "krypto.h" #include "krypto_locl.h" krypto_context * krypto_new (cid, hid, key, keylen, iv, ivlen, seq, state) unsigned cid; unsigned hid; unsigned char *key; unsigned keylen; unsigned char *iv; unsigned ivlen; unsigned char *seq; unsigned state; { krypto_context *cc; cipher_desc *cd; hash_desc *hd; if ((cc = (krypto_context *) malloc (sizeof (krypto_context))) == 0) return 0; if ((cc->key = (unsigned char *) malloc (keylen)) == 0) return 0; cc->keylen = keylen; memcpy (cc->key, key, keylen); cc->state = state; n2l (seq, cc->seqnum); if ((cd = cipher_getdescbyid (cid)) == 0) return 0; cc->c = cipher_new (cd); if (state) cipher_initencrypt (cc->c, key, keylen); else cipher_initdecrypt (cc->c, key, keylen); if (iv) cipher_setiv (cc->c, iv, ivlen); if ((hd = hash_getdescbyid (hid)) == 0) return 0; cc->h = hash_new (hd); return cc; } void krypto_delete (cc) krypto_context *cc; { if (!cc) return; cipher_delete (cc->c); hash_delete (cc->h); if (cc->key) { memset (cc->key, 0, cc->keylen); free (cc->key); } free (cc); } int krypto_msg_getaddlen (cc) krypto_context *cc; { /* return added length of private message which is: pvno (4) + msgtype (4) + confounder_length + checksum_length + length (4) + sequence number (4) + pad (at least blklen) */ return 24 + hash_getoutlen (cc->h) + 8 + cipher_getblklen (cc->c); } int krypto_msg_safe (cc, in, out, len) krypto_context *cc; unsigned char *in; unsigned char *out; int len; { uint32 pvno = KRYPTO_PROT_VERSION; uint32 msgtype = KRYPTO_MSG_SAFE; unsigned char *mark; unsigned hashlen = hash_getoutlen (cc->h); if (cc->state) { /* set protocol version and message type */ l2n(pvno,out); l2n(msgtype,out); /* set length, user data and sequence number */ mark = out; l2n(len,out); memcpy (out, in, len); out += len; l2n (cc->seqnum,out); cc->seqnum = (uint32) (((uint32) cc->seqnum) + 1); /* compute keyed inner hash of length, user data and sequence number */ hash_init (cc->h); hash_update (cc->h, cc->key, cc->keylen); hash_update (cc->h, mark, len + 8); hash_final (cc->h, out); /* compute keyed outer hash of inner hash */ hash_init (cc->h); hash_update (cc->h, cc->key, cc->keylen); hash_update (cc->h, out, hashlen); hash_final (cc->h, out); /* return length */ return len + 16 + hashlen; } else { uint32 outlen; uint32 seqnum; unsigned char checksum[512]; if (len < 16 + hashlen) return -1; /* get protocol version and message type */ n2l(in,pvno); n2l(in,msgtype); if ((pvno != KRYPTO_PROT_VERSION) || (msgtype != KRYPTO_MSG_SAFE)) return -1; /* get length and verify if matches packet length */ mark = in; n2l(in,outlen); if (outlen != len - 16 - hashlen) return -1; /* computer inner hash, outer hash and compare */ hash_init (cc->h); hash_update (cc->h, cc->key, cc->keylen); hash_update (cc->h, mark, outlen + 8); hash_final (cc->h, checksum); hash_init (cc->h); hash_update (cc->h, cc->key, cc->keylen); hash_update (cc->h, checksum, hashlen); hash_final (cc->h, checksum); if (memcmp (checksum, in+outlen+4, hashlen)) return -1; /* get sequence number and compare */ mark = in; in += outlen; n2l(in,seqnum); if (seqnum != (uint32) cc->seqnum) return -1; cc->seqnum = (uint32) (((uint32) cc->seqnum) + 1); /* copy message and return length */ memcpy (out, mark, outlen); return outlen; } } int krypto_msg_priv (cc, in, out, len) krypto_context *cc; unsigned char *in; unsigned char *out; int len; { uint32 pvno = KRYPTO_PROT_VERSION; uint32 msgtype = KRYPTO_MSG_PRIV; unsigned char *mark; unsigned conflen = 8; unsigned hashlen = hash_getoutlen (cc->h); /* at least give safe encoding if no cipher */ if (cc->c->cipher->id == CIPHER_ID_NONE) return (krypto_msg_safe (cc, in, out, len)); if (cc->state) { unsigned char confounder[512]; uint32 cryptlen = cipher_getoutlen (cc->c, len+hashlen+conflen+8); /* initialize confounder */ krypto_rand_conf (confounder, conflen); /* set protocol version and message type */ l2n(pvno,out); l2n(msgtype,out); /* initialize packet to all zeros */ mark = out; memset (out, 0, cryptlen); /* set confounder, length, user data, and sequence number */ /* leave room for checksum */ memcpy (out, confounder, conflen); out += conflen; out += hashlen; l2n(len,out); memcpy (out, in, len); out += len; l2n (cc->seqnum,out); cc->seqnum = (uint32) (((uint32) cc->seqnum) + 1); /* set checksum on confounder, length, user data, zeroed checksum */ /* and sequence number */ hash_init (cc->h); hash_update (cc->h, mark, len+hashlen+conflen+8); hash_final (cc->h, mark + conflen); /* encrypt confounder, checksum, length, user-data and sequence number */ cipher_crypt (cc->c, mark, mark, cryptlen); /* return length */ return cryptlen + 8; } else { uint32 outlen; unsigned char checksum[512]; uint32 seqnum; if (len < 16 + hashlen + conflen) return -1; /* get protocol version and message type */ n2l(in,pvno); n2l(in,msgtype); if ((pvno != KRYPTO_PROT_VERSION) || (msgtype != KRYPTO_MSG_PRIV)) return -1; len -= 8; /* verify that data was properly padded for this cipher */ if (len != cipher_getoutlen (cc->c, len)) return -1; /* decrypt data */ cipher_crypt (cc->c, in, in, len); /* get confounder, checksum, and user data length */ mark = in; in += conflen; memcpy (checksum, in, hashlen); memset (in, 0, hashlen); in += hashlen; n2l(in,outlen); /* verify length is correct */ if (outlen > len - 8 - conflen - hashlen) return -1; /* compute checksum and verify */ hash_init (cc->h); hash_update (cc->h, mark, outlen+hashlen+conflen+8); hash_final (cc->h, mark+conflen); if (memcmp (checksum, mark+conflen, hashlen)) return -1; mark = in; in += outlen; /* get sequence number and verify */ n2l(in,seqnum); if (seqnum != (uint32) cc->seqnum) return -1; cc->seqnum = (uint32) (((uint32) cc->seqnum) + 1); /* copy message and return length */ memcpy (out, mark, outlen); return outlen; } }