/*
smbencrypt.java
Created: 15 March 2001
Java Port By: Jonathan Abbey, jonabbey@arlut.utexas.edu
-----------------------------------------------------------------------
Based on smbencrypt.c and smbdes.c in Samba
Unix SMB/Netbios implementation.
Version 1.9.
a partial implementation of DES designed for use in the
SMB authentication protocol
Copyright (C) Andrew Tridgell 1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA
-----------------------------------------------------------------------
Ganymede Directory Management System
Copyright (C) 1996-2010
The University of Texas at Austin
Contact information
Web site: http://www.arlut.utexas.edu/gash2
Author Email: ganymede_author@arlut.utexas.edu
Email mailing list: ganymede@arlut.utexas.edu
US Mail:
Computer Science Division
Applied Research Laboratories
The University of Texas at Austin
PO Box 8029, Austin TX 78713-8029
Telephone: (512) 835-3200
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package arlut.csd.crypto;
import java.security.MessageDigest;
/*------------------------------------------------------------------------------
class
smbencrypt
------------------------------------------------------------------------------*/
/**
* <p>This Java class implements the two cryptographic hash methods used
* by SMB clients. In particular, the LANMANHash() and NTUNICODEHash()
* static methods in this class perform the two hashes required for Samba's
* encrypted password entries. As such, this class and the other classes
* in the broadly named arlut.csd.crypto package provide all the support
* functions necessary to allow Ganymede to manage Samba's password file.</p>
*
* <p>The following notes are from the original Samba source code for the
* smbdes.c module, which this module is adapted from.</p>
*
* <p>--------------------------------------------------</p>
*
* <p>Unix SMB/Netbios implementation.<br>
* Version 1.9.</p>
*
* <p>a partial implementation of DES designed for use in the
* SMB authentication protocol</p>
*
* <p>Copyright (C) Andrew Tridgell 1998</p>
*
* <p>--------------------------------------------------</p>
*
* <p>NOTES:</p>
*
* <p>This code makes no attempt to be fast! In fact, it is a very
* slow implementation.</p>
*
* <p>This code is NOT a complete DES implementation. It implements only
* the minimum necessary for SMB authentication, as used by all SMB
* products (including every copy of Microsoft Windows95 ever sold)</p>
*
* <p>In particular, it can only do a unchained forward DES pass. This
* means it is not possible to use this code for encryption/decryption
* of data, instead it is only useful as a "hash" algorithm.</p>
*
* <p>There is no entry point into this code that allows normal DES operation.</p>
*
* <p>I believe this means that this code does not come under ITAR
* regulations but this is NOT a legal opinion. If you are concerned
* about the applicability of ITAR regulations to this code then you
* should confirm it for yourself (and maybe let me know if you come
* up with a different answer to the one above)</p>
*/
public class smbencrypt {
static char hexdigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
//[56]
static byte perm1[] = {57, 49, 41, 33, 25, 17, 9,
1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27,
19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15,
7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29,
21, 13, 5, 28, 20, 12, 4};
//[48]
static byte perm2[] = {14, 17, 11, 24, 1, 5,
3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8,
16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55,
30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53,
46, 42, 50, 36, 29, 32};
//[64]
static byte perm3[] = {58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7};
//[48]
static byte perm4[] = { 32, 1, 2, 3, 4, 5,
4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13,
12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21,
20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29,
28, 29, 30, 31, 32, 1};
//[32]
static byte perm5[] = { 16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25};
//[64]
static byte perm6[] ={ 40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25};
//[16]
static byte sc[] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
//[8][4][16]
static byte sbox[][][] = {
{{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
{0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
{4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
{{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
{3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
{0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
{{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
{1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
{{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
{3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
{{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
{4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
{{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
{10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
{9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
{4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
{{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
{13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
{1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
{6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
{{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
{1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
{7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
{2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
/**
* convert an encoded unsigned byte value into a int
* with the unsigned value.
*/
static private final int bytes2u(byte b)
{
return (int) b & 0xff;
}
/**
* generates a two character hexadecimal string from
* an unsigned-encoded byte
*/
private static String byteToHex(byte b)
{
char cary[] = new char[2];
cary[0] = hexdigits[bytes2u(b) / 16];
cary[1] = hexdigits[bytes2u(b) % 16];
return new String(cary);
}
static void permute(byte[] out, byte[] in, byte[] p, int n)
{
for (int i=0; i<n; i++)
{
out[i] = in[p[i]-1];
}
}
static void lshift(byte[] d, int count, int n)
{
byte out[] = new byte[64];
/* -- */
for (int i=0; i<n; i++)
{
out[i] = d[(i+count)%n];
}
for (int i=0; i<n; i++)
{
d[i] = out[i];
}
}
static void concat(byte[] out, byte[] in1, byte[] in2, int l1, int l2)
{
int i = 0;
int j = 0;
/* -- */
while (l1-- > 0)
{
out[i] = in1[i];
i++;
}
while (l2-- > 0)
{
out[i] = in2[j];
i++;
j++;
}
}
static void xor(byte[] out, byte[] in1, byte[] in2, int n)
{
for (int i=0; i<n; i++)
{
out[i] = (byte) (in1[i] ^ in2[i]);
}
}
static void dohash(byte[] out, byte[] in, byte[] key, boolean forw)
{
int i, j, k;
byte pk1[] = new byte[56];
byte c[] = new byte[28];
byte d[] = new byte[28];
byte cd[] = new byte[56];
byte ki[][] = new byte[16][48];
byte pd1[] = new byte[64];
byte l[] = new byte[32];
byte r[] = new byte[32];
byte rl[] = new byte[64];
/* -- */
permute(pk1, key, perm1, 56);
for (i=0; i<28; i++)
{
c[i] = pk1[i];
}
for (i=0; i<28; i++)
{
d[i] = pk1[i+28];
}
for (i=0; i<16; i++)
{
lshift(c, sc[i], 28);
lshift(d, sc[i], 28);
concat(cd, c, d, 28, 28);
permute(ki[i], cd, perm2, 48);
}
permute(pd1, in, perm3, 64);
for (j=0; j<32; j++)
{
l[j] = pd1[j];
r[j] = pd1[j+32];
}
for (i=0; i<16; i++)
{
byte er[] = new byte[48];
byte erk[] = new byte[48];
byte b[][] = new byte[8][6];
byte cb[] = new byte[32];
byte pcb[] = new byte[32];
byte r2[] = new byte[32];
permute(er, r, perm4, 48);
xor(erk, er, ki[forw ? i : 15 - i], 48);
for (j=0; j<8; j++)
{
for (k=0; k<6; k++)
{
b[j][k] = erk[j*6 + k];
}
}
for (j=0; j<8; j++)
{
int m, n;
m = (b[j][0]<<1) | b[j][5];
n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4];
for (k=0; k<4; k++)
{
b[j][k] = (byte) ((sbox[j][m][n] & (1<<(3-k))) != 0 ?1:0);
}
}
for (j=0; j<8; j++)
{
for (k=0; k<4; k++)
{
cb[j*4+k] = b[j][k];
}
}
permute(pcb, cb, perm5, 32);
xor(r2, l, pcb, 32);
for (j=0; j<32; j++)
{
l[j] = r[j];
}
for (j=0; j<32; j++)
{
r[j] = r2[j];
}
}
concat(rl, r, l, 32, 32);
permute(out, rl, perm6, 64);
}
/**
* <p>This function reads from str and writes into key</p>
*/
static void str_to_key(byte[] str, int str_offset, byte[] key)
{
int i;
/* -- */
key[0] = (byte) (str[str_offset]>>>1);
key[1] = (byte) (((str[str_offset]&0x01)<<6) | (str[str_offset+1]>>>2));
key[2] = (byte) (((str[str_offset+1]&0x03)<<5) | (str[str_offset+2]>>>3));
key[3] = (byte) (((str[str_offset+2]&0x07)<<4) | (str[str_offset+3]>>>4));
key[4] = (byte) (((str[str_offset+3]&0x0F)<<3) | (str[str_offset+4]>>>5));
key[5] = (byte) (((str[str_offset+4]&0x1F)<<2) | (str[str_offset+5]>>>6));
key[6] = (byte) (((str[str_offset+5]&0x3F)<<1) | (str[str_offset+6]>>>7));
key[7] = (byte) (str[str_offset+6]&0x7F);
for (i=0; i<8; i++)
{
key[i] = (byte) (key[i]<<1);
}
}
/**
* <p>This method actually performs the SMB hash algorithm.</p>
*
* @param out An 8 element byte array to hold the results of the
* hash function
*
* @param in An 8 element byte array to hold the known pattern
* that we are hashing
*
* @param key An 8 element byte array that holds information
* from the secret that we are using to hash the appropriate
* known pattern.
*/
static void smbhash(byte[] out, byte[] in, byte[] key)
{
smbhash(out, 0, in, 0, key, 0, true);
}
/**
* <p>This method actually performs the SMB hash algorithm.</p>
*
* @param out An 8 element byte array to hold the results of the
* hash function
*
* @param in An 8 element byte array to hold the known pattern
* that we are hashing
*
* @param key An 8 element byte array that holds information
* from the secret that we are using to hash the appropriate
* known pattern.
*
* @param forw If true, we do a forward XOR operation. If false,
* we reverse it.
*/
static void smbhash(byte[] out, byte[] in, byte[] key, boolean forw)
{
smbhash(out, 0, in, 0, key, 0, forw);
}
/**
* <p>This method actually performs the SMB hash algorithm.</p>
*
* @param out An 8 element byte array to hold the results of the
* hash function
*
* @param in An 8 element byte array to hold the known pattern
* that we are hashing
*
* @param key An 8 element byte array that holds information
* from the secret that we are using to hash the appropriate
* known pattern.
*/
static void smbhash(byte[] out, int out_offset, byte[] in, int in_offset, byte[] key, int key_offset)
{
smbhash(out, out_offset, in, in_offset, key, key_offset, true);
}
/**
* <p>This method actually performs the SMB hash algorithm.</p>
*
* @param out An 8 element byte array to hold the results of the
* hash function
*
* @param in An 8 element byte array to hold the known pattern
* that we are hashing
*
* @param key An 8 element byte array that holds information
* from the secret that we are using to hash the appropriate
* known pattern.
*
* @param forw If true, we do a forward XOR operation. If false,
* we reverse it.
*/
static void smbhash(byte[] out, int out_offset, byte[] in, int in_offset, byte[] key, int key_offset, boolean forw)
{
int i;
byte outb[] = new byte[64];
byte inb[] = new byte[64];
byte keyb[] = new byte[64];
byte key2[] = new byte[8];
/* -- */
str_to_key(key, key_offset, key2);
for (i=0; i<64; i++)
{
inb[i] = (byte) ((in[in_offset + i/8] & (1<<(7-(i%8)))) != 0 ? 1 : 0);
keyb[i] = (byte) ((key2[i/8] & (1<<(7-(i%8)))) != 0 ? 1 : 0);
outb[i] = 0;
}
dohash(outb, inb, keyb, forw);
for (i=0; i<8; i++)
{
out[out_offset + i] = 0;
}
for (i=0; i<64; i++)
{
if (outb[i] != 0)
{
out[out_offset + i/8] |= (1<<(7-(i%8)));
}
}
}
/**
* <p>This method actually performs the standard LANMAN DES hashing, using
* the 14 byte password array p14 as the hashing key and the magic
* string 'KGS!@#$%' as the data to be hashed.</p>
*/
static void E_P16(byte[] p14, byte[] p16)
{
byte sp8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; // KGS!@#$%
/* -- */
smbhash(p16, sp8, p14);
smbhash(p16, 8, sp8, 0, p14, 7);
}
static void E_P24(byte[] p21, byte[] c8, byte[] p24)
{
smbhash(p24, c8, p21);
smbhash(p24, 8, c8, 0, p21, 7);
smbhash(p24, 16, c8, 0, p21, 14);
}
static void D_P16(byte[] p14, byte[] in, byte[] out)
{
smbhash(out, in, p14, false);
smbhash(out, 8, in, 8, p14, 7, false);
}
static void E_old_pw_hash( byte[] p14, byte[] in, byte[] out)
{
smbhash(out, in, p14);
smbhash(out, 8, in, 8, p14, 7);
}
public static void cred_hash1(byte[] out,byte[] in,byte[] key)
{
byte buf[] = new byte[8];
/* -- */
smbhash(buf, in, key);
smbhash(out, 0, buf, 0, key, 9);
}
public static void cred_hash2(byte[] out,byte[] in,byte[] key)
{
byte buf[] = new byte[8];
byte key2[] = new byte[8];
/* -- */
smbhash(buf, in, key);
key2[0] = key[7];
smbhash(out, buf, key2);
}
public static void cred_hash3(byte[] out,byte[] in,byte[] key, boolean forw)
{
byte key2[] = new byte[8];
/* -- */
smbhash(out, in, key, forw);
key2[0] = key[7];
smbhash(out, 8, in, 8, key2, 0, forw);
}
/**
* <p>This function does the password hashing used by the NT SAM database</p>
*/
public static void SamOEMhash(byte[] data, byte[] key, boolean bigbuf)
{
byte s_box[] = new byte[256];
int index_i = 0; // was unsigned char
int index_j = 0; // was unsigned char
int j = 0; // was unsigned char
int ind;
/* -- */
for (ind = 0; ind < 256; ind++)
{
s_box[ind] = (byte) ind;
}
for (ind = 0; ind < 256; ind++)
{
byte tc;
j += (bytes2u(s_box[ind]) + bytes2u(key[ind%16]));
// the original C code depended on j overflowing and wrapping,
// we have to do it manually since we are using a larger type
j &= 0xff;
tc = s_box[ind];
s_box[ind] = s_box[j];
s_box[j] = tc;
}
for (ind = 0; ind < (bigbuf ? 516 : 16); ind++)
{
byte tc;
int t;
index_i++;
index_i &= 0xff;
index_j += s_box[index_i];
index_j &= 0xff;
tc = s_box[index_i];
s_box[index_i] = s_box[index_j];
s_box[index_j] = tc;
t = bytes2u(s_box[index_i]) + bytes2u(s_box[index_j]);
data[ind] = (byte) (data[ind] ^ s_box[t]);
}
}
/**
* <p>This method generates a LANMAN-compatible DES hashing of the
* input password string, as used in the Samba encrypted password
* file.</p>
*
* <p>Only the first fourteen characters of the password are used,
* and they are converted to uppercase before hashing.</p>
*/
public static String LANMANHash(String password)
{
byte input[] = new byte[14];
byte output[] = new byte[16];
if (password.length() > 14)
{
password = password.substring(0,14);
}
char c_ary[] = password.toUpperCase().toCharArray();
int i;
for (i = 0; i < c_ary.length && i < 14; i++)
{
input[i] = (byte) c_ary[i];
}
E_P16(input, output);
StringBuilder result = new StringBuilder();
for (i = 0; i < 16; i++)
{
result.append(byteToHex(output[i]));
}
return result.toString();
}
/**
* <p>This method generates a SMB-compatible MD4 hashing of the
* input password string in little-endian Unicode format, as used in
* the Samba encrypted password file.</p>
*
* <p>This method hashes the first 128 characters of the password,
* with full Unicode range and case preservation.</p>
*/
public static String NTUNICODEHash(String password)
{
if (password.length() > 128)
{
password = password.substring(0, 128);
}
char c_ary[] = password.toCharArray();
// we need to simulate NT's little endian Unicode
// representation before we pass this to md4
byte wpwd[] = new byte[c_ary.length * 2];
for (int i = 0; i < c_ary.length; i++)
{
char c = c_ary[i];
wpwd[2*i] = (byte) (c & 0x00ff);
wpwd[2*i+1] = (byte) (c & 0xff00);
}
md4 m = new md4(wpwd);
m.calc();
return m.toString();
}
/**
* <p>test rig.</p>
*/
public static void main(String argv[])
{
if (argv.length != 1)
{
System.err.println("Error, must provide a password value to hash");
System.exit(1);
}
System.err.println(LANMANHash(argv[0]));
System.err.println(NTUNICODEHash(argv[0]));
System.exit(0);
}
}