diff options
author | EuAndreh <eu@euandre.org> | 2025-05-15 13:23:59 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2025-05-15 15:13:15 -0300 |
commit | 81f0c44d475e57fd56c6d8009e0c88f65d2fd84e (patch) | |
tree | fb2708925293b0bb7cdf3ab81d4df19c0b0d5af9 | |
parent | rm -rf tests/functional/version/ (diff) | |
download | scrypt-81f0c44d475e57fd56c6d8009e0c88f65d2fd84e.tar.gz scrypt-81f0c44d475e57fd56c6d8009e0c88f65d2fd84e.tar.xz |
src/scrypt.go: Improve CLI handling, add tests alongside
-rw-r--r-- | src/scrypt.go | 90 | ||||
-rw-r--r-- | tests/benchmarks/hash/scrypt.go | 2 | ||||
-rw-r--r-- | tests/functional/hash-and-check/scrypt.go | 8 | ||||
-rw-r--r-- | tests/fuzz/api/scrypt.go | 2 | ||||
-rwxr-xr-x | tests/integration.sh | 2 | ||||
-rw-r--r-- | tests/scrypt.go | 112 |
6 files changed, 187 insertions, 29 deletions
diff --git a/src/scrypt.go b/src/scrypt.go index 511546f..6e212b4 100644 --- a/src/scrypt.go +++ b/src/scrypt.go @@ -3,12 +3,14 @@ package scrypt import ( "crypto/rand" "encoding/hex" + "flag" "fmt" "io" "os" "slices" gt "gotext" + g "gobang" ) @@ -39,17 +41,29 @@ var ( -type HashInput struct{ +type HashInputT struct{ Password []byte Salt []byte } -type CheckInput struct{ +type CheckInputT struct{ Password []byte Salt []byte Hash []byte } +type argsT struct{ + allArgs []string + input HashInputT +} + +type envT struct{ + args argsT + in io.Reader + out io.Writer + err io.Writer +} + // Package scrypt implements the scrypt key derivation function as defined in @@ -108,7 +122,7 @@ func scrypt( return out, nil } -func Hash(input HashInput) ([]byte, error) { +func Hash(input HashInputT) ([]byte, error) { if len(input.Salt) < _SALT_MIN_LENGTH { return nil, ErrSaltTooSmall } @@ -137,8 +151,8 @@ func Salt() ([]byte, error) { return SaltFrom(rand.Reader) } -func Check(input CheckInput) (bool, error) { - hashInput := HashInput{ +func Check(input CheckInputT) (bool, error) { + hashInput := HashInputT{ Password: input.Password, Salt: input.Salt, } @@ -151,29 +165,67 @@ func Check(input CheckInput) (bool, error) { return slices.Equal(candidate, input.Hash), nil } +func usage(argv0 string, w io.Writer) { + fmt.Fprintf( + w, + "Usage: %s PASSWORD SALT\n", + argv0, + ) +} +func getopt(allArgs []string, w io.Writer) (argsT, int) { + argv0 := allArgs[0] + argv := allArgs[1:] + fs := flag.NewFlagSet("", flag.ContinueOnError) + fs.Usage = func() {} + fs.SetOutput(w) -func Main() { - if len(os.Args) != 3 { - fmt.Fprintf(os.Stderr, gt.Gettext("Usage: scrypt PASSWORD SALT\n")) - os.Exit(2) + if fs.Parse(argv) != nil { + usage(argv0, w) + return argsT{}, 2 + } + + subArgs := fs.Args() + if len(subArgs) != 2 { + usage(argv0, w) + return argsT{}, 2 } - password := []byte(os.Args[1]) - salt := []byte(os.Args[2]) - input := HashInput{ + password := []byte(subArgs[0]) + salt := []byte(subArgs[1]) + input := HashInputT{ Password: password, Salt: salt, } - payload, err := Hash(input) + return argsT{ + allArgs: allArgs, + input: input, + }, 0 +} + +func run(env envT) int { + payload, err := Hash(env.args.input) if err != nil { - if err == ErrSaltTooSmall { - fmt.Fprintln(os.Stderr, err) - os.Exit(2) - } - panic(err) + fmt.Fprintln(env.err, err) + return 1 } - fmt.Println(hex.EncodeToString(payload)) + fmt.Fprintln(env.out, hex.EncodeToString(payload)) + return 0 +} + + + +func Main() { + g.Init() + gt.Init(Name, LOCALEDIR) + args, rc := getopt(os.Args, os.Stderr) + g.ExitIf(rc) + os.Exit(run(envT{ + args: args, + in: os.Stdin, + out: os.Stdout, + err: os.Stderr, + })) } diff --git a/tests/benchmarks/hash/scrypt.go b/tests/benchmarks/hash/scrypt.go index 54657d0..894e6da 100644 --- a/tests/benchmarks/hash/scrypt.go +++ b/tests/benchmarks/hash/scrypt.go @@ -18,7 +18,7 @@ func MainTest() { password := []byte("password") salt := []byte("salt0123456789abcdef0123456789abcdef") - input := HashInput{ + input := HashInputT{ Password: password, Salt: salt, } diff --git a/tests/functional/hash-and-check/scrypt.go b/tests/functional/hash-and-check/scrypt.go index 065b9b5..9eeeb22 100644 --- a/tests/functional/hash-and-check/scrypt.go +++ b/tests/functional/hash-and-check/scrypt.go @@ -14,14 +14,14 @@ func MainTest() { salt = []byte("a fixed salt____________________") ) - hashInput := HashInput{ + hashInput := HashInputT{ Password: password, Salt: salt, } hash, err := Hash(hashInput) g.TErrorIf(err) - checkInput := CheckInput{ + checkInput := CheckInputT{ Password: password, Salt: salt, Hash: hash, @@ -38,14 +38,14 @@ func MainTest() { salt, err := Salt() g.TErrorIf(err) - hashInput := HashInput{ + hashInput := HashInputT{ Password: password, Salt: salt, } hash, err := Hash(hashInput) g.TErrorIf(err) - checkInput := CheckInput{ + checkInput := CheckInputT{ Password: password, Salt: salt, Hash: hash, diff --git a/tests/fuzz/api/scrypt.go b/tests/fuzz/api/scrypt.go index c037add..7c2c503 100644 --- a/tests/fuzz/api/scrypt.go +++ b/tests/fuzz/api/scrypt.go @@ -14,7 +14,7 @@ func api(f *testing.F) { return } - input := HashInput{ + input := HashInputT{ Password: password, Salt: salt, } diff --git a/tests/integration.sh b/tests/integration.sh index e31ae4d..8115ce5 100755 --- a/tests/integration.sh +++ b/tests/integration.sh @@ -13,7 +13,7 @@ test_needs_minimum_salt_length() { trap 'rm -f "$OUT" "$ERR"' EXIT STATUS=0 ./scrypt.bin password salt 1>"$OUT" 2>"$ERR" || STATUS=$? - assert_status 2 + assert_status 1 assert_empty_stdout assert_fgrep_stderr 'salt is too small' rm -f "$OUT" "$ERR" diff --git a/tests/scrypt.go b/tests/scrypt.go index 710ea82..0a9f599 100644 --- a/tests/scrypt.go +++ b/tests/scrypt.go @@ -217,7 +217,7 @@ func test_Hash() { salt, err := Salt() g.TErrorIf(err) - input := HashInput{ + input := HashInputT{ Password: password, Salt: salt, } @@ -236,7 +236,7 @@ func test_Check() { g.TestStart("Check()") h := func(password []byte, salt []byte) []byte { - input := HashInput{ + input := HashInputT{ Password: password, Salt: salt, } @@ -246,7 +246,7 @@ func test_Check() { } chk := func(password []byte, salt []byte, hash []byte) bool { - input := CheckInput{ + input := CheckInputT{ Password: password, Salt: salt, Hash: hash, @@ -301,12 +301,118 @@ func test_Check() { }) } +func test_usage() { + g.TestStart("usage()") + + g.Testing("it writes the usage string to the io.Writer", func() { + w := strings.Builder{} + usage("xxx", &w) + + expected := g.Heredoc(` + Usage: xxx PASSWORD SALT + `) + g.TAssertEqual(w.String(), expected) + }) +} + +func test_getopt() { + g.TestStart("getopt()") + + usage := g.Heredoc(` + Usage: $0 PASSWORD SALT + `) + + g.Testing("we suppress the default error message", func() { + w := strings.Builder{} + argv := []string{"$0", "-h"} + _, rc := getopt(argv, &w) + + g.TAssertEqual(w.String(), usage) + g.TAssertEqual(rc, 2) + }) + + g.Testing("we get an unsupported option error", func() { + w := strings.Builder{} + argv := []string{"$0", "-a"} + _, rc := getopt(argv, &w) + + const message = "flag provided but not defined: -a\n" + g.TAssertEqual(w.String(), message + usage) + g.TAssertEqual(rc, 2) + }) + + g.Testing("the args have the input data", func() { + w := strings.Builder{} + argv := []string{"$0", "abcdef", "aaaabbbbccccdddd"} + args, rc := getopt(argv, &w) + + expected := argsT{ + allArgs: []string{"$0", "abcdef", "aaaabbbbccccdddd"}, + input: HashInputT{ + Password: []byte("abcdef"), + Salt: []byte("aaaabbbbccccdddd"), + }, + } + + g.TAssertEqual(args, expected) + g.TAssertEqual(rc, 0) + }) +} + +func test_run() { + g.TestStart("run()") + + g.Testing("a small salt returns 1", func() { + w := strings.Builder{} + env := envT{ + args: argsT{ + input: HashInputT{ + Salt: []byte("a salt"), + }, + }, + err: &w, + } + + rc := run(env) + g.TAssertEqual(rc, 1) + g.TAssertEqual(w.String(), "scrypt: salt is too small\n") + }) + + g.Testing("return of 0 and output otherwise", func() { + w := strings.Builder{} + env := envT{ + args: argsT{ + input: HashInputT{ + Salt: []byte( + "eeeeffffgggghhhh" + + "iiiijjjjjkkkkllll", + ), + Password: []byte("password"), + }, + }, + out: &w, + } + + const expected = + "9073ca9ad51942a30b20af1747e98195" + + "960e76fd8f20b246821692ce82c2d6f7\n" + rc := run(env) + g.TAssertEqual(w.String(), expected) + g.TAssertEqual(rc, 0) + }) +} + func MainTest() { + g.Init() + test_scrypt() test_SaltFrom() test_Salt() test_Hash() test_Check() + test_usage() + test_getopt() + test_run() } |