func (decryptor *PgDecryptor) GetTagBeginLength() int {
	return decryptor.binaryDecryptor.GetTagBeginLength()
}
GetTagBeginLength returns begin tag length, depends on decryptor type
package postgresql
import (
	"bytes"
	"context"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"os"
	"github.com/cossacklabs/acra/decryptor/base"
	"github.com/cossacklabs/acra/decryptor/binary"
	"github.com/cossacklabs/acra/keystore"
	"github.com/cossacklabs/acra/logging"
	"github.com/cossacklabs/acra/utils"
	"github.com/cossacklabs/acra/zone"
	"github.com/cossacklabs/themis/gothemis/keys"
	log "github.com/sirupsen/logrus"
	"go.opencensus.io/trace"
)
var errPlainData = errors.New("plain data without AcraStruct signature")
type PgDecryptor struct {
	isWithZone         bool
	isWholeMatch       bool
	keyStore           keystore.DecryptionKeyStore
	zoneMatcher        *zone.Matcher
	binaryDecryptor    base.DataDecryptor
	checkPoisonRecords bool
	clientID             []byte
	matchBuffer          []byte
	matchIndex           int
	callbackStorage      *base.PoisonCallbackStorage
	logger               *log.Entry
	dataProcessor        base.DataProcessor
	dataProcessorContext *base.DataProcessorContext
}
func NewPgDecryptor(clientID []byte, decryptor base.DataDecryptor, withZone bool, keystore keystore.DecryptionKeyStore) *PgDecryptor {
	logger := log.WithField("client_id", string(clientID))
	decryptor.SetLogger(logger)
	return &PgDecryptor{
		isWithZone:      withZone,
		binaryDecryptor: binary.NewBinaryDecryptor(logger),
		clientID:        clientID,
		matchBuffer:          make([]byte, len(EscapeTagBegin)+len(base.TagBegin)),
		matchIndex:           0,
		isWholeMatch:         true,
		logger:               logger,
		checkPoisonRecords:   true,
		keyStore:             keystore,
		dataProcessorContext: base.NewDataProcessorContext(clientID, withZone, keystore).UseContext(logging.SetLoggerToContext(context.Background(), logger)),
	}
}
func (decryptor *PgDecryptor) SetClientID(clientID []byte) {
	decryptor.clientID = clientID
	decryptor.dataProcessorContext.ClientID = clientID
}
func (decryptor *PgDecryptor) SetLogger(logger *log.Entry) {
	decryptor.binaryDecryptor.SetLogger(logger)
}
func (decryptor *PgDecryptor) SetWithZone(b bool) {
	decryptor.isWithZone = b
}
func (decryptor *PgDecryptor) SetZoneMatcher(zoneMatcher *zone.Matcher) {
	decryptor.zoneMatcher = zoneMatcher
}
func (decryptor *PgDecryptor) GetZoneMatcher() *zone.Matcher {
	return decryptor.zoneMatcher
}
func (decryptor *PgDecryptor) IsMatchedZone() bool {
	return decryptor.zoneMatcher.IsMatched() && decryptor.keyStore.HasZonePrivateKey(decryptor.zoneMatcher.GetZoneID())
}
func (decryptor *PgDecryptor) MatchZone(b byte) bool {
	return decryptor.zoneMatcher.Match(b)
}
func (decryptor *PgDecryptor) GetMatchedZoneID() []byte {
	if decryptor.IsWithZone() {
		return decryptor.zoneMatcher.GetZoneID()
	}
	return nil
}
func (decryptor *PgDecryptor) ResetZoneMatch() {
	if decryptor.zoneMatcher != nil {
		decryptor.zoneMatcher.Reset()
	}
}
func (decryptor *PgDecryptor) MatchBeginTag(char byte) bool {
	matched := decryptor.binaryDecryptor.MatchBeginTag(char)
	if matched {
		decryptor.matchBuffer[decryptor.matchIndex] = char
		decryptor.matchIndex++
	}
	return matched
}
func (decryptor *PgDecryptor) IsWithZone() bool {
	return decryptor.isWithZone
}
func (decryptor *PgDecryptor) IsMatched() bool {
	return decryptor.binaryDecryptor.IsMatched()
}
func (decryptor *PgDecryptor) Reset() {
	decryptor.binaryDecryptor.Reset()
	decryptor.matchIndex = 0
}
func (decryptor *PgDecryptor) GetMatched() []byte {
	return decryptor.matchBuffer[:decryptor.matchIndex]
}
func (decryptor *PgDecryptor) ReadSymmetricKey(privateKey *keys.PrivateKey, reader io.Reader) ([]byte, []byte, error) {
	symmetricKey, rawData, err := decryptor.binaryDecryptor.ReadSymmetricKey(privateKey, reader)
	if err != nil {
		return symmetricKey, rawData, err
	}
	return symmetricKey, rawData, nil
}
func (decryptor *PgDecryptor) ReadSymmetricKeyRotated(privateKeys []*keys.PrivateKey, reader io.Reader) ([]byte, []byte, error) {
	symmetricKey, rawData, err := decryptor.binaryDecryptor.ReadSymmetricKeyRotated(privateKeys, reader)
	if err != nil {
		return symmetricKey, rawData, err
	}
	return symmetricKey, rawData, nil
}
func (decryptor *PgDecryptor) ReadData(symmetricKey, zoneID []byte, reader io.Reader) ([]byte, error) {
	logger := log.NewEntry(decryptor.logger.Logger)
	if decryptor.GetMatchedZoneID() != nil {
		logger = decryptor.logger.WithField("zone_id", string(decryptor.GetMatchedZoneID()))
		decryptor.binaryDecryptor.SetLogger(logger)
		defer decryptor.binaryDecryptor.SetLogger(decryptor.logger)
	}
	correctMatchBeginTagLength := len(decryptor.binaryDecryptor.GetMatched())
	falseBufferedBeginTagLength := decryptor.matchIndex - correctMatchBeginTagLength
	if falseBufferedBeginTagLength > 0 {
		logger.Debugf("Return with false matched %v bytes", falseBufferedBeginTagLength)
		decrypted, err := decryptor.binaryDecryptor.ReadData(symmetricKey, zoneID, reader)
		if err != nil {
			return nil, err
		}
		logger.Debugln("Decrypted AcraStruct")
		return append(decryptor.matchBuffer[:falseBufferedBeginTagLength], decrypted...), nil
	}
	decrypted, err := decryptor.binaryDecryptor.ReadData(symmetricKey, zoneID, reader)
	if err != nil {
		return nil, err
	}
	logger.Debugln("Decrypted AcraStruct")
	return decrypted, nil
}
func (decryptor *PgDecryptor) SetKeyStore(store keystore.DecryptionKeyStore) {
	decryptor.keyStore = store
}
func (decryptor *PgDecryptor) GetPrivateKey() (*keys.PrivateKey, error) {
	if decryptor.IsWithZone() {
		return decryptor.keyStore.GetZonePrivateKey(decryptor.GetMatchedZoneID())
	}
	return decryptor.keyStore.GetServerDecryptionPrivateKey(decryptor.clientID)
}
func (decryptor *PgDecryptor) GetPrivateKeys() ([]*keys.PrivateKey, error) {
	if decryptor.IsWithZone() {
		return decryptor.keyStore.GetZonePrivateKeys(decryptor.GetMatchedZoneID())
	}
	return decryptor.keyStore.GetServerDecryptionPrivateKeys(decryptor.clientID)
}
func (decryptor *PgDecryptor) TurnOnPoisonRecordCheck(val bool) {
	decryptor.logger.Debugf("Set poison record check: %v", val)
	decryptor.checkPoisonRecords = val
}
func (decryptor *PgDecryptor) IsPoisonRecordCheckOn() bool {
	return decryptor.checkPoisonRecords
}
func (decryptor *PgDecryptor) GetPoisonCallbackStorage() *base.PoisonCallbackStorage {
	if decryptor.callbackStorage == nil {
		decryptor.callbackStorage = base.NewPoisonCallbackStorage()
	}
	return decryptor.callbackStorage
}
func (decryptor *PgDecryptor) SetPoisonCallbackStorage(storage *base.PoisonCallbackStorage) {
	decryptor.callbackStorage = storage
}
func (decryptor *PgDecryptor) IsWholeMatch() bool {
	return decryptor.isWholeMatch
}
func (decryptor *PgDecryptor) SetWholeMatch(value bool) {
	decryptor.isWholeMatch = value
	if logging.IsDebugLevel(decryptor.logger) {
		if value {
			decryptor.logger = decryptor.logger.WithField("decrypt_mode", base.DecryptWhole)
		}
		decryptor.logger = decryptor.logger.WithField("decrypt_mode", base.DecryptInline)
	}
}
func (decryptor *PgDecryptor) MatchZoneBlock(block []byte) {
	for _, c := range block {
		if !decryptor.MatchZone(c) {
			return
		}
	}
}
var HexPrefix = []byte{'\\', 'x'}
func (decryptor *PgDecryptor) SkipBeginInBlock(block []byte) ([]byte, error) {
	n := 0
	for _, c := range block {
		if !decryptor.MatchBeginTag(c) {
			return []byte{}, base.ErrFakeAcraStruct
		}
		n++
		if decryptor.IsMatched() {
			break
		}
	}
	if !decryptor.IsMatched() {
		return []byte{}, base.ErrFakeAcraStruct
	}
	return block[n:], nil
}
func (decryptor *PgDecryptor) DecryptBlock(block []byte) ([]byte, error) {
	ctx := decryptor.dataProcessorContext.UseZoneID(decryptor.GetMatchedZoneID())
	ctx.WithZone = decryptor.IsWithZone()
	decrypted, err := decryptor.dataProcessor.Process(block, ctx)
	if err == nil {
		return decrypted, nil
	}
	if err == base.ErrIncorrectAcraStructTagBegin {
		return nil, errPlainData
	} else if err == base.ErrIncorrectAcraStructLength {
		return nil, errPlainData
	}
	return nil, err
}
func (decryptor *PgDecryptor) CheckPoisonRecord(reader io.Reader) (bool, error) {
	if !decryptor.IsPoisonRecordCheckOn() {
		return false, nil
	}
	data, err := ioutil.ReadAll(reader)
	if err != nil {
		return false, err
	}
	poisonKeys, err := decryptor.keyStore.GetPoisonPrivateKeys()
	if err != nil {
		decryptor.logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantReadKeys).WithError(err).Errorln("Can't load poison keypair")
		return true, err
	}
	defer utils.ZeroizePrivateKeys(poisonKeys)
	_, err = base.DecryptRotatedAcrastruct(data, poisonKeys, nil)
	if err == nil {
		decryptor.logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorRecognizedPoisonRecord).Warningln("Recognized poison record")
		if decryptor.GetPoisonCallbackStorage().HasCallbacks() {
			err = decryptor.GetPoisonCallbackStorage().Call()
			if err != nil {
				decryptor.logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantCheckPoisonRecord).WithError(err).Errorln("Unexpected error in poison record callbacks")
			}
			decryptor.logger.Debugln("Processed all callbacks on poison record")
		}
		return true, nil
	}
	decryptor.logger.WithField("data", string(data[:10])).WithError(err).Errorln("Didn't decrypt poison record")
	return false, nil
}
var hexTagSymbols = hex.EncodeToString([]byte{base.TagSymbol})
var HexSymbol = byte(hexTagSymbols[0])
func (decryptor *PgDecryptor) BeginTagIndex(block []byte) (int, int) {
	if i := bytes.Index(block, base.TagBegin); i != utils.NotFound {
		decryptor.logger.Debugln("Matched binary decryptor")
		return i, decryptor.binaryDecryptor.GetTagBeginLength()
	}
	return utils.NotFound, decryptor.GetTagBeginLength()
}
var hexZoneSymbols = hex.EncodeToString([]byte{zone.ZoneTagSymbol})
var HexZoneSymbol = byte(hexZoneSymbols[0])
func (decryptor *PgDecryptor) MatchZoneInBlock(block []byte) {
	sliceCopy := block[:]
	for {
		i := bytes.Index(block, zone.ZoneIDBegin)
		if i == utils.NotFound {
			break
		} else {
			if decryptor.keyStore.HasZonePrivateKey(sliceCopy[i : i+zone.ZoneIDBlockLength]) {
				decryptor.zoneMatcher.SetMatched(sliceCopy[i : i+EscapeZoneIDBlockLength])
				return
			}
			sliceCopy = sliceCopy[i+1:]
		}
	}
	return
}