package ntlm

import (
	
	
	
	
	
	
	

	
)

// NTLM v2 server
type Server struct {
	targetName string
	accounts   map[string]string // User: Password

	nmsg    []byte
	cmsg    []byte
	session *Session
}

func ( string) *Server {
	return &Server{
		targetName: ,
		accounts:   make(map[string]string),
	}
}

func ( *Server) (,  string) {
	.accounts[] = 
}

func ( *Server) ( []byte) ( []byte,  error) {
	//        NegotiateMessage
	//   0-8: Signature
	//  8-12: MessageType
	// 12-16: NegotiateFlags
	// 16-24: DomainNameFields
	// 24-32: WorkstationFields
	// 32-40: Version
	//   40-: Payload

	.nmsg = 

	if len() < 32 {
		return nil, errors.New("message length is too short")
	}

	if !bytes.Equal([:8], signature) {
		return nil, errors.New("invalid signature")
	}

	if le.Uint32([8:12]) != NtLmNegotiate {
		return nil, errors.New("invalid message type")
	}

	 := le.Uint32([12:16]) & defaultFlags

	//        ChallengeMessage
	//   0-8: Signature
	//  8-12: MessageType
	// 12-20: TargetNameFields
	// 20-24: NegotiateFlags
	// 24-32: ServerChallenge
	// 32-40: _
	// 40-48: TargetInfoFields
	// 48-56: Version
	//   56-: Payload

	 := 48

	if &NTLMSSP_NEGOTIATE_VERSION != 0 {
		 += 8
	}

	 := utf16le.EncodeStringToBytes(.targetName)

	 = make([]byte, +len()+4)

	copy([:8], signature)
	le.PutUint32([8:12], NtLmChallenge)
	le.PutUint32([20:24], )

	if  != nil && &NTLMSSP_REQUEST_TARGET != 0 {
		 := copy([:], )
		le.PutUint16([12:14], uint16())
		le.PutUint16([14:16], uint16())
		le.PutUint32([16:20], uint32())
		 += 
	}

	if &NTLMSSP_NEGOTIATE_TARGET_INFO != 0 {
		 := copy([:], []byte{0x00, 0x00, 0x00, 0x00}) // AvId: MsvAvEOL, AvLen: 0
		le.PutUint16([40:42], uint16())
		le.PutUint16([42:44], uint16())
		le.PutUint32([44:48], uint32())
		 += 
	}

	_,  = rand.Read([24:32])
	if  != nil {
		return nil, 
	}

	if &NTLMSSP_NEGOTIATE_VERSION != 0 {
		copy([48:56], version)
	}

	.cmsg = 

	return , nil
}

func ( *Server) ( []byte) ( error) {
	//        AuthenticateMessage
	//   0-8: Signature
	//  8-12: MessageType
	// 12-20: LmChallengeResponseFields
	// 20-28: NtChallengeResponseFields
	// 28-36: DomainNameFields
	// 36-44: UserNameFields
	// 44-52: WorkstationFields
	// 52-60: EncryptedRandomSessionKeyFields
	// 60-64: NegotiateFlags
	// 64-72: Version
	// 72-88: MIC
	//   88-: Payload

	if len() < 64 {
		return errors.New("message length is too short")
	}

	if !bytes.Equal([:8], signature) {
		return errors.New("invalid signature")
	}

	if le.Uint32([8:12]) != NtLmAuthenticate {
		return errors.New("invalid message type")
	}

	 := le.Uint32([60:64])

	 := le.Uint16([20:22])    // amsg.NtChallengeResponseLen
	 := le.Uint16([22:24]) // amsg.NtChallengeResponseMaxLen
	if  <  {
		return errors.New("invalid LM challenge format")
	}
	 := le.Uint32([24:28]) // amsg.NtChallengeResponseBufferOffset
	if len() < int(+uint32()) {
		return errors.New("invalid LM challenge format")
	}
	 := [ : +uint32()] // amsg.NtChallengeResponse

	 := le.Uint16([28:30])    // amsg.DomainNameLen
	 := le.Uint16([30:32]) // amsg.DomainNameMaxLen
	if  <  {
		return errors.New("invalid domain name format")
	}
	 := le.Uint32([32:36]) // amsg.DomainNameBufferOffset
	if len() < int(+uint32()) {
		return errors.New("invalid domain name format")
	}
	 := [ : +uint32()] // amsg.DomainName

	 := le.Uint16([36:38])    // amsg.UserNameLen
	 := le.Uint16([38:40]) // amsg.UserNameMaxLen
	if  <  {
		return errors.New("invalid user name format")
	}
	 := le.Uint32([40:44]) // amsg.UserNameBufferOffset
	if len() < int(+uint32()) {
		return errors.New("invalid user name format")
	}
	 := [ : +uint32()] // amsg.UserName

	 := le.Uint16([52:54])    // amsg.EncryptedRandomSessionKeyLen
	 := le.Uint16([54:56]) // amsg.EncryptedRandomSessionKeyMaxLen
	if  <  {
		return errors.New("invalid user name format")
	}
	 := le.Uint32([56:60]) // amsg.EncryptedRandomSessionKeyBufferOffset
	if len() < int(+uint32()) {
		return errors.New("invalid user name format")
	}
	 := [ : +uint32()] // amsg.EncryptedRandomSessionKey

	if len() != 0 || len() != 0 {
		 := utf16le.DecodeToString()
		 := make([]byte, len())
		 := [16:]
		 := utf16le.EncodeStringToBytes(strings.ToUpper())
		 := utf16le.EncodeStringToBytes(.accounts[])
		 := hmac.New(md5.New, ntowfv2(, , ))
		 := .cmsg[24:32]
		 := [8:16]
		 := [16:24]
		 := [28:]
		encodeNtlmv2Response(, , , , , bytesEncoder())
		if !bytes.Equal(, ) {
			return errors.New("login failure")
		}

		 := new(Session)

		.isClientSide = false

		.user = 
		.negotiateFlags = 

		.Reset()
		.Write([:16])
		 := .Sum(nil)

		 :=  // if ntlm version == 2

		if &NTLMSSP_NEGOTIATE_KEY_EXCH != 0 {
			.exportedSessionKey = make([]byte, 16)
			,  := rc4.NewCipher()
			if  != nil {
				return 
			}
			.XORKeyStream(.exportedSessionKey, )
		} else {
			.exportedSessionKey = 
		}

		if ,  := parseAvPairs();  {
			if ,  := [MsvAvFlags];  && le.Uint32()&0x02 != 0 {
				 := make([]byte, 16)
				if &NTLMSSP_NEGOTIATE_VERSION != 0 {
					copy(, [72:88])
					copy([72:88], zero[:])
				} else {
					copy(, [64:80])
					copy([64:80], zero[:])
				}
				 = hmac.New(md5.New, .exportedSessionKey)
				.Write(.nmsg)
				.Write(.cmsg)
				.Write()
				if !bytes.Equal(, .Sum(nil)) {
					return errors.New("login failure")
				}
			}
		}

		{
			.clientSigningKey = signKey(, .exportedSessionKey, true)
			.serverSigningKey = signKey(, .exportedSessionKey, false)

			.clientHandle,  = rc4.NewCipher(sealKey(, .exportedSessionKey, true))
			if  != nil {
				return 
			}

			.serverHandle,  = rc4.NewCipher(sealKey(, .exportedSessionKey, false))
			if  != nil {
				return 
			}
		}

		.session = 

		return nil
	}

	return errors.New("credential is empty")
}

func ( *Server) () *Session {
	return .session
}