From 4c8585781ebbe4164fd847f582adbd97d213855d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lie=20DELHAIE?= Date: Mon, 11 Aug 2025 23:51:00 +0200 Subject: [PATCH] add things --- build.sh | 53 +++++++++++++++++++ commands/base64/base64.go | 102 ++++++++++++++++++++++++++++++++++++ commands/bcrypt/bcrypt.go | 50 ++++++++++++++---- commands/version/version.go | 34 ++++++++++++ go.mod | 1 + go.sum | 2 + main.go | 8 +++ 7 files changed, 239 insertions(+), 11 deletions(-) create mode 100755 build.sh create mode 100644 commands/base64/base64.go create mode 100644 commands/version/version.go diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..24f0b2b --- /dev/null +++ b/build.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +MAKE_PACKAGE=false + +usage() { + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " --package Make a delivery package instead of plain binary" +} + +# Function to handle options and arguments +handle_options() { + while [ $# -gt 0 ]; do + case $1 in + --package) + MAKE_PACKAGE=true + ;; + *) + echo "Invalid option: $1" >&2 + usage + exit 1 + ;; + esac + shift + done +} + +# Main script execution +handle_options "$@" + +if [ ! -d "./build" ]; then + mkdir ./build +fi + +platforms=("linux/386" "linux/amd64" "linux/arm64" "linux/riscv64" "darwin/arm64" "windows/arm64" "windows/amd64" "freebsd/amd64" "android/arm64") + +for platform in "${platforms[@]}"; do + echo "* Compiling for $platform..." + platform_split=(${platform//\// }) + + EXT="" + if [ "${platform_split[0]}" == "windows" ]; then + EXT=.exe + fi + + if [ "$MAKE_PACKAGE" == "true" ]; then + CGO_ENABLED=0 GOOS=${platform_split[0]} GOARCH=${platform_split[1]} go build -o build/hash_utils$EXT -a + tar -czf build/hash_utils_${platform_split[0]}_${platform_split[1]}.tar.gz build/hash_utils$EXT + rm build/hash_utils$EXT + else + CGO_ENABLED=0 GOOS=${platform_split[0]} GOARCH=${platform_split[1]} go build -o build/hash_utils_${platform_split[0]}_${platform_split[1]}$EXT -a + fi +done diff --git a/commands/base64/base64.go b/commands/base64/base64.go new file mode 100644 index 0000000..e2d8157 --- /dev/null +++ b/commands/base64/base64.go @@ -0,0 +1,102 @@ +package base64 + +import ( + "context" + "encoding/base64" + "flag" + "fmt" + "io" + "os" + "strings" + + "github.com/google/subcommands" +) + +type ( + Base64Cmd struct { + file string + decoderMode bool + } +) + +func (*Base64Cmd) Name() string { return "base64" } +func (*Base64Cmd) Synopsis() string { return "" } +func (*Base64Cmd) Usage() string { + return `Usage: hash_utils base64 + +Options: +` +} + +func (p *Base64Cmd) SetFlags(f *flag.FlagSet) { + f.StringVar(&p.file, "file", "", "get the checksum of a file") + f.BoolVar(&p.decoderMode, "decode", false, "decode instead of encode") +} + +func (p *Base64Cmd) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { + if p.decoderMode { + return p.decode(ctx, f, args) + } + return p.encode(ctx, f, args) +} + +func (p *Base64Cmd) encode(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if len(p.file) > 0 { + b, err := os.ReadFile(p.file) + if err != nil { + fmt.Fprintln(os.Stderr, "error: failed to open the file:", err) + return subcommands.ExitFailure + } + + e := base64.NewEncoder(base64.RawStdEncoding, os.Stdout) + if _, err := e.Write(b); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return subcommands.ExitFailure + } + fmt.Println() + return subcommands.ExitSuccess + } + + for _, arg := range f.Args() { + fmt.Print(arg, ": ") + e := base64.NewEncoder(base64.RawStdEncoding, os.Stdout) + if _, err := e.Write([]byte(arg)); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return subcommands.ExitFailure + } + fmt.Println() + } + + return subcommands.ExitSuccess +} + +func (p *Base64Cmd) decode(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if len(p.file) > 0 { + file, err := os.OpenFile(p.file, os.O_RDONLY, 0) + if err != nil { + fmt.Fprintln(os.Stderr, "error: failed to open the file:", err) + return subcommands.ExitFailure + } + defer file.Close() + + e := base64.NewDecoder(base64.RawStdEncoding, file) + if _, err := io.Copy(os.Stdout, e); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return subcommands.ExitFailure + } + fmt.Println() + return subcommands.ExitSuccess + } + + for _, arg := range f.Args() { + r := strings.NewReader(arg) + e := base64.NewDecoder(base64.RawStdEncoding, r) + if _, err := io.Copy(os.Stdout, e); err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return subcommands.ExitFailure + } + fmt.Println() + } + + return subcommands.ExitSuccess +} diff --git a/commands/bcrypt/bcrypt.go b/commands/bcrypt/bcrypt.go index 6b7a2d7..cb06559 100644 --- a/commands/bcrypt/bcrypt.go +++ b/commands/bcrypt/bcrypt.go @@ -3,32 +3,60 @@ package bcrypt import ( "context" "flag" + "fmt" + "os" "github.com/google/subcommands" + "golang.org/x/crypto/bcrypt" ) type ( - MD5Cmd struct { - file string - round int + BCryptCmd struct { + cost int + check bool } ) -func (*MD5Cmd) Name() string { return "bcrypt" } -func (*MD5Cmd) Synopsis() string { return "" } -func (*MD5Cmd) Usage() string { +func (*BCryptCmd) Name() string { return "bcrypt" } +func (*BCryptCmd) Synopsis() string { return "" } +func (*BCryptCmd) Usage() string { return `Usage: hash_utils bcrypt Options: ` } -func (p *MD5Cmd) SetFlags(f *flag.FlagSet) { - f.StringVar(&p.file, "file", "", "get the checksum of a file") - f.IntVar(&p.round, "round", 12, "number of iteration") +func (p *BCryptCmd) SetFlags(f *flag.FlagSet) { + f.IntVar(&p.cost, "cost", 12, "number of iteration") + f.BoolVar(&p.check, "check", false, "check a password") } -func (p *MD5Cmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - +func (p *BCryptCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if p.check { + if f.NArg() != 2 { + fmt.Fprintln(os.Stderr, "error: invalid number of parameters") + return subcommands.ExitUsageError + } + if err := bcrypt.CompareHashAndPassword([]byte(f.Arg(0)), []byte(f.Arg(1))); err != nil { + fmt.Println("no match") + return subcommands.ExitSuccess + } + fmt.Println("match") + return subcommands.ExitSuccess + } + if p.cost < bcrypt.MinCost { + fmt.Fprintln(os.Stderr, "error: invalid cost settings: cost should be over", bcrypt.MinCost) + return subcommands.ExitUsageError + } + if p.cost > bcrypt.MaxCost { + fmt.Fprintln(os.Stderr, "error: invalid cost settings: cost should be under", bcrypt.MaxCost) + return subcommands.ExitUsageError + } + h, err := bcrypt.GenerateFromPassword([]byte(f.Arg(0)), p.cost) + if err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + return subcommands.ExitFailure + } + fmt.Println(string(h)) return subcommands.ExitSuccess } diff --git a/commands/version/version.go b/commands/version/version.go new file mode 100644 index 0000000..5ea4886 --- /dev/null +++ b/commands/version/version.go @@ -0,0 +1,34 @@ +package version + +import ( + "context" + "flag" + "fmt" + "runtime" + + "github.com/google/subcommands" +) + +const ( + version string = "2.0.0" +) + +type ( + VersionCmd struct { + } +) + +func (*VersionCmd) Name() string { return "version" } +func (*VersionCmd) Synopsis() string { return "" } +func (*VersionCmd) Usage() string { + return `Usage: hash_utils version +` +} + +func (p *VersionCmd) SetFlags(f *flag.FlagSet) { +} + +func (p *VersionCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + fmt.Printf("hash_utils %s %s/%s\n", version, runtime.GOOS, runtime.GOARCH) + return subcommands.ExitSuccess +} diff --git a/go.mod b/go.mod index 0825158..ed40445 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,5 @@ go 1.24 require ( github.com/google/subcommands v1.2.0 github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f + golang.org/x/crypto v0.41.0 ) diff --git a/go.sum b/go.sum index c0392ed..c2b0f92 100644 --- a/go.sum +++ b/go.sum @@ -2,3 +2,5 @@ github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f h1:1R9KdKjCNSd7F8iGTxIpoID9prlYH8nuNYKt0XvweHA= github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f/go.mod h1:vQhwQ4meQEDfahT5kd61wLAF5AAeh5ZPLVI4JJ/tYo8= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= diff --git a/main.go b/main.go index 28bf60d..0ca5cf2 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,14 @@ package main import ( "context" "flag" + "hash_utils/commands/base64" + "hash_utils/commands/bcrypt" "hash_utils/commands/crc8" "hash_utils/commands/md5" "hash_utils/commands/sha1" "hash_utils/commands/sha256" "hash_utils/commands/sha512" + "hash_utils/commands/version" "os" "github.com/google/subcommands" @@ -17,6 +20,7 @@ func main() { subcommands.Register(subcommands.HelpCommand(), "help") subcommands.Register(subcommands.FlagsCommand(), "help") subcommands.Register(subcommands.CommandsCommand(), "help") + subcommands.Register(&version.VersionCmd{}, "help") subcommands.Register(&md5.MD5Cmd{}, "unkeyed cryptographic hash functions") subcommands.Register(&sha1.SHA1Cmd{}, "unkeyed cryptographic hash functions") @@ -25,6 +29,10 @@ func main() { subcommands.Register(&crc8.CRC8Cmd{}, "cyclic redundancy checks") + subcommands.Register(&bcrypt.BCryptCmd{}, "password hashing functions") + + subcommands.Register(&base64.Base64Cmd{}, "other") + flag.Parse() ctx := context.Background()