]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_gss_crypto.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_gss_crypto.c
diff --git a/bsd/nfs/nfs_gss_crypto.c b/bsd/nfs/nfs_gss_crypto.c
new file mode 100644 (file)
index 0000000..1d275ba
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * 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]);
+       }
+}
+
+/*
+ * Make a triple des key schedule, from a triple des key.
+ */
+int
+des3_key_sched(des_cblock key[3], des_key_schedule sched[3])
+{
+       int i;
+       int rc = 0;
+       
+       for (i = 0; i < 3; i++)
+               rc |= des_key_sched(&key[i], sched[i]);
+
+       return (rc);
+}
+
+/*
+ * Triple DES cipher block chaining mode encryption.
+ */
+void
+des3_cbc_encrypt(des_cblock *input, des_cblock *output, int32_t length, 
+                des_key_schedule schedule[3], des_cblock *ivec, des_cblock *retvec, int encrypt)
+{
+       register DES_LONG tin0,tin1;
+       register DES_LONG tout0,tout1,xor0,xor1;
+       register unsigned char *in,*out,*retval;
+       register int32_t l=length;
+       DES_LONG tin[2];
+       unsigned char *iv;
+       tin0 = tin1 = 0;
+
+       in=(unsigned char *)input;
+       out=(unsigned char *)output;
+       retval=(unsigned char *)retvec;
+       iv=(unsigned char *)ivec;
+
+       if (encrypt) {
+               c2l(iv,tout0);
+               c2l(iv,tout1);
+               for (l-=8; l>=0; l-=8) {
+                       c2l(in,tin0);
+                       c2l(in,tin1);
+                       tin0^=tout0; tin[0]=tin0;
+                       tin1^=tout1; tin[1]=tin1;
+                       des_encrypt3((DES_LONG *)tin,schedule[0], schedule[1], schedule[2]);
+                       tout0=tin[0]; l2c(tout0,out);
+                       tout1=tin[1]; l2c(tout1,out);
+               }
+               if (l != -8) {
+                       c2ln(in,tin0,tin1,l+8);
+                       tin0^=tout0; tin[0]=tin0;
+                       tin1^=tout1; tin[1]=tin1;
+                       des_encrypt3((DES_LONG *)tin,schedule[0], schedule[1], schedule[2]);
+                       tout0=tin[0]; l2c(tout0,out);
+                       tout1=tin[1]; l2c(tout1,out);
+               }
+               if (retval) {
+                       l2c(tout0,retval);
+                       l2c(tout1,retval);
+               }
+       } else {
+               c2l(iv,xor0);
+               c2l(iv,xor1);
+               for (l-=8; l>=0; l-=8) {
+                       c2l(in,tin0); tin[0]=tin0;
+                       c2l(in,tin1); tin[1]=tin1;
+                       des_decrypt3((DES_LONG *)tin,schedule[0],schedule[1],schedule[2]);
+                       tout0=tin[0]^xor0;
+                       tout1=tin[1]^xor1;
+                       l2c(tout0,out);
+                       l2c(tout1,out);
+                       xor0=tin0;
+                       xor1=tin1;
+               }
+               if (l != -8) {
+                       c2l(in,tin0); tin[0]=tin0;
+                       c2l(in,tin1); tin[1]=tin1;
+                       des_decrypt3((DES_LONG *)tin,schedule[0],schedule[1],schedule[2]);
+                       tout0=tin[0]^xor0;
+                       tout1=tin[1]^xor1;
+                       l2cn(tout0,tout1,out,l+8);
+               /*      xor0=tin0;
+                       xor1=tin1; */
+               }
+               if (retval) {
+                       l2c(tin0,retval);
+                       l2c(tin1,retval);
+               }
+       }
+       tin0=tin1=tout0=tout1=xor0=xor1=0;
+       tin[0]=tin[1]=0;
+}
+
+/*
+ * 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;
+       des_key_schedule sched[3];
+       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_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);
+}
+
+/*
+ * XXX This function borrowed from OpenBSD.
+ * It will likely be moved into kernel crypto.
+ */
+DES_LONG
+des_cbc_cksum(des_cblock *input, des_cblock *output,
+               int32_t length, des_key_schedule schedule, des_cblock *ivec)
+{
+       register DES_LONG tout0,tout1,tin0,tin1;
+       register int32_t l=length;
+       DES_LONG tin[2];
+       unsigned char *in,*out,*iv;
+
+       in=(unsigned char *)input;
+       out=(unsigned char *)output;
+       iv=(unsigned char *)ivec;
+
+       c2l(iv,tout0);
+       c2l(iv,tout1);
+       for (; l>0; l-=8) {
+               if (l >= 8) {
+                       c2l(in,tin0);
+                       c2l(in,tin1);
+               } else
+                       c2ln(in,tin0,tin1,l);
+                       
+               tin0^=tout0; tin[0]=tin0;
+               tin1^=tout1; tin[1]=tin1;
+               des_encrypt1((DES_LONG *)tin,schedule,DES_ENCRYPT);
+               /* fix 15/10/91 eay - thanks to keithr@sco.COM */
+               tout0=tin[0];
+               tout1=tin[1];
+       }
+       if (out != NULL) {
+               l2c(tout0,out);
+               l2c(tout1,out);
+       }
+       tout0=tin0=tin1=tin[0]=tin[1]=0;
+       return(tout1);
+}
+
+/*
+ * XXX This function borrowed from OpenBSD.
+ * It will likely be moved into kernel crypto.
+ */
+void
+des_cbc_encrypt(des_cblock *input, des_cblock *output, int32_t length,
+               des_key_schedule schedule, des_cblock *ivec, des_cblock *retvec, int encrypt)
+{
+       register DES_LONG tin0,tin1;
+       register DES_LONG tout0,tout1,xor0,xor1;
+       register unsigned char *in,*out,*retval;
+       register int32_t l=length;
+       DES_LONG tin[2];
+       unsigned char *iv;
+       tin0 = tin1 = 0;
+
+       in=(unsigned char *)input;
+       out=(unsigned char *)output;
+       retval=(unsigned char *)retvec;
+       iv=(unsigned char *)ivec;
+
+       if (encrypt) {
+               c2l(iv,tout0);
+               c2l(iv,tout1);
+               for (l-=8; l>=0; l-=8) {
+                       c2l(in,tin0);
+                       c2l(in,tin1);
+                       tin0^=tout0; tin[0]=tin0;
+                       tin1^=tout1; tin[1]=tin1;
+                       des_encrypt1((DES_LONG *)tin,schedule,DES_ENCRYPT);
+                       tout0=tin[0]; l2c(tout0,out);
+                       tout1=tin[1]; l2c(tout1,out);
+               }
+               if (l != -8) {
+                       c2ln(in,tin0,tin1,l+8);
+                       tin0^=tout0; tin[0]=tin0;
+                       tin1^=tout1; tin[1]=tin1;
+                       des_encrypt1((DES_LONG *)tin,schedule,DES_ENCRYPT);
+                       tout0=tin[0]; l2c(tout0,out);
+                       tout1=tin[1]; l2c(tout1,out);
+               }
+               if (retval) {
+                       l2c(tout0,retval);
+                       l2c(tout1,retval);
+               }
+       } else {
+               c2l(iv,xor0);
+               c2l(iv,xor1);
+               for (l-=8; l>=0; l-=8) {
+                       c2l(in,tin0); tin[0]=tin0;
+                       c2l(in,tin1); tin[1]=tin1;
+                       des_encrypt1((DES_LONG *)tin,schedule,DES_DECRYPT);
+                       tout0=tin[0]^xor0;
+                       tout1=tin[1]^xor1;
+                       l2c(tout0,out);
+                       l2c(tout1,out);
+                       xor0=tin0;
+                       xor1=tin1;
+               }
+               if (l != -8) {
+                       c2l(in,tin0); tin[0]=tin0;
+                       c2l(in,tin1); tin[1]=tin1;
+                       des_encrypt1((DES_LONG *)tin,schedule,DES_DECRYPT);
+                       tout0=tin[0]^xor0;
+                       tout1=tin[1]^xor1;
+                       l2cn(tout0,tout1,out,l+8);
+               /*      xor0=tin0;
+                       xor1=tin1; */
+               }
+               if (retval) {
+                       l2c(tin0,retval);
+                       l2c(tin1,retval);
+               }
+       }
+       tin0=tin1=tout0=tout1=xor0=xor1=0;
+       tin[0]=tin[1]=0;
+}
+
+/*
+ * Initialize an MD5 DES CBC context with a schedule.
+ */
+void MD5_DESCBC_Init(MD5_DESCBC_CTX *ctx, des_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)
+{
+       des_cblock iv0;
+       unsigned char md5_digest[MD5_DIGEST_LENGTH];
+       
+       MD5Final(md5_digest, &ctx->md5_ctx);
+       
+       /*
+        * Now get the DES CBC checksum for the digest.
+        */
+       bzero(iv0, sizeof (iv0));
+       (void) des_cbc_cksum((des_cblock *) md5_digest, (des_cblock *)digest,
+                               sizeof (md5_digest), *ctx->sched, &iv0);
+}      
+