func (s *Strobe) Operate(meta bool, operation string, dataConst []byte, length int, more bool) []byte {
	var flags flag
	var ok bool
	if flags, ok = operationMap[operation]; !ok {
		panic("not a valid operation")
	}
	if meta {
		flags |= flagM
	}
	var data []byte
	if (flags&(flagI|flagT) != (flagI | flagT)) && (flags&(flagI|flagA) != flagA) {
		if length == 0 {
			panic("A length should be set for this operation.")
		}
		data = bytes.Repeat([]byte{0}, length)
	} else {
		if length != 0 {
			panic("Output length must be zero except for PRF, send_MAC and RATCHET operations.")
		}
		data = make([]byte, len(dataConst))
		copy(data, dataConst)
	}
	if more {
		if flags != s.curFlags {
			panic("Flag should be the same when streaming operations.")
		}
	} else {
		s.beginOp(flags)
		s.curFlags = flags
	}
	cAfter := (flags & (flagC | flagI | flagT)) == (flagC | flagT)
	cBefore := (flags&flagC != 0) && (!cAfter)
	s.duplex(data, cBefore, cAfter, false)
	if (flags & (flagI | flagA)) == (flagI | flagA) {
		return data
	} else if (flags & (flagI | flagT)) == flagT {
		return data
	} else if (flags & (flagI | flagA | flagT)) == (flagI | flagT) {
		if more {
			panic("not supposed to check a MAC with the 'more' streaming option")
		}
		var failures byte
		for _, dataByte := range data {
			failures |= dataByte
		}
		return []byte{failures}  
	}
	return nil
}
Operate runs an operation (see OperationMap for a list of operations).
For operations that only require a length, provide the length via the
length argument with an empty slice []byte{}. For other operations provide
a zero length.
Result is always retrieved through the return value. For boolean results,
check that the first index is 0 for true, 1 for false.
package strobe
import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
)
const (
	MACLEN = 16
)
func (s *Strobe) KEY(key []byte) {
	s.Operate(false, "KEY", key, 0, false)
}
func (s *Strobe) PRF(outputLen int) []byte {
	return s.Operate(false, "PRF", []byte{}, outputLen, false)
}
func (s *Strobe) Send_ENC_unauthenticated(meta bool, plaintext []byte) []byte {
	return s.Operate(meta, "send_ENC", plaintext, 0, false)
}
func (s *Strobe) Recv_ENC_unauthenticated(meta bool, ciphertext []byte) []byte {
	return s.Operate(meta, "recv_ENC", ciphertext, 0, false)
}
func (s *Strobe) AD(meta bool, additionalData []byte) {
	s.Operate(meta, "AD", additionalData, 0, false)
}
func (s *Strobe) Send_CLR(meta bool, cleartext []byte) {
	s.Operate(meta, "send_CLR", cleartext, 0, false)
}
func (s *Strobe) Recv_CLR(meta bool, cleartext []byte) {
	s.Operate(meta, "recv_CLR", cleartext, 0, false)
}
func (s *Strobe) Send_MAC(meta bool, output_length int) []byte {
	return s.Operate(meta, "send_MAC", []byte{}, output_length, false)
}
func (s *Strobe) Recv_MAC(meta bool, MAC []byte) bool {
	if s.Operate(meta, "recv_MAC", MAC, 0, false)[0] == 0 {
		return true
	}
	return false
}
func (s *Strobe) RATCHET(length int) {
	s.Operate(false, "RATCHET", []byte{}, length, false)
}
func (s *Strobe) Send_AEAD(plaintext, ad []byte) (ciphertext []byte) {
	ciphertext = append(ciphertext, s.Send_ENC_unauthenticated(false, plaintext)...)
	s.AD(false, ad)
	ciphertext = append(ciphertext, s.Send_MAC(false, MACLEN)...)
	return
}
func (s *Strobe) Recv_AEAD(ciphertext, ad []byte) (plaintext []byte, ok bool) {
	if len(ciphertext) < MACLEN {
		ok = false
		return
	}
	plaintext = s.Recv_ENC_unauthenticated(false, ciphertext[:len(ciphertext)-MACLEN])
	s.AD(false, ad)
	ok = s.Recv_MAC(false, ciphertext[len(ciphertext)-MACLEN:])
	return
}
type role uint8  
const (
	iInitiator role = iota  
	iResponder              
	iNone                   
)
type Strobe struct {
	duplexRate int  
	StrobeR    int  
	initialized bool   
	posBegin    uint8  
	I0          role
	curFlags flag
	a            [25]uint64  
	buf          []byte      
	storage      []byte      
	tempStateBuf []byte      
}
func (s Strobe) Clone() *Strobe {
	ret := s
	ret.storage = make([]byte, s.duplexRate)
	copy(ret.storage, s.storage)
	ret.tempStateBuf = make([]byte, s.duplexRate)
	copy(ret.tempStateBuf, s.tempStateBuf)
	ret.buf = ret.storage[:len(ret.buf)]
	return &ret
}
func (s Strobe) Serialize() []byte {
	serialized := make([]byte, 6+25*8)  
	security := (1600/8 - s.duplexRate) * 4
	if security == 128 {
		serialized[0] = 0
	} else {
		serialized[0] = 1
	}
	if s.initialized {
		serialized[1] = 1
	} else {
		serialized[1] = 0
	}
	serialized[2] = byte(s.I0)
	serialized[3] = byte(s.curFlags)
	serialized[4] = byte(s.posBegin)
	serialized[5] = byte(len(s.buf))
	var buf [1600 / 8]byte
	var state [25]uint64
	copy(buf[:len(s.buf)], s.storage[:len(s.buf)])  
	copy(state[:], s.a[:])
	xorState(&state, buf[:])
	var b []byte
	b = serialized[6:]
	for i := 0; len(b) >= 8; i++ {
		binary.LittleEndian.PutUint64(b, state[i])
		b = b[8:]
	}
	return serialized
}
func RecoverState(serialized []byte) (s Strobe) {
	if len(serialized) != 6+25*8 {
		panic("strobe: cannot recover state of invalid length")
	}
	if serialized[0] > 1 {
		panic("strobe: cannot recover state with invalid security")
	}
	security := 128
	if security == 1 {
		security = 256
	}
	s.duplexRate = 1600/8 - security/4
	s.StrobeR = s.duplexRate - 2
	s.storage = make([]byte, s.duplexRate)
	s.tempStateBuf = make([]byte, s.duplexRate)
	if serialized[1] == 1 {
		s.initialized = true
	} else {
		s.initialized = false
	}
	if serialized[2] > 3 {
		panic("strobe: cannot recover state with invalid role")
	}
	s.I0 = role(serialized[2])
	s.curFlags = flag(serialized[3])
	s.posBegin = uint8(serialized[4])
	pos := int(serialized[5])
	s.buf = s.storage[:pos]
	serialized = serialized[6:]
	for i := 0; i < 25; i++ {
		a := binary.LittleEndian.Uint64(serialized[:8])
		s.a[i] = a
		serialized = serialized[8:]
	}
	return
}
type flag uint8
const (
	flagI flag = 1 << iota
	flagA
	flagC
	flagT
	flagM
	flagK
)
var operationMap = map[string]flag{
	"AD":       flagA,
	"KEY":      flagA | flagC,
	"PRF":      flagI | flagA | flagC,
	"send_CLR": flagA | flagT,
	"recv_CLR": flagI | flagA | flagT,
	"send_ENC": flagA | flagC | flagT,
	"recv_ENC": flagI | flagA | flagC | flagT,
	"send_MAC": flagC | flagT,
	"recv_MAC": flagI | flagC | flagT,
	"RATCHET":  flagC,
}
func xorState(state *[25]uint64, buf []byte) {
	n := len(buf) / 8
	for i := 0; i < n; i++ {
		a := binary.LittleEndian.Uint64(buf)
		state[i] ^= a
		buf = buf[8:]
	}
}
func outState(state [25]uint64, b []byte) {
	for i := 0; len(b) >= 8; i++ {
		binary.LittleEndian.PutUint64(b, state[i])
		b = b[8:]
	}
}
func (s Strobe) debugPrintState() string {
	var buf [1600 / 8]byte
	copy(buf[:len(s.buf)], s.storage[:len(s.buf)])
	var state [25]uint64
	copy(state[:], s.a[:])
	xorState(&state, buf[:])
	outState(state, buf[:])
	return hex.EncodeToString(buf[:])
}
func InitStrobe(customizationString string, security int) (s Strobe) {
	if security != 128 && security != 256 {
		panic("strobe: security must be set to either 128 or 256")
	}
	s.duplexRate = 1600/8 - security/4
	s.StrobeR = s.duplexRate - 2
	s.storage = make([]byte, s.duplexRate)
	s.tempStateBuf = make([]byte, s.duplexRate)
	s.I0 = iNone
	s.initialized = false
	domain := []byte{1, byte(s.StrobeR + 2), 1, 0, 1, 12 * 8}
	domain = append(domain, []byte("STROBEv1.0.2")...)
	s.buf = s.storage[:0]
	s.duplex(domain, false, false, true)
	s.initialized = true
	s.Operate(true, "AD", []byte(customizationString), 0, false)
	return
}
func (s *Strobe) runF() {
	if s.initialized {
		if len(s.buf) > s.StrobeR {
			panic("strobe: buffer is never supposed to reach strobeR")
		}
		s.buf = append(s.buf, s.posBegin)
		s.buf = append(s.buf, 0x04)
		zerosStart := len(s.buf)
		s.buf = s.storage[:s.duplexRate]
		for i := zerosStart; i < s.duplexRate; i++ {
			s.buf[i] = 0
		}
		s.buf[s.duplexRate-1] ^= 0x80
		xorState(&s.a, s.buf)
	} else if len(s.buf) != 0 {
		zerosStart := len(s.buf)  
		s.buf = s.storage[:s.duplexRate]
		for i := zerosStart; i < s.duplexRate; i++ {
			s.buf[i] = 0
		}
		xorState(&s.a, s.buf)
	}
	keccakF1600(&s.a, 24)
	s.buf = s.storage[:0]
	s.posBegin = 0
}
func (s *Strobe) duplex(data []byte, cbefore, cafter, forceF bool) {
	for len(data) > 0 {
		todo := s.StrobeR - len(s.buf)
		if todo > len(data) {
			todo = len(data)
		}
		if cbefore {
			outState(s.a, s.tempStateBuf)
			for idx, state := range s.tempStateBuf[len(s.buf) : len(s.buf)+todo] {
				data[idx] ^= state
			}
		}
		s.buf = append(s.buf, data[:todo]...)
		if cafter {
			outState(s.a, s.tempStateBuf)
			for idx, state := range s.tempStateBuf[len(s.buf)-todo : len(s.buf)] {
				data[idx] ^= state
			}
		}
		data = data[todo:]
		if len(s.buf) == s.StrobeR {
			s.runF()
		}
	}
	if forceF && len(s.buf) != 0 {
		s.runF()
	}
	return
}