public static ByteBuffer generateAuthenticateMessage(NtlmState ntlmState) {
// Allocate memory for blocks from given fixed offset
int blocksCursor = BLOCKS_OFFSET;
ByteBuffer buf = new ByteBuffer(4096);
// Signature: "NTLMSSP\0"
buf.writeString(NTLMSSP, RdpConstants.CHARSET_8);
buf.writeByte(0);
// NTLM Message Type: NTLMSSP_AUTH (0x00000003)
buf.writeIntLE(NtlmConstants.NTLMSSP_AUTH);
// Although the protocol allows authentication to succeed if the client
// provides either LmChallengeResponse or NtChallengeResponse, Windows
// implementations provide both.
// LM V2 response
blocksCursor = writeBlock(buf, ntlmState.lmChallengeResponse, blocksCursor);
// NT v2 response
blocksCursor = writeBlock(buf, ntlmState.ntChallengeResponse, blocksCursor);
// DomainName
blocksCursor = writeStringBlock(buf, ntlmState.domain, blocksCursor, RdpConstants.CHARSET_16);
// UserName
blocksCursor = writeStringBlock(buf, ntlmState.user, blocksCursor, RdpConstants.CHARSET_16);
// Workstation
blocksCursor = writeStringBlock(buf, ntlmState.workstation, blocksCursor, RdpConstants.CHARSET_16);
// EncryptedRandomSessionKey, 16 bytes
blocksCursor = writeBlock(buf, ntlmState.encryptedRandomSessionKey, blocksCursor);
// NegotiateFlags (4 bytes): In connection-oriented mode, a NEGOTIATE
// structure that contains the set of bit flags (section 2.2.2.5) negotiated
// in the previous messages.
buf.writeIntLE(/*ntlmState.negotiatedFlags.value*/0xe288b235); // FIXME: remove hardcoded value
buf.writeBytes(generateVersion());
// If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an
// MsvAvTimestamp present, the client SHOULD provide a MIC(Message Integrity
// Check)
int savedCursorForMIC = buf.cursor; // Save cursor position to write MIC
// later
buf.writeBytes(new byte[16]); // Write 16 zeroes
if (BLOCKS_OFFSET != buf.cursor)
throw new RuntimeException("BUG: Actual offset of first byte of allocated blocks is not equal hardcoded offset. Hardcoded offset: " + BLOCKS_OFFSET
+ ", actual offset: " + buf.cursor + ". Update hardcoded offset to match actual offset.");
buf.cursor = blocksCursor;
buf.trimAtCursor();
ntlmState.authenticateMessage = buf.toByteArray();
// Calculate and write MIC to reserved position
ntlmState.ntlm_compute_message_integrity_check();
buf.cursor = savedCursorForMIC;
buf.writeBytes(ntlmState.messageIntegrityCheck);
buf.rewindCursor();
return buf;
}