OpenSS7
SS7 for the
Common Man

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified:

Home Overview Status News Documentation Resources About
   
 Overview
 Status
 News
 Documentation
 Resources
 About

   
Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sctp/sctp_cookie.c


File /code/strss7/drivers/sctp/sctp_cookie.c



#ident "@(#) $RCSfile: sctp_cookie.c,v $ $Name:  $($Revision: 0.8.2.7 $) $Date: 2003/05/30 21:15:52 $"

static char const ident[] =
    "$RCSfile: sctp_cookie.c,v $ $Name:  $($Revision: 0.8.2.7 $) $Date: 2003/05/30 21:15:52 $";

/*
 *  This file contains all the algorithms for generating MAC for SCTP cookies
 *  and for verifying existing cookies.  The SHA-1 and MD5 code takes a lot of
 *  space (about 8k kernel space per algorithm) so I have added some
 *  configuration options to allow removing unused algorithms.
 */

#define __NO_VERSION__

#include <linux/config.h>
#include <linux/version.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>

#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>
#include <sys/dki.h>

#include <linux/random.h>	/* for secure_tcp_sequence_number */

#include "../debug.h"
#include "../bufq.h"

#include "sctp.h"
#include "sctp_defs.h"
#include "sctp_cookie.h"

/*
 *  =========================================================================
 *
 *  SHA-1
 *
 *  =========================================================================
 *  
 *  This is a GPL version fo the FIPS 180-1 Secure Hash Algorithm written by
 *  Brian Bidulock <bidulock@openss7.org>.
 *
 *  Adapted from code written November 2000 by David Ireland of DI Management
 *  Services Pty Limited <code@di-mgt.com.au>
 *  
 *  Adapted from code in the Python Cryptography Toolkit, version 1.0.0 by
 *  A.M.  Kuchling 1995.
 */

/*
 *  The structure for storing SHS info
 */
typedef struct {
	u32 dig[5];				/* Message digest */
	u32 lo, hi;				/* 64-bit bit count */
	u32 dat[16];				/* SHS data buffer */
} SHA_CTX;

/*
 *  The SHS f()-functions.  The f1 and f3 functions can be optimized to save one
 *  boolean operation each - thanks to Rich Schroeppel, rcs@cs.arizona.edu for
 *  discovering this
 */
#define f1(x,y,z)   (z^(x&(y^z)))	/* Rounds 0-19 */
#define f2(x,y,z)   (x^y^z)	/* Rounds 20-39 */
#define f3(x,y,z)   ((x&y)|(z&(x|y)))	/* Rounds 40-59 */
#define f4(x,y,z)   (x^y^z)	/* Rounds 60-79 */

/*
 *  32-bit rotate left - kludged with shifts
 */
#define ROTL(n,X)   (((X)<<n)|((X)>>(32-n)))

/*
 *  The initial expanding function.  The hash function is defined over an
 *  80-UINT2 expanded input array W, where the first 16 are copies of the input
 *  data, and the remaining 64 are defined by
 *
 *	W[i] = W[i - 16] ^ W[i - 14] ^ W[i - 8] ^ W[i - 3]
 *
 *  This implementation generates these values on the fly in a circular buffer -
 *  thanks to Colin Plumb, colin@nyx10.cs.du.edu for this optimization.
 *
 *  The updated SHS changes the expanding function by adding a rotate of 1 bit.
 *  Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor for this
 *  information
 */

#define x(W,i) \
	(W[i & 15] = \
	  ROTL(1, (W[i&15] ^ W[(i-14)&15] ^ W[(i-8)&15] ^ W[(i-3)&15])))

/*
 *  The prototype SHS sub-round.  The fundamental sub-round is:
 *
 *	a' = e + ROTL(5, a) + f(b, c, d) + k + data;
 *	b' = a;
 *	c' = ROTL(30, b);
 *	d' = c;
 *	e' = d;
 *
 *  but this is implemented by unrolling the loop 5 times and renaming the
 *  variables (e, a, b, c, d) = (a', b', c', d', e') each iteration.  This
 *  code is then replicated 20 times for each of the 4 functions, using the next
 *  20 values from the W[] array each time
 */

#define subRound(a,b,c,d,e,f,k,data) \
    (e += ROTL(5,a)+f(b,c,d)+k+data, b = ROTL(30,b))

/*
 *  Initialize the SHS values
 */
STATIC void SHAInit(SHA_CTX * sha1)
{
	/* Set the h-vars to their initial values */
	sha1->dig[0] = 0x67452301L;
	sha1->dig[1] = 0xefcdab89L;
	sha1->dig[2] = 0x98badcfeL;
	sha1->dig[3] = 0x10325476L;
	sha1->dig[4] = 0xc3d2e1f0L;

	/* Initialise bit count */
	sha1->lo = sha1->hi = 0;
}

/*
 *  Perform the SHS transformation.  Note that this code, like MD5, seems to
 *  break some optimizing compilers due to the complexity of the expressions and
 *  the size of the basic block.  It may be necessary to split it into sections,
 *  e.g. based on the four subrounds
 *
 *  Note that this corrupts the sha1->data area
 */
STATIC void SHSTransform(u32 * dig, u32 * dat)
{
	u32 A, B, C, D, E;			/* Local vars */
	u32 xd[16];				/* Expanded data */

	/* Set up first buffer and local data buffer */
	A = dig[0];
	B = dig[1];
	C = dig[2];
	D = dig[3];
	E = dig[4];
	memcpy(xd, dat, 64);

	/* Heavy mangling, in 4 sub-rounds of 20 interations each. */
	subRound(A, B, C, D, E, f1, 0x5a827999L, xd[0]);
	subRound(E, A, B, C, D, f1, 0x5a827999L, xd[1]);
	subRound(D, E, A, B, C, f1, 0x5a827999L, xd[2]);
	subRound(C, D, E, A, B, f1, 0x5a827999L, xd[3]);
	subRound(B, C, D, E, A, f1, 0x5a827999L, xd[4]);
	subRound(A, B, C, D, E, f1, 0x5a827999L, xd[5]);
	subRound(E, A, B, C, D, f1, 0x5a827999L, xd[6]);
	subRound(D, E, A, B, C, f1, 0x5a827999L, xd[7]);
	subRound(C, D, E, A, B, f1, 0x5a827999L, xd[8]);
	subRound(B, C, D, E, A, f1, 0x5a827999L, xd[9]);
	subRound(A, B, C, D, E, f1, 0x5a827999L, xd[10]);
	subRound(E, A, B, C, D, f1, 0x5a827999L, xd[11]);
	subRound(D, E, A, B, C, f1, 0x5a827999L, xd[12]);
	subRound(C, D, E, A, B, f1, 0x5a827999L, xd[13]);
	subRound(B, C, D, E, A, f1, 0x5a827999L, xd[14]);
	subRound(A, B, C, D, E, f1, 0x5a827999L, xd[15]);
	subRound(E, A, B, C, D, f1, 0x5a827999L, x(xd, 16));
	subRound(D, E, A, B, C, f1, 0x5a827999L, x(xd, 17));
	subRound(C, D, E, A, B, f1, 0x5a827999L, x(xd, 18));
	subRound(B, C, D, E, A, f1, 0x5a827999L, x(xd, 19));

	subRound(A, B, C, D, E, f2, 0x63d9eba1L, x(xd, 20));
	subRound(E, A, B, C, D, f2, 0x63d9eba1L, x(xd, 21));
	subRound(D, E, A, B, C, f2, 0x63d9eba1L, x(xd, 22));
	subRound(C, D, E, A, B, f2, 0x63d9eba1L, x(xd, 23));
	subRound(B, C, D, E, A, f2, 0x63d9eba1L, x(xd, 24));
	subRound(A, B, C, D, E, f2, 0x63d9eba1L, x(xd, 25));
	subRound(E, A, B, C, D, f2, 0x63d9eba1L, x(xd, 26));
	subRound(D, E, A, B, C, f2, 0x63d9eba1L, x(xd, 27));
	subRound(C, D, E, A, B, f2, 0x63d9eba1L, x(xd, 28));
	subRound(B, C, D, E, A, f2, 0x63d9eba1L, x(xd, 29));
	subRound(A, B, C, D, E, f2, 0x63d9eba1L, x(xd, 30));
	subRound(E, A, B, C, D, f2, 0x63d9eba1L, x(xd, 31));
	subRound(D, E, A, B, C, f2, 0x63d9eba1L, x(xd, 32));
	subRound(C, D, E, A, B, f2, 0x63d9eba1L, x(xd, 33));
	subRound(B, C, D, E, A, f2, 0x63d9eba1L, x(xd, 34));
	subRound(A, B, C, D, E, f2, 0x63d9eba1L, x(xd, 35));
	subRound(E, A, B, C, D, f2, 0x63d9eba1L, x(xd, 36));
	subRound(D, E, A, B, C, f2, 0x63d9eba1L, x(xd, 37));
	subRound(C, D, E, A, B, f2, 0x63d9eba1L, x(xd, 38));
	subRound(B, C, D, E, A, f2, 0x63d9eba1L, x(xd, 39));

	subRound(A, B, C, D, E, f3, 0x8f1bbcdcL, x(xd, 40));
	subRound(E, A, B, C, D, f3, 0x8f1bbcdcL, x(xd, 41));
	subRound(D, E, A, B, C, f3, 0x8f1bbcdcL, x(xd, 42));
	subRound(C, D, E, A, B, f3, 0x8f1bbcdcL, x(xd, 43));
	subRound(B, C, D, E, A, f3, 0x8f1bbcdcL, x(xd, 44));
	subRound(A, B, C, D, E, f3, 0x8f1bbcdcL, x(xd, 45));
	subRound(E, A, B, C, D, f3, 0x8f1bbcdcL, x(xd, 46));
	subRound(D, E, A, B, C, f3, 0x8f1bbcdcL, x(xd, 47));
	subRound(C, D, E, A, B, f3, 0x8f1bbcdcL, x(xd, 48));
	subRound(B, C, D, E, A, f3, 0x8f1bbcdcL, x(xd, 49));
	subRound(A, B, C, D, E, f3, 0x8f1bbcdcL, x(xd, 50));
	subRound(E, A, B, C, D, f3, 0x8f1bbcdcL, x(xd, 51));
	subRound(D, E, A, B, C, f3, 0x8f1bbcdcL, x(xd, 52));
	subRound(C, D, E, A, B, f3, 0x8f1bbcdcL, x(xd, 53));
	subRound(B, C, D, E, A, f3, 0x8f1bbcdcL, x(xd, 54));
	subRound(A, B, C, D, E, f3, 0x8f1bbcdcL, x(xd, 55));
	subRound(E, A, B, C, D, f3, 0x8f1bbcdcL, x(xd, 56));
	subRound(D, E, A, B, C, f3, 0x8f1bbcdcL, x(xd, 57));
	subRound(C, D, E, A, B, f3, 0x8f1bbcdcL, x(xd, 58));
	subRound(B, C, D, E, A, f3, 0x8f1bbcdcL, x(xd, 59));

	subRound(A, B, C, D, E, f4, 0xca62c1d6L, x(xd, 60));
	subRound(E, A, B, C, D, f4, 0xca62c1d6L, x(xd, 61));
	subRound(D, E, A, B, C, f4, 0xca62c1d6L, x(xd, 62));
	subRound(C, D, E, A, B, f4, 0xca62c1d6L, x(xd, 63));
	subRound(B, C, D, E, A, f4, 0xca62c1d6L, x(xd, 64));
	subRound(A, B, C, D, E, f4, 0xca62c1d6L, x(xd, 65));
	subRound(E, A, B, C, D, f4, 0xca62c1d6L, x(xd, 66));
	subRound(D, E, A, B, C, f4, 0xca62c1d6L, x(xd, 67));
	subRound(C, D, E, A, B, f4, 0xca62c1d6L, x(xd, 68));
	subRound(B, C, D, E, A, f4, 0xca62c1d6L, x(xd, 69));
	subRound(A, B, C, D, E, f4, 0xca62c1d6L, x(xd, 70));
	subRound(E, A, B, C, D, f4, 0xca62c1d6L, x(xd, 71));
	subRound(D, E, A, B, C, f4, 0xca62c1d6L, x(xd, 72));
	subRound(C, D, E, A, B, f4, 0xca62c1d6L, x(xd, 73));
	subRound(B, C, D, E, A, f4, 0xca62c1d6L, x(xd, 74));
	subRound(A, B, C, D, E, f4, 0xca62c1d6L, x(xd, 75));
	subRound(E, A, B, C, D, f4, 0xca62c1d6L, x(xd, 76));
	subRound(D, E, A, B, C, f4, 0xca62c1d6L, x(xd, 77));
	subRound(C, D, E, A, B, f4, 0xca62c1d6L, x(xd, 78));
	subRound(B, C, D, E, A, f4, 0xca62c1d6L, x(xd, 79));

	/* Build message digest */
	dig[0] += A;
	dig[1] += B;
	dig[2] += C;
	dig[3] += D;
	dig[4] += E;
}

/*
 *  When run on a little-endian CPU we need to perform byte reversal on an array
 *  of long words.
 */
#ifdef __LITTLE_ENDIAN
STATIC void longReverse(u32 * buf, int cnt)
{
	u32 val;

	cnt /= sizeof(u32);
	while (cnt--) {
		val = *buf;
		val = ((val & 0xff00ff00L) >> 8) | ((val & 0x00ff00ffL) << 8);
		*buf++ = (val << 16) | (val >> 16);
	}
}
#else
#define longReverse(__buf, __cnt) do { } while(0)
#endif

/*
 *  Update SHS for a block of data
 */
STATIC void SHAUpdate(SHA_CTX * sha1, u8 * buf, int len)
{
	u32 tmp;
	int cnt;

	/* Update bitcount */
	tmp = sha1->lo;
	if ((sha1->lo = tmp + ((u32) len << 3)) < tmp)
		sha1->hi++;	/* Carry from low to high */
	sha1->hi += len >> 29;

	/* Get count of bytes already in data */
	cnt = (int) (tmp >> 3) & 0x3F;

	/* Handle any leading odd-sized chunks */
	if (cnt) {
		u8 *p = (u8 *) sha1->dat + cnt;

		cnt = 64 - cnt;
		if (len < cnt) {
			memcpy(p, buf, len);
			return;
		}
		memcpy(p, buf, cnt);
		longReverse(sha1->dat, 64);
		SHSTransform(sha1->dig, sha1->dat);
		buf += cnt;
		len -= cnt;
	}

	/* Process data in 64 chunks */
	while (len >= 64) {
		memcpy(sha1->dat, buf, 64);
		longReverse(sha1->dat, 64);
		SHSTransform(sha1->dig, sha1->dat);
		buf += 64;
		len -= 64;
	}

	/* Handle any remaining bytes of data. */
	memcpy(sha1->dat, buf, len);
}

/*
 *  Final wrapup - pad to 64-byte boundary with the bit pattern 1 0*
 *  (64-bit count of bits processed, MSB-first)
 */
STATIC void SHAFinal(u8 * out, SHA_CTX * sha1)
{
	int len;
	unsigned int i, j;
	u8 *dat;

	/* Compute number of bytes mod 64 */
	len = (int) sha1->lo;
	len = (len >> 3) & 0x3F;

	/* 
	 *  Set the first char of padding to 0x80.  This is safe since there is
	 *  always at least one byte free
	 */
	dat = (u8 *) sha1->dat + len;
	*dat++ = 0x80;

	/* Bytes of padding needed to make 64 bytes */
	len = 64 - 1 - len;

	/* Pad out to 56 mod 64 */
	if (len < 8) {
		/* Two lots of padding: Pad the first block to 64 bytes */
		memset(dat, 0, len);
		longReverse(sha1->dat, 64);
		SHSTransform(sha1->dig, sha1->dat);

		/* Now fill the next block with 56 bytes */
		memset(sha1->dat, 0, 64 - 8);
	} else
		/* Pad block to 56 bytes */
		memset(dat, 0, len - 8);

	/* Append length in bits and transform */
	sha1->dat[14] = sha1->hi;
	sha1->dat[15] = sha1->lo;

	longReverse(sha1->dat, 64 - 8);
	SHSTransform(sha1->dig, sha1->dat);

	/* Output SHA digest in byte array */
	for (i = 0, j = 0; j < 20; i++, j += 4) {
		out[j + 3] = (u8) (sha1->dig[i] & 0xff);
		out[j + 2] = (u8) ((sha1->dig[i] >> 8) & 0xff);
		out[j + 1] = (u8) ((sha1->dig[i] >> 16) & 0xff);
		out[j] = (u8) ((sha1->dig[i] >> 24) & 0xff);
	}

	/* Zeroise sensitive stuff */
	memset(sha1, 0, sizeof(sha1));
}

/*
 *  -------------------------------------------------------------------------
 *
 *  HMAC-SHA-1
 *
 *  -------------------------------------------------------------------------
 *
 *  Code adapted directly from RFC 2401.
 */
STATIC void hmac_sha1(u8 * text, int tlen, u8 * key, int klen, u8 * digest)
{
	SHA_CTX context;
	u8 k_ipad[64];
	u8 k_opad[64];
	u8 tk[16];

	int i;

	if (klen > 64) {
		SHA_CTX ctx;
		SHAInit(&ctx);
		SHAUpdate(&ctx, key, klen);
		SHAFinal(tk, &ctx);

		key = tk;
		klen = 16;
	}

	memset(k_ipad, 0, sizeof(k_ipad));
	memset(k_opad, 0, sizeof(k_opad));
	memcpy(k_ipad, key, klen);
	memcpy(k_opad, key, klen);

	for (i = 0; i < 64; i++) {
		k_ipad[i] ^= 0x36;
		k_opad[i] ^= 0x5c;
	}

	/* inner */
	SHAInit(&context);
	SHAUpdate(&context, k_ipad, 64);
	SHAUpdate(&context, text, tlen);
	SHAFinal(digest, &context);

	/* outer */
	SHAInit(&context);
	SHAUpdate(&context, k_opad, 64);
	SHAUpdate(&context, digest, 20);
	SHAFinal(digest, &context);
}

/*
 *  =========================================================================
 *
 *  MD5
 *
 *  =========================================================================
 */

typedef struct {
	u32 buf[4];
	u32 lo, hi;
	u32 dat[16];
} MD5_CTX;

/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))

/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f,w,x,y,z,dat,s) \
	 (w += f(x,y,z) + dat, w = (w<<s | w>>(32-s)) + x)

/*
 *  The core of the MD5 algorithm, this alters an existing MD5 hash to reflect
 *  the addition of 16 longwords of new data.  MD5Update blocks the data and
 *  converts bytes into longwords for this routine.
 */
STATIC void MD5Transform(u32 dig[4], u32 const dat[16])
{
	register u32 a, b, c, d;

	a = dig[0];
	b = dig[1];
	c = dig[2];
	d = dig[3];

	MD5STEP(F1, a, b, c, d, dat[0] + 0xd76aa478, 7);
	MD5STEP(F1, d, a, b, c, dat[1] + 0xe8c7b756, 12);
	MD5STEP(F1, c, d, a, b, dat[2] + 0x242070db, 17);
	MD5STEP(F1, b, c, d, a, dat[3] + 0xc1bdceee, 22);
	MD5STEP(F1, a, b, c, d, dat[4] + 0xf57c0faf, 7);
	MD5STEP(F1, d, a, b, c, dat[5] + 0x4787c62a, 12);
	MD5STEP(F1, c, d, a, b, dat[6] + 0xa8304613, 17);
	MD5STEP(F1, b, c, d, a, dat[7] + 0xfd469501, 22);
	MD5STEP(F1, a, b, c, d, dat[8] + 0x698098d8, 7);
	MD5STEP(F1, d, a, b, c, dat[9] + 0x8b44f7af, 12);
	MD5STEP(F1, c, d, a, b, dat[10] + 0xffff5bb1, 17);
	MD5STEP(F1, b, c, d, a, dat[11] + 0x895cd7be, 22);
	MD5STEP(F1, a, b, c, d, dat[12] + 0x6b901122, 7);
	MD5STEP(F1, d, a, b, c, dat[13] + 0xfd987193, 12);
	MD5STEP(F1, c, d, a, b, dat[14] + 0xa679438e, 17);
	MD5STEP(F1, b, c, d, a, dat[15] + 0x49b40821, 22);

	MD5STEP(F2, a, b, c, d, dat[1] + 0xf61e2562, 5);
	MD5STEP(F2, d, a, b, c, dat[6] + 0xc040b340, 9);
	MD5STEP(F2, c, d, a, b, dat[11] + 0x265e5a51, 14);
	MD5STEP(F2, b, c, d, a, dat[0] + 0xe9b6c7aa, 20);
	MD5STEP(F2, a, b, c, d, dat[5] + 0xd62f105d, 5);
	MD5STEP(F2, d, a, b, c, dat[10] + 0x02441453, 9);
	MD5STEP(F2, c, d, a, b, dat[15] + 0xd8a1e681, 14);
	MD5STEP(F2, b, c, d, a, dat[4] + 0xe7d3fbc8, 20);
	MD5STEP(F2, a, b, c, d, dat[9] + 0x21e1cde6, 5);
	MD5STEP(F2, d, a, b, c, dat[14] + 0xc33707d6, 9);
	MD5STEP(F2, c, d, a, b, dat[3] + 0xf4d50d87, 14);
	MD5STEP(F2, b, c, d, a, dat[8] + 0x455a14ed, 20);
	MD5STEP(F2, a, b, c, d, dat[13] + 0xa9e3e905, 5);
	MD5STEP(F2, d, a, b, c, dat[2] + 0xfcefa3f8, 9);
	MD5STEP(F2, c, d, a, b, dat[7] + 0x676f02d9, 14);
	MD5STEP(F2, b, c, d, a, dat[12] + 0x8d2a4c8a, 20);

	MD5STEP(F3, a, b, c, d, dat[5] + 0xfffa3942, 4);
	MD5STEP(F3, d, a, b, c, dat[8] + 0x8771f681, 11);
	MD5STEP(F3, c, d, a, b, dat[11] + 0x6d9d6122, 16);
	MD5STEP(F3, b, c, d, a, dat[14] + 0xfde5380c, 23);
	MD5STEP(F3, a, b, c, d, dat[1] + 0xa4beea44, 4);
	MD5STEP(F3, d, a, b, c, dat[4] + 0x4bdecfa9, 11);
	MD5STEP(F3, c, d, a, b, dat[7] + 0xf6bb4b60, 16);
	MD5STEP(F3, b, c, d, a, dat[10] + 0xbebfbc70, 23);
	MD5STEP(F3, a, b, c, d, dat[13] + 0x289b7ec6, 4);
	MD5STEP(F3, d, a, b, c, dat[0] + 0xeaa127fa, 11);
	MD5STEP(F3, c, d, a, b, dat[3] + 0xd4ef3085, 16);
	MD5STEP(F3, b, c, d, a, dat[6] + 0x04881d05, 23);
	MD5STEP(F3, a, b, c, d, dat[9] + 0xd9d4d039, 4);
	MD5STEP(F3, d, a, b, c, dat[12] + 0xe6db99e5, 11);
	MD5STEP(F3, c, d, a, b, dat[15] + 0x1fa27cf8, 16);
	MD5STEP(F3, b, c, d, a, dat[2] + 0xc4ac5665, 23);

	MD5STEP(F4, a, b, c, d, dat[0] + 0xf4292244, 6);
	MD5STEP(F4, d, a, b, c, dat[7] + 0x432aff97, 10);
	MD5STEP(F4, c, d, a, b, dat[14] + 0xab9423a7, 15);
	MD5STEP(F4, b, c, d, a, dat[5] + 0xfc93a039, 21);
	MD5STEP(F4, a, b, c, d, dat[12] + 0x655b59c3, 6);
	MD5STEP(F4, d, a, b, c, dat[3] + 0x8f0ccc92, 10);
	MD5STEP(F4, c, d, a, b, dat[10] + 0xffeff47d, 15);
	MD5STEP(F4, b, c, d, a, dat[1] + 0x85845dd1, 21);
	MD5STEP(F4, a, b, c, d, dat[8] + 0x6fa87e4f, 6);
	MD5STEP(F4, d, a, b, c, dat[15] + 0xfe2ce6e0, 10);
	MD5STEP(F4, c, d, a, b, dat[6] + 0xa3014314, 15);
	MD5STEP(F4, b, c, d, a, dat[13] + 0x4e0811a1, 21);
	MD5STEP(F4, a, b, c, d, dat[4] + 0xf7537e82, 6);
	MD5STEP(F4, d, a, b, c, dat[11] + 0xbd3af235, 10);
	MD5STEP(F4, c, d, a, b, dat[2] + 0x2ad7d2bb, 15);
	MD5STEP(F4, b, c, d, a, dat[9] + 0xeb86d391, 21);

	dig[0] += a;
	dig[1] += b;
	dig[2] += c;
	dig[3] += d;
}

#ifdef __BIG_ENDIAN
void byteSwap(u32 * buf, unsigned cnt)
{
	u8 *p = (u8 *) buf;

	do {
		*buf++ = (u32) ((unsigned) p[3] << 8 | p[2]) << 16 | ((unsigned) p[1] << 8 | p[0]);
		p += 4;
	} while (--cnt);
}
#else
#define byteSwap(__buf,__cnt)
#endif

/*
 * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization constants.
 */
STATIC void MD5Init(MD5_CTX * md5)
{
	md5->buf[0] = 0x67452301;
	md5->buf[1] = 0xefcdab89;
	md5->buf[2] = 0x98badcfe;
	md5->buf[3] = 0x10325476;

	md5->lo = 0;
	md5->hi = 0;
}

/*
 * Update context to reflect the concatenation of another buffer full
 * of bytes.
 */
STATIC void MD5Update(MD5_CTX * md5, u8 const *buf, unsigned len)
{
	u32 t;

	/* Update byte count */

	t = md5->lo;
	if ((md5->lo = t + len) < t)
		md5->hi++;	/* Carry from low to high */

	t = 64 - (t & 0x3f);	/* Space available in md5->dat (at least 1) */
	if (t > len) {
		memcpy((u8 *) md5->dat + 64 - t, buf, len);
		return;
	}
	/* First chunk is an odd size */
	memcpy((u8 *) md5->dat + 64 - t, buf, t);
	byteSwap(md5->dat, 16);
	MD5Transform(md5->buf, md5->dat);
	buf += t;
	len -= t;

	/* Process data in 64-byte chunks */
	while (len >= 64) {
		memcpy(md5->dat, buf, 64);
		byteSwap(md5->dat, 16);
		MD5Transform(md5->buf, md5->dat);
		buf += 64;
		len -= 64;
	}

	/* Handle any remaining bytes of data. */
	memcpy(md5->dat, buf, len);
}

/*
 * Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
STATIC void MD5Final(u8 dig[16], MD5_CTX * md5)
{
	int count = md5->lo & 0x3f;		/* Number of bytes in md5->dat */
	u8 *p = (u8 *) md5->dat + count;

	/* Set the first char of padding to 0x80.  There is always room. */
	*p++ = 0x80;

	/* Bytes of padding needed to make 56 bytes (-8..55) */
	count = 56 - 1 - count;

	if (count < 0) {	/* Padding forces an extra block */
		memset(p, 0, count + 8);
		byteSwap(md5->dat, 16);
		MD5Transform(md5->buf, md5->dat);
		p = (u8 *) md5->dat;
		count = 56;
	}
	memset(p, 0, count);
	byteSwap(md5->dat, 14);

	/* Append length in bits and transform */
	md5->dat[14] = md5->lo << 3;
	md5->dat[15] = md5->hi << 3 | md5->lo >> 29;
	MD5Transform(md5->buf, md5->dat);

	byteSwap(md5->buf, 4);
	memcpy(dig, md5->buf, 16);
	memset(md5, 0, sizeof(md5));	/* In case it's sensitive */
}

/*
 *  -------------------------------------------------------------------------
 *
 *  HMAC-MD5
 *
 *  -------------------------------------------------------------------------
 *
 *  Code adapted directly from RFC 2401.
 */
STATIC void hmac_md5(u8 * text, int tlen, u8 * key, int klen, u8 * digest)
{
	MD5_CTX context;
	u8 k_ipad[65];
	u8 k_opad[65];
	u8 tk[16];

	int i;

	if (klen > 64) {
		MD5_CTX ctx;
		MD5Init(&ctx);
		MD5Update(&ctx, key, klen);
		MD5Final(tk, &ctx);

		key = tk;
		klen = 16;
	}

	memset(k_ipad, 0, sizeof(k_ipad));
	memset(k_opad, 0, sizeof(k_opad));
	memcpy(k_ipad, key, klen);
	memcpy(k_opad, key, klen);

	for (i = 0; i < 64; i++) {
		k_ipad[i] ^= 0x36;
		k_opad[i] ^= 0x5c;
	}

	/* inner */
	MD5Init(&context);
	MD5Update(&context, k_ipad, 64);
	MD5Update(&context, text, tlen);
	MD5Final(digest, &context);

	/* outer */
	MD5Init(&context);
	MD5Update(&context, k_opad, 64);
	MD5Update(&context, digest, 16);
	MD5Final(digest, &context);
}

/*
 *  =========================================================================
 *
 *  SCTP Key handling
 *
 *  =========================================================================
 *
 *  This algorithm uses 2^n keys (NUM_KEYS) which are recycled quickly and
 *  evenly.  Each key has a key tag which is placed in the SCTP cookie.  Each
 *  key is valid for the cookie lifespan plus a key life.  The key life should
 *  be set to whatever cookie life increment has been permitted.  When a cookie
 *  is checked for validity, its MAC is verified using the key with the key tag
 *  in the cookie.  If the key has already been recycled, the tagged key will
 *  not fit the lock anymore.  Note that the keys are cycled only as quickly as
 *  the requests for signatures come in.  This adds another degree of
 *  variability to the key selection.
 */

struct sctp_key {
	union {
		u32 seq[16];
		u8 key[64];
	} u;
	unsigned int last;
	unsigned long created;
};

#define NUM_KEYS 4
STATIC struct sctp_key sctp_keys[NUM_KEYS];
STATIC int sctp_current_key = 0;

extern u32 secure_tcp_sequence_number(u32, u32, u16, u16);

/*
 *  TODO:  This rekeying is too predicatable.  There are several things bad
 *  about it: (1) the key has a historic component, which is bad; (2) initial
 *  keys have zeros in alot of places which makes it no stronger than if only
 *  32 bit keys were used.
 */
STATIC void sctp_rekey(k)
	int k;
{
	u32 *seq;
	int n = (sctp_keys[k].last + 1) & 0xf;

	sctp_keys[k].last = n;
	seq = &sctp_keys[k].u.seq[n];
	*seq = secure_tcp_sequence_number(*(seq + 1), *(seq + 2), *(seq + 3), *(seq + 4));
}

STATIC int sctp_get_key(sp)
	sctp_t *sp;
{
	int k = sctp_current_key;
	unsigned long duration = ((sp->ck_life + NUM_KEYS - 1) / NUM_KEYS) + sp->ck_inc;
	unsigned long created = sctp_keys[k].created;

	if (!created) {
		sctp_keys[k].created = jiffies;
		return k;
	}
	if (created + duration < jiffies) {
		k = (k + 1) % NUM_KEYS;
		sctp_rekey(k);
		sctp_current_key = k;
	}
	return k;
}

STATIC void sctp_hmac(sp, text, tlen, key, klen, hmac)
	sctp_t *sp;
	u8 *text;
	int tlen;
	u8 *key;
	int klen;
	u8 *hmac;
{
	memset(hmac, 0xff, HMAC_SIZE);
	switch (sp->hmac) {
	case SCTP_HMAC_SHA_1:
		hmac_sha1(text, tlen, key, klen, hmac);
		break;
	case SCTP_HMAC_MD5:
		hmac_md5(text, tlen, key, klen, hmac);
		break;
	default:
	case SCTP_HMAC_NONE:
		break;
	}
}

void sctp_sign_cookie(sp, ck)
	sctp_t *sp;
	struct sctp_cookie *ck;
{
	u8 *text = (u8 *) ck;
	int tlen = raw_cookie_size(ck);
	int ktag = sctp_get_key(sp);
	u8 *key = sctp_keys[ktag].u.key;
	int klen = sizeof(sctp_keys[0].u.key);
	u8 *hmacp = ((u8 *) ck) + tlen;

#if 0
	int i;
	ptrace(("Signing cookie:\n"));
	printk("text = %u, tlen = %d\n", (uint) text, tlen);
	printk("ktag = %d, klen = %d\n", ktag, klen);
	printk("key = ");
	for (i = 0; i < klen; i++)
		printk("%02x", key[i]);
	printk("\n");
#endif
	ck->key_tag = ktag;
	sctp_hmac(sp, text, tlen, key, klen, hmacp);
#if 0
	printk("hmac = ");
	for (i = 0; i < HMAC_SIZE; i++)
		printk("%02x", hmacp[i]);
	printk("\n");
#endif
}

/* Note: caller must verify length of cookie */
int sctp_verify_cookie(sp, ck)
	sctp_t *sp;
	struct sctp_cookie *ck;
{
	u8 hmac[HMAC_SIZE];
	u8 *text = (u8 *) ck;
	int tlen = raw_cookie_size(ck);
	int ktag = (ck->key_tag) % NUM_KEYS;
	u8 *key = sctp_keys[ktag].u.key;
	int klen = sizeof(sctp_keys[0].u.key);
	u8 *hmacp = ((u8 *) ck) + tlen;

#if 0
	int i;
	ptrace(("Verifying cookie:\n"));
	printk("text = %u, tlen = %d\n", (uint) text, tlen);
	printk("ktag = %d, klen = %d\n", ktag, klen);
	printk("key = ");
	for (i = 0; i < klen; i++)
		printk("%02x", key[i]);
	printk("\n");
	printk("hmac = ");
	for (i = 0; i < HMAC_SIZE; i++)
		printk("%02x", hmacp[i]);
	printk("\n");
#endif
	sctp_hmac(sp, text, tlen, key, klen, hmac);
#if 0
	printk("hmac = ");
	for (i = 0; i < HMAC_SIZE; i++)
		printk("%02x", hmac[i]);
	printk("\n");
#endif
	return memcmp(hmacp, hmac, HMAC_SIZE);
}


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sctp/sctp_cookie.c

OpenSS7
SS7 for the
Common Man
Home Overview Status News Documentation Resources About

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified: