lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 2d7813749e677b4bfc91d376379cf853410c7e6a
parent 3bb07d2fe41caf6143c14b9f25e72b7f6fb9531c
Author: Sean Enck <sean@ttypty.com>
Date:   Sat, 16 Jul 2022 19:29:24 -0400

use pbkdf2 for padding keyphrase

Diffstat:
Minternal/encrypt/core.go | 45++++++++++++++++++++++++++++++++++-----------
Minternal/encrypt/core_test.go | 8++------
2 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/internal/encrypt/core.go b/internal/encrypt/core.go @@ -2,6 +2,7 @@ package encrypt import ( "crypto/rand" + "crypto/sha512" "errors" "io" random "math/rand" @@ -13,12 +14,14 @@ import ( "github.com/enckse/lockbox/internal/inputs" "github.com/google/shlex" "golang.org/x/crypto/nacl/secretbox" + "golang.org/x/crypto/pbkdf2" ) const ( keyLength = 32 nonceLength = 24 padLength = 256 + saltLength = 16 // PlainKeyMode is plaintext based key resolution. PlainKeyMode = "plaintext" // CommandKeyMode will run an external command to get the key (from stdout). @@ -88,18 +91,21 @@ func newLockbox(key, keyMode, file string) (Lockbox, error) { return Lockbox{}, errors.New("key is empty") } - if len(b) > keyLength { - return Lockbox{}, errors.New("key is too large for use") - } - - for len(b) < keyLength { - b = append(b, byte(0)) - } var secretKey [keyLength]byte copy(secretKey[:], b) return Lockbox{secret: secretKey, file: file}, nil } +func pad(salt, key []byte) ([keyLength]byte, error) { + d := pbkdf2.Key(key, salt, 4096, keyLength, sha512.New) + if len(d) != keyLength { + return [keyLength]byte{}, errors.New("invalid key result from pad") + } + var obj [keyLength]byte + copy(obj[:], d[:keyLength]) + return obj, nil +} + func getKey(keyMode, name string) ([]byte, error) { var data []byte switch keyMode { @@ -145,23 +151,40 @@ func (l Lockbox) Encrypt(datum []byte) error { if _, err := io.ReadFull(rand.Reader, padding[:]); err != nil { return err } + var salt [saltLength]byte + if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { + return err + } var write []byte write = append(write, byte(padTo)) write = append(write, padding[0:padTo]...) write = append(write, data...) - encrypted := secretbox.Seal(nonce[:], write, &nonce, &l.secret) - return os.WriteFile(l.file, encrypted, 0600) + key, err := pad(salt[:], l.secret[:]) + if err != nil { + return err + } + encrypted := secretbox.Seal(nonce[:], write, &nonce, &key) + var persist []byte + persist = append(persist, salt[:]...) + persist = append(persist, encrypted...) + return os.WriteFile(l.file, persist, 0600) } // Decrypt will decrypt an object from file. func (l Lockbox) Decrypt() ([]byte, error) { var nonce [nonceLength]byte + var salt [saltLength]byte encrypted, err := os.ReadFile(l.file) if err != nil { return nil, err } - copy(nonce[:], encrypted[:nonceLength]) - decrypted, ok := secretbox.Open(nil, encrypted[nonceLength:], &nonce, &l.secret) + copy(salt[:], encrypted[:saltLength]) + copy(nonce[:], encrypted[saltLength:saltLength+nonceLength]) + key, err := pad(salt[:], l.secret[:]) + if err != nil { + return nil, err + } + decrypted, ok := secretbox.Open(nil, encrypted[saltLength+nonceLength:], &nonce, &key) if !ok { return nil, errors.New("decrypt not ok") } diff --git a/internal/encrypt/core_test.go b/internal/encrypt/core_test.go @@ -56,17 +56,13 @@ func TestEmptyKey(t *testing.T) { func TestKeyLength(t *testing.T) { val := "" - for i := 0; i < 32; i++ { + for i := 0; i < keyLength+10; i++ { val = fmt.Sprintf("a%s", val) _, err := NewLockbox(LockboxOptions{KeyMode: PlainKeyMode, Key: val}) if err != nil { t.Error("no error expected") } } - _, err := NewLockbox(LockboxOptions{KeyMode: PlainKeyMode, Key: fmt.Sprintf("%sa", val)}) - if err == nil || err.Error() != "key is too large for use" { - t.Errorf("invalid error: %v", err) - } } func TestUnknownMode(t *testing.T) { @@ -87,7 +83,7 @@ func TestEncryptDecryptPlainText(t *testing.T) { } d, err := e.Decrypt() if err != nil { - t.Errorf("failed to encrypt: %v", err) + t.Errorf("failed to decrypt: %v", err) } if string(d) != string(data) { t.Error("data mismatch")