/*
 * SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
 * SPDX-License-Identifier: Apache-2.0
 */

package badger

import (
	
	
	
	
	
	
	
	
	
	

	
	
	
)

const (
	// KeyRegistryFileName is the file name for the key registry file.
	KeyRegistryFileName = "KEYREGISTRY"
	// KeyRegistryRewriteFileName is the file name for the rewrite key registry file.
	KeyRegistryRewriteFileName = "REWRITE-KEYREGISTRY"
)

// SanityText is used to check whether the given user provided storage key is valid or not
var sanityText = []byte("Hello Badger")

// KeyRegistry used to maintain all the data keys.
type KeyRegistry struct {
	sync.RWMutex
	dataKeys    map[uint64]*pb.DataKey
	lastCreated int64 //lastCreated is the timestamp(seconds) of the last data key generated.
	nextKeyID   uint64
	fp          *os.File
	opt         KeyRegistryOptions
}

type KeyRegistryOptions struct {
	Dir                           string
	ReadOnly                      bool
	EncryptionKey                 []byte
	EncryptionKeyRotationDuration time.Duration
	InMemory                      bool
}

// newKeyRegistry returns KeyRegistry.
func newKeyRegistry( KeyRegistryOptions) *KeyRegistry {
	return &KeyRegistry{
		dataKeys:  make(map[uint64]*pb.DataKey),
		nextKeyID: 0,
		opt:       ,
	}
}

// OpenKeyRegistry opens key registry if it exists, otherwise it'll create key registry
// and returns key registry.
func ( KeyRegistryOptions) (*KeyRegistry, error) {
	// sanity check the encryption key length.
	if len(.EncryptionKey) > 0 {
		switch len(.EncryptionKey) {
		default:
			return nil, y.Wrapf(ErrInvalidEncryptionKey, "During OpenKeyRegistry")
		case 16, 24, 32:
			break
		}
	}
	// If db is opened in InMemory mode, we don't need to write key registry to the disk.
	if .InMemory {
		return newKeyRegistry(), nil
	}
	 := filepath.Join(.Dir, KeyRegistryFileName)
	var  y.Flags
	if .ReadOnly {
		 |= y.ReadOnly
	} else {
		 |= y.Sync
	}
	,  := y.OpenExistingFile(, )
	// OpenExistingFile just open file.
	// So checking whether the file exist or not. If not
	// We'll create new keyregistry.
	if os.IsNotExist() {
		// Creating new registry file if not exist.
		 := newKeyRegistry()
		if .ReadOnly {
			return , nil
		}
		// Writing the key registry to the file.
		if  := WriteKeyRegistry(, );  != nil {
			return nil, y.Wrapf(, "Error while writing key registry.")
		}
		,  = y.OpenExistingFile(, )
		if  != nil {
			return nil, y.Wrapf(, "Error while opening newly created key registry.")
		}
	} else if  != nil {
		return nil, y.Wrapf(, "Error while opening key registry.")
	}
	,  := readKeyRegistry(, )
	if  != nil {
		// This case happens only if the file is opened properly and
		// not able to read.
		.Close()
		return nil, 
	}
	if .ReadOnly {
		// We'll close the file in readonly mode.
		return , .Close()
	}
	.fp = 
	return , nil
}

// keyRegistryIterator reads all the datakey from the key registry
type keyRegistryIterator struct {
	encryptionKey []byte
	fp            *os.File
	// lenCrcBuf contains crc buf and data length to move forward.
	lenCrcBuf [8]byte
}

// newKeyRegistryIterator returns iterator which will allow you to iterate
// over the data key of the key registry.
func newKeyRegistryIterator( *os.File,  []byte) (*keyRegistryIterator, error) {
	return &keyRegistryIterator{
		encryptionKey: ,
		fp:            ,
		lenCrcBuf:     [8]byte{},
	}, validRegistry(, )
}

// validRegistry checks that given encryption key is valid or not.
func validRegistry( *os.File,  []byte) error {
	 := make([]byte, aes.BlockSize)
	var  error
	if _,  = .Read();  != nil {
		return y.Wrapf(, "Error while reading IV for key registry.")
	}
	 := make([]byte, len(sanityText))
	if _,  = .Read();  != nil {
		return y.Wrapf(, "Error while reading sanity text.")
	}
	if len() > 0 {
		// Decrypting sanity text.
		if ,  = y.XORBlockAllocate(, , );  != nil {
			return y.Wrapf(, "During validRegistry")
		}
	}
	// Check the given key is valid or not.
	if !bytes.Equal(, sanityText) {
		return ErrEncryptionKeyMismatch
	}
	return nil
}

func ( *keyRegistryIterator) () (*pb.DataKey, error) {
	var  error
	// Read crc buf and data length.
	if _,  = .fp.Read(.lenCrcBuf[:]);  != nil {
		// EOF means end of the iteration.
		if  != io.EOF {
			return nil, y.Wrapf(, "While reading crc in keyRegistryIterator.next")
		}
		return nil, 
	}
	 := int64(binary.BigEndian.Uint32(.lenCrcBuf[0:4]))
	// Read protobuf data.
	 := make([]byte, )
	if _,  = .fp.Read();  != nil {
		// EOF means end of the iteration.
		if  != io.EOF {
			return nil, y.Wrapf(, "While reading protobuf in keyRegistryIterator.next")
		}
		return nil, 
	}
	// Check checksum.
	if crc32.Checksum(, y.CastagnoliCrcTable) != binary.BigEndian.Uint32(.lenCrcBuf[4:]) {
		return nil, y.Wrapf(y.ErrChecksumMismatch, "Error while checking checksum for data key.")
	}
	 := &pb.DataKey{}
	if  = proto.Unmarshal(, );  != nil {
		return nil, y.Wrapf(, "While unmarshal of datakey in keyRegistryIterator.next")
	}
	if len(.encryptionKey) > 0 {
		// Decrypt the key if the storage key exists.
		if .Data,  = y.XORBlockAllocate(.Data, .encryptionKey, .Iv);  != nil {
			return nil, y.Wrapf(, "While decrypting datakey in keyRegistryIterator.next")
		}
	}
	return , nil
}

// readKeyRegistry will read the key registry file and build the key registry struct.
func readKeyRegistry( *os.File,  KeyRegistryOptions) (*KeyRegistry, error) {
	,  := newKeyRegistryIterator(, .EncryptionKey)
	if  != nil {
		return nil, 
	}
	 := newKeyRegistry()
	var  *pb.DataKey
	,  = .next()
	for  == nil &&  != nil {
		if .KeyId > .nextKeyID {
			// Set the maximum key ID for next key ID generation.
			.nextKeyID = .KeyId
		}
		if .CreatedAt > .lastCreated {
			// Set the last generated key timestamp.
			.lastCreated = .CreatedAt
		}
		// No need to lock since we are building the initial state.
		.dataKeys[.KeyId] = 
		// Forward the iterator.
		,  = .next()
	}
	// We read all the key. So, Ignoring this error.
	if  == io.EOF {
		 = nil
	}
	return , 
}

/*
Structure of Key Registry.
+-------------------+---------------------+--------------------+--------------+------------------+
|     IV            | Sanity Text         | DataKey1           | DataKey2     | ...              |
+-------------------+---------------------+--------------------+--------------+------------------+
*/

// WriteKeyRegistry will rewrite the existing key registry file with new one.
// It is okay to give closed key registry. Since, it's using only the datakey.
func ( *KeyRegistry,  KeyRegistryOptions) error {
	 := &bytes.Buffer{}
	,  := y.GenerateIV()
	y.Check()
	// Encrypt sanity text if the encryption key is presents.
	 := sanityText
	if len(.EncryptionKey) > 0 {
		var  error
		,  = y.XORBlockAllocate(, .EncryptionKey, )
		if  != nil {
			return y.Wrapf(, "Error while encrypting sanity text in WriteKeyRegistry")
		}
	}
	y.Check2(.Write())
	y.Check2(.Write())
	// Write all the datakeys to the buf.
	for ,  := range .dataKeys {
		// Writing the datakey to the given buffer.
		if  := storeDataKey(, .EncryptionKey, );  != nil {
			return y.Wrapf(, "Error while storing datakey in WriteKeyRegistry")
		}
	}
	 := filepath.Join(.Dir, KeyRegistryRewriteFileName)
	// Open temporary file to write the data and do atomic rename.
	,  := y.OpenTruncFile(, true)
	if  != nil {
		return y.Wrapf(, "Error while opening tmp file in WriteKeyRegistry")
	}
	// Write buf to the disk.
	if _,  = .Write(.Bytes());  != nil {
		// close the fd before returning error. We're not using defer
		// because, for windows we need to close the fd explicitly before
		// renaming.
		.Close()
		return y.Wrapf(, "Error while writing buf in WriteKeyRegistry")
	}
	// In Windows the files should be closed before doing a Rename.
	if  = .Close();  != nil {
		return y.Wrapf(, "Error while closing tmp file in WriteKeyRegistry")
	}
	// Rename to the original file.
	if  = os.Rename(, filepath.Join(.Dir, KeyRegistryFileName));  != nil {
		return y.Wrapf(, "Error while renaming file in WriteKeyRegistry")
	}
	// Sync Dir.
	return syncDir(.Dir)
}

// DataKey returns datakey of the given key id.
func ( *KeyRegistry) ( uint64) (*pb.DataKey, error) {
	.RLock()
	defer .RUnlock()
	if  == 0 {
		// nil represent plain text.
		return nil, nil
	}
	,  := .dataKeys[]
	if ! {
		return nil, y.Wrapf(ErrInvalidDataKeyID, "Error for the KEY ID %d", )
	}
	return , nil
}

// LatestDataKey will give you the latest generated datakey based on the rotation
// period. If the last generated datakey lifetime exceeds the rotation period.
// It'll create new datakey.
func ( *KeyRegistry) () (*pb.DataKey, error) {
	if len(.opt.EncryptionKey) == 0 {
		// nil is for no encryption.
		return nil, nil
	}
	// validKey return datakey if the last generated key duration less than
	// rotation duration.
	 := func() (*pb.DataKey, bool) {
		// Time difference from the last generated time.
		 := time.Since(time.Unix(.lastCreated, 0))
		if  < .opt.EncryptionKeyRotationDuration {
			return .dataKeys[.nextKeyID], true
		}
		return nil, false
	}
	.RLock()
	,  := ()
	.RUnlock()
	if  {
		// If less than EncryptionKeyRotationDuration, returns the last generated key.
		return , nil
	}
	.Lock()
	defer .Unlock()
	// Key might have generated by another go routine. So,
	// checking once again.
	,  = ()
	if  {
		return , nil
	}
	 := make([]byte, len(.opt.EncryptionKey))
	,  := y.GenerateIV()
	if  != nil {
		return nil, 
	}
	_,  = rand.Read()
	if  != nil {
		return nil, 
	}
	// Otherwise Increment the KeyID and generate new datakey.
	.nextKeyID++
	 := &pb.DataKey{
		KeyId:     .nextKeyID,
		Data:      ,
		CreatedAt: time.Now().Unix(),
		Iv:        ,
	}
	// Don't store the datakey on file if badger is running in InMemory mode.
	if !.opt.InMemory {
		// Store the datekey.
		 := &bytes.Buffer{}
		if  = storeDataKey(, .opt.EncryptionKey, );  != nil {
			return nil, 
		}
		// Persist the datakey to the disk
		if _,  = .fp.Write(.Bytes());  != nil {
			return nil, 
		}
	}
	// storeDatakey encrypts the datakey So, placing un-encrypted key in the memory.
	.Data = 
	.lastCreated = .CreatedAt
	.dataKeys[.nextKeyID] = 
	return , nil
}

// Close closes the key registry.
func ( *KeyRegistry) () error {
	if !(.opt.ReadOnly || .opt.InMemory) {
		return .fp.Close()
	}
	return nil
}

// storeDataKey stores datakey in an encrypted format in the given buffer. If storage key preset.
func storeDataKey( *bytes.Buffer,  []byte,  *pb.DataKey) error {
	// xor will encrypt the IV and xor with the given data.
	// It'll used for both encryption and decryption.
	 := func() error {
		if len() == 0 {
			return nil
		}
		var  error
		.Data,  = y.XORBlockAllocate(.Data, , .Iv)
		return 
	}
	// In memory datakey will be plain text so encrypting before storing to the disk.
	var  error
	if  = ();  != nil {
		return y.Wrapf(, "Error while encrypting datakey in storeDataKey")
	}
	var  []byte
	if ,  = proto.Marshal();  != nil {
		 = y.Wrapf(, "Error while marshaling datakey in storeDataKey")
		var  error
		// decrypting the datakey back.
		if  = ();  != nil {
			return y.Wrapf(,
				y.Wrapf(, "Error while decrypting datakey in storeDataKey").Error())
		}
		return 
	}
	var  [8]byte
	binary.BigEndian.PutUint32([0:4], uint32(len()))
	binary.BigEndian.PutUint32([4:8], crc32.Checksum(, y.CastagnoliCrcTable))
	y.Check2(.Write([:]))
	y.Check2(.Write())
	// Decrypting the datakey back since we're using the pointer.
	return ()
}