func (e *Encryption) Transcrypt(i int, in, out []byte) error {
	hasher := e.hashFunc()
	_, err := hasher.Write(e.key)
	if err != nil {
		return err
	}
	ctrBytes := make([]byte, 4)
	binary.LittleEndian.PutUint32(ctrBytes, uint32(i)+e.initCtr)
	_, err = hasher.Write(ctrBytes)
	if err != nil {
		return err
	}
	ctrHash := hasher.Sum(nil)
	hasher.Reset()
	_, err = hasher.Write(ctrHash)
	if err != nil {
		return err
	}
	segmentKey := hasher.Sum(nil)
	hasher.Reset()
	inLength := len(in)
	for j := 0; j < inLength; j++ {
		out[j] = in[j] ^ segmentKey[j]
	}
	pad(out[inLength:])
	return nil
}
used for segmentwise transformation
if in is shorter than out, padding is used
package encryption
import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"hash"
)
const (
	KeyLength     = 32
	ReferenceSize = 64
)
type Key []byte
type Encrypter interface {
	Key() Key
	Encrypt(data []byte) ([]byte, error)
}
type Decrypter interface {
	Key() Key
	Decrypt(data []byte) ([]byte, error)
}
type Interface interface {
	Encrypter
	Decrypter
	Reset()
}
type Encryption struct {
	key      Key               
	keyLen   int               
	padding  int               
	index    int               
	initCtr  uint32            
	hashFunc func() hash.Hash  
}
func New(key Key, padding int, initCtr uint32, hashFunc func() hash.Hash) Interface {
	return &Encryption{
		key:      key,
		keyLen:   len(key),
		padding:  padding,
		initCtr:  initCtr,
		hashFunc: hashFunc,
	}
}
func (e *Encryption) Key() Key {
	return e.key
}
func (e *Encryption) Encrypt(data []byte) ([]byte, error) {
	length := len(data)
	outLength := length
	isFixedPadding := e.padding > 0
	if isFixedPadding {
		if length > e.padding {
			return nil, fmt.Errorf("data length longer than padding, data length %v padding %v", length, e.padding)
		}
		outLength = e.padding
	}
	out := make([]byte, outLength)
	err := e.transform(data, out)
	if err != nil {
		return nil, err
	}
	return out, nil
}
func (e *Encryption) Decrypt(data []byte) ([]byte, error) {
	length := len(data)
	if e.padding > 0 && length != e.padding {
		return nil, fmt.Errorf("data length different than padding, data length %v padding %v", length, e.padding)
	}
	out := make([]byte, length)
	err := e.transform(data, out)
	if err != nil {
		return nil, err
	}
	return out, nil
}
func (e *Encryption) Reset() {
	e.index = 0
}
func (e *Encryption) transform(in, out []byte) error {
	inLength := len(in)
	for i := 0; i < inLength; i += e.keyLen {
		l := min(e.keyLen, inLength-i)
		err := e.Transcrypt(e.index, in[i:i+l], out[i:i+l])
		if err != nil {
			return err
		}
		e.index++
	}
	pad(out[inLength:])
	return nil
}