feat(general): key derivation algorithm for cache encryption (#3799)

Add an option to select the password-based key derivation algorithm
for the local cache encryption key when connecting to a kopia
repository server.
This commit is contained in:
Sirish Bathina
2024-04-25 14:45:12 -10:00
committed by GitHub
parent 02463ab118
commit 1e98511c2e
6 changed files with 26 additions and 7 deletions

View File

@@ -6,6 +6,7 @@
"github.com/pkg/errors"
"github.com/kopia/kopia/internal/crypto"
"github.com/kopia/kopia/internal/passwordpersist"
"github.com/kopia/kopia/repo"
)
@@ -13,8 +14,9 @@
type commandRepositoryConnectServer struct {
co *connectOptions
connectAPIServerURL string
connectAPIServerCertFingerprint string
connectAPIServerURL string
connectAPIServerCertFingerprint string
connectAPIServerLocalCacheKeyDerivationAlgorithm string
svc advancedAppServices
out textOutput
@@ -28,13 +30,21 @@ func (c *commandRepositoryConnectServer) setup(svc advancedAppServices, parent c
cmd := parent.Command("server", "Connect to a repository API Server.")
cmd.Flag("url", "Server URL").Required().StringVar(&c.connectAPIServerURL)
cmd.Flag("server-cert-fingerprint", "Server certificate fingerprint").StringVar(&c.connectAPIServerCertFingerprint)
//nolint:lll
cmd.Flag("local-cache-key-derivation-algorithm", "Key derivation algorithm used to derive the local cache encryption key").Hidden().Default(repo.DefaultKeyDerivationAlgorithm).EnumVar(&c.connectAPIServerLocalCacheKeyDerivationAlgorithm, crypto.AllowedKeyDerivationAlgorithms()...)
cmd.Action(svc.noRepositoryAction(c.run))
}
func (c *commandRepositoryConnectServer) run(ctx context.Context) error {
localCacheKeyDerivationAlgorithm := c.connectAPIServerLocalCacheKeyDerivationAlgorithm
if localCacheKeyDerivationAlgorithm == "" {
localCacheKeyDerivationAlgorithm = repo.DefaultKeyDerivationAlgorithm
}
as := &repo.APIServerInfo{
BaseURL: strings.TrimSuffix(c.connectAPIServerURL, "/"),
TrustedServerCertificateFingerprint: strings.ToLower(c.connectAPIServerCertFingerprint),
LocalCacheKeyDerivationAlgorithm: localCacheKeyDerivationAlgorithm,
}
configFile := c.svc.repositoryConfigFileName()

View File

@@ -39,7 +39,7 @@ func RegisterKeyDerivers(name string, keyDeriver KeyDeriver) {
func DeriveKeyFromPassword(password string, salt []byte, algorithm string) ([]byte, error) {
kd, ok := keyDerivers[algorithm]
if !ok {
return nil, errors.Errorf("unsupported key algorithm: %v, supported algorithms %v", algorithm, AllowedKeyDerivationAlgorithms())
return nil, errors.Errorf("unsupported key derivation algorithm: %v, supported algorithms %v", algorithm, AllowedKeyDerivationAlgorithms())
}
//nolint:wrapcheck
@@ -50,7 +50,7 @@ func DeriveKeyFromPassword(password string, salt []byte, algorithm string) ([]by
func RecommendedSaltLength(algorithm string) (int, error) {
kd, ok := keyDerivers[algorithm]
if !ok {
return 0, errors.Errorf("unsupported key algorithm: %v, supported algorithms %v", algorithm, AllowedKeyDerivationAlgorithms())
return 0, errors.Errorf("failed to get salt length for unsupported key derivation algorithm: %v, supported algorithms %v", algorithm, AllowedKeyDerivationAlgorithms())
}
return kd.RecommendedSaltLength(), nil

View File

@@ -79,6 +79,8 @@ func StartServer(t *testing.T, env *repotesting.Environment, tls bool) *repo.API
asi.BaseURL = hs.URL
}
asi.LocalCacheKeyDerivationAlgorithm = repo.DefaultKeyDerivationAlgorithm
t.Cleanup(hs.Close)
return asi

View File

@@ -10,6 +10,7 @@
type APIServerInfo struct {
BaseURL string `json:"url"`
TrustedServerCertificateFingerprint string `json:"serverCertFingerprint"`
LocalCacheKeyDerivationAlgorithm string `json:"localCacheKeyDerivationAlgorithm,omitempty"`
}
// ConnectAPIServer sets up repository connection to a particular API server.

View File

@@ -58,6 +58,9 @@
// localCacheIntegrityHMACSecretLength length of HMAC secret protecting local cache items.
const localCacheIntegrityHMACSecretLength = 16
// DefaultKeyDerivationAlgorithm is the default key derivation algorithm used to derive a cache encryption key.
const DefaultKeyDerivationAlgorithm = crypto.DefaultKeyDerivationAlgorithm
//nolint:gochecknoglobals
var localCacheIntegrityPurpose = []byte("local-cache-integrity")
@@ -131,7 +134,7 @@ func Open(ctx context.Context, configFile, password string, options *Options) (r
return openDirect(ctx, configFile, lc, password, options)
}
func getContentCacheOrNil(ctx context.Context, opt *content.CachingOptions, password string, mr *metrics.Registry, timeNow func() time.Time) (*cache.PersistentCache, error) {
func getContentCacheOrNil(ctx context.Context, si *APIServerInfo, opt *content.CachingOptions, password string, mr *metrics.Registry, timeNow func() time.Time) (*cache.PersistentCache, error) {
opt = opt.CloneOrDefault()
cs, err := cache.NewStorageOrNil(ctx, opt.CacheDirectory, opt.ContentCacheSizeBytes, "server-contents")
@@ -143,7 +146,7 @@ func getContentCacheOrNil(ctx context.Context, opt *content.CachingOptions, pass
// derive content cache key from the password & HMAC secret
saltWithPurpose := append([]byte("content-cache-protection"), opt.HMACSecret...)
cacheEncryptionKey, err := crypto.DeriveKeyFromPassword(password, saltWithPurpose, crypto.DefaultKeyDerivationAlgorithm)
cacheEncryptionKey, err := crypto.DeriveKeyFromPassword(password, saltWithPurpose, si.LocalCacheKeyDerivationAlgorithm)
if err != nil {
return nil, errors.Wrap(err, "unable to derive cache encryption key from password")
}
@@ -171,7 +174,7 @@ func openAPIServer(ctx context.Context, si *APIServerInfo, cliOpts ClientOptions
mr := metrics.NewRegistry()
contentCache, err := getContentCacheOrNil(ctx, cachingOptions, password, mr, options.TimeNowFunc)
contentCache, err := getContentCacheOrNil(ctx, si, cachingOptions, password, mr, options.TimeNowFunc)
if err != nil {
return nil, errors.Wrap(err, "error opening content cache")
}

View File

@@ -136,6 +136,7 @@ func testAPIServerRepository(t *testing.T, allowRepositoryUsers bool) {
rep, err := servertesting.ConnectAndOpenAPIServer(t, ctx2, &repo.APIServerInfo{
BaseURL: sp.BaseURL,
TrustedServerCertificateFingerprint: sp.SHA256Fingerprint,
LocalCacheKeyDerivationAlgorithm: repo.DefaultKeyDerivationAlgorithm,
}, repo.ClientOptions{
Username: "foo",
Hostname: "bar",
@@ -258,6 +259,7 @@ func testAPIServerRepository(t *testing.T, allowRepositoryUsers bool) {
servertesting.ConnectAndOpenAPIServer(t, ctx, &repo.APIServerInfo{
BaseURL: sp.BaseURL,
TrustedServerCertificateFingerprint: sp.SHA256Fingerprint,
LocalCacheKeyDerivationAlgorithm: repo.DefaultKeyDerivationAlgorithm,
}, repo.ClientOptions{
Username: "foo",
Hostname: "bar",
@@ -328,6 +330,7 @@ func TestFindManifestsPaginationOverGRPC(t *testing.T) {
rep, err := servertesting.ConnectAndOpenAPIServer(t, ctx, &repo.APIServerInfo{
BaseURL: sp.BaseURL,
TrustedServerCertificateFingerprint: sp.SHA256Fingerprint,
LocalCacheKeyDerivationAlgorithm: repo.DefaultKeyDerivationAlgorithm,
}, repo.ClientOptions{
Username: "foo",
Hostname: "bar",