// Copyright 2016 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

package sasl

import (
	
	
	
)

// State represents the current state of a Negotiator.
// The first two bits represent the actual state of the state machine and the
// last 3 bits are a bitmask that define the machines behavior.
// The remaining bits should not be used.
type State uint8

// The current step of the Server or Client (represented by the first two bits
// of the state byte).
const (
	Initial State = iota
	AuthTextSent
	ResponseSent
	ValidServerResponse

	// Bitmask used for extracting the step from the state byte.
	StepMask = 0x3
)

const (
	// RemoteCB bit is on if the remote client or server supports channel binding.
	RemoteCB State = 1 << (iota + 3)

	// Errored bit is on if the machine has errored.
	Errored

	// Receiving bit is on if the machine is a server.
	Receiving
)

// NewClient creates a new SASL Negotiator that supports creating authentication
// requests using the given mechanism.
func ( Mechanism,  ...Option) *Negotiator {
	 := &Negotiator{
		mechanism: ,
	}
	getOpts(, ...)
	for ,  := range .remoteMechanisms {
		 := .Name
		if  ==  && strings.HasSuffix(, "-PLUS") {
			.state |= RemoteCB
			break
		}
	}
	if len(.nonce) == 0 {
		.nonce = nonce(noncerandlen, rand.Reader)
	}
	return 
}

// NewServer creates a new SASL Negotiator that supports receiving
// authentication requests using the given mechanism.
// A nil permissions function is the same as a function that always returns
// false.
func ( Mechanism,  func(*Negotiator) bool,  ...Option) *Negotiator {
	 := &Negotiator{
		mechanism: ,
		state:     AuthTextSent | Receiving,
	}
	getOpts(, ...)
	if  != nil {
		.permissions = 
	}
	for ,  := range .remoteMechanisms {
		 := .Name
		if  ==  && strings.HasSuffix(, "-PLUS") {
			.state |= RemoteCB
			break
		}
	}
	if len(.nonce) == 0 {
		.nonce = nonce(noncerandlen, rand.Reader)
	}
	return 
}

// A Negotiator represents a SASL client or server state machine that can
// attempt to negotiate auth. Negotiators should not be used from multiple
// goroutines, and must be reset between negotiation attempts.
type Negotiator struct {
	tlsState         *tls.ConnectionState
	remoteMechanisms []string
	credentials      func() (Username, Password, Identity []byte)
	permissions      func(*Negotiator) bool
	mechanism        Mechanism
	state            State
	nonce            []byte
	cache            interface{}
}

// Nonce returns a unique nonce that is reset for each negotiation attempt. It
// is used by SASL Mechanisms and should generally not be called directly.
func ( *Negotiator) () []byte {
	return .nonce
}

// Step attempts to transition the state machine to its next state. If Step is
// called after a previous invocation generates an error (and the state machine
// has not been reset to its initial state), Step panics.
func ( *Negotiator) ( []byte) ( bool,  []byte,  error) {
	if .state&Errored == Errored {
		panic("sasl: Step called on a SASL state machine that has errored")
	}
	defer func() {
		if  != nil {
			.state |= Errored
		}
	}()

	switch .state & StepMask {
	case Initial:
		, , .cache,  = .mechanism.Start()
		.state = .state&^StepMask | AuthTextSent
	case AuthTextSent:
		, , .cache,  = .mechanism.Next(, , .cache)
		.state = .state&^StepMask | ResponseSent
	case ResponseSent:
		, , .cache,  = .mechanism.Next(, , .cache)
		.state = .state&^StepMask | ValidServerResponse
	case ValidServerResponse:
		, , .cache,  = .mechanism.Next(, , .cache)
	}

	if  != nil {
		return false, nil, 
	}

	return , , 
}

// State returns the internal state of the SASL state machine.
func ( *Negotiator) () State {
	return .state
}

// Reset resets the state machine to its initial state so that it can be reused
// in another SASL exchange.
func ( *Negotiator) () {
	.state = .state & (Receiving | RemoteCB)

	// Skip the start step for servers
	if .state&Receiving == Receiving {
		.state = .state&^StepMask | AuthTextSent
	}

	.nonce = nonce(noncerandlen, rand.Reader)
	.cache = nil
}

// Credentials returns a username, and password for authentication and optional
// identity for authorization.
func ( *Negotiator) () (, ,  []byte) {
	if .credentials != nil {
		return .credentials()
	}
	return
}

// Permissions is the callback used by the server to authenticate the user.
func ( *Negotiator) ( ...Option) bool {
	if .permissions != nil {
		 := *
		getOpts(&, ...)
		return .permissions(&)
	}
	return false
}

// TLSState is the state of any TLS connections being used to negotiate SASL
// (it can be used for channel binding).
func ( *Negotiator) () *tls.ConnectionState {
	if .tlsState != nil {
		return .tlsState
	}
	return nil
}

// RemoteMechanisms is a list of mechanisms as advertised by the other side of a
// SASL negotiation.
func ( *Negotiator) () []string {
	if .remoteMechanisms != nil {
		return .remoteMechanisms
	}
	return nil
}