mirror of
https://github.com/kopia/kopia.git
synced 2026-04-20 22:18:02 -04:00
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:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user