mirror of
https://github.com/kopia/kopia.git
synced 2026-03-19 22:56:29 -04:00
simplified vault credentials
This commit is contained in:
@@ -100,12 +100,11 @@ func runCreateCommand(context *kingpin.ParseContext) error {
|
||||
repoFormat.ObjectFormat,
|
||||
repoFormat.MaxBlobSize)
|
||||
|
||||
masterKey, password, err := getKeyOrPassword(true)
|
||||
creds, err := getVaultCredentials(true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get credentials: %v", err)
|
||||
}
|
||||
|
||||
var vlt *vault.Vault
|
||||
vf, err := vaultFormat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to initialize vault format: %v", err)
|
||||
@@ -115,11 +114,7 @@ func runCreateCommand(context *kingpin.ParseContext) error {
|
||||
"Initializing vault in '%s' with encryption '%v'.\n",
|
||||
vaultStorage.Configuration().Config.ToURL().String(),
|
||||
vf.Encryption)
|
||||
if masterKey != nil {
|
||||
vlt, err = vault.CreateWithMasterKey(vaultStorage, vf, masterKey)
|
||||
} else {
|
||||
vlt, err = vault.CreateWithPassword(vaultStorage, vf, password)
|
||||
}
|
||||
vlt, err := vault.Create(vaultStorage, vf, creds)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create vault: %v", err)
|
||||
}
|
||||
|
||||
@@ -118,50 +118,46 @@ func openVaultSpecifiedByFlag() (*vault.Vault, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
masterKey, password, err := getKeyOrPassword(false)
|
||||
creds, err := getVaultCredentials(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if masterKey != nil {
|
||||
return vault.OpenWithMasterKey(storage, masterKey)
|
||||
}
|
||||
|
||||
return vault.OpenWithPassword(storage, password)
|
||||
return vault.Open(storage, creds)
|
||||
}
|
||||
|
||||
var errPasswordTooShort = errors.New("password too short")
|
||||
|
||||
func getKeyOrPassword(isNew bool) ([]byte, string, error) {
|
||||
func getVaultCredentials(isNew bool) (vault.Credentials, error) {
|
||||
if *key != "" {
|
||||
k, err := hex.DecodeString(*key)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid key format: %v", err)
|
||||
return nil, fmt.Errorf("invalid key format: %v", err)
|
||||
}
|
||||
|
||||
return k, "", nil
|
||||
return vault.MasterKey(k)
|
||||
}
|
||||
|
||||
if *password != "" {
|
||||
return nil, strings.TrimSpace(*password), nil
|
||||
return vault.Password(strings.TrimSpace(*password))
|
||||
}
|
||||
|
||||
if *keyFile != "" {
|
||||
key, err := ioutil.ReadFile(*keyFile)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("unable to read key file: %v", err)
|
||||
return nil, fmt.Errorf("unable to read key file: %v", err)
|
||||
}
|
||||
|
||||
return key, "", nil
|
||||
return vault.MasterKey(key)
|
||||
}
|
||||
|
||||
if *passwordFile != "" {
|
||||
f, err := ioutil.ReadFile(*passwordFile)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("unable to read password file: %v", err)
|
||||
return nil, fmt.Errorf("unable to read password file: %v", err)
|
||||
}
|
||||
|
||||
return nil, strings.TrimSpace(string(f)), nil
|
||||
return vault.Password(strings.TrimSpace(string(f)))
|
||||
}
|
||||
if isNew {
|
||||
for {
|
||||
@@ -174,28 +170,28 @@ func getKeyOrPassword(isNew bool) ([]byte, string, error) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Re-enter password for verification: ")
|
||||
p2, err := askPass()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println()
|
||||
if p1 != p2 {
|
||||
fmt.Println("Passwords don't match!")
|
||||
} else {
|
||||
return nil, p1, nil
|
||||
return vault.Password(p1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Enter password to open vault: ")
|
||||
p1, err := askPass()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
fmt.Println()
|
||||
return nil, p1, nil
|
||||
return vault.Password(p1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
57
vault/creds.go
Normal file
57
vault/creds.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
const (
|
||||
passwordBasedKeySize = 32
|
||||
|
||||
pbkdf2Rounds = 10000
|
||||
|
||||
// MinPasswordLength is the minimum allowed length of a password in charcters.
|
||||
MinPasswordLength = 12
|
||||
|
||||
// MinMasterKeyLength is the minimum allowed length of a master key, in bytes.
|
||||
MinMasterKeyLength = 16
|
||||
)
|
||||
|
||||
// Credentials em
|
||||
type Credentials interface {
|
||||
getMasterKey(salt []byte) []byte
|
||||
}
|
||||
|
||||
type masterKeyCredentials struct {
|
||||
key []byte
|
||||
}
|
||||
|
||||
func (mkc *masterKeyCredentials) getMasterKey(salt []byte) []byte {
|
||||
return mkc.key
|
||||
}
|
||||
|
||||
func MasterKey(key []byte) (Credentials, error) {
|
||||
if len(key) < MinMasterKeyLength {
|
||||
return nil, fmt.Errorf("master key too short")
|
||||
}
|
||||
|
||||
return &masterKeyCredentials{key}, nil
|
||||
}
|
||||
|
||||
type passwordCredentials struct {
|
||||
password string
|
||||
}
|
||||
|
||||
func (pc *passwordCredentials) getMasterKey(salt []byte) []byte {
|
||||
return pbkdf2.Key([]byte(pc.password), salt, pbkdf2Rounds, passwordBasedKeySize, sha256.New)
|
||||
}
|
||||
|
||||
func Password(password string) (Credentials, error) {
|
||||
if len(password) < MinPasswordLength {
|
||||
return nil, fmt.Errorf("password too short")
|
||||
}
|
||||
|
||||
return &passwordCredentials{password}, nil
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package vault
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
const (
|
||||
pbkdf2Rounds = 10000
|
||||
masterKeySize = 32
|
||||
)
|
||||
|
||||
// UserPrivateKey encapsulates secret key belonging to a user.
|
||||
type UserPrivateKey struct {
|
||||
key [32]byte
|
||||
}
|
||||
|
||||
// UserPublicKey encapsulates public key belonging to a user.
|
||||
type UserPublicKey struct {
|
||||
key [32]byte
|
||||
}
|
||||
|
||||
// Bytes returns the private key bytes.
|
||||
func (prv UserPrivateKey) Bytes() []byte {
|
||||
r := make([]byte, 32)
|
||||
copy(r, prv.key[:])
|
||||
return r
|
||||
}
|
||||
|
||||
// PublicKey returns public key associated with the private key.
|
||||
func (prv UserPrivateKey) PublicKey() *UserPublicKey {
|
||||
pub := &UserPublicKey{}
|
||||
|
||||
curve25519.ScalarBaseMult(&pub.key, &prv.key)
|
||||
return pub
|
||||
}
|
||||
|
||||
func newPrivateKey(key []byte) (*UserPrivateKey, error) {
|
||||
if len(key) != 32 {
|
||||
return nil, errors.New("unsupported key length")
|
||||
}
|
||||
|
||||
k := &UserPrivateKey{}
|
||||
copy(k.key[:], key)
|
||||
return k, nil
|
||||
}
|
||||
@@ -17,19 +17,12 @@
|
||||
"github.com/kopia/kopia/repo"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
const (
|
||||
formatBlock = "format"
|
||||
checksumBlock = "checksum"
|
||||
repositoryConfigBlock = "repo"
|
||||
|
||||
// MinPasswordLength is the minimum allowed length of a password in charcters.
|
||||
MinPasswordLength = 12
|
||||
|
||||
// MinMasterKeyLength is the minimum allowed length of a master key, in bytes.
|
||||
MinMasterKeyLength = 16
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -216,35 +209,23 @@ func (v *Vault) List(prefix string) ([]string, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateWithPassword creates a password-protected Vault in the specified storage.
|
||||
func CreateWithPassword(storage blob.Storage, format *Format, password string) (*Vault, error) {
|
||||
// Create creates a Vault in the specified storage.
|
||||
func Create(storage blob.Storage, format *Format, creds Credentials) (*Vault, error) {
|
||||
if err := format.ensureUniqueID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(password) < MinPasswordLength {
|
||||
return nil, fmt.Errorf("password too short, must be at least %v characters, got %v", MinPasswordLength, len(password))
|
||||
}
|
||||
masterKey := pbkdf2.Key([]byte(password), format.UniqueID, pbkdf2Rounds, masterKeySize, sha256.New)
|
||||
return CreateWithMasterKey(storage, format, masterKey)
|
||||
}
|
||||
|
||||
// CreateWithMasterKey creates a master key-protected Vault in the specified storage.
|
||||
func CreateWithMasterKey(storage blob.Storage, format *Format, masterKey []byte) (*Vault, error) {
|
||||
if len(masterKey) < MinMasterKeyLength {
|
||||
return nil, fmt.Errorf("key too short, must be at least %v bytes, got %v", MinMasterKeyLength, len(masterKey))
|
||||
}
|
||||
|
||||
v := Vault{
|
||||
Storage: storage,
|
||||
MasterKey: masterKey,
|
||||
Format: *format,
|
||||
Storage: storage,
|
||||
Format: *format,
|
||||
}
|
||||
v.Format.Version = "1"
|
||||
if err := v.Format.ensureUniqueID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.MasterKey = creds.getMasterKey(v.Format.UniqueID)
|
||||
|
||||
formatBytes, err := json.Marshal(&v.Format)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -264,11 +245,11 @@ func CreateWithMasterKey(storage blob.Storage, format *Format, masterKey []byte)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return OpenWithMasterKey(storage, masterKey)
|
||||
return Open(storage, creds)
|
||||
}
|
||||
|
||||
// OpenWithPassword opens a password-protected vault.
|
||||
func OpenWithPassword(storage blob.Storage, password string) (*Vault, error) {
|
||||
// OpenWithPassword opens a vault.
|
||||
func Open(storage blob.Storage, creds Credentials) (*Vault, error) {
|
||||
v := Vault{
|
||||
Storage: storage,
|
||||
}
|
||||
@@ -283,7 +264,7 @@ func OpenWithPassword(storage blob.Storage, password string) (*Vault, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.MasterKey = pbkdf2.Key([]byte(password), v.Format.UniqueID, pbkdf2Rounds, masterKeySize, sha256.New)
|
||||
v.MasterKey = creds.getMasterKey(v.Format.UniqueID)
|
||||
|
||||
if _, err := v.readEncryptedBlock(checksumBlock); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user