Files
kopia/internal/cache/storage_protection.go
Jarek Kowalski 35d0f31c0d huge: replaced the use of allocated byte slices with populating gather.WriteBuffer in the repository (#1244)
This helps recycle buffers more efficiently during snapshots.
Also, improved memory tracking, enabled profiling flags and added pprof
by default.
2021-08-20 08:45:10 -07:00

120 lines
3.4 KiB
Go

package cache
import (
"crypto/sha256"
"github.com/pkg/errors"
"github.com/kopia/kopia/internal/gather"
"github.com/kopia/kopia/internal/hmac"
"github.com/kopia/kopia/repo/encryption"
)
// encryptionProtectionAlgorithm is the authenticated encryption algorithm used by authenticatedEncryptionProtection.
var encryptionProtectionAlgorithm = "AES256-GCM-HMAC-SHA256"
// StorageProtection encapsulates protection (HMAC and/or encryption) applied to local cache items.
type StorageProtection interface {
SupportsPartial() bool
Protect(id string, input gather.Bytes, output *gather.WriteBuffer)
Verify(id string, input gather.Bytes, output *gather.WriteBuffer) error
}
type nullStorageProtection struct{}
func (nullStorageProtection) Protect(id string, input gather.Bytes, output *gather.WriteBuffer) {
output.Reset()
input.WriteTo(output) // nolint:errcheck
}
func (nullStorageProtection) Verify(id string, input gather.Bytes, output *gather.WriteBuffer) error {
output.Reset()
input.WriteTo(output) // nolint:errcheck
return nil
}
func (nullStorageProtection) SupportsPartial() bool {
return true
}
// NoProtection returns implementation of StorageProtection that offers no protection.
func NoProtection() StorageProtection {
return nullStorageProtection{}
}
type checksumProtection struct {
Secret []byte
}
func (p checksumProtection) Protect(id string, input gather.Bytes, output *gather.WriteBuffer) {
output.Reset()
hmac.Append(input, p.Secret, output)
}
func (p checksumProtection) Verify(id string, input gather.Bytes, output *gather.WriteBuffer) error {
output.Reset()
// nolint:wrapcheck
return hmac.VerifyAndStrip(input, p.Secret, output)
}
func (checksumProtection) SupportsPartial() bool {
return false
}
// ChecksumProtection returns StorageProtection that protects cached data using HMAC checksums without encryption.
func ChecksumProtection(key []byte) StorageProtection {
return checksumProtection{key}
}
type authenticatedEncryptionProtection struct {
e encryption.Encryptor
}
func (p authenticatedEncryptionProtection) deriveIV(id string) []byte {
contentID := sha256.Sum256([]byte(id))
return contentID[:]
}
func (p authenticatedEncryptionProtection) Protect(id string, input gather.Bytes, output *gather.WriteBuffer) {
output.Reset()
if err := p.e.Encrypt(input, p.deriveIV(id), output); err != nil {
panic("encryption unexpectedly failed: " + err.Error())
}
}
func (authenticatedEncryptionProtection) SupportsPartial() bool {
return false
}
func (p authenticatedEncryptionProtection) Verify(id string, input gather.Bytes, output *gather.WriteBuffer) error {
output.Reset()
if err := p.e.Decrypt(input, p.deriveIV(id), output); err != nil {
return errors.Wrap(err, "unable to decrypt cache content")
}
return nil
}
type authenticatedEncryptionProtectionKey []byte
func (k authenticatedEncryptionProtectionKey) GetEncryptionAlgorithm() string {
return encryptionProtectionAlgorithm
}
func (k authenticatedEncryptionProtectionKey) GetMasterKey() []byte {
return k
}
// AuthenticatedEncryptionProtection returns StorageProtection that protects cached data using authenticated encryption.
func AuthenticatedEncryptionProtection(key []byte) (StorageProtection, error) {
e, err := encryption.CreateEncryptor(authenticatedEncryptionProtectionKey(key))
if err != nil {
return nil, errors.Wrap(err, "unable to create encryptor")
}
return authenticatedEncryptionProtection{e}, nil
}