private static void SetupSalt(uint salt, ref CryptExtendedData data) { if (salt == data.oldSalt) { return; } data.oldSalt = salt; uint saltbits = 0; uint saltbit = 1; uint obit = 0x800000; for (byte i = 0; i < 24; i++) { if ((salt & saltbit) != 0) { saltbits |= obit; } saltbit <<= 1; obit >>= 1; } data.saltbits = saltbits; }
public static string Crypt(string password, string setting) { lock (init_lock) { if (!initialized) { Init(); initialized = true; } } CryptExtendedData data = new CryptExtendedData(); data.Init(); int keyIndex = 0; byte[] keyBuffer = new byte[8]; for (int i = 0; i < 8; i++) { keyBuffer[i] |= (byte)(password[keyIndex] << 1); if (password[keyIndex] != 0) { keyIndex++; } } if (SetKey(keyBuffer, ref data) != 0) { return(null); } uint salt = 0; uint count = 0; int outputIndex = 0; if (setting[0] == passwordEFMT1) { /* "new"-style: * setting - underscore, 4 chars of count, 4 chars of salt * key - unlimited characters */ for (int i = 1; i < 5; i++) { int value = ConvertASCIIToBin(setting[i]); if (ascii64[value] != setting[i]) { return(null); } count |= (uint)value << (i - 1) * 6; } if (count == 0) { return(null); } for (byte i = 5; i < 9; i++) { int value = ConvertASCIIToBin(setting[i]); if (ascii64[value] != setting[i]) { return(null); } salt |= (uint)(value << (i - 5) * 6); } while (password.Length > keyIndex) { // Encrypt the key with itself. if (Cipher(keyBuffer, keyBuffer, 0, 1, ref data) != 0) { return(null); } // And XOR with the next 8 characters of the key. for (int i = 0; i < 8 && password.Length > keyIndex; i++) { keyBuffer[i] ^= (byte)(password[keyIndex++] << 1); } if (SetKey(keyBuffer, ref data) != 0) { return(null); } } for (int i = 0; i < 9; i++) { data.output[i] = (byte)setting[i]; } data.output[9] = 0; outputIndex = 9; } else { /* "old"-style: * setting - 2 chars of salt * key - up to 8 characters */ count = 25; if (IsASCIIUnsafe(setting[0]) || IsASCIIUnsafe(setting[1])) { return(null); } salt = (uint)((ConvertASCIIToBin(setting[1]) << 6) | ConvertASCIIToBin(setting[0])); data.output[0] = (byte)setting[0]; data.output[1] = (byte)setting[1]; outputIndex = 2; } SetupSalt(salt, ref data); // Do it. uint l, r0 = 0, r1 = 0; if (DoDes(0, 0, ref r0, ref r1, (int)count, ref data) != 0) { return(null); } // Now encode the result... l = (r0 >> 8); data.output[outputIndex++] = (byte)ascii64[(int)(l >> 18) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)(l >> 12) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)(l >> 6) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)l & 0x3f]; l = (r0 << 16) | ((r1 >> 16) & 0xffff); data.output[outputIndex++] = (byte)ascii64[(int)(l >> 18) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)(l >> 12) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)(l >> 6) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)(l & 0x3f)]; l = r1 << 2; data.output[outputIndex++] = (byte)ascii64[(int)(l >> 12) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)(l >> 6) & 0x3f]; data.output[outputIndex++] = (byte)ascii64[(int)l & 0x3f]; data.output[outputIndex] = 0; return(Encoding.ASCII.GetString(data.output).Trim('\0')); }
private static int Cipher(byte[] inBuffer, byte[] outBuffer, uint salt, int count, ref CryptExtendedData data) { uint leftOut = 0; uint rightOut = 0; SetupSalt(salt, ref data); uint rawLeft = (uint)(byte)inBuffer[3] | ((uint)(byte)inBuffer[2] << 8) | ((uint)(byte)inBuffer[1] << 16) | ((uint)(byte)inBuffer[0] << 24); uint rawRight = (uint)(byte)inBuffer[7] | ((uint)(byte)inBuffer[6] << 8) | ((uint)(byte)inBuffer[5] << 16) | ((uint)(byte)inBuffer[4] << 24); int result = DoDes(rawLeft, rawRight, ref leftOut, ref rightOut, count, ref data); outBuffer[0] = (byte)(leftOut >> 24); outBuffer[1] = (byte)(leftOut >> 16); outBuffer[2] = (byte)(leftOut >> 8); outBuffer[3] = (byte)leftOut; outBuffer[4] = (byte)(rightOut >> 24); outBuffer[5] = (byte)(rightOut >> 16); outBuffer[6] = (byte)(rightOut >> 8); outBuffer[7] = (byte)rightOut; return(result); }
private static int DoDes(uint l_in, uint r_in, ref uint l_out, ref uint r_out, int count, ref CryptExtendedData data) { // l_in, r_in, l_out, and r_out are in pseudo - "big-endian" format. uint[] kl1, kr1; if (count == 0) { return(1); } else if (count > 0) { // Encrypting kl1 = data.encryptKeysL; kr1 = data.encryptKeysR; } else { // Decrypting count = -count; kl1 = data.decryptKeysL; kr1 = data.decryptKeysR; } // Do initial permutation (IP). uint l = ip_maskl[0, l_in >> 24] | ip_maskl[1, (l_in >> 16) & 0xff] | ip_maskl[2, (l_in >> 8) & 0xff] | ip_maskl[3, l_in & 0xff] | ip_maskl[4, r_in >> 24] | ip_maskl[5, (r_in >> 16) & 0xff] | ip_maskl[6, (r_in >> 8) & 0xff] | ip_maskl[7, r_in & 0xff]; uint r = ip_maskr[0, l_in >> 24] | ip_maskr[1, (l_in >> 16) & 0xff] | ip_maskr[2, (l_in >> 8) & 0xff] | ip_maskr[3, l_in & 0xff] | ip_maskr[4, r_in >> 24] | ip_maskr[5, (r_in >> 16) & 0xff] | ip_maskr[6, (r_in >> 8) & 0xff] | ip_maskr[7, r_in & 0xff]; uint saltbits = data.saltbits; while (count-- != 0) { uint f = 0; // Do each round. int klIndex = 0; int krIndex = 0; int round = 16; while (round-- != 0) { // Expand R to 48 bits (simulate the E-box). uint r48l = ((r & 0x00000001) << 23) | ((r & 0xf8000000) >> 9) | ((r & 0x1f800000) >> 11) | ((r & 0x01f80000) >> 13) | ((r & 0x001f8000) >> 15); uint r48r = ((r & 0x0001f800) << 7) | ((r & 0x00001f80) << 5) | ((r & 0x000001f8) << 3) | ((r & 0x0000001f) << 1) | ((r & 0x80000000) >> 31); /* Do salting for crypt() and friends, and * XOR with the permuted key. */ f = (r48l ^ r48r) & saltbits; r48l ^= f ^ kl1[klIndex++]; r48r ^= f ^ kr1[krIndex++]; /* Do sbox lookups (which shrink it back to 32 bits) * and do the pbox permutation at the same time. */ f = psbox[0, m_sbox[0, r48l >> 12]] | psbox[1, m_sbox[1, r48l & 0xfff]] | psbox[2, m_sbox[2, r48r >> 12]] | psbox[3, m_sbox[3, r48r & 0xfff]]; // Now that we've permuted things, complete f(). f ^= l; l = r; r = f; } r = l; l = f; } // Do final permutation (inverse of IP). l_out = fp_maskl[0, l >> 24] | fp_maskl[1, (l >> 16) & 0xff] | fp_maskl[2, (l >> 8) & 0xff] | fp_maskl[3, l & 0xff] | fp_maskl[4, r >> 24] | fp_maskl[5, (r >> 16) & 0xff] | fp_maskl[6, (r >> 8) & 0xff] | fp_maskl[7, r & 0xff]; r_out = fp_maskr[0, l >> 24] | fp_maskr[1, (l >> 16) & 0xff] | fp_maskr[2, (l >> 8) & 0xff] | fp_maskr[3, l & 0xff] | fp_maskr[4, r >> 24] | fp_maskr[5, (r >> 16) & 0xff] | fp_maskr[6, (r >> 8) & 0xff] | fp_maskr[7, r & 0xff]; return(0); }
private static int SetKey(byte[] password, ref CryptExtendedData data) { uint rawKey0, rawKey1; rawKey0 = (uint)(byte)password[3] | ((uint)(byte)password[2] << 8) | ((uint)(byte)password[1] << 16) | ((uint)(byte)password[0] << 24); rawKey1 = (uint)(byte)password[7] | ((uint)(byte)password[6] << 8) | ((uint)(byte)password[5] << 16) | ((uint)(byte)password[4] << 24); if ((rawKey0 | rawKey1) != 0 && rawKey0 == data.oldRawKey0 && rawKey1 == data.oldRawKey1) { /* Already setup for this key. * This optimisation fails on a zero key (which is weak and * has bad parity anyway) in order to simplify the starting * conditions. */ return(0); } data.oldRawKey0 = rawKey0; data.oldRawKey1 = rawKey1; // Do key permutation and split into two 28-bit subkeys. uint key0 = key_perm_maskl[0, rawKey0 >> 25] | key_perm_maskl[1, (rawKey0 >> 17) & 0x7f] | key_perm_maskl[2, (rawKey0 >> 9) & 0x7f] | key_perm_maskl[3, (rawKey0 >> 1) & 0x7f] | key_perm_maskl[4, rawKey1 >> 25] | key_perm_maskl[5, (rawKey1 >> 17) & 0x7f] | key_perm_maskl[6, (rawKey1 >> 9) & 0x7f] | key_perm_maskl[7, (rawKey1 >> 1) & 0x7f]; uint key1 = key_perm_maskr[0, rawKey0 >> 25] | key_perm_maskr[1, (rawKey0 >> 17) & 0x7f] | key_perm_maskr[2, (rawKey0 >> 9) & 0x7f] | key_perm_maskr[3, (rawKey0 >> 1) & 0x7f] | key_perm_maskr[4, rawKey1 >> 25] | key_perm_maskr[5, (rawKey1 >> 17) & 0x7f] | key_perm_maskr[6, (rawKey1 >> 9) & 0x7f] | key_perm_maskr[7, (rawKey1 >> 1) & 0x7f]; // Rotate subkeys and do compression permutation. int shifts = 0; for (int round = 0; round < 16; round++) { shifts += key_shifts[round]; uint t0 = (key0 << shifts) | (key0 >> (28 - shifts)); uint t1 = (key1 << shifts) | (key1 >> (28 - shifts)); data.decryptKeysL[15 - round] = data.encryptKeysL[round] = comp_maskl[0, (t0 >> 21) & 0x7f] | comp_maskl[1, (t0 >> 14) & 0x7f] | comp_maskl[2, (t0 >> 7) & 0x7f] | comp_maskl[3, t0 & 0x7f] | comp_maskl[4, (t1 >> 21) & 0x7f] | comp_maskl[5, (t1 >> 14) & 0x7f] | comp_maskl[6, (t1 >> 7) & 0x7f] | comp_maskl[7, t1 & 0x7f]; data.decryptKeysR[15 - round] = data.encryptKeysR[round] = comp_maskr[0, (t0 >> 21) & 0x7f] | comp_maskr[1, (t0 >> 14) & 0x7f] | comp_maskr[2, (t0 >> 7) & 0x7f] | comp_maskr[3, t0 & 0x7f] | comp_maskr[4, (t1 >> 21) & 0x7f] | comp_maskr[5, (t1 >> 14) & 0x7f] | comp_maskr[6, (t1 >> 7) & 0x7f] | comp_maskr[7, t1 & 0x7f]; } return(0); }