lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 8f325a33327480535fe20215d2a278cce7fc8b18
parent f3997de0ff6b8eeed1deba9d7d8177fc6b42a392
Author: Sean Enck <sean@ttypty.com>
Date:   Sun, 18 Sep 2022 13:10:02 -0400

restructure some encrypt/decrypt, better errors for bad data

Diffstat:
Minternal/encrypt/core.go | 42++++++++++++++++++++++++++++--------------
Minternal/encrypt/core_test.go | 30++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/internal/encrypt/core.go b/internal/encrypt/core.go @@ -23,8 +23,11 @@ const ( ) var ( - cryptoVers = []byte{0, 1} - cryptoVersLength = len(cryptoVers) + cryptoMajorVers = uint8(0) + cryptoMinorVers = uint8(1) + cryptoVers = []byte{cryptoMajorVers, cryptoMinorVers} + cryptoVersLength = len(cryptoVers) + requiredEncryptLength = cryptoVersLength + saltLength + nonceLength ) type ( @@ -91,11 +94,6 @@ func init() { // Encrypt will encrypt contents to file. func (l Lockbox) Encrypt(datum []byte) error { - var nonce [nonceLength]byte - padTo := random.Intn(padLength) - if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { - return err - } data := datum if data == nil { b, err := inputs.RawStdin() @@ -104,18 +102,26 @@ func (l Lockbox) Encrypt(datum []byte) error { } data = b } + if len(data) == 0 { + return errors.New("no data") + } var padding [padLength]byte 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 - } + padTo := random.Intn(padLength) var write []byte write = append(write, byte(padTo)) write = append(write, padding[0:padTo]...) write = append(write, data...) + var salt [saltLength]byte + if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { + return err + } + var nonce [nonceLength]byte + if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + return err + } key, err := pad(salt[:], l.secret[:]) if err != nil { return err @@ -130,18 +136,26 @@ func (l Lockbox) Encrypt(datum []byte) error { // 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 } + if len(encrypted) <= requiredEncryptLength { + return nil, errors.New("invalid encrypted data") + } + major := encrypted[0] + minor := encrypted[1] + if major != cryptoMajorVers || minor != cryptoMinorVers { + return nil, errors.New("invalid data, bad header") + } + var salt [saltLength]byte copy(salt[:], encrypted[cryptoVersLength:saltLength+cryptoVersLength]) - copy(nonce[:], encrypted[cryptoVersLength+saltLength:cryptoVersLength+saltLength+nonceLength]) key, err := pad(salt[:], l.secret[:]) if err != nil { return nil, err } + var nonce [nonceLength]byte + copy(nonce[:], encrypted[cryptoVersLength+saltLength:cryptoVersLength+saltLength+nonceLength]) decrypted, ok := secretbox.Open(nil, encrypted[cryptoVersLength+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 @@ -91,3 +91,33 @@ func TestEncryptDecryptPlainText(t *testing.T) { t.Error("data mismatch") } } + +func TestEncryptDecryptErrors(t *testing.T) { + file := setupData(t) + e, _ := encrypt.NewLockbox(encrypt.LockboxOptions{Key: "plain", KeyMode: inputs.PlainKeyMode, File: file}) + if err := e.Encrypt([]byte{}); err.Error() != "no data" { + t.Errorf("failed, should be no data: %v", err) + } + os.WriteFile(file, []byte{0, 2, 3}, 0600) + if _, err := e.Decrypt(); err.Error() != "invalid encrypted data" { + t.Errorf("failed, should be invalid data: %v", err) + } + e.Encrypt([]byte("TEST")) + b, _ := os.ReadFile(file) + b[0] = 1 + os.WriteFile(file, b, 0600) + if _, err := e.Decrypt(); err.Error() != "invalid data, bad header" { + t.Errorf("failed, should be invalid header data: %v", err) + } + b[0] = 0 + b[1] = 0 + os.WriteFile(file, b, 0600) + if _, err := e.Decrypt(); err.Error() != "invalid data, bad header" { + t.Errorf("failed, should be invalid header data: %v", err) + } + b[1] = 1 + os.WriteFile(file, b, 0600) + if _, err := e.Decrypt(); err != nil { + t.Error("decrypt should succeed") + } +}