feat: add trix command-line tool for encoding, decoding, and hashing files
This commit is contained in:
parent
68acd6b775
commit
c5de11834d
6 changed files with 307 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ merged_covdata/
|
||||||
coverage.txt
|
coverage.txt
|
||||||
coverage.html
|
coverage.html
|
||||||
coverage.out
|
coverage.out
|
||||||
|
test.*
|
||||||
35
.goreleaser.yml
Normal file
35
.goreleaser.yml
Normal file
|
|
@ -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:'
|
||||||
83
README.md
83
README.md
|
|
@ -15,3 +15,86 @@ To get started with Enchantrix, you'll need to have Go installed. You can then r
|
||||||
```shell
|
```shell
|
||||||
go test ./...
|
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 <output-file> --magic <magic-number> [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 <output-file> --magic <magic-number> [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
|
||||||
|
```
|
||||||
|
|
|
||||||
185
cmd/trix/main.go
Normal file
185
cmd/trix/main.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
1
go.mod
1
go.mod
|
|
@ -3,6 +3,7 @@ module github.com/Snider/Enchantrix
|
||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/leaanthony/clir v1.7.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
golang.org/x/crypto v0.43.0
|
golang.org/x/crypto v0.43.0
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
go.sum
2
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue