14891f2d0SKevin Coffman /*
24891f2d0SKevin Coffman  * COPYRIGHT (c) 2008
34891f2d0SKevin Coffman  * The Regents of the University of Michigan
44891f2d0SKevin Coffman  * ALL RIGHTS RESERVED
54891f2d0SKevin Coffman  *
64891f2d0SKevin Coffman  * Permission is granted to use, copy, create derivative works
74891f2d0SKevin Coffman  * and redistribute this software and such derivative works
84891f2d0SKevin Coffman  * for any purpose, so long as the name of The University of
94891f2d0SKevin Coffman  * Michigan is not used in any advertising or publicity
104891f2d0SKevin Coffman  * pertaining to the use of distribution of this software
114891f2d0SKevin Coffman  * without specific, written prior authorization.  If the
124891f2d0SKevin Coffman  * above copyright notice or any other identification of the
134891f2d0SKevin Coffman  * University of Michigan is included in any copy of any
144891f2d0SKevin Coffman  * portion of this software, then the disclaimer below must
154891f2d0SKevin Coffman  * also be included.
164891f2d0SKevin Coffman  *
174891f2d0SKevin Coffman  * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
184891f2d0SKevin Coffman  * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
194891f2d0SKevin Coffman  * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
204891f2d0SKevin Coffman  * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
214891f2d0SKevin Coffman  * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
224891f2d0SKevin Coffman  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
234891f2d0SKevin Coffman  * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
244891f2d0SKevin Coffman  * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
254891f2d0SKevin Coffman  * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
264891f2d0SKevin Coffman  * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
274891f2d0SKevin Coffman  * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
284891f2d0SKevin Coffman  * SUCH DAMAGES.
294891f2d0SKevin Coffman  */
304891f2d0SKevin Coffman 
314891f2d0SKevin Coffman /*
324891f2d0SKevin Coffman  * Copyright (C) 1998 by the FundsXpress, INC.
334891f2d0SKevin Coffman  *
344891f2d0SKevin Coffman  * All rights reserved.
354891f2d0SKevin Coffman  *
364891f2d0SKevin Coffman  * Export of this software from the United States of America may require
374891f2d0SKevin Coffman  * a specific license from the United States Government.  It is the
384891f2d0SKevin Coffman  * responsibility of any person or organization contemplating export to
394891f2d0SKevin Coffman  * obtain such a license before exporting.
404891f2d0SKevin Coffman  *
414891f2d0SKevin Coffman  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
424891f2d0SKevin Coffman  * distribute this software and its documentation for any purpose and
434891f2d0SKevin Coffman  * without fee is hereby granted, provided that the above copyright
444891f2d0SKevin Coffman  * notice appear in all copies and that both that copyright notice and
454891f2d0SKevin Coffman  * this permission notice appear in supporting documentation, and that
464891f2d0SKevin Coffman  * the name of FundsXpress. not be used in advertising or publicity pertaining
474891f2d0SKevin Coffman  * to distribution of the software without specific, written prior
484891f2d0SKevin Coffman  * permission.  FundsXpress makes no representations about the suitability of
494891f2d0SKevin Coffman  * this software for any purpose.  It is provided "as is" without express
504891f2d0SKevin Coffman  * or implied warranty.
514891f2d0SKevin Coffman  *
524891f2d0SKevin Coffman  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
534891f2d0SKevin Coffman  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
544891f2d0SKevin Coffman  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
554891f2d0SKevin Coffman  */
564891f2d0SKevin Coffman 
574891f2d0SKevin Coffman #include <linux/err.h>
584891f2d0SKevin Coffman #include <linux/types.h>
594891f2d0SKevin Coffman #include <linux/crypto.h>
604891f2d0SKevin Coffman #include <linux/sunrpc/gss_krb5.h>
614891f2d0SKevin Coffman #include <linux/sunrpc/xdr.h>
62c692554bSLuis Henriques #include <linux/lcm.h>
634891f2d0SKevin Coffman 
644891f2d0SKevin Coffman #ifdef RPC_DEBUG
654891f2d0SKevin Coffman # define RPCDBG_FACILITY        RPCDBG_AUTH
664891f2d0SKevin Coffman #endif
674891f2d0SKevin Coffman 
684891f2d0SKevin Coffman /*
694891f2d0SKevin Coffman  * This is the n-fold function as described in rfc3961, sec 5.1
704891f2d0SKevin Coffman  * Taken from MIT Kerberos and modified.
714891f2d0SKevin Coffman  */
724891f2d0SKevin Coffman 
734891f2d0SKevin Coffman static void krb5_nfold(u32 inbits, const u8 *in,
744891f2d0SKevin Coffman 		       u32 outbits, u8 *out)
754891f2d0SKevin Coffman {
76c692554bSLuis Henriques 	unsigned long ulcm;
774891f2d0SKevin Coffman 	int byte, i, msbit;
784891f2d0SKevin Coffman 
794891f2d0SKevin Coffman 	/* the code below is more readable if I make these bytes
804891f2d0SKevin Coffman 	   instead of bits */
814891f2d0SKevin Coffman 
824891f2d0SKevin Coffman 	inbits >>= 3;
834891f2d0SKevin Coffman 	outbits >>= 3;
844891f2d0SKevin Coffman 
854891f2d0SKevin Coffman 	/* first compute lcm(n,k) */
86c692554bSLuis Henriques 	ulcm = lcm(inbits, outbits);
874891f2d0SKevin Coffman 
884891f2d0SKevin Coffman 	/* now do the real work */
894891f2d0SKevin Coffman 
904891f2d0SKevin Coffman 	memset(out, 0, outbits);
914891f2d0SKevin Coffman 	byte = 0;
924891f2d0SKevin Coffman 
934891f2d0SKevin Coffman 	/* this will end up cycling through k lcm(k,n)/k times, which
944891f2d0SKevin Coffman 	   is correct */
95c692554bSLuis Henriques 	for (i = ulcm-1; i >= 0; i--) {
964891f2d0SKevin Coffman 		/* compute the msbit in k which gets added into this byte */
974891f2d0SKevin Coffman 		msbit = (
984891f2d0SKevin Coffman 			/* first, start with the msbit in the first,
994891f2d0SKevin Coffman 			 * unrotated byte */
1004891f2d0SKevin Coffman 			 ((inbits << 3) - 1)
1014891f2d0SKevin Coffman 			 /* then, for each byte, shift to the right
1024891f2d0SKevin Coffman 			  * for each repetition */
1034891f2d0SKevin Coffman 			 + (((inbits << 3) + 13) * (i/inbits))
1044891f2d0SKevin Coffman 			 /* last, pick out the correct byte within
1054891f2d0SKevin Coffman 			  * that shifted repetition */
1064891f2d0SKevin Coffman 			 + ((inbits - (i % inbits)) << 3)
1074891f2d0SKevin Coffman 			 ) % (inbits << 3);
1084891f2d0SKevin Coffman 
1094891f2d0SKevin Coffman 		/* pull out the byte value itself */
1104891f2d0SKevin Coffman 		byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)|
1114891f2d0SKevin Coffman 				  (in[((inbits) - (msbit >> 3)) % inbits]))
1124891f2d0SKevin Coffman 				 >> ((msbit & 7) + 1)) & 0xff;
1134891f2d0SKevin Coffman 
1144891f2d0SKevin Coffman 		/* do the addition */
1154891f2d0SKevin Coffman 		byte += out[i % outbits];
1164891f2d0SKevin Coffman 		out[i % outbits] = byte & 0xff;
1174891f2d0SKevin Coffman 
1184891f2d0SKevin Coffman 		/* keep around the carry bit, if any */
1194891f2d0SKevin Coffman 		byte >>= 8;
1204891f2d0SKevin Coffman 
1214891f2d0SKevin Coffman 	}
1224891f2d0SKevin Coffman 
1234891f2d0SKevin Coffman 	/* if there's a carry bit left over, add it back in */
1244891f2d0SKevin Coffman 	if (byte) {
1254891f2d0SKevin Coffman 		for (i = outbits - 1; i >= 0; i--) {
1264891f2d0SKevin Coffman 			/* do the addition */
1274891f2d0SKevin Coffman 			byte += out[i];
1284891f2d0SKevin Coffman 			out[i] = byte & 0xff;
1294891f2d0SKevin Coffman 
1304891f2d0SKevin Coffman 			/* keep around the carry bit, if any */
1314891f2d0SKevin Coffman 			byte >>= 8;
1324891f2d0SKevin Coffman 		}
1334891f2d0SKevin Coffman 	}
1344891f2d0SKevin Coffman }
1354891f2d0SKevin Coffman 
1364891f2d0SKevin Coffman /*
1374891f2d0SKevin Coffman  * This is the DK (derive_key) function as described in rfc3961, sec 5.1
1384891f2d0SKevin Coffman  * Taken from MIT Kerberos and modified.
1394891f2d0SKevin Coffman  */
1404891f2d0SKevin Coffman 
14147d84807SKevin Coffman u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
1424891f2d0SKevin Coffman 		    const struct xdr_netobj *inkey,
1434891f2d0SKevin Coffman 		    struct xdr_netobj *outkey,
1441f4c86c0STrond Myklebust 		    const struct xdr_netobj *in_constant,
1451f4c86c0STrond Myklebust 		    gfp_t gfp_mask)
1464891f2d0SKevin Coffman {
1474891f2d0SKevin Coffman 	size_t blocksize, keybytes, keylength, n;
1484891f2d0SKevin Coffman 	unsigned char *inblockdata, *outblockdata, *rawkey;
1494891f2d0SKevin Coffman 	struct xdr_netobj inblock, outblock;
1504891f2d0SKevin Coffman 	struct crypto_blkcipher *cipher;
1514891f2d0SKevin Coffman 	u32 ret = EINVAL;
1524891f2d0SKevin Coffman 
1534891f2d0SKevin Coffman 	blocksize = gk5e->blocksize;
1544891f2d0SKevin Coffman 	keybytes = gk5e->keybytes;
1554891f2d0SKevin Coffman 	keylength = gk5e->keylength;
1564891f2d0SKevin Coffman 
1574891f2d0SKevin Coffman 	if ((inkey->len != keylength) || (outkey->len != keylength))
1584891f2d0SKevin Coffman 		goto err_return;
1594891f2d0SKevin Coffman 
1604891f2d0SKevin Coffman 	cipher = crypto_alloc_blkcipher(gk5e->encrypt_name, 0,
1614891f2d0SKevin Coffman 					CRYPTO_ALG_ASYNC);
1624891f2d0SKevin Coffman 	if (IS_ERR(cipher))
1634891f2d0SKevin Coffman 		goto err_return;
1644891f2d0SKevin Coffman 	if (crypto_blkcipher_setkey(cipher, inkey->data, inkey->len))
1654891f2d0SKevin Coffman 		goto err_return;
1664891f2d0SKevin Coffman 
1674891f2d0SKevin Coffman 	/* allocate and set up buffers */
1684891f2d0SKevin Coffman 
1694891f2d0SKevin Coffman 	ret = ENOMEM;
1701f4c86c0STrond Myklebust 	inblockdata = kmalloc(blocksize, gfp_mask);
1714891f2d0SKevin Coffman 	if (inblockdata == NULL)
1724891f2d0SKevin Coffman 		goto err_free_cipher;
1734891f2d0SKevin Coffman 
1741f4c86c0STrond Myklebust 	outblockdata = kmalloc(blocksize, gfp_mask);
1754891f2d0SKevin Coffman 	if (outblockdata == NULL)
1764891f2d0SKevin Coffman 		goto err_free_in;
1774891f2d0SKevin Coffman 
1781f4c86c0STrond Myklebust 	rawkey = kmalloc(keybytes, gfp_mask);
1794891f2d0SKevin Coffman 	if (rawkey == NULL)
1804891f2d0SKevin Coffman 		goto err_free_out;
1814891f2d0SKevin Coffman 
1824891f2d0SKevin Coffman 	inblock.data = (char *) inblockdata;
1834891f2d0SKevin Coffman 	inblock.len = blocksize;
1844891f2d0SKevin Coffman 
1854891f2d0SKevin Coffman 	outblock.data = (char *) outblockdata;
1864891f2d0SKevin Coffman 	outblock.len = blocksize;
1874891f2d0SKevin Coffman 
1884891f2d0SKevin Coffman 	/* initialize the input block */
1894891f2d0SKevin Coffman 
1904891f2d0SKevin Coffman 	if (in_constant->len == inblock.len) {
1914891f2d0SKevin Coffman 		memcpy(inblock.data, in_constant->data, inblock.len);
1924891f2d0SKevin Coffman 	} else {
1934891f2d0SKevin Coffman 		krb5_nfold(in_constant->len * 8, in_constant->data,
1944891f2d0SKevin Coffman 			   inblock.len * 8, inblock.data);
1954891f2d0SKevin Coffman 	}
1964891f2d0SKevin Coffman 
1974891f2d0SKevin Coffman 	/* loop encrypting the blocks until enough key bytes are generated */
1984891f2d0SKevin Coffman 
1994891f2d0SKevin Coffman 	n = 0;
2004891f2d0SKevin Coffman 	while (n < keybytes) {
2014891f2d0SKevin Coffman 		(*(gk5e->encrypt))(cipher, NULL, inblock.data,
2024891f2d0SKevin Coffman 				   outblock.data, inblock.len);
2034891f2d0SKevin Coffman 
2044891f2d0SKevin Coffman 		if ((keybytes - n) <= outblock.len) {
2054891f2d0SKevin Coffman 			memcpy(rawkey + n, outblock.data, (keybytes - n));
2064891f2d0SKevin Coffman 			break;
2074891f2d0SKevin Coffman 		}
2084891f2d0SKevin Coffman 
2094891f2d0SKevin Coffman 		memcpy(rawkey + n, outblock.data, outblock.len);
2104891f2d0SKevin Coffman 		memcpy(inblock.data, outblock.data, outblock.len);
2114891f2d0SKevin Coffman 		n += outblock.len;
2124891f2d0SKevin Coffman 	}
2134891f2d0SKevin Coffman 
2144891f2d0SKevin Coffman 	/* postprocess the key */
2154891f2d0SKevin Coffman 
2164891f2d0SKevin Coffman 	inblock.data = (char *) rawkey;
2174891f2d0SKevin Coffman 	inblock.len = keybytes;
2184891f2d0SKevin Coffman 
2194891f2d0SKevin Coffman 	BUG_ON(gk5e->mk_key == NULL);
2204891f2d0SKevin Coffman 	ret = (*(gk5e->mk_key))(gk5e, &inblock, outkey);
2214891f2d0SKevin Coffman 	if (ret) {
2224891f2d0SKevin Coffman 		dprintk("%s: got %d from mk_key function for '%s'\n",
2234891f2d0SKevin Coffman 			__func__, ret, gk5e->encrypt_name);
2244891f2d0SKevin Coffman 		goto err_free_raw;
2254891f2d0SKevin Coffman 	}
2264891f2d0SKevin Coffman 
2274891f2d0SKevin Coffman 	/* clean memory, free resources and exit */
2284891f2d0SKevin Coffman 
2294891f2d0SKevin Coffman 	ret = 0;
2304891f2d0SKevin Coffman 
2314891f2d0SKevin Coffman err_free_raw:
2324891f2d0SKevin Coffman 	memset(rawkey, 0, keybytes);
2334891f2d0SKevin Coffman 	kfree(rawkey);
2344891f2d0SKevin Coffman err_free_out:
2354891f2d0SKevin Coffman 	memset(outblockdata, 0, blocksize);
2364891f2d0SKevin Coffman 	kfree(outblockdata);
2374891f2d0SKevin Coffman err_free_in:
2384891f2d0SKevin Coffman 	memset(inblockdata, 0, blocksize);
2394891f2d0SKevin Coffman 	kfree(inblockdata);
2404891f2d0SKevin Coffman err_free_cipher:
2414891f2d0SKevin Coffman 	crypto_free_blkcipher(cipher);
2424891f2d0SKevin Coffman err_return:
2434891f2d0SKevin Coffman 	return ret;
2444891f2d0SKevin Coffman }
245958142e9SKevin Coffman 
246958142e9SKevin Coffman #define smask(step) ((1<<step)-1)
247958142e9SKevin Coffman #define pstep(x, step) (((x)&smask(step))^(((x)>>step)&smask(step)))
248958142e9SKevin Coffman #define parity_char(x) pstep(pstep(pstep((x), 4), 2), 1)
249958142e9SKevin Coffman 
250958142e9SKevin Coffman static void mit_des_fixup_key_parity(u8 key[8])
251958142e9SKevin Coffman {
252958142e9SKevin Coffman 	int i;
253958142e9SKevin Coffman 	for (i = 0; i < 8; i++) {
254958142e9SKevin Coffman 		key[i] &= 0xfe;
255958142e9SKevin Coffman 		key[i] |= 1^parity_char(key[i]);
256958142e9SKevin Coffman 	}
257958142e9SKevin Coffman }
258958142e9SKevin Coffman 
259958142e9SKevin Coffman /*
260958142e9SKevin Coffman  * This is the des3 key derivation postprocess function
261958142e9SKevin Coffman  */
262958142e9SKevin Coffman u32 gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
263958142e9SKevin Coffman 			   struct xdr_netobj *randombits,
264958142e9SKevin Coffman 			   struct xdr_netobj *key)
265958142e9SKevin Coffman {
266958142e9SKevin Coffman 	int i;
267958142e9SKevin Coffman 	u32 ret = EINVAL;
268958142e9SKevin Coffman 
269958142e9SKevin Coffman 	if (key->len != 24) {
270958142e9SKevin Coffman 		dprintk("%s: key->len is %d\n", __func__, key->len);
271958142e9SKevin Coffman 		goto err_out;
272958142e9SKevin Coffman 	}
273958142e9SKevin Coffman 	if (randombits->len != 21) {
274958142e9SKevin Coffman 		dprintk("%s: randombits->len is %d\n",
275958142e9SKevin Coffman 			__func__, randombits->len);
276958142e9SKevin Coffman 		goto err_out;
277958142e9SKevin Coffman 	}
278958142e9SKevin Coffman 
279958142e9SKevin Coffman 	/* take the seven bytes, move them around into the top 7 bits of the
280958142e9SKevin Coffman 	   8 key bytes, then compute the parity bits.  Do this three times. */
281958142e9SKevin Coffman 
282958142e9SKevin Coffman 	for (i = 0; i < 3; i++) {
283958142e9SKevin Coffman 		memcpy(key->data + i*8, randombits->data + i*7, 7);
284958142e9SKevin Coffman 		key->data[i*8+7] = (((key->data[i*8]&1)<<1) |
285958142e9SKevin Coffman 				    ((key->data[i*8+1]&1)<<2) |
286958142e9SKevin Coffman 				    ((key->data[i*8+2]&1)<<3) |
287958142e9SKevin Coffman 				    ((key->data[i*8+3]&1)<<4) |
288958142e9SKevin Coffman 				    ((key->data[i*8+4]&1)<<5) |
289958142e9SKevin Coffman 				    ((key->data[i*8+5]&1)<<6) |
290958142e9SKevin Coffman 				    ((key->data[i*8+6]&1)<<7));
291958142e9SKevin Coffman 
292958142e9SKevin Coffman 		mit_des_fixup_key_parity(key->data + i*8);
293958142e9SKevin Coffman 	}
294958142e9SKevin Coffman 	ret = 0;
295958142e9SKevin Coffman err_out:
296958142e9SKevin Coffman 	return ret;
297958142e9SKevin Coffman }
298934a95aaSKevin Coffman 
299934a95aaSKevin Coffman /*
300934a95aaSKevin Coffman  * This is the aes key derivation postprocess function
301934a95aaSKevin Coffman  */
302934a95aaSKevin Coffman u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
303934a95aaSKevin Coffman 			  struct xdr_netobj *randombits,
304934a95aaSKevin Coffman 			  struct xdr_netobj *key)
305934a95aaSKevin Coffman {
306934a95aaSKevin Coffman 	u32 ret = EINVAL;
307934a95aaSKevin Coffman 
308934a95aaSKevin Coffman 	if (key->len != 16 && key->len != 32) {
309934a95aaSKevin Coffman 		dprintk("%s: key->len is %d\n", __func__, key->len);
310934a95aaSKevin Coffman 		goto err_out;
311934a95aaSKevin Coffman 	}
312934a95aaSKevin Coffman 	if (randombits->len != 16 && randombits->len != 32) {
313934a95aaSKevin Coffman 		dprintk("%s: randombits->len is %d\n",
314934a95aaSKevin Coffman 			__func__, randombits->len);
315934a95aaSKevin Coffman 		goto err_out;
316934a95aaSKevin Coffman 	}
317934a95aaSKevin Coffman 	if (randombits->len != key->len) {
318934a95aaSKevin Coffman 		dprintk("%s: randombits->len is %d, key->len is %d\n",
319934a95aaSKevin Coffman 			__func__, randombits->len, key->len);
320934a95aaSKevin Coffman 		goto err_out;
321934a95aaSKevin Coffman 	}
322934a95aaSKevin Coffman 	memcpy(key->data, randombits->data, key->len);
323934a95aaSKevin Coffman 	ret = 0;
324934a95aaSKevin Coffman err_out:
325934a95aaSKevin Coffman 	return ret;
326934a95aaSKevin Coffman }
327934a95aaSKevin Coffman 
328