From 8d62a7adb4feafe150e28c4c6361e485313fdc39 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 15 Oct 2022 16:01:38 +0200 Subject: [PATCH] identify keys by ID and not name --- cmd/restic/cmd_cat.go | 2 +- cmd/restic/cmd_key.go | 22 ++++++++-------- internal/repository/key.go | 43 ++++++++++++++----------------- internal/repository/repository.go | 24 ++++++++--------- 4 files changed, 43 insertions(+), 48 deletions(-) diff --git a/cmd/restic/cmd_cat.go b/cmd/restic/cmd_cat.go index ec2da564a..f46502d5a 100644 --- a/cmd/restic/cmd_cat.go +++ b/cmd/restic/cmd_cat.go @@ -93,7 +93,7 @@ func runCat(ctx context.Context, gopts GlobalOptions, args []string) error { Println(string(buf)) return nil case "key": - key, err := repository.LoadKey(ctx, repo, id.String()) + key, err := repository.LoadKey(ctx, repo, id) if err != nil { return err } diff --git a/cmd/restic/cmd_key.go b/cmd/restic/cmd_key.go index cab5a9d4d..a673e5756 100644 --- a/cmd/restic/cmd_key.go +++ b/cmd/restic/cmd_key.go @@ -59,14 +59,14 @@ func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions var keys []keyInfo err := s.List(ctx, restic.KeyFile, func(id restic.ID, size int64) error { - k, err := repository.LoadKey(ctx, s, id.String()) + k, err := repository.LoadKey(ctx, s, id) if err != nil { Warnf("LoadKey() failed: %v\n", err) return nil } key := keyInfo{ - Current: id.String() == s.KeyName(), + Current: id == s.KeyID(), ID: id.Str(), UserName: k.Username, HostName: k.Hostname, @@ -141,18 +141,18 @@ func addKey(ctx context.Context, repo *repository.Repository, gopts GlobalOption return nil } -func deleteKey(ctx context.Context, repo *repository.Repository, name string) error { - if name == repo.KeyName() { +func deleteKey(ctx context.Context, repo *repository.Repository, id restic.ID) error { + if id == repo.KeyID() { return errors.Fatal("refusing to remove key currently used to access repository") } - h := restic.Handle{Type: restic.KeyFile, Name: name} + h := restic.Handle{Type: restic.KeyFile, Name: id.String()} err := repo.Backend().Remove(ctx, h) if err != nil { return err } - Verbosef("removed key %v\n", name) + Verbosef("removed key %v\n", id) return nil } @@ -166,14 +166,14 @@ func changePassword(ctx context.Context, repo *repository.Repository, gopts Glob if err != nil { return errors.Fatalf("creating new key failed: %v\n", err) } - oldID := repo.KeyName() + oldID := repo.KeyID() err = switchToNewKeyAndRemoveIfBroken(ctx, repo, id, pw) if err != nil { return err } - h := restic.Handle{Type: restic.KeyFile, Name: oldID} + h := restic.Handle{Type: restic.KeyFile, Name: oldID.String()} err = repo.Backend().Remove(ctx, h) if err != nil { return err @@ -187,10 +187,10 @@ func changePassword(ctx context.Context, repo *repository.Repository, gopts Glob func switchToNewKeyAndRemoveIfBroken(ctx context.Context, repo *repository.Repository, key *repository.Key, pw string) error { // Verify new key to make sure it really works. A broken key can render the // whole repository inaccessible - err := repo.SearchKey(ctx, pw, 0, key.Name()) + err := repo.SearchKey(ctx, pw, 0, key.ID().String()) if err != nil { // the key is invalid, try to remove it - h := restic.Handle{Type: restic.KeyFile, Name: key.Name()} + h := restic.Handle{Type: restic.KeyFile, Name: key.ID().String()} _ = repo.Backend().Remove(ctx, h) return errors.Fatalf("failed to access repository with new key: %v", err) } @@ -236,7 +236,7 @@ func runKey(ctx context.Context, gopts GlobalOptions, args []string) error { return err } - return deleteKey(ctx, repo, id.String()) + return deleteKey(ctx, repo, id) case "passwd": lock, ctx, err := lockRepoExclusive(ctx, repo) defer unlockRepo(lock) diff --git a/internal/repository/key.go b/internal/repository/key.go index f0e091239..b3e13ade4 100644 --- a/internal/repository/key.go +++ b/internal/repository/key.go @@ -40,7 +40,7 @@ type Key struct { user *crypto.Key master *crypto.Key - name string + id restic.ID } // Params tracks the parameters used for the KDF. If not set, it will be @@ -62,10 +62,10 @@ func createMasterKey(ctx context.Context, s *Repository, password string) (*Key, } // OpenKey tries do decrypt the key specified by name with the given password. -func OpenKey(ctx context.Context, s *Repository, name string, password string) (*Key, error) { - k, err := LoadKey(ctx, s, name) +func OpenKey(ctx context.Context, s *Repository, id restic.ID, password string) (*Key, error) { + k, err := LoadKey(ctx, s, id) if err != nil { - debug.Log("LoadKey(%v) returned error %v", name, err) + debug.Log("LoadKey(%v) returned error %v", id.String(), err) return nil, err } @@ -99,7 +99,7 @@ func OpenKey(ctx context.Context, s *Repository, name string, password string) ( debug.Log("Unmarshal() returned error %v", err) return nil, errors.Wrap(err, "Unmarshal") } - k.name = name + k.id = id if !k.Valid() { return nil, errors.New("Invalid key for repository") @@ -119,7 +119,7 @@ func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int, id, err := restic.Find(ctx, s.Backend(), restic.KeyFile, keyHint) if err == nil { - key, err := OpenKey(ctx, s, id.String(), password) + key, err := OpenKey(ctx, s, id, password) if err == nil { debug.Log("successfully opened hinted key %v", id) @@ -136,22 +136,16 @@ func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int, defer cancel() // try at most maxKeys keys in repo - err = s.Backend().List(listCtx, restic.KeyFile, func(fi restic.FileInfo) error { + err = s.List(listCtx, restic.KeyFile, func(id restic.ID, size int64) error { checked++ if maxKeys > 0 && checked > maxKeys { return ErrMaxKeysReached } - _, err := restic.ParseID(fi.Name) + debug.Log("trying key %q", id.String()) + key, err := OpenKey(ctx, s, id, password) if err != nil { - debug.Log("rejecting key with invalid name: %v", fi.Name) - return nil - } - - debug.Log("trying key %q", fi.Name) - key, err := OpenKey(ctx, s, fi.Name, password) - if err != nil { - debug.Log("key %v returned error %v", fi.Name, err) + debug.Log("key %v returned error %v", id.String(), err) // ErrUnauthenticated means the password is wrong, try the next key if errors.Is(err, crypto.ErrUnauthenticated) { @@ -161,7 +155,7 @@ func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int, return err } - debug.Log("successfully opened key %v", fi.Name) + debug.Log("successfully opened key %v", id.String()) k = key cancel() return nil @@ -183,8 +177,8 @@ func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int, } // LoadKey loads a key from the backend. -func LoadKey(ctx context.Context, s *Repository, name string) (k *Key, err error) { - h := restic.Handle{Type: restic.KeyFile, Name: name} +func LoadKey(ctx context.Context, s *Repository, id restic.ID) (k *Key, err error) { + h := restic.Handle{Type: restic.KeyFile, Name: id.String()} data, err := backend.LoadAll(ctx, nil, s.be, h) if err != nil { return nil, err @@ -274,10 +268,11 @@ func AddKey(ctx context.Context, s *Repository, password, username, hostname str return nil, errors.Wrap(err, "Marshal") } + id := restic.Hash(buf) // store in repository and return h := restic.Handle{ Type: restic.KeyFile, - Name: restic.Hash(buf).String(), + Name: id.String(), } err = s.be.Save(ctx, h, restic.NewByteReader(buf, s.be.Hasher())) @@ -285,7 +280,7 @@ func AddKey(ctx context.Context, s *Repository, password, username, hostname str return nil, err } - newkey.name = h.Name + newkey.id = id return newkey, nil } @@ -297,9 +292,9 @@ func (k *Key) String() string { return fmt.Sprintf("", k.Username, k.Hostname, k.Created) } -// Name returns an identifier for the key. -func (k Key) Name() string { - return k.name +// ID returns an identifier for the key. +func (k Key) ID() restic.ID { + return k.id } // Valid tests whether the mac and encryption keys are valid (i.e. not zero) diff --git a/internal/repository/repository.go b/internal/repository/repository.go index cee0d762b..3cae7c597 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -35,12 +35,12 @@ const MaxPackSize = 128 * 1024 * 1024 // Repository is used to access a repository in a backend. type Repository struct { - be restic.Backend - cfg restic.Config - key *crypto.Key - keyName string - idx *index.MasterIndex - Cache *cache.Cache + be restic.Backend + cfg restic.Config + key *crypto.Key + keyID restic.ID + idx *index.MasterIndex + Cache *cache.Cache opts Options @@ -709,10 +709,10 @@ func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int } r.key = key.master - r.keyName = key.Name() + r.keyID = key.ID() cfg, err := restic.LoadConfig(ctx, r) if err == crypto.ErrUnauthenticated { - return errors.Fatalf("config or key %v is damaged: %v", key.Name(), err) + return errors.Fatalf("config or key %v is damaged: %v", key.ID(), err) } else if err != nil { return errors.Fatalf("config cannot be loaded: %v", err) } @@ -760,7 +760,7 @@ func (r *Repository) init(ctx context.Context, password string, cfg restic.Confi } r.key = key.master - r.keyName = key.Name() + r.keyID = key.ID() r.setConfig(cfg) return restic.SaveConfig(ctx, r, cfg) } @@ -770,9 +770,9 @@ func (r *Repository) Key() *crypto.Key { return r.key } -// KeyName returns the name of the current key in the backend. -func (r *Repository) KeyName() string { - return r.keyName +// KeyID returns the id of the current key in the backend. +func (r *Repository) KeyID() restic.ID { + return r.keyID } // List runs fn for all files of type t in the repo.