nfs_gss_crypto.c   [plain text]


/*
 * Copyright (c) 2008 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */


/*
 * Copyright (C) 1998 by the FundsXpress, INC.
 * 
 * All rights reserved.
 * 
 * Export of this software from the United States of America may require
 * a specific license from the United States Government.  It is the
 * responsibility of any person or organization contemplating export to
 * obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of FundsXpress. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  FundsXpress makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "nfs_gss_crypto.h"


/*
n-fold(k-bits):
  l = lcm(n,k)
  r = l/k
  s = k-bits | k-bits rot 13 | k-bits rot 13*2 | ... | k-bits rot 13*(r-1)
  compute the 1's complement sum:
	n-fold = s[0..n-1]+s[n..2n-1]+s[2n..3n-1]+..+s[(k-1)*n..k*n-1]
*/

/* representation: msb first, assume n and k are multiples of 8, and
   that k>=16.  this is the case of all the cryptosystems which are
   likely to be used.  this function can be replaced if that
   assumption ever fails.  */

/* input length is in bits */

void
krb5_nfold(unsigned int inbits, const unsigned char *in, unsigned int outbits,
	   unsigned char *out)
{
    int a,b,c,lcm;
    int byte, i, msbit;

    /* the code below is more readable if I make these bytes
       instead of bits */

    inbits >>= 3;
    outbits >>= 3;

    /* first compute lcm(n,k) */

    a = outbits;
    b = inbits;

    while(b != 0) {
	c = b;
	b = a%b;
	a = c;
    }

    lcm = outbits*inbits/a;

    /* now do the real work */

    memset(out, 0, outbits);
    byte = 0;

    /* this will end up cycling through k lcm(k,n)/k times, which
       is correct */
    for (i=lcm-1; i>=0; i--) {
	/* compute the msbit in k which gets added into this byte */
	msbit = (/* first, start with the msbit in the first, unrotated
		    byte */
		 ((inbits<<3)-1)
		 /* then, for each byte, shift to the right for each
		    repetition */
		 +(((inbits<<3)+13)*(i/inbits))
		 /* last, pick out the correct byte within that
		    shifted repetition */
		 +((inbits-(i%inbits))<<3)
		 )%(inbits<<3);

	/* pull out the byte value itself */
	byte += (((in[((inbits-1)-(msbit>>3))%inbits]<<8)|
		  (in[((inbits)-(msbit>>3))%inbits]))
		 >>((msbit&7)+1))&0xff;

	/* do the addition */
	byte += out[i%outbits];
	out[i%outbits] = byte&0xff;

#if 0
	printf("msbit[%d] = %d\tbyte = %02x\tsum = %03x\n", i, msbit,
	       (((in[((inbits-1)-(msbit>>3))%inbits]<<8)|
		 (in[((inbits)-(msbit>>3))%inbits]))
		>>((msbit&7)+1))&0xff, byte);
#endif

	/* keep around the carry bit, if any */
	byte >>= 8;

#if 0
	printf("carry=%d\n", byte);
#endif
    }

    /* if there's a carry bit left over, add it back in */
    if (byte) {
	for (i=outbits-1; i>=0; i--) {
	    /* do the addition */
	    byte += out[i];
	    out[i] = byte&0xff;

	    /* keep around the carry bit, if any */
	    byte >>= 8;
	}
    }
}

/*
 * Given 21 bytes of random bits, make a triple DES key.
 */

void
des3_make_key(const unsigned char randombits[21], des_cblock key[3])
{
	int i;
	
	for (i = 0; i < 3; i++) {
		memcpy(&key[i], &randombits[i*7], 7);
		key[i][7] = (((key[i][0] & 1) << 1) |
			     ((key[i][1] & 1) << 2) |
			     ((key[i][2] & 1) << 3) |
			     ((key[i][3] & 1) << 4) |
			     ((key[i][4] & 1) << 5) |
			     ((key[i][5] & 1) << 6) |
			     ((key[i][6] & 1) << 7));
		des_fixup_key_parity(&key[i]);
	}
}

/*
 * Key derivation for triple DES.
 * Given the session key in in key, produce a new key in out key using
 * the supplied constant.
 */
 
int
des3_derive_key(des_cblock inkey[3], des_cblock outkey[3],
		const unsigned char *constant, int clen)
{
	des_cblock inblock, outblock, ivec;
	des3_cbc_key_schedule sched;
	unsigned char rawkey[21];
	size_t n, keybytes = sizeof(rawkey);

	/* initialize the input block */

	if (clen == sizeof(des_cblock)) {
		memcpy(inblock, constant, clen);
	} else {
		krb5_nfold(clen*8, constant, sizeof(des_cblock)*8, inblock);
	}

	/* loop encrypting the blocks until enough key bytes are generated */

	bzero(ivec, sizeof(ivec));
	des3_cbc_key_sched(inkey, &sched);
	for (n = 0; n < sizeof(rawkey); n += sizeof(des_cblock)) {
		des3_cbc_encrypt(&inblock, &outblock, sizeof(outblock), &sched, &ivec, NULL, 1);
		if ((keybytes - n) <= sizeof (des_cblock)) {
			memcpy(rawkey+n, outblock, (keybytes - n));
			break;
		}
		memcpy(rawkey+n, outblock, sizeof(des_cblock));
		memcpy(inblock, outblock, sizeof(des_cblock));
	}

	/* postprocess the key */
	des3_make_key(rawkey, outkey);

	/* clean memory, free resources and exit */

	bzero(inblock, sizeof (des_cblock));
	bzero(outblock, sizeof (des_cblock));
	bzero(rawkey, keybytes);
	bzero(&sched, sizeof (sched));

	return(0);
}

/*
 * Initialize a context for HMAC SHA1
 * if drived is true we derive a new key
 * based on KG_USAGE_SIGN
 */
 
void
HMAC_SHA1_DES3KD_Init(HMAC_SHA1_DES3KD_CTX *ctx, des_cblock key[3], int derive)
{
	unsigned char ipad[64];
	size_t i, j;
	
	SHA1Init(&ctx->sha1_ctx);
	if (derive)
		des3_derive_key(key, ctx->dk, KEY_USAGE_DES3_SIGN, KEY_USAGE_LEN);
	else
		memcpy(ctx->dk, key, 3*sizeof(des_cblock));
	memset(ipad, 0x36, sizeof(ipad));
	for (i = 0; i < 3; i++)
		for (j = 0; j < sizeof(des_cblock); j++)
			ipad[j + i * sizeof(des_cblock)] ^= ctx->dk[i][j];
	SHA1Update(&ctx->sha1_ctx, ipad, sizeof(ipad));
}

/*
 * Update the HMAC SHA1 context with the supplied data.
 */
void
HMAC_SHA1_DES3KD_Update(HMAC_SHA1_DES3KD_CTX *ctx, void *data, size_t len)
{
	SHA1Update(&ctx->sha1_ctx, data, len);
}

/*
 * Finish the context and produce the HMAC SHA1 digest.
 */
 
void
HMAC_SHA1_DES3KD_Final(void *digest, HMAC_SHA1_DES3KD_CTX *ctx)
{
	unsigned char opad[64];
	size_t i, j;

	SHA1Final(digest, &ctx->sha1_ctx);
	memset(opad, 0x5c, sizeof(opad));
	for (i = 0; i < 3; i++)
		for (j = 0; j < sizeof(des_cblock); j++)
			opad[j + i * sizeof(des_cblock)] ^= ctx->dk[i][j];
	SHA1Init(&ctx->sha1_ctx);
	SHA1Update(&ctx->sha1_ctx, opad, sizeof(opad));
	SHA1Update(&ctx->sha1_ctx, digest, SHA1_RESULTLEN);
	SHA1Final(digest, &ctx->sha1_ctx);
}

/*
 * Initialize an MD5 DES CBC context with a schedule.
 */
 
void MD5_DESCBC_Init(MD5_DESCBC_CTX *ctx, des_cbc_key_schedule *sched)
{
	MD5Init(&ctx->md5_ctx);
	ctx->sched = sched;
}

/*
 * Update MD5 DES CBC context with the supplied data.
 */
 
void MD5_DESCBC_Update(MD5_DESCBC_CTX *ctx, void *data, size_t len)
{
	MD5Update(&ctx->md5_ctx, data, len);
}

/*
 * Finalize the context and extract the digest.
 */
 
void MD5_DESCBC_Final(void *digest, MD5_DESCBC_CTX *ctx)
{
	unsigned char md5_digest[MD5_DIGEST_LENGTH];
	
	MD5Final(md5_digest, &ctx->md5_ctx);
	
	/*
	 * Now get the DES CBC checksum for the digest.
	 */
	des_cbc_cksum((des_cblock *) md5_digest, (des_cblock *)digest,
				sizeof (md5_digest), ctx->sched);
}