lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 18d131176b593d1f53af52c556836d2c94e24df0
parent 34cc44911425161f481f9fd590a720cea99ee2a2
Author: Sean Enck <sean@ttypty.com>
Date:   Tue, 25 Jul 2023 18:24:24 -0400

support a shortened hash

Diffstat:
Minternal/backend/query.go | 10++++++++++
Minternal/backend/query_test.go | 13+++++++++++++
Minternal/cli/core_test.go | 2+-
Minternal/inputs/env.go | 39+++++++++++++++++++++++++++++++--------
Minternal/inputs/env_test.go | 55++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Minternal/platform/clipboard_test.go | 2+-
Mtests/expected.log | 6++++++
Mtests/run.sh | 3+++
8 files changed, 119 insertions(+), 11 deletions(-)

diff --git a/internal/backend/query.go b/internal/backend/query.go @@ -126,6 +126,13 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) { } jsonMode = m } + var hashLength int + if jsonMode == inputs.JSONDataOutputHash { + hashLength, err = inputs.GetHashLength() + if err != nil { + return nil, err + } + } var results []QueryEntity for _, k := range keys { entity := QueryEntity{Path: k} @@ -146,6 +153,9 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) { data = val case inputs.JSONDataOutputHash: data = fmt.Sprintf("%x", sha512.Sum512([]byte(val))) + if hashLength > 0 && len(data) > hashLength { + data = data[0:hashLength] + } } t := getValue(e.backing, modTimeKey) s := JSON{ModTime: t, Data: data} diff --git a/internal/backend/query_test.go b/internal/backend/query_test.go @@ -96,6 +96,19 @@ func TestValueModes(t *testing.T) { if len(m.ModTime) < 20 { t.Errorf("invalid date/time") } + os.Setenv("LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH", "10") + defer os.Clearenv() + q, err = fullSetup(t, true).Get("test/test/abc", backend.JSONValue) + if err != nil { + t.Errorf("no error: %v", err) + } + m = backend.JSON{} + if err := json.Unmarshal([]byte(q.Value), &m); err != nil { + t.Errorf("no error: %v", err) + } + if m.Data != "44276ba24d" { + t.Errorf("invalid result value: %s", q.Value) + } q, err = fullSetup(t, true).Get("test/test/ab11c", backend.SecretValue) if err != nil { t.Errorf("no error: %v", err) diff --git a/internal/cli/core_test.go b/internal/cli/core_test.go @@ -14,7 +14,7 @@ func TestUsage(t *testing.T) { t.Errorf("invalid usage, out of date? %d", len(u)) } u, _ = cli.Usage(true) - if len(u) != 94 { + if len(u) != 95 { t.Errorf("invalid verbose usage, out of date? %d", len(u)) } for _, usage := range u { diff --git a/internal/inputs/env.go b/internal/inputs/env.go @@ -68,6 +68,8 @@ const ( MaxTOTPTimeDefault = "120" // JSONDataOutputEnv controls how JSON is output JSONDataOutputEnv = prefixKey + "JSON_DATA_OUTPUT" + defaultHashLength = 0 + hashJSONLengthEnv = JSONDataOutputEnv + "_HASH_LENGTH" ) var isYesNoArgs = []string{system.Yes, system.No} @@ -122,19 +124,39 @@ func GetReKey(args []string) ([]string, error) { // GetClipboardMax will get max time to keep an entry in the clipboard before clearing func GetClipboardMax() (int, error) { - max := defaultMaxClipboard - useMax := os.Getenv(clipMaxEnv) - if useMax != "" { - i, err := strconv.Atoi(useMax) + return getPositiveIntEnv(defaultMaxClipboard, clipMaxEnv, "clipboard max time", false) +} + +// GetHashLength will get the maximum hash length allowed in JSON output hashing mode +func GetHashLength() (int, error) { + return getPositiveIntEnv(defaultHashLength, hashJSONLengthEnv, "hash length", true) +} + +func getPositiveIntEnv(defaultVal int, key, desc string, canBeZero bool) (int, error) { + val := defaultVal + use := os.Getenv(key) + if use != "" { + i, err := strconv.Atoi(use) if err != nil { return -1, err } - if i < 1 { - return -1, errors.New("clipboard max time must be greater than 0") + invalid := false + check := "" + if canBeZero { + check = "=" + } + switch i { + case 0: + invalid = !canBeZero + default: + invalid = i < 0 + } + if invalid { + return -1, fmt.Errorf("%s must be >%s 0", desc, check) } - max = i + val = i } - return max, nil + return val, nil } // GetKey will get the encryption key setup for lb @@ -280,5 +302,6 @@ func ListEnvironmentVariables(showValues bool) []string { results = append(results, e.formatEnvironmentVariable(false, KeyFileEnv, "", "additional keyfile to access/protect the database", []string{"keyfile"})) results = append(results, e.formatEnvironmentVariable(false, ModTimeEnv, ModTimeFormat, fmt.Sprintf("input modification time to set for the entry\n(expected format: %s)", ModTimeFormat), []string{"modtime"})) results = append(results, e.formatEnvironmentVariable(false, JSONDataOutputEnv, string(JSONDataOutputHash), fmt.Sprintf("changes what the data field in JSON outputs will contain\nuse '%s' with CAUTION", JSONDataOutputRaw), []string{string(JSONDataOutputRaw), string(JSONDataOutputHash), string(JSONDataOutputBlank)})) + results = append(results, e.formatEnvironmentVariable(false, hashJSONLengthEnv, fmt.Sprintf("%d", defaultHashLength), fmt.Sprintf("maximum hash length the JSON output should contain\nwhen '%s' mode is set for JSON output", JSONDataOutputHash), []string{"integer"})) return results } diff --git a/internal/inputs/env_test.go b/internal/inputs/env_test.go @@ -112,7 +112,7 @@ func TestListVariables(t *testing.T) { known[trim] = struct{}{} } l := len(known) - if l != 21 { + if l != 22 { t.Errorf("invalid env count, outdated? %d", l) } } @@ -145,3 +145,56 @@ func TestReKey(t *testing.T) { os.Setenv("LOCKBOX_KEY_NEW", "") os.Setenv("LOCKBOX_KEYFILE_NEW", "") } + +func TestGetClipboardMax(t *testing.T) { + os.Setenv("LOCKBOX_CLIP_MAX", "") + defer os.Clearenv() + max, err := inputs.GetClipboardMax() + if err != nil || max != 45 { + t.Error("invalid clipboard read") + } + os.Setenv("LOCKBOX_CLIP_MAX", "1") + max, err = inputs.GetClipboardMax() + if err != nil || max != 1 { + t.Error("invalid clipboard read") + } + os.Setenv("LOCKBOX_CLIP_MAX", "-1") + if _, err := inputs.GetClipboardMax(); err == nil || err.Error() != "clipboard max time must be > 0" { + t.Errorf("invalid err: %v", err) + } + os.Setenv("LOCKBOX_CLIP_MAX", "alk;ja") + if _, err := inputs.GetClipboardMax(); err == nil || err.Error() != "strconv.Atoi: parsing \"alk;ja\": invalid syntax" { + t.Errorf("invalid err: %v", err) + } + os.Setenv("LOCKBOX_CLIP_MAX", "0") + if _, err := inputs.GetClipboardMax(); err == nil || err.Error() != "clipboard max time must be > 0" { + t.Errorf("invalid err: %v", err) + } +} + +func TestGetHashLength(t *testing.T) { + os.Setenv("LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH", "") + defer os.Clearenv() + val, err := inputs.GetHashLength() + if err != nil || val != 0 { + t.Error("invalid hash read") + } + os.Setenv("LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH", "1") + val, err = inputs.GetHashLength() + if err != nil || val != 1 { + t.Error("invalid hash read") + } + os.Setenv("LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH", "0") + val, err = inputs.GetHashLength() + if err != nil || val != 0 { + t.Error("invalid hash read") + } + os.Setenv("LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH", "-1") + if _, err := inputs.GetHashLength(); err == nil || err.Error() != "hash length must be >= 0" { + t.Errorf("invalid err: %v", err) + } + os.Setenv("LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH", "-aoaofaij;p1") + if _, err := inputs.GetHashLength(); err == nil || err.Error() != "strconv.Atoi: parsing \"-aoaofaij;p1\": invalid syntax" { + t.Errorf("invalid err: %v", err) + } +} diff --git a/internal/platform/clipboard_test.go b/internal/platform/clipboard_test.go @@ -41,7 +41,7 @@ func TestMaxTime(t *testing.T) { } os.Setenv("LOCKBOX_CLIP_MAX", "-1") _, err = platform.NewClipboard() - if err == nil || err.Error() != "clipboard max time must be greater than 0" { + if err == nil || err.Error() != "clipboard max time must be > 0" { t.Errorf("invalid max time error: %v", err) } os.Setenv("LOCKBOX_CLIP_MAX", "$&(+") diff --git a/tests/expected.log b/tests/expected.log @@ -155,5 +155,11 @@ test2 "modtime": "XXXX-XX-XX", } } +{ + "keys/k/one2": { + "modtime": "XXXX-XX-XX", + "data": "6d2" + } +} clipboard will clear in 5 seconds Wrong password? HMAC-SHA256 of header mismatching diff --git a/tests/run.sh b/tests/run.sh @@ -115,6 +115,9 @@ _rekey() { ${LB_BINARY} json k export LOCKBOX_JSON_DATA_OUTPUT=empty ${LB_BINARY} json k + export LOCKBOX_JSON_DATA_OUTPUT=hash + export LOCKBOX_JSON_DATA_OUTPUT_HASH_LENGTH=3 + ${LB_BINARY} json k } _clipboard() {