mirror of
https://github.com/kopia/kopia.git
synced 2026-10-22 22:24:49 -04:00
added auth.KeyManager
This commit is contained in:
1 parent
44f8940a8f
commit
f7a1fb37b0
7 files changed
+62
-54
No files matched your search
@@ -0,0 +1,32 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// KeyManager manages key derivation from a master key.
|
||||
type KeyManager struct {
|
||||
masterKey []byte
|
||||
uniqueID []byte
|
||||
}
|
||||
|
||||
// DeriveKey computes a key for a specific purpose and length using HKDF based on the master key.
|
||||
func (km *KeyManager) DeriveKey(purpose []byte, length int) []byte {
|
||||
key := make([]byte, length)
|
||||
k := hkdf.New(sha256.New, km.masterKey, km.uniqueID, purpose)
|
||||
io.ReadFull(k, key)
|
||||
return key
|
||||
}
|
||||
|
||||
// NewKeyManager constructs a KeyManager with given credentials and options.
|
||||
func NewKeyManager(creds Credentials, so SecurityOptions) (*KeyManager, error) {
|
||||
mk, err := creds.GetMasterKey(so)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &KeyManager{mk, so.UniqueID}, nil
|
||||
}
|
||||
+17
-6
@@ -90,9 +90,19 @@ func Connect(ctx context.Context, configFile string, st storage.Storage, creds a
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := r.Metadata.connectionConfiguration()
|
||||
cip, ok := st.(storage.ConnectionInfoProvider)
|
||||
if !ok {
|
||||
return errors.New("repository does not support persisting configuration")
|
||||
}
|
||||
|
||||
masterKey, err := creds.GetMasterKey(r.Metadata.format.SecurityOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("can't get master key: %v", err)
|
||||
}
|
||||
|
||||
cfg := &config.RepositoryConnectionInfo{
|
||||
ConnectionInfo: cip.ConnectionInfo(),
|
||||
Key: masterKey,
|
||||
}
|
||||
|
||||
var lc config.LocalConfig
|
||||
@@ -145,10 +155,11 @@ func connect(ctx context.Context, st storage.Storage, creds auth.Credentials, op
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
Blocks: bm,
|
||||
Objects: om,
|
||||
Metadata: mm,
|
||||
Storage: st,
|
||||
Blocks: bm,
|
||||
Objects: om,
|
||||
Metadata: mm,
|
||||
Storage: st,
|
||||
KeyManager: mm.keyManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ func Initialize(st storage.Storage, opt *NewRepositoryOptions, creds auth.Creden
|
||||
format: metadataFormatFromOptions(opt),
|
||||
}
|
||||
|
||||
mm.masterKey, err = creds.GetMasterKey(mm.format.SecurityOptions)
|
||||
mm.keyManager, err = auth.NewKeyManager(creds, mm.format.SecurityOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,8 +13,6 @@ import (
|
||||
"github.com/kopia/kopia/auth"
|
||||
"github.com/kopia/kopia/internal/config"
|
||||
"github.com/kopia/kopia/storage"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,8 +55,7 @@ type MetadataManager struct {
|
||||
cache *metadataCache
|
||||
format config.MetadataFormat
|
||||
repoConfig config.EncryptedRepositoryConfig
|
||||
|
||||
masterKey []byte
|
||||
keyManager *auth.KeyManager
|
||||
|
||||
aead cipher.AEAD // authenticated encryption to use
|
||||
authData []byte // additional data to authenticate
|
||||
@@ -121,14 +117,6 @@ func (mm *MetadataManager) decryptBlock(content []byte) ([]byte, error) {
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// DeriveKey computes a key for a specific purpose and length using HKDF based on the master key.
|
||||
func (mm *MetadataManager) DeriveKey(purpose []byte, length int) []byte {
|
||||
key := make([]byte, length)
|
||||
k := hkdf.New(sha256.New, mm.masterKey, mm.format.UniqueID, purpose)
|
||||
io.ReadFull(k, key)
|
||||
return key
|
||||
}
|
||||
|
||||
// GetMetadata returns the contents of a specified metadata item.
|
||||
func (mm *MetadataManager) GetMetadata(itemID string) ([]byte, error) {
|
||||
if err := checkReservedName(itemID); err != nil {
|
||||
@@ -219,22 +207,6 @@ func (mm *MetadataManager) ListContents(prefix string) (map[string][]byte, error
|
||||
return mm.MultiGet(itemIDs)
|
||||
}
|
||||
|
||||
// Config returns a configuration of storage its credentials that's suitable
|
||||
// for storing in configuration file.
|
||||
func (mm *MetadataManager) connectionConfiguration() (*config.RepositoryConnectionInfo, error) {
|
||||
cip, ok := mm.storage.(storage.ConnectionInfoProvider)
|
||||
if !ok {
|
||||
return nil, errors.New("repository does not support persisting configuration")
|
||||
}
|
||||
|
||||
ci := cip.ConnectionInfo()
|
||||
|
||||
return &config.RepositoryConnectionInfo{
|
||||
ConnectionInfo: ci,
|
||||
Key: mm.masterKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Remove removes the specified metadata item.
|
||||
func (mm *MetadataManager) Remove(itemID string) error {
|
||||
if err := checkReservedName(itemID); err != nil {
|
||||
@@ -310,7 +282,7 @@ func newMetadataManager(st storage.Storage, creds auth.Credentials) (*MetadataMa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mm.masterKey, err = creds.GetMasterKey(mm.format.SecurityOptions)
|
||||
mm.keyManager, err = auth.NewKeyManager(creds, mm.format.SecurityOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -340,8 +312,8 @@ func (mm *MetadataManager) initCrypto() error {
|
||||
case "NONE": // do nothing
|
||||
return nil
|
||||
case "AES256_GCM":
|
||||
aesKey := mm.DeriveKey(purposeAESKey, 32)
|
||||
mm.authData = mm.DeriveKey(purposeAuthData, 32)
|
||||
aesKey := mm.keyManager.DeriveKey(purposeAESKey, 32)
|
||||
mm.authData = mm.keyManager.DeriveKey(purposeAuthData, 32)
|
||||
|
||||
blk, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -47,15 +47,6 @@ func TestMetadataManager(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := v.connectionConfiguration()
|
||||
if err != nil {
|
||||
t.Errorf("error getting token1 %v", err)
|
||||
}
|
||||
|
||||
if got, want := cfg.ConnectionInfo.Type, "filesystem"; got != want {
|
||||
t.Errorf("invalid connection type: %q, want %q", got, want)
|
||||
}
|
||||
|
||||
_, err = newMetadataManager(st, otherCreds)
|
||||
if err == nil {
|
||||
t.Errorf("unexpectedly opened repository with invalid credentials")
|
||||
|
||||
+6
-4
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/kopia/kopia/auth"
|
||||
"github.com/kopia/kopia/block"
|
||||
"github.com/kopia/kopia/object"
|
||||
"github.com/kopia/kopia/storage"
|
||||
@@ -11,10 +12,11 @@ import (
|
||||
|
||||
// Repository represents storage where both content-addressable and user-addressable data is kept.
|
||||
type Repository struct {
|
||||
Blocks *block.Manager
|
||||
Objects *object.Manager
|
||||
Metadata *MetadataManager
|
||||
Storage storage.Storage
|
||||
Blocks *block.Manager
|
||||
Objects *object.Manager
|
||||
Metadata *MetadataManager
|
||||
Storage storage.Storage
|
||||
KeyManager *auth.KeyManager
|
||||
|
||||
ConfigFile string
|
||||
CacheDirectory string
|
||||
|
||||
+2
-2
@@ -311,7 +311,7 @@ func (m *Manager) ListPolicies() ([]*Policy, error) {
|
||||
func NewManager(r *repo.Repository) *Manager {
|
||||
return &Manager{
|
||||
r,
|
||||
r.Metadata.DeriveKey([]byte("snapshot-id"), 32),
|
||||
r.Metadata.DeriveKey([]byte("policyID-id"), 32),
|
||||
r.KeyManager.DeriveKey([]byte("snapshot-id"), 32),
|
||||
r.KeyManager.DeriveKey([]byte("policyID-id"), 32),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user