package smb2

import (
	
	
	
	
	
	
	
	
	
	

	
	

	. 
	. 
)

func sessionSetup( *conn,  Initiator,  context.Context) (*session, error) {
	 := newSpnegoClient([]Initiator{})

	,  := .initSecContext()
	if  != nil {
		return nil, &InvalidResponseError{.Error()}
	}

	 := &SessionSetupRequest{
		Flags:             0,
		Capabilities:      .capabilities & (SMB2_GLOBAL_CAP_DFS),
		Channel:           0,
		SecurityBuffer:    ,
		PreviousSessionId: 0,
	}

	if .requireSigning {
		.SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED
	} else {
		.SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED
	}

	.CreditCharge = 1
	.CreditRequestResponse = .account.initRequest()

	,  := .send(, )
	if  != nil {
		return nil, 
	}

	,  := .recv()
	if  != nil {
		return nil, 
	}

	 := PacketCodec()

	if NtStatus(.Status()) != STATUS_MORE_PROCESSING_REQUIRED {
		return nil, &InvalidResponseError{fmt.Sprintf("expected status: %v, got %v", STATUS_MORE_PROCESSING_REQUIRED, NtStatus(.Status()))}
	}

	,  := accept(SMB2_SESSION_SETUP, )
	if  != nil {
		return nil, 
	}

	 := SessionSetupResponseDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken session setup response format"}
	}

	 := .SessionFlags()
	if .requireSigning {
		if &SMB2_SESSION_FLAG_IS_GUEST != 0 {
			return nil, &InvalidResponseError{"guest account doesn't support signing"}
		}
		if &SMB2_SESSION_FLAG_IS_NULL != 0 {
			return nil, &InvalidResponseError{"anonymous account doesn't support signing"}
		}
	}

	 := &session{
		conn:           ,
		treeConnTables: make(map[uint32]*treeConn),
		sessionFlags:   ,
		sessionId:      .SessionId(),
	}

	switch .dialect {
	case SMB311:
		.preauthIntegrityHashValue = .preauthIntegrityHashValue

		switch .preauthIntegrityHashId {
		case SHA512:
			 := sha512.New()
			.Write(.preauthIntegrityHashValue[:])
			.Write(.pkt)
			.Sum(.preauthIntegrityHashValue[:0])

			.Reset()
			.Write(.preauthIntegrityHashValue[:])
			.Write()
			.Sum(.preauthIntegrityHashValue[:0])
		}

	}

	,  = .acceptSecContext(.SecurityBuffer())
	if  != nil {
		return nil, &InvalidResponseError{.Error()}
	}

	.SecurityBuffer = 

	.CreditRequestResponse = 0

	// We set session before sending packet just for setting hdr.SessionId.
	// But, we should not permit access from receiver until the session information is completed.
	.session = 

	,  = .send(, )
	if  != nil {
		return nil, 
	}

	if .sessionFlags&(SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL) == 0 {
		 := .sessionKey()

		switch .dialect {
		case SMB202, SMB210:
			.signer = hmac.New(sha256.New, )
			.verifier = hmac.New(sha256.New, )
		case SMB300, SMB302:
			 := kdf(, []byte("SMB2AESCMAC\x00"), []byte("SmbSign\x00"))
			,  := aes.NewCipher()
			if  != nil {
				return nil, &InternalError{.Error()}
			}
			.signer = cmac.New()
			.verifier = cmac.New()

			// s.applicationKey = kdf(sessionKey, []byte("SMB2APP\x00"), []byte("SmbRpc\x00"))

			 := kdf(, []byte("SMB2AESCCM\x00"), []byte("ServerIn \x00"))
			 := kdf(, []byte("SMB2AESCCM\x00"), []byte("ServerOut\x00"))

			,  = aes.NewCipher()
			if  != nil {
				return nil, &InternalError{.Error()}
			}
			.encrypter,  = ccm.NewCCMWithNonceAndTagSizes(, 11, 16)
			if  != nil {
				return nil, &InternalError{.Error()}
			}

			,  = aes.NewCipher()
			if  != nil {
				return nil, &InternalError{.Error()}
			}
			.decrypter,  = ccm.NewCCMWithNonceAndTagSizes(, 11, 16)
			if  != nil {
				return nil, &InternalError{.Error()}
			}
		case SMB311:
			switch .preauthIntegrityHashId {
			case SHA512:
				 := sha512.New()
				.Write(.preauthIntegrityHashValue[:])
				.Write(.pkt)
				.Sum(.preauthIntegrityHashValue[:0])
			}

			 := kdf(, []byte("SMBSigningKey\x00"), .preauthIntegrityHashValue[:])
			,  := aes.NewCipher()
			if  != nil {
				return nil, &InternalError{.Error()}
			}
			.signer = cmac.New()
			.verifier = cmac.New()

			// s.applicationKey = kdf(sessionKey, []byte("SMBAppKey\x00"), preauthIntegrityHashValue)

			 := kdf(, []byte("SMBC2SCipherKey\x00"), .preauthIntegrityHashValue[:])
			 := kdf(, []byte("SMBS2CCipherKey\x00"), .preauthIntegrityHashValue[:])

			switch .cipherId {
			case AES128CCM:
				,  := aes.NewCipher()
				if  != nil {
					return nil, &InternalError{.Error()}
				}
				.encrypter,  = ccm.NewCCMWithNonceAndTagSizes(, 11, 16)
				if  != nil {
					return nil, &InternalError{.Error()}
				}

				,  = aes.NewCipher()
				if  != nil {
					return nil, &InternalError{.Error()}
				}
				.decrypter,  = ccm.NewCCMWithNonceAndTagSizes(, 11, 16)
				if  != nil {
					return nil, &InternalError{.Error()}
				}
			case AES128GCM:
				,  := aes.NewCipher()
				if  != nil {
					return nil, &InternalError{.Error()}
				}
				.encrypter,  = cipher.NewGCMWithNonceSize(, 12)
				if  != nil {
					return nil, &InternalError{.Error()}
				}

				,  = aes.NewCipher()
				if  != nil {
					return nil, &InternalError{.Error()}
				}
				.decrypter,  = cipher.NewGCMWithNonceSize(, 12)
				if  != nil {
					return nil, &InternalError{.Error()}
				}
			}
		}
	}

	,  = .recv()
	if  != nil {
		return nil, 
	}

	,  = accept(SMB2_SESSION_SETUP, )
	if  != nil {
		return nil, 
	}

	 = SessionSetupResponseDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken session setup response format"}
	}

	if NtStatus(PacketCodec().Status()) != STATUS_SUCCESS {
		return nil, &InvalidResponseError{"broken session setup response format"}
	}

	.sessionFlags = .SessionFlags()

	// now, allow access from receiver
	.enableSession()

	return , nil
}

type session struct {
	*conn
	treeConnTables            map[uint32]*treeConn
	sessionFlags              uint16
	sessionId                 uint64
	preauthIntegrityHashValue [64]byte

	signer    hash.Hash
	verifier  hash.Hash
	encrypter cipher.AEAD
	decrypter cipher.AEAD

	// applicationKey []byte
}

func ( *session) ( context.Context) error {
	 := new(LogoffRequest)

	.CreditCharge = 1

	,  := .sendRecv(SMB2_LOGOFF, , )
	if  != nil {
		return 
	}

	.conn.rdone <- struct{}{}
	.conn.t.Close()

	return nil
}

func ( *session) ( uint16,  Packet,  context.Context) ( []byte,  error) {
	,  := .send(, )
	if  != nil {
		return nil, 
	}

	,  := .recv()
	if  != nil {
		return nil, 
	}

	return accept(, )
}

func ( *session) ( *requestResponse) ( []byte,  error) {
	,  = .conn.recv()
	if  != nil {
		return nil, 
	}
	if  := PacketCodec().SessionId();  != .sessionId {
		return nil, &InvalidResponseError{fmt.Sprintf("expected session id: %v, got %v", .sessionId, )}
	}
	return , 
}

func ( *session) ( []byte) []byte {
	 := PacketCodec()

	.SetFlags(.Flags() | SMB2_FLAGS_SIGNED)

	 := .signer

	.Reset()

	.Write()

	.SetSignature(.Sum(nil))

	return 
}

func ( *session) ( []byte) ( bool) {
	 := PacketCodec()

	 := append([]byte{}, .Signature()...)

	.SetSignature(zero[:])

	 := .verifier

	.Reset()

	.Write()

	.SetSignature(.Sum(nil))

	return bytes.Equal(, .Signature())
}

func ( *session) ( []byte) ([]byte, error) {
	 := make([]byte, .encrypter.NonceSize())

	,  := rand.Read()
	if  != nil {
		return nil, 
	}

	 := make([]byte, 52+len()+16)

	 := TransformCodec()

	.SetProtocolId()
	.SetNonce()
	.SetOriginalMessageSize(uint32(len()))
	.SetFlags(Encrypted)
	.SetSessionId(.sessionId)

	.encrypter.Seal([:52], , , .AssociatedData())

	.SetSignature([len()-16:])

	 = [:len()-16]

	return , nil
}

func ( *session) ( []byte) ([]byte, error) {
	 := TransformCodec()

	 := append(.EncryptedData(), .Signature()...)

	return .decrypter.Open(
		[:0],
		.Nonce()[:.decrypter.NonceSize()],
		,
		.AssociatedData(),
	)
}