added auth.KeyManager

This commit is contained in:
Jarek Kowalski committed 2017-10-15 09:00:53 -07:00
1 parent 44f8940a8f
commit f7a1fb37b0
7 files changed
+62 -54

No files matched your search

+32
View File
@@ -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
View File
@@ -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
View File
@@ -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 -32
View File
@@ -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 {
-9
View File
@@ -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
View File
@@ -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
View File
@@ -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),
}
}