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 573b5cf20cSHerbert Xu #include <crypto/skcipher.h> 584891f2d0SKevin Coffman #include <linux/err.h> 594891f2d0SKevin Coffman #include <linux/types.h> 604891f2d0SKevin Coffman #include <linux/sunrpc/gss_krb5.h> 614891f2d0SKevin Coffman #include <linux/sunrpc/xdr.h> 62c692554bSLuis Henriques #include <linux/lcm.h> 634891f2d0SKevin Coffman 64d50b8152SChuck Lever #include "gss_krb5_internal.h" 65d50b8152SChuck Lever 66f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 674891f2d0SKevin Coffman # define RPCDBG_FACILITY RPCDBG_AUTH 684891f2d0SKevin Coffman #endif 694891f2d0SKevin Coffman 704891f2d0SKevin Coffman /* 714891f2d0SKevin Coffman * This is the n-fold function as described in rfc3961, sec 5.1 724891f2d0SKevin Coffman * Taken from MIT Kerberos and modified. 734891f2d0SKevin Coffman */ 744891f2d0SKevin Coffman 754891f2d0SKevin Coffman static void krb5_nfold(u32 inbits, const u8 *in, 764891f2d0SKevin Coffman u32 outbits, u8 *out) 774891f2d0SKevin Coffman { 78c692554bSLuis Henriques unsigned long ulcm; 794891f2d0SKevin Coffman int byte, i, msbit; 804891f2d0SKevin Coffman 814891f2d0SKevin Coffman /* the code below is more readable if I make these bytes 824891f2d0SKevin Coffman instead of bits */ 834891f2d0SKevin Coffman 844891f2d0SKevin Coffman inbits >>= 3; 854891f2d0SKevin Coffman outbits >>= 3; 864891f2d0SKevin Coffman 874891f2d0SKevin Coffman /* first compute lcm(n,k) */ 88c692554bSLuis Henriques ulcm = lcm(inbits, outbits); 894891f2d0SKevin Coffman 904891f2d0SKevin Coffman /* now do the real work */ 914891f2d0SKevin Coffman 924891f2d0SKevin Coffman memset(out, 0, outbits); 934891f2d0SKevin Coffman byte = 0; 944891f2d0SKevin Coffman 954891f2d0SKevin Coffman /* this will end up cycling through k lcm(k,n)/k times, which 964891f2d0SKevin Coffman is correct */ 97c692554bSLuis Henriques for (i = ulcm-1; i >= 0; i--) { 984891f2d0SKevin Coffman /* compute the msbit in k which gets added into this byte */ 994891f2d0SKevin Coffman msbit = ( 1004891f2d0SKevin Coffman /* first, start with the msbit in the first, 1014891f2d0SKevin Coffman * unrotated byte */ 1024891f2d0SKevin Coffman ((inbits << 3) - 1) 1034891f2d0SKevin Coffman /* then, for each byte, shift to the right 1044891f2d0SKevin Coffman * for each repetition */ 1054891f2d0SKevin Coffman + (((inbits << 3) + 13) * (i/inbits)) 1064891f2d0SKevin Coffman /* last, pick out the correct byte within 1074891f2d0SKevin Coffman * that shifted repetition */ 1084891f2d0SKevin Coffman + ((inbits - (i % inbits)) << 3) 1094891f2d0SKevin Coffman ) % (inbits << 3); 1104891f2d0SKevin Coffman 1114891f2d0SKevin Coffman /* pull out the byte value itself */ 1124891f2d0SKevin Coffman byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)| 1134891f2d0SKevin Coffman (in[((inbits) - (msbit >> 3)) % inbits])) 1144891f2d0SKevin Coffman >> ((msbit & 7) + 1)) & 0xff; 1154891f2d0SKevin Coffman 1164891f2d0SKevin Coffman /* do the addition */ 1174891f2d0SKevin Coffman byte += out[i % outbits]; 1184891f2d0SKevin Coffman out[i % outbits] = byte & 0xff; 1194891f2d0SKevin Coffman 1204891f2d0SKevin Coffman /* keep around the carry bit, if any */ 1214891f2d0SKevin Coffman byte >>= 8; 1224891f2d0SKevin Coffman 1234891f2d0SKevin Coffman } 1244891f2d0SKevin Coffman 1254891f2d0SKevin Coffman /* if there's a carry bit left over, add it back in */ 1264891f2d0SKevin Coffman if (byte) { 1274891f2d0SKevin Coffman for (i = outbits - 1; i >= 0; i--) { 1284891f2d0SKevin Coffman /* do the addition */ 1294891f2d0SKevin Coffman byte += out[i]; 1304891f2d0SKevin Coffman out[i] = byte & 0xff; 1314891f2d0SKevin Coffman 1324891f2d0SKevin Coffman /* keep around the carry bit, if any */ 1334891f2d0SKevin Coffman byte >>= 8; 1344891f2d0SKevin Coffman } 1354891f2d0SKevin Coffman } 1364891f2d0SKevin Coffman } 1374891f2d0SKevin Coffman 1384891f2d0SKevin Coffman /* 1394891f2d0SKevin Coffman * This is the DK (derive_key) function as described in rfc3961, sec 5.1 1404891f2d0SKevin Coffman * Taken from MIT Kerberos and modified. 1414891f2d0SKevin Coffman */ 142*2691a27dSChuck Lever static int krb5_DK(const struct gss_krb5_enctype *gk5e, 143*2691a27dSChuck Lever const struct xdr_netobj *inkey, u8 *rawkey, 144*2691a27dSChuck Lever const struct xdr_netobj *in_constant, gfp_t gfp_mask) 1454891f2d0SKevin Coffman { 1464891f2d0SKevin Coffman size_t blocksize, keybytes, keylength, n; 147*2691a27dSChuck Lever unsigned char *inblockdata, *outblockdata; 1484891f2d0SKevin Coffman struct xdr_netobj inblock, outblock; 149e9e575b8SKees Cook struct crypto_sync_skcipher *cipher; 150*2691a27dSChuck Lever int ret = -EINVAL; 1514891f2d0SKevin Coffman 1524891f2d0SKevin Coffman keybytes = gk5e->keybytes; 1534891f2d0SKevin Coffman keylength = gk5e->keylength; 1544891f2d0SKevin Coffman 155*2691a27dSChuck Lever if (inkey->len != keylength) 1564891f2d0SKevin Coffman goto err_return; 1574891f2d0SKevin Coffman 158e9e575b8SKees Cook cipher = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0); 1594891f2d0SKevin Coffman if (IS_ERR(cipher)) 1604891f2d0SKevin Coffman goto err_return; 161f03640a1SChuck Lever blocksize = crypto_sync_skcipher_blocksize(cipher); 162e9e575b8SKees Cook if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len)) 1634891f2d0SKevin Coffman goto err_return; 1644891f2d0SKevin Coffman 165*2691a27dSChuck Lever ret = -ENOMEM; 1661f4c86c0STrond Myklebust inblockdata = kmalloc(blocksize, gfp_mask); 1674891f2d0SKevin Coffman if (inblockdata == NULL) 1684891f2d0SKevin Coffman goto err_free_cipher; 1694891f2d0SKevin Coffman 1701f4c86c0STrond Myklebust outblockdata = kmalloc(blocksize, gfp_mask); 1714891f2d0SKevin Coffman if (outblockdata == NULL) 1724891f2d0SKevin Coffman goto err_free_in; 1734891f2d0SKevin Coffman 1744891f2d0SKevin Coffman inblock.data = (char *) inblockdata; 1754891f2d0SKevin Coffman inblock.len = blocksize; 1764891f2d0SKevin Coffman 1774891f2d0SKevin Coffman outblock.data = (char *) outblockdata; 1784891f2d0SKevin Coffman outblock.len = blocksize; 1794891f2d0SKevin Coffman 1804891f2d0SKevin Coffman /* initialize the input block */ 1814891f2d0SKevin Coffman 1824891f2d0SKevin Coffman if (in_constant->len == inblock.len) { 1834891f2d0SKevin Coffman memcpy(inblock.data, in_constant->data, inblock.len); 1844891f2d0SKevin Coffman } else { 1854891f2d0SKevin Coffman krb5_nfold(in_constant->len * 8, in_constant->data, 1864891f2d0SKevin Coffman inblock.len * 8, inblock.data); 1874891f2d0SKevin Coffman } 1884891f2d0SKevin Coffman 1894891f2d0SKevin Coffman /* loop encrypting the blocks until enough key bytes are generated */ 1904891f2d0SKevin Coffman 1914891f2d0SKevin Coffman n = 0; 1924891f2d0SKevin Coffman while (n < keybytes) { 193d50b8152SChuck Lever krb5_encrypt(cipher, NULL, inblock.data, outblock.data, 194d50b8152SChuck Lever inblock.len); 1954891f2d0SKevin Coffman 1964891f2d0SKevin Coffman if ((keybytes - n) <= outblock.len) { 1974891f2d0SKevin Coffman memcpy(rawkey + n, outblock.data, (keybytes - n)); 1984891f2d0SKevin Coffman break; 1994891f2d0SKevin Coffman } 2004891f2d0SKevin Coffman 2014891f2d0SKevin Coffman memcpy(rawkey + n, outblock.data, outblock.len); 2024891f2d0SKevin Coffman memcpy(inblock.data, outblock.data, outblock.len); 2034891f2d0SKevin Coffman n += outblock.len; 2044891f2d0SKevin Coffman } 2054891f2d0SKevin Coffman 2064891f2d0SKevin Coffman ret = 0; 2074891f2d0SKevin Coffman 208453431a5SWaiman Long kfree_sensitive(outblockdata); 2094891f2d0SKevin Coffman err_free_in: 210453431a5SWaiman Long kfree_sensitive(inblockdata); 2114891f2d0SKevin Coffman err_free_cipher: 212e9e575b8SKees Cook crypto_free_sync_skcipher(cipher); 2134891f2d0SKevin Coffman err_return: 2144891f2d0SKevin Coffman return ret; 2154891f2d0SKevin Coffman } 216958142e9SKevin Coffman 217958142e9SKevin Coffman #define smask(step) ((1<<step)-1) 218958142e9SKevin Coffman #define pstep(x, step) (((x)&smask(step))^(((x)>>step)&smask(step))) 219958142e9SKevin Coffman #define parity_char(x) pstep(pstep(pstep((x), 4), 2), 1) 220958142e9SKevin Coffman 221958142e9SKevin Coffman static void mit_des_fixup_key_parity(u8 key[8]) 222958142e9SKevin Coffman { 223958142e9SKevin Coffman int i; 224958142e9SKevin Coffman for (i = 0; i < 8; i++) { 225958142e9SKevin Coffman key[i] &= 0xfe; 226958142e9SKevin Coffman key[i] |= 1^parity_char(key[i]); 227958142e9SKevin Coffman } 228958142e9SKevin Coffman } 229958142e9SKevin Coffman 230*2691a27dSChuck Lever static int krb5_random_to_key_v1(const struct gss_krb5_enctype *gk5e, 231958142e9SKevin Coffman struct xdr_netobj *randombits, 232958142e9SKevin Coffman struct xdr_netobj *key) 233958142e9SKevin Coffman { 234*2691a27dSChuck Lever int i, ret = -EINVAL; 235958142e9SKevin Coffman 236958142e9SKevin Coffman if (key->len != 24) { 237958142e9SKevin Coffman dprintk("%s: key->len is %d\n", __func__, key->len); 238958142e9SKevin Coffman goto err_out; 239958142e9SKevin Coffman } 240958142e9SKevin Coffman if (randombits->len != 21) { 241958142e9SKevin Coffman dprintk("%s: randombits->len is %d\n", 242958142e9SKevin Coffman __func__, randombits->len); 243958142e9SKevin Coffman goto err_out; 244958142e9SKevin Coffman } 245958142e9SKevin Coffman 246958142e9SKevin Coffman /* take the seven bytes, move them around into the top 7 bits of the 247958142e9SKevin Coffman 8 key bytes, then compute the parity bits. Do this three times. */ 248958142e9SKevin Coffman 249958142e9SKevin Coffman for (i = 0; i < 3; i++) { 250958142e9SKevin Coffman memcpy(key->data + i*8, randombits->data + i*7, 7); 251958142e9SKevin Coffman key->data[i*8+7] = (((key->data[i*8]&1)<<1) | 252958142e9SKevin Coffman ((key->data[i*8+1]&1)<<2) | 253958142e9SKevin Coffman ((key->data[i*8+2]&1)<<3) | 254958142e9SKevin Coffman ((key->data[i*8+3]&1)<<4) | 255958142e9SKevin Coffman ((key->data[i*8+4]&1)<<5) | 256958142e9SKevin Coffman ((key->data[i*8+5]&1)<<6) | 257958142e9SKevin Coffman ((key->data[i*8+6]&1)<<7)); 258958142e9SKevin Coffman 259958142e9SKevin Coffman mit_des_fixup_key_parity(key->data + i*8); 260958142e9SKevin Coffman } 261958142e9SKevin Coffman ret = 0; 262958142e9SKevin Coffman err_out: 263958142e9SKevin Coffman return ret; 264958142e9SKevin Coffman } 265934a95aaSKevin Coffman 266*2691a27dSChuck Lever /** 267*2691a27dSChuck Lever * krb5_derive_key_v1 - Derive a subkey for an RFC 3961 enctype 268*2691a27dSChuck Lever * @gk5e: Kerberos 5 enctype profile 269*2691a27dSChuck Lever * @inkey: base protocol key 270*2691a27dSChuck Lever * @outkey: OUT: derived key 271*2691a27dSChuck Lever * @label: subkey usage label 272*2691a27dSChuck Lever * @gfp_mask: memory allocation control flags 273*2691a27dSChuck Lever * 274*2691a27dSChuck Lever * Caller sets @outkey->len to the desired length of the derived key. 275*2691a27dSChuck Lever * 276*2691a27dSChuck Lever * On success, returns 0 and fills in @outkey. A negative errno value 277*2691a27dSChuck Lever * is returned on failure. 278934a95aaSKevin Coffman */ 279*2691a27dSChuck Lever int krb5_derive_key_v1(const struct gss_krb5_enctype *gk5e, 280*2691a27dSChuck Lever const struct xdr_netobj *inkey, 281*2691a27dSChuck Lever struct xdr_netobj *outkey, 282*2691a27dSChuck Lever const struct xdr_netobj *label, 283*2691a27dSChuck Lever gfp_t gfp_mask) 284*2691a27dSChuck Lever { 285*2691a27dSChuck Lever struct xdr_netobj inblock; 286*2691a27dSChuck Lever int ret; 287*2691a27dSChuck Lever 288*2691a27dSChuck Lever inblock.len = gk5e->keybytes; 289*2691a27dSChuck Lever inblock.data = kmalloc(inblock.len, gfp_mask); 290*2691a27dSChuck Lever if (!inblock.data) 291*2691a27dSChuck Lever return -ENOMEM; 292*2691a27dSChuck Lever 293*2691a27dSChuck Lever ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask); 294*2691a27dSChuck Lever if (!ret) 295*2691a27dSChuck Lever ret = krb5_random_to_key_v1(gk5e, &inblock, outkey); 296*2691a27dSChuck Lever 297*2691a27dSChuck Lever kfree_sensitive(inblock.data); 298*2691a27dSChuck Lever return ret; 299*2691a27dSChuck Lever } 300*2691a27dSChuck Lever 301*2691a27dSChuck Lever /* 302*2691a27dSChuck Lever * This is the identity function, with some sanity checking. 303*2691a27dSChuck Lever */ 304*2691a27dSChuck Lever static int krb5_random_to_key_v2(const struct gss_krb5_enctype *gk5e, 305934a95aaSKevin Coffman struct xdr_netobj *randombits, 306934a95aaSKevin Coffman struct xdr_netobj *key) 307934a95aaSKevin Coffman { 308*2691a27dSChuck Lever int ret = -EINVAL; 309934a95aaSKevin Coffman 310934a95aaSKevin Coffman if (key->len != 16 && key->len != 32) { 311934a95aaSKevin Coffman dprintk("%s: key->len is %d\n", __func__, key->len); 312934a95aaSKevin Coffman goto err_out; 313934a95aaSKevin Coffman } 314934a95aaSKevin Coffman if (randombits->len != 16 && randombits->len != 32) { 315934a95aaSKevin Coffman dprintk("%s: randombits->len is %d\n", 316934a95aaSKevin Coffman __func__, randombits->len); 317934a95aaSKevin Coffman goto err_out; 318934a95aaSKevin Coffman } 319934a95aaSKevin Coffman if (randombits->len != key->len) { 320934a95aaSKevin Coffman dprintk("%s: randombits->len is %d, key->len is %d\n", 321934a95aaSKevin Coffman __func__, randombits->len, key->len); 322934a95aaSKevin Coffman goto err_out; 323934a95aaSKevin Coffman } 324934a95aaSKevin Coffman memcpy(key->data, randombits->data, key->len); 325934a95aaSKevin Coffman ret = 0; 326934a95aaSKevin Coffman err_out: 327934a95aaSKevin Coffman return ret; 328934a95aaSKevin Coffman } 329*2691a27dSChuck Lever 330*2691a27dSChuck Lever /** 331*2691a27dSChuck Lever * krb5_derive_key_v2 - Derive a subkey for an RFC 3962 enctype 332*2691a27dSChuck Lever * @gk5e: Kerberos 5 enctype profile 333*2691a27dSChuck Lever * @inkey: base protocol key 334*2691a27dSChuck Lever * @outkey: OUT: derived key 335*2691a27dSChuck Lever * @label: subkey usage label 336*2691a27dSChuck Lever * @gfp_mask: memory allocation control flags 337*2691a27dSChuck Lever * 338*2691a27dSChuck Lever * Caller sets @outkey->len to the desired length of the derived key. 339*2691a27dSChuck Lever * 340*2691a27dSChuck Lever * On success, returns 0 and fills in @outkey. A negative errno value 341*2691a27dSChuck Lever * is returned on failure. 342*2691a27dSChuck Lever */ 343*2691a27dSChuck Lever int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e, 344*2691a27dSChuck Lever const struct xdr_netobj *inkey, 345*2691a27dSChuck Lever struct xdr_netobj *outkey, 346*2691a27dSChuck Lever const struct xdr_netobj *label, 347*2691a27dSChuck Lever gfp_t gfp_mask) 348*2691a27dSChuck Lever { 349*2691a27dSChuck Lever struct xdr_netobj inblock; 350*2691a27dSChuck Lever int ret; 351*2691a27dSChuck Lever 352*2691a27dSChuck Lever inblock.len = gk5e->keybytes; 353*2691a27dSChuck Lever inblock.data = kmalloc(inblock.len, gfp_mask); 354*2691a27dSChuck Lever if (!inblock.data) 355*2691a27dSChuck Lever return -ENOMEM; 356*2691a27dSChuck Lever 357*2691a27dSChuck Lever ret = krb5_DK(gk5e, inkey, inblock.data, label, gfp_mask); 358*2691a27dSChuck Lever if (!ret) 359*2691a27dSChuck Lever ret = krb5_random_to_key_v2(gk5e, &inblock, outkey); 360*2691a27dSChuck Lever 361*2691a27dSChuck Lever kfree_sensitive(inblock.data); 362*2691a27dSChuck Lever return ret; 363*2691a27dSChuck Lever } 364