From c5de11834d4b6c99b78b8d95e763ccb31ae66841 Mon Sep 17 00:00:00 2001 From: Snider Date: Mon, 3 Nov 2025 04:05:32 +0000 Subject: [PATCH] feat: add trix command-line tool for encoding, decoding, and hashing files --- .gitignore | 1 + .goreleaser.yml | 35 +++++++++ README.md | 83 +++++++++++++++++++++ cmd/trix/main.go | 185 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + 6 files changed, 307 insertions(+) create mode 100644 .goreleaser.yml create mode 100644 cmd/trix/main.go diff --git a/.gitignore b/.gitignore index 9364f1a..f895bcd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ merged_covdata/ coverage.txt coverage.html coverage.out +test.* \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..2970b70 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,35 @@ +# .goreleaser.yml +before: + hooks: + - go mod tidy +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + main: ./cmd/trix + binary: trix + id: "trix" + +archives: + - replacements: + darwin: Darwin + linux: Linux + windows: Windows + 386: i386 + amd64: x86_64 + +checksum: + name_template: 'checksums.txt' + +snapshot: + name_template: "{{ incpatch .Version }}-next" + +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/README.md b/README.md index 5814e4f..c87c3e1 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,86 @@ To get started with Enchantrix, you'll need to have Go installed. You can then r ```shell go test ./... ``` + +## `trix` Command-Line Tool + +Enchantrix includes a command-line tool called `trix` for encoding and decoding files using the `.trix` format. + +### Installation + +You can install the `trix` tool using `go install`: + +```shell +go install github.com/Snider/Enchantrix/cmd/trix@latest +``` + +### Usage + +The `trix` tool can read from a file using the `--input` flag or from `stdin` if the flag is omitted. + +#### Encode + +To encode a file, use the `encode` subcommand, followed by any sigils you want to apply: + +```shell +trix encode --output --magic [sigil1] [sigil2]... +``` + +- `--input`: The path to the input file (optional, reads from stdin if omitted). +- `--output`: The path to the output `.trix` file. +- `--magic`: A 4-byte magic number to identify the file type. +- `[sigil...]`: A space-separated list of sigils to apply to the data. + +Example: +```shell +echo "Hello, Trix!" | trix encode --output test.trix --magic TRIX base64 +``` + +#### Decode + +To decode a `.trix` file, use the `decode` subcommand: + +```shell +trix decode --output --magic [sigil1] [sigil2]... +``` + +- `--input`: The path to the input `.trix` file (optional, reads from stdin if omitted). +- `--output`: The path to the decoded output file. +- `--magic`: The 4-byte magic number used during encoding. +- `[sigil...]`: A space-separated list of sigils to apply for unpacking. + +Example: +```shell +trix decode --input test.trix --output test.txt --magic TRIX base64 +``` + +#### Hash + +To hash data, use the `hash` subcommand, followed by the desired algorithm: + +```shell +trix hash [algorithm] +``` + +- `--input`: The path to the input file (optional, reads from stdin if omitted). +- `[algorithm]`: The hashing algorithm to use (e.g., `sha256`). + +Example: +```shell +echo "Hello, Trix!" | trix hash sha256 +``` + +#### Sigils + +You can also apply any sigil directly as a subcommand: + +```shell +trix [sigil] +``` + +- `--input`: The path to the input file or a string (optional, reads from stdin if omitted). + +Example: +```shell +echo "Hello, Trix!" | trix hex +``` diff --git a/cmd/trix/main.go b/cmd/trix/main.go new file mode 100644 index 0000000..6564548 --- /dev/null +++ b/cmd/trix/main.go @@ -0,0 +1,185 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/Snider/Enchantrix/pkg/crypt" + "github.com/Snider/Enchantrix/pkg/enchantrix" + "github.com/Snider/Enchantrix/pkg/trix" + "github.com/leaanthony/clir" +) + +var availableSigils = []string{ + "reverse", "hex", "base64", "gzip", "json", "json-indent", "md4", "md5", + "sha1", "sha224", "sha256", "sha384", "sha512", "ripemd160", "sha3-224", + "sha3-256", "sha3-384", "sha3-512", "sha512-224", "sha512-256", + "blake2s-256", "blake2b-256", "blake2b-384", "blake2b-512", +} + +func main() { + app := clir.NewCli("trix", "A tool for encoding and decoding .trix files", "v0.0.1") + + // Encode command + encodeCmd := app.NewSubCommand("encode", "Encode a file to the .trix format") + var encodeInput, encodeOutput, encodeMagic string + encodeCmd.StringFlag("input", "Input file (or stdin)", &encodeInput) + encodeCmd.StringFlag("output", "Output file", &encodeOutput) + encodeCmd.StringFlag("magic", "Magic number (4 bytes)", &encodeMagic) + encodeCmd.Action(func() error { + sigils := encodeCmd.OtherArgs() + return handleEncode(encodeInput, encodeOutput, encodeMagic, sigils) + }) + + // Decode command + decodeCmd := app.NewSubCommand("decode", "Decode a .trix file") + var decodeInput, decodeOutput, decodeMagic string + decodeCmd.StringFlag("input", "Input file (or stdin)", &decodeInput) + decodeCmd.StringFlag("output", "Output file", &decodeOutput) + decodeCmd.StringFlag("magic", "Magic number (4 bytes)", &decodeMagic) + decodeCmd.Action(func() error { + sigils := decodeCmd.OtherArgs() + return handleDecode(decodeInput, decodeOutput, decodeMagic, sigils) + }) + + // Hash command + hashCmd := app.NewSubCommand("hash", "Hash a file using a specified algorithm") + var hashInput string + var hashAlgo string + hashCmd.StringFlag("input", "Input file (or stdin)", &hashInput) + hashCmd.Action(func() error { + algo := hashCmd.OtherArgs() + if len(algo) > 0 { + hashAlgo = algo[0] + } + return handleHash(hashInput, hashAlgo) + }) + + // Sigil commands + for _, sigil := range availableSigils { + sigil := sigil // capture range variable + sigilCmd := app.NewSubCommand(sigil, "Apply the "+sigil+" sigil") + var input string + sigilCmd.StringFlag("input", "Input file or string (or stdin)", &input) + sigilCmd.Action(func() error { + return handleSigil(sigil, input) + }) + } + + if err := app.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func readInput(inputFile string) ([]byte, error) { + if inputFile == "" { + return ioutil.ReadAll(os.Stdin) + } + return ioutil.ReadFile(inputFile) +} + +func handleSigil(sigilName, input string) error { + s, err := enchantrix.NewSigil(sigilName) + if err != nil { + return err + } + var data []byte + // check if input is a file or a string + if _, err := os.Stat(input); err == nil { + data, err = readInput(input) + if err != nil { + return err + } + } else { + if input == "" { + data, err = readInput("") + if err != nil { + return err + } + } else { + data = []byte(input) + } + } + + out, err := s.In(data) + if err != nil { + return err + } + fmt.Print(string(out)) + return nil +} + +func handleHash(inputFile, algo string) error { + if algo == "" { + return fmt.Errorf("hash algorithm is required") + } + + data, err := readInput(inputFile) + if err != nil { + return err + } + + service := crypt.NewService() + hash := service.Hash(crypt.HashType(algo), string(data)) + fmt.Println(hash) + return nil +} + +func handleEncode(inputFile, outputFile, magicNumber string, sigils []string) error { + if outputFile == "" { + return fmt.Errorf("output file is required") + } + if len(magicNumber) != 4 { + return fmt.Errorf("magic number must be 4 bytes long") + } + + payload, err := readInput(inputFile) + if err != nil { + return err + } + + t := &trix.Trix{ + Header: make(map[string]interface{}), + Payload: payload, + InSigils: sigils, + } + + if err := t.Pack(); err != nil { + return err + } + + encoded, err := trix.Encode(t, magicNumber, nil) + if err != nil { + return err + } + + return ioutil.WriteFile(outputFile, encoded, 0644) +} + +func handleDecode(inputFile, outputFile, magicNumber string, sigils []string) error { + if outputFile == "" { + return fmt.Errorf("output file is required") + } + if len(magicNumber) != 4 { + return fmt.Errorf("magic number must be 4 bytes long") + } + + data, err := readInput(inputFile) + if err != nil { + return err + } + + t, err := trix.Decode(data, magicNumber, nil) + if err != nil { + return err + } + + t.OutSigils = sigils + if err := t.Unpack(); err != nil { + return err + } + + return ioutil.WriteFile(outputFile, t.Payload, 0644) +} diff --git a/go.mod b/go.mod index 240d180..2993c37 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Snider/Enchantrix go 1.25 require ( + github.com/leaanthony/clir v1.7.0 github.com/stretchr/testify v1.11.1 golang.org/x/crypto v0.43.0 ) diff --git a/go.sum b/go.sum index cd3b418..68e5ad5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/leaanthony/clir v1.7.0 h1:xiAnhl7ryPwuH3ERwPWZp/pCHk8wTeiwuAOt6MiNyAw= +github.com/leaanthony/clir v1.7.0/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=