package ntlm

import (
	
	
	
	
	
	

	
)

var zero [16]byte

var version = []byte{
	0: WINDOWS_MAJOR_VERSION_10,
	1: WINDOWS_MINOR_VERSION_0,
	7: NTLMSSP_REVISION_W2K3,
}

const defaultFlags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_KEY_EXCH | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY | NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_VERSION

var le = binary.LittleEndian

const (
	NtLmNegotiate    = 0x00000001
	NtLmChallenge    = 0x00000002
	NtLmAuthenticate = 0x00000003
)

const (
	NTLMSSP_NEGOTIATE_UNICODE = 1 << iota
	NTLM_NEGOTIATE_OEM
	NTLMSSP_REQUEST_TARGET
	_
	NTLMSSP_NEGOTIATE_SIGN
	NTLMSSP_NEGOTIATE_SEAL
	NTLMSSP_NEGOTIATE_DATAGRAM
	NTLMSSP_NEGOTIATE_LM_KEY
	_
	NTLMSSP_NEGOTIATE_NTLM
	_
	NTLMSSP_ANONYMOUS
	NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED
	NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED
	_
	NTLMSSP_NEGOTIATE_ALWAYS_SIGN
	NTLMSSP_TARGET_TYPE_DOMAIN
	NTLMSSP_TARGET_TYPE_SERVER
	_
	NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
	NTLMSSP_NEGOTIATE_IDENTIFY
	_
	NTLMSSP_REQUEST_NON_NT_SESSION_KEY
	NTLMSSP_NEGOTIATE_TARGET_INFO
	_
	NTLMSSP_NEGOTIATE_VERSION
	_
	_
	_
	NTLMSSP_NEGOTIATE_128
	NTLMSSP_NEGOTIATE_KEY_EXCH
	NTLMSSP_NEGOTIATE_56
)

const (
	MsvAvEOL = iota
	MsvAvNbComputerName
	MsvAvNbDomainName
	MsvAvDnsComputerName
	MsvAvDnsDomainName
	MsvAvDnsTreeName
	MsvAvFlags
	MsvAvTimestamp
	MsvAvSingleHost
	MsvAvTargetName
	MsvAvChannelBindings
)

type addr struct {
	typ uint32
	val []byte
}

// channelBindings represents gss_channel_bindings_struct
type channelBindings struct {
	InitiatorAddress addr
	AcceptorAddress  addr
	AppData          []byte
}

var signature = []byte("NTLMSSP\x00")

//      Version
// 0-1: ProductMajorVersion
// 1-2: ProductMinorVersion
// 2-4: ProductBuild
// 4-7: Reserved
// 7-8: NTLMRevisionCurrent

const (
	WINDOWS_MAJOR_VERSION_5  = 0x05
	WINDOWS_MAJOR_VERSION_6  = 0x06
	WINDOWS_MAJOR_VERSION_10 = 0x0a
)

const (
	WINDOWS_MINOR_VERSION_0 = 0x00
	WINDOWS_MINOR_VERSION_1 = 0x01
	WINDOWS_MINOR_VERSION_2 = 0x02
	WINDOWS_MINOR_VERSION_3 = 0x03
)

const (
	NTLMSSP_REVISION_W2K3 = 0x0f
)

func ntowfv2(, ,  []byte) []byte {
	 := md4.New()
	.Write()
	 := .Sum(nil)
	return ntowfv2Hash(, , )
}

func ntowfv2Hash(, ,  []byte) []byte {
	 := hmac.New(md5.New, )
	.Write()
	.Write()
	return .Sum(nil)
}

func encodeNtlmv2Response( []byte,  hash.Hash, , ,  []byte,  encoder) {
	//        NTLMv2Response
	//  0-16: Response
	//   16-: NTLMv2ClientChallenge

	 := [16:]

	//        NTLMv2ClientChallenge
	//   0-1: RespType
	//   1-2: HiRespType
	//   2-4: _
	//   4-8: _
	//  8-16: TimeStamp
	// 16-24: ChallengeFromClient
	// 24-28: _
	//   28-: AvPairs

	[0] = 1
	[1] = 1
	copy([8:16], )
	copy([16:24], )
	.encode([28:])

	.Write()
	.Write()
	.Sum([:0]) // ntChallengeResponse.Response
}

type encoder interface {
	size() int
	encode(bs []byte)
}

type bytesEncoder []byte

func ( bytesEncoder) () int {
	return len()
}

func ( bytesEncoder) ( []byte) {
	copy(, )
}

type targetInfoEncoder struct {
	Info    []byte
	SPN     []byte
	InfoMap map[uint16][]byte
}

func newTargetInfoEncoder(,  []byte) *targetInfoEncoder {
	,  := parseAvPairs()
	if ! {
		return nil
	}
	return &targetInfoEncoder{
		Info:    ,
		SPN:     ,
		InfoMap: ,
	}
}

func ( *targetInfoEncoder) () int {
	 := len(.Info)
	if ,  := .InfoMap[MsvAvFlags]; ! {
		 += 8
	}
	 += 20
	if len(.SPN) != 0 {
		 += 4 + len(.SPN)
	}
	return 
}

func ( *targetInfoEncoder) ( []byte) {
	var  int

	if ,  := .InfoMap[MsvAvFlags];  {
		le.PutUint32(, le.Uint32()|0x02)

		 = copy(, .Info[:len(.Info)-4])
	} else {
		 = copy(, .Info[:len(.Info)-4])

		le.PutUint16([:+2], MsvAvFlags)
		le.PutUint16([+2:+4], 4)
		le.PutUint32([+4:+8], 0x02)

		 += 8
	}

	le.PutUint16([:+2], MsvAvChannelBindings)
	le.PutUint16([+2:+4], 16)

	 += 20

	if len(.SPN) != 0 {
		le.PutUint16([:+2], MsvAvTargetName)
		le.PutUint16([+2:+4], uint16(len(.SPN)))
		copy([+4:], .SPN)

		 += 4 + len(.SPN)
	}

	le.PutUint16([:+2], MsvAvEOL)
	le.PutUint16([+2:+4], 0)

	 += 4
}

func mac( []byte,  uint32,  *rc4.Cipher,  []byte,  uint32,  []byte) ([]byte, uint32) {
	,  := sliceForAppend(, 16)
	if &NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY == 0 {
		//        NtlmsspMessageSignature
		//   0-4: Version
		//   4-8: RandomPad
		//  8-12: Checksum
		// 12-16: SeqNum

		le.PutUint32([:4], 0x00000001)
		le.PutUint32([8:12], crc32.ChecksumIEEE())
		.XORKeyStream([4:8], [4:8])
		.XORKeyStream([8:12], [8:12])
		.XORKeyStream([12:16], [12:16])
		[12] ^= byte()
		[13] ^= byte( >> 8)
		[14] ^= byte( >> 16)
		[15] ^= byte( >> 24)
		if &NTLMSSP_NEGOTIATE_DATAGRAM == 0 {
			++
		}
		[4] = 0
		[5] = 0
		[6] = 0
		[7] = 0
	} else {
		//        NtlmsspMessageSignatureExt
		//   0-4: Version
		//  4-12: Checksum
		// 12-16: SeqNum

		le.PutUint32([:4], 0x00000001)
		le.PutUint32([12:16], )
		 := hmac.New(md5.New, )
		.Write([12:16])
		.Write()
		copy([4:12], .Sum(nil))
		if &NTLMSSP_NEGOTIATE_KEY_EXCH != 0 {
			.XORKeyStream([4:12], [4:12])
		}
		++
	}

	return , 
}

func signKey( uint32,  []byte,  bool) []byte {
	if &NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY != 0 {
		 := md5.New()
		.Write()
		if  {
			.Write([]byte("session key to client-to-server signing key magic constant\x00"))
		} else {
			.Write([]byte("session key to server-to-client signing key magic constant\x00"))
		}
		return .Sum(nil)
	}
	return nil
}

func sealKey( uint32,  []byte,  bool) []byte {
	if &NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY != 0 {
		 := md5.New()
		switch {
		case &NTLMSSP_NEGOTIATE_128 != 0:
			.Write()
		case &NTLMSSP_NEGOTIATE_56 != 0:
			.Write([:7])
		default:
			.Write([:5])
		}
		if  {
			.Write([]byte("session key to client-to-server sealing key magic constant\x00"))
		} else {
			.Write([]byte("session key to server-to-client sealing key magic constant\x00"))
		}
		return .Sum(nil)
	}

	if &NTLMSSP_NEGOTIATE_LM_KEY != 0 {
		 := make([]byte, 8)
		if &NTLMSSP_NEGOTIATE_56 != 0 {
			copy(, [:7])
			[7] = 0xa0
		} else {
			copy(, [:5])
			[5] = 0xe5
			[6] = 0x38
			[7] = 0xb0
		}
		return 
	}

	return 
}

func parseAvPairs( []byte) ( map[uint16][]byte,  bool) {
	//        AvPair
	//   0-2: AvId
	//   2-4: AvLen
	//    4-: Value

	if len() < 4 {
		return nil, false
	}

	// check MsvAvEOL
	for ,  := range [len()-4:] {
		if  != 0x00 {
			return nil, false
		}
	}

	 = make(map[uint16][]byte)

	for len() > 0 {
		if len() < 4 {
			return nil, false
		}

		 := le.Uint16([:2])
		// if _, dup := pairs[id]; dup {
		// return nil, false
		// }

		 := int(le.Uint16([2:4]))
		if len() < 4+ {
			return nil, false
		}

		[] = [4 : 4+]

		 = [4+:]
	}

	return , true
}

func sliceForAppend( []byte,  int) (,  []byte) {
	if  := len() + ; cap() >=  {
		 = [:]
	} else {
		 = make([]byte, )
		copy(, )
	}
	 = [len():]
	return
}