package com.knowgate.jcifs.smb;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import com.knowgate.debug.*;
import com.knowgate.jcifs.Config;
/**
* To filter 0 len updates and for debugging
*/
public class SigningDigest {
private static final int LM_COMPATIBILITY = Config.getInt( "jcifs.smb.lmCompatibility", 0);
private MessageDigest digest;
private byte[] macSigningKey;
private int updates;
private int signSequence;
public SigningDigest( SmbTransport transport,
NtlmPasswordAuthentication auth ) throws SmbException {
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException ex) {
if( DebugFile.trace )
new ErrorHandler(ex);
throw new SmbException( "MD5", ex );
}
try {
switch (LM_COMPATIBILITY) {
case 0:
case 1:
case 2:
macSigningKey = new byte[40];
auth.getUserSessionKey(transport.server.encryptionKey, macSigningKey, 0);
System.arraycopy(auth.getUnicodeHash(transport.server.encryptionKey),
0, macSigningKey, 16, 24);
break;
case 3:
case 4:
case 5:
macSigningKey = new byte[16];
auth.getUserSessionKey(transport.server.encryptionKey, macSigningKey, 0);
break;
default:
macSigningKey = new byte[40];
auth.getUserSessionKey(transport.server.encryptionKey, macSigningKey, 0);
System.arraycopy(auth.getUnicodeHash(transport.server.encryptionKey),
0, macSigningKey, 16, 24);
break;
}
} catch( Exception ex ) {
throw new SmbException( "", ex );
}
if( DebugFile.trace ) {
DebugFile.writeln( "LM_COMPATIBILITY=" + LM_COMPATIBILITY );
}
}
public void update( byte[] input, int offset, int len ) {
if( DebugFile.trace ) {
DebugFile.writeln( "update: " + updates + " " + offset + ":" + len );
}
if( len == 0 ) {
return; /* CRITICAL */
}
digest.update( input, offset, len );
updates++;
}
public byte[] digest() {
byte[] b;
b = digest.digest();
if( DebugFile.trace ) {
DebugFile.writeln( "digest: " );
}
updates = 0;
return b;
}
/**
* Performs MAC signing of the SMB. This is done as follows.
* The signature field of the SMB is overwritted with the sequence number;
* The MD5 digest of the MAC signing key + the entire SMB is taken;
* The first 8 bytes of this are placed in the signature field.
*
* @param data The data.
* @param offset The starting offset at which the SMB header begins.
* @param length The length of the SMB data starting at offset.
*/
void sign(byte[] data, int offset, int length,
ServerMessageBlock request, ServerMessageBlock response) {
request.signSeq = signSequence;
if( response != null ) {
response.signSeq = signSequence + 1;
response.verifyFailed = false;
}
try {
update(macSigningKey, 0, macSigningKey.length);
int index = offset + ServerMessageBlock.SIGNATURE_OFFSET;
for (int i = 0; i < 8; i++) data[index + i] = 0;
ServerMessageBlock.writeInt4(signSequence, data, index);
update(data, offset, length);
System.arraycopy(digest(), 0, data, index, 8);
} catch (Exception ex) {
if( DebugFile.trace )
new ErrorHandler(ex);
} finally {
signSequence += 2;
}
}
/**
* Performs MAC signature verification. This calculates the signature
* of the SMB and compares it to the signature field on the SMB itself.
*
* @param data The data.
* @param offset The starting offset at which the SMB header begins.
* @param length The length of the SMB data starting at offset.
*/
boolean verify(byte[] data, int offset, ServerMessageBlock response) {
update(macSigningKey, 0, macSigningKey.length);
int index = offset;
update(data, index, ServerMessageBlock.SIGNATURE_OFFSET);
index += ServerMessageBlock.SIGNATURE_OFFSET;
byte[] sequence = new byte[8];
ServerMessageBlock.writeInt4(response.signSeq, sequence, 0);
update(sequence, 0, sequence.length);
index += 8;
if( response.command == ServerMessageBlock.SMB_COM_READ_ANDX ) {
/* SmbComReadAndXResponse reads directly from the stream into separate byte[] b.
*/
SmbComReadAndXResponse raxr = (SmbComReadAndXResponse)response;
int length = response.length - raxr.dataLength;
update(data, index, length - ServerMessageBlock.SIGNATURE_OFFSET - 8);
update(raxr.b, raxr.off, raxr.dataLength);
} else {
update(data, index, response.length - ServerMessageBlock.SIGNATURE_OFFSET - 8);
}
byte[] signature = digest();
for (int i = 0; i < 8; i++) {
if (signature[i] != data[offset + ServerMessageBlock.SIGNATURE_OFFSET + i]) {
if( DebugFile.trace ) {
DebugFile.writeln( "signature verification failure" );
}
return response.verifyFailed = true;
}
}
return response.verifyFailed = false;
}
}