package org.apache.kerberos.kerb.crypto.key; import org.apache.kerberos.kerb.KrbException; import org.apache.kerberos.kerb.crypto.BytesUtil; import org.apache.kerberos.kerb.crypto.Des; import org.apache.kerberos.kerb.crypto.Util; import org.apache.kerberos.kerb.crypto.enc.EncryptProvider; public class DesKeyMaker extends AbstractKeyMaker { public DesKeyMaker(EncryptProvider encProvider) { super(encProvider); } @Override public byte[] str2key(String string, String salt, byte[] param) throws KrbException { String error = null; int type = 0; if (param != null) { if (param.length != 1) { error = "Invalid param to S2K"; } type = param[0]; if (type != 0 && type != 1) { error = "Invalid param to S2K"; } } if (type == 1) { error = "AFS not supported yet"; } if (error != null) { throw new KrbException(error); } char[] passwdSalt = makePasswdSalt(string, salt); byte[] key = toKey(passwdSalt); return key; } /** mit_des_string_to_key(string,salt) { odd = 1; s = string | salt; tempstring = 0; // 56-bit string pad(s); // with nulls to 8 byte boundary for (8byteblock in s) { 56bitstring = removeMSBits(8byteblock); if (odd == 0) reverse(56bitstring); odd = ! odd; tempstring = tempstring XOR 56bitstring; } tempkey = key_correction(add_parity_bits(tempstring)); key = key_correction(DES-CBC-check(s,tempkey)); return(key); } */ private byte[] toKey(char[] passwdChars) throws KrbException { int keySize = encProvider().keySize(); byte[] bytes = (new String(passwdChars)).getBytes(); // padded with zero-valued octets to a multiple of eight octets. byte[] paddedBytes = BytesUtil.padding(bytes, keySize); int blocksOfbytes8 = paddedBytes.length / keySize; boolean odd = true; byte[] bits56 = new byte[8]; byte[] tempString = new byte[8]; for (int i = 0; i < blocksOfbytes8; ++i) { System.arraycopy(paddedBytes, 8 * i, bits56, 0, 8); removeMSBits(bits56); if (odd) { reverse(bits56); } odd = ! odd; Util.xor(bits56, 0, tempString); } byte[] keyBytes = addParityBits(tempString); keyCorrection(keyBytes); byte[] resultKey = null; if (encProvider().supportCbcMac()) { resultKey = encProvider().cbcMac(keyBytes, keyBytes, paddedBytes); } else { throw new KrbException("cbcMac should be supported by the provider: " + encProvider().getClass()); } keyCorrection(resultKey); return resultKey; } /** * Note this isn't hit any test yet, and very probably problematic */ @Override public byte[] random2Key(byte[] randomBits) throws KrbException { if (randomBits.length != encProvider().keyInputSize()) { throw new KrbException("Invalid random bits, not of correct bytes size"); } /** * Ref. k5_rand2key_des in random_to_key.c in MIT krb5 * Take the seven bytes, move them around into the top 7 bits of the * 8 key bytes, then compute the parity bits. Do this three times. */ byte[] key = new byte[encProvider().keySize()]; int tmp; System.arraycopy(randomBits, 0, key, 0, 7); key[7] = (byte) (((key[0] & 1) << 1) | ((key[1] & 1) << 2) | ((key[2] & 1) << 3) | ((key[3] & 1) << 4) | ((key[4] & 1) << 5) | ((key[5] & 1) << 6) | ((key[6] & 1) << 7)); for (int i = 0; i < 8; i++) { tmp = key[i] & 0xfe; tmp |= (Integer.bitCount(tmp) & 1) ^ 1; key[i] = (byte) tmp; } Des.fixKey(key, 0, 8); return key; } // Processing an 8bytesblock private static byte[] removeMSBits(byte[] bits56) { /** Treats a 64 bit block as 8 octets and removes the MSB in each octet (in big endian mode) and concatenates the result. E.g., the input octet string: 01110000 01100001 11110011 01110011 11110111 01101111 11110010 01100100 => 1110000 1100001 1110011 1110011 1110111 1101111 1110010 1100100 */ /** * We probably do nothing here, just pretending the MSB bit to be discarded, * and ensure the MSB will not be used in the following processing. */ return bits56; } // Processing an 56bitblock private static void reverse(byte[] bits56) { /** Treats a 56-bit block as a binary string and reverses it. E.g., the input string: 1000001 1010100 1001000 1000101 1001110 1000001 0101110 1001101 => 1000001 0010101 0001001 1010001 0111001 1000001 0101110 1011001 => 1011001 0111010 1000001 0111001 1010001 0001001 0010101 1000001 */ // Reversing in a 7bit int t1, t2; byte bt; for (int i = 0; i < 8; ++i) { bt = bits56[i]; t1 = (bt >> 6) & 1; t2 = (bt >> 0) & 1; if (t1 != t2) bt ^= (1 << 6 | 1 << 0); t1 = (bt >> 5) & 1; t2 = (bt >> 1) & 1; if (t1 != t2) bt ^= (1 << 5 | 1 << 1); t1 = (bt >> 4) & 1; t2 = (bt >> 2) & 1; if (t1 != t2) bt ^= (1 << 4 | 1 << 2); bits56[i] = bt; } // Reversing the 8 7bit bt = bits56[7]; bits56[7] = bits56[0]; bits56[0] = bt; bt = bits56[6]; bits56[6] = bits56[1]; bits56[1] = bt; bt = bits56[5]; bits56[5] = bits56[2]; bits56[2] = bt; bt = bits56[4]; bits56[4] = bits56[3]; bits56[3] = bt; } private static byte[] addParityBits(byte[] bits56) { /** Copies a 56-bit block into a 64-bit block, left shifts content in each octet, and add DES parity bit. E.g., the input string: 1100000 0001111 0011100 0110100 1000101 1100100 0110110 0010111 => 11000001 00011111 00111000 01101000 10001010 11001000 01101101 00101111 */ byte bt; for (int i = 0; i < 8; i++) { bits56[i] <<= 1; } addParity(bits56); return bits56; } private static void keyCorrection(byte[] key) { addParity(key); if (Des.isWeakKey(key, 0, key.length)) { Des.fixKey(key, 0, key.length); } } private static int smask(int step) { return (1 << step) - 1; } private static byte pstep(byte x, int step) { return (byte) ((x & smask(step)) ^ ((x >> step) & smask(step))); } private static byte parityChar(byte abyte) { //#define smask(step) ((1<<step)-1) //#define pstep(x,step) (((x)&smask(step))^(((x)>>step)&smask(step))) //#define parity_char(x) pstep(pstep(pstep((x),4),2),1) return pstep(pstep(pstep(abyte, 4), 2), 1); } private static void addParity(byte[] key) { for (int i = 0; i < key.length; ++i) { key[i] &= 0xfe; key[i] |= 1 ^ parityChar(key[i]); } } // Returns true if the key has correct des parity private static boolean checkKeyParity(byte[] key) { for (int i = 0; i < key.length; ++i) { if ((key[i] & 1) == parityChar((byte) (key[i] & 0xfe))) { return false; } } return true; } }