commit e13f01f8e083cac0fdd31d547e3233e5712e3133
parent fcd10fd994414c3523fca96c05b566ee03ecc221
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 5 Feb 2023 19:23:42 -0500
support rekey command
Diffstat:
8 files changed, 55 insertions(+), 26 deletions(-)
diff --git a/cmd/main.go b/cmd/main.go
@@ -115,6 +115,10 @@ func run() error {
return wrapped("unable to build transaction model", err)
}
switch command {
+ case cli.ReKeyCommand:
+ if err := t.ReKey(); err != nil {
+ return wrapped("unable to rekey", err)
+ }
case cli.ListCommand, cli.FindCommand:
opts := backend.QueryOptions{}
opts.Mode = backend.ListMode
diff --git a/contrib/rekey.sh b/contrib/rekey.sh
@@ -1,24 +0,0 @@
-#!/usr/bin/env bash
-if [ -z "$LOCKBOX_REKEY" ] && [ -z "$LOCKBOX_REKEYFILE" ]; then
- echo "LOCKBOX_REKEY/LOCKBOX_REKEYFILE are not set properly for rekeying"
- exit 1
-fi
-
-NEW_STORE="$(date +%Y%m%d%H%M%S).lb.kdbx"
-_rekey() {
- local entry modtime tmp
- tmp=$(mktemp).kdbx
- for entry in $(lb ls); do
- modtime=$(lb stats "$entry" | grep '^modtime:' | cut -d ":" -f 2- | sed 's/^\s*//g')
- echo "migrating: $entry"
- if ! lb show "$entry" | LOCKBOX_HOOKDIR="" LOCKBOX_SET_MODTIME="$modtime" LOCKBOX_STORE="$tmp" LOCKBOX_KEY="$LOCKBOX_REKEY" LOCKBOX_KEYFILE="$LOCKBOX_REKEYFILE" lb insert "$entry" > /dev/null; then
- echo "failed"
- rm -f "$tmp"
- exit 1
- fi
- done
- mv "$tmp" "$NEW_STORE"
-}
-
-_rekey
-echo "completed, '$NEW_STORE' created"
diff --git a/internal/backend/actions.go b/internal/backend/actions.go
@@ -217,6 +217,31 @@ func splitComponents(path string) ([]string, string, error) {
return parts, title, nil
}
+func (t *Transaction) ReKey() error {
+ return t.act(func(c Context) error {
+ t.write = false
+ if err := inputs.SetReKey(); err != nil {
+ return err
+ }
+ n, err := NewTransaction()
+ if err != nil {
+ return err
+ }
+ if err := c.db.UnlockProtectedEntries(); err != nil {
+ return err
+ }
+ err = n.act(func(nCtx Context) error {
+ n.write = true
+ nCtx.db.Content.Root = c.db.Content.Root
+ return nCtx.db.LockProtectedEntries()
+ })
+ if err != nil {
+ return err
+ }
+ return c.db.LockProtectedEntries()
+ })
+}
+
// Move will move a src object to a dst location
func (t *Transaction) Move(src QueryEntity, dst string) error {
if strings.TrimSpace(src.Path) == "" {
diff --git a/internal/cli/core.go b/internal/cli/core.go
@@ -60,6 +60,7 @@ const (
BashCommand = "bash"
// BashDefaultsCommand will generate environment agnostic completions
BashDefaultsCommand = "-defaults"
+ ReKeyCommand = "key"
)
//go:embed "completions.bash"
@@ -164,7 +165,7 @@ func BashCompletions(defaults bool) ([]string, error) {
c.CanClip = isClip
c.ReadOnly = isReadOnly
c.CanTOTP = isTOTP
- options := []string{EnvCommand, FindCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, StatsCommand}
+ options := []string{EnvCommand, FindCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, StatsCommand, ReKeyCommand}
if c.CanClip {
options = append(options, ClipCommand)
}
@@ -205,6 +206,7 @@ func Usage() ([]string, error) {
results = append(results, command(ListCommand, "", "list entries"))
results = append(results, command(MoveCommand, "src dst", "move an entry from one location to another with the store"))
results = append(results, command(RemoveCommand, "entry", "remove an entry from the store"))
+ results = append(results, command(ReKeyCommand, "", "rekey the database"))
results = append(results, command(ShowCommand, "entry", "show the entry's value"))
results = append(results, command(StatsCommand, "entry", "display entry detail information"))
results = append(results, command(TOTPCommand, "entry", "display an updating totp generated code"))
diff --git a/internal/cli/core_test.go b/internal/cli/core_test.go
@@ -9,7 +9,7 @@ import (
func TestUsage(t *testing.T) {
u, _ := cli.Usage()
- if len(u) != 21 {
+ if len(u) != 22 {
t.Errorf("invalid usage, out of date? %d", len(u))
}
}
diff --git a/internal/inputs/env.go b/internal/inputs/env.go
@@ -14,6 +14,16 @@ import (
"github.com/google/shlex"
)
+func SetReKey() error {
+ for _, k := range []string{keyModeEnv, keyEnv, KeyFileEnv, StoreEnv} {
+ val := os.Getenv(fmt.Sprintf("%s_NEW", k))
+ if err := os.Setenv(k, val); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
const (
otpAuth = "otpauth"
otpIssuer = "lbissuer"
diff --git a/scripts/check.go b/scripts/check.go
@@ -124,4 +124,15 @@ func main() {
rm("keys/k2/*")
fmt.Println()
ls()
+ reKeyStore := fmt.Sprintf("%s.rekey.kdbx", store)
+ reKey := "rekey"
+ os.Setenv("LOCKBOX_STORE_NEW", reKeyStore)
+ os.Setenv("LOCKBOX_KEY_NEW", reKey)
+ os.Setenv("LOCKBOX_KEYMODE_NEW", "plaintext")
+ os.Setenv("LOCKBOX_KEYFILE_NEW", "")
+ runCommand([]string{"key"}, nil)
+ os.Setenv("LOCKBOX_STORE", reKeyStore)
+ os.Setenv("LOCKBOX_KEYFILE", "")
+ os.Setenv("LOCKBOX_KEY", reKey)
+ ls()
}
diff --git a/scripts/tests.expected.log b/scripts/tests.expected.log
@@ -116,3 +116,4 @@ post rm keys/k2/t2/one2
CALLED
keys/k/one2
+keys/k/one2