diff --git a/.github/workflows/mkdocs.yml b/.github/workflows/mkdocs.yml new file mode 100644 index 0000000..8a0acae --- /dev/null +++ b/.github/workflows/mkdocs.yml @@ -0,0 +1,17 @@ +name: Publish Docs +on: + push: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: '3.x' + - run: | + pip install mkdocs-material + - run: | + cd docs && mkdocs gh-deploy --force --clean --verbose diff --git a/.gitignore b/.gitignore index 1334267..9364f1a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ node_modules package-lock.json .idea -go.sum covdata/ merged_covdata/ coverage.txt diff --git a/docs/docs/checksums.md b/docs/docs/checksums.md new file mode 100644 index 0000000..ccb0653 --- /dev/null +++ b/docs/docs/checksums.md @@ -0,0 +1,33 @@ +# Checksums + +This example demonstrates how to use the `crypt` service to calculate checksums using various algorithms. + +```go +package main + +import ( + "fmt" + + "github.com/Snider/Enchantrix/pkg/crypt" +) + +func demoChecksums() { + fmt.Println("--- Checksum Demo ---") + cryptService := crypt.NewService() + + // Luhn + luhnPayloadGood := "49927398716" + luhnPayloadBad := "49927398717" + fmt.Printf("Luhn Checksum:\n") + fmt.Printf(" - Payload '%s' is valid: %v\n", luhnPayloadGood, cryptService.Luhn(luhnPayloadGood)) + fmt.Printf(" - Payload '%s' is valid: %v\n", luhnPayloadBad, cryptService.Luhn(luhnPayloadBad)) + + // Fletcher + fletcherPayload := "abcde" + fmt.Printf("\nFletcher Checksums (Payload: \"%s\"):\n", fletcherPayload) + fmt.Printf(" - Fletcher16: %d\n", cryptService.Fletcher16(fletcherPayload)) + fmt.Printf(" - Fletcher32: %d\n", cryptService.Fletcher32(fletcherPayload)) + fmt.Printf(" - Fletcher64: %d\n", cryptService.Fletcher64(fletcherPayload)) + fmt.Println() +} +``` diff --git a/docs/docs/hashing.md b/docs/docs/hashing.md new file mode 100644 index 0000000..a3a1a44 --- /dev/null +++ b/docs/docs/hashing.md @@ -0,0 +1,34 @@ +# Hashing + +This example demonstrates how to use the `crypt` service to hash a payload using various algorithms. + +```go +package main + +import ( + "fmt" + + "github.com/Snider/Enchantrix/pkg/crypt" +) + +func demoHashing() { + fmt.Println("--- Hashing Demo ---") + cryptService := crypt.NewService() + payload := "Enchantrix" + + hashTypes := []crypt.HashType{ + crypt.LTHN, + crypt.MD5, + crypt.SHA1, + crypt.SHA256, + crypt.SHA512, + } + + fmt.Printf("Payload to hash: \"%s\"\n", payload) + for _, hashType := range hashTypes { + hash := cryptService.Hash(hashType, payload) + fmt.Printf(" - %-6s: %s\n", hashType, hash) + } + fmt.Println() +} +``` diff --git a/docs/docs/index.md b/docs/docs/index.md new file mode 100644 index 0000000..4ff9bf4 --- /dev/null +++ b/docs/docs/index.md @@ -0,0 +1,17 @@ +# Welcome to Enchantrix + +Enchantrix is a Go-based crypto library and miner application. This documentation provides information on how to use the various features of the Enchantrix library. + +## Trix File Format + +The `.trix` file format is a generic and flexible binary container for storing an arbitrary data payload alongside structured metadata. For more information, see the [Trix File Format](./trix_format.md) page. + +## Examples + +The following pages provide examples of how to use the Enchantrix library: + +* [Trix & Sigil Chaining](./trix_and_sigils.md) +* [Hashing](./hashing.md) +* [Checksums](./checksums.md) +* [RSA](./rsa.md) +* [Standalone Sigils](./standalone_sigils.md) diff --git a/docs/docs/rsa.md b/docs/docs/rsa.md new file mode 100644 index 0000000..1478f28 --- /dev/null +++ b/docs/docs/rsa.md @@ -0,0 +1,52 @@ +# RSA + +This example demonstrates how to use the `crypt` service to generate an RSA key pair, encrypt a message, and then decrypt it. + +```go +package main + +import ( + "encoding/base64" + "fmt" + "log" + + "github.com/Snider/Enchantrix/pkg/crypt" +) + +func demoRSA() { + fmt.Println("--- RSA Demo ---") + cryptService := crypt.NewService() + + // 1. Generate RSA key pair + fmt.Println("Generating 2048-bit RSA key pair...") + publicKey, privateKey, err := cryptService.GenerateRSAKeyPair(2048) + if err != nil { + log.Fatalf("Failed to generate RSA key pair: %v", err) + } + fmt.Println("Key pair generated successfully.") + + // 2. Encrypt a message + message := []byte("This is a secret message for RSA.") + fmt.Printf("\nOriginal message: %s\n", message) + ciphertext, err := cryptService.EncryptRSA(publicKey, message, nil) + if err != nil { + log.Fatalf("Failed to encrypt with RSA: %v", err) + } + fmt.Printf("Encrypted ciphertext (base64): %s\n", base64.StdEncoding.EncodeToString(ciphertext)) + + // 3. Decrypt the message + decrypted, err := cryptService.DecryptRSA(privateKey, ciphertext, nil) + if err != nil { + log.Fatalf("Failed to decrypt with RSA: %v", err) + } + fmt.Printf("Decrypted message: %s\n", decrypted) + + // 4. Verify + if string(message) == string(decrypted) { + fmt.Println("\nSuccess! RSA decrypted message matches the original.") + } else { + fmt.Println("\nFailure! RSA decrypted message does not match the original.") + } + fmt.Println() +} +``` diff --git a/docs/docs/standalone_sigils.md b/docs/docs/standalone_sigils.md new file mode 100644 index 0000000..2a03fbc --- /dev/null +++ b/docs/docs/standalone_sigils.md @@ -0,0 +1,59 @@ +# Standalone Sigils + +This example demonstrates how to use sigils independently to transform data. + +```go +package main + +import ( + "fmt" + "log" + + "github.com/Snider/Enchantrix/pkg/enchantrix" +) + +func demoSigils() { + fmt.Println("--- Standalone Sigil Demo ---") + data := []byte(`{"message": "hello world"}`) + fmt.Printf("Original data: %s\n", data) + + // A chain of sigils to apply + sigils := []string{"gzip", "base64"} + fmt.Printf("Applying sigil chain: %v\n", sigils) + + var transformedData = data + for _, name := range sigils { + s, err := enchantrix.NewSigil(name) + if err != nil { + log.Fatalf("Failed to create sigil %s: %v", name, err) + } + transformedData, err = s.In(transformedData) + if err != nil { + log.Fatalf("Failed to apply sigil %s 'In': %v", name, err) + } + fmt.Printf(" -> After '%s': %s\n", name, transformedData) + } + + fmt.Println("\nReversing sigil chain...") + // Reverse the transformations + for i := len(sigils) - 1; i >= 0; i-- { + name := sigils[i] + s, err := enchantrix.NewSigil(name) + if err != nil { + log.Fatalf("Failed to create sigil %s: %v", name, err) + } + transformedData, err = s.Out(transformedData) + if err != nil { + log.Fatalf("Failed to apply sigil %s 'Out': %v", name, err) + } + fmt.Printf(" -> After '%s' Out: %s\n", name, transformedData) + } + + if string(data) == string(transformedData) { + fmt.Println("Success! Data returned to original state.") + } else { + fmt.Println("Failure! Data did not return to original state.") + } + fmt.Println() +} +``` diff --git a/docs/docs/trix_and_sigils.md b/docs/docs/trix_and_sigils.md new file mode 100644 index 0000000..6a2fe61 --- /dev/null +++ b/docs/docs/trix_and_sigils.md @@ -0,0 +1,124 @@ +# Trix & Sigil Chaining + +This example demonstrates how to use the Trix container with a chain of sigils to obfuscate and then encrypt a payload. + +```go +package main + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "log" + "time" + + "github.com/Snider/Enchantrix/pkg/crypt" + "github.com/Snider/Enchantrix/pkg/crypt/std/chachapoly" + "github.com/Snider/Enchantrix/pkg/trix" +) + +func demoTrix() { + fmt.Println("--- Trix & Sigil Chaining Demo ---") + + // 1. Original plaintext (JSON data) and encryption key + type Message struct { + Author string `json:"author"` + Time int64 `json:"time"` + Body string `json:"body"` + } + originalMessage := Message{Author: "Jules", Time: time.Now().Unix(), Body: "This is a super secret message!"} + plaintext, err := json.Marshal(originalMessage) + if err != nil { + log.Fatalf("Failed to marshal JSON: %v", err) + } + key := make([]byte, 32) // In a real application, use a secure key + for i := range key { + key[i] = 1 + } + + fmt.Printf("Original Payload (JSON):\n%s\n\n", plaintext) + + // 2. Create a Trix container with the plaintext and attach a chain of sigils + sigilChain := []string{"json-indent", "gzip", "base64", "reverse"} + trixContainer := &trix.Trix{ + Header: map[string]interface{}{}, + Payload: plaintext, + InSigils: sigilChain, + } + + // 3. Pack the Trix container to apply the sigil transformations + fmt.Println("Packing payload with sigils:", sigilChain) + if err := trixContainer.Pack(); err != nil { + log.Fatalf("Failed to pack trix container: %v", err) + } + fmt.Printf("Packed (obfuscated) payload is now non-human-readable bytes.\n\n") + + // 4. Encrypt the packed payload + ciphertext, err := chachapoly.Encrypt(trixContainer.Payload, key) + if err != nil { + log.Fatalf("Failed to encrypt: %v", err) + } + trixContainer.Payload = ciphertext // Update the payload with the ciphertext + + // 5. Add encryption metadata and checksum to the header + nonce := ciphertext[:24] + trixContainer.Header = map[string]interface{}{ + "content_type": "application/json", + "encryption_algorithm": "chacha20poly1305", + "nonce": base64.StdEncoding.EncodeToString(nonce), + "created_at": time.Now().UTC().Format(time.RFC3339), + } + trixContainer.ChecksumAlgo = crypt.SHA512 + fmt.Printf("Checksum will be calculated with %s and added to the header.\n", trixContainer.ChecksumAlgo) + + // 6. Encode the .trix container into its binary format + magicNumber := "MyT1" + encodedTrix, err := trix.Encode(trixContainer, magicNumber, nil) + if err != nil { + log.Fatalf("Failed to encode .trix container: %v", err) + } + fmt.Println("Successfully created .trix container.") + + // --- DECODING --- + fmt.Println("--- DECODING ---") + + // 7. Decode the .trix container + decodedTrix, err := trix.Decode(encodedTrix, magicNumber, nil) + if err != nil { + log.Fatalf("Failed to decode .trix container: %v", err) + } + fmt.Println("Successfully decoded .trix container. Checksum verified.") + fmt.Printf("Decoded Header: %+v\n", decodedTrix.Header) + + // 8. Decrypt the payload + decryptedPayload, err := chachapoly.Decrypt(decodedTrix.Payload, key) + if err != nil { + log.Fatalf("Failed to decrypt: %v", err) + } + decodedTrix.Payload = decryptedPayload + fmt.Println("Payload decrypted.") + + // 9. Unpack the Trix container to reverse the sigil transformations + decodedTrix.InSigils = trixContainer.InSigils // Re-attach sigils for unpacking + fmt.Println("Unpacking payload by reversing sigils:", decodedTrix.InSigils) + if err := decodedTrix.Unpack(); err != nil { + log.Fatalf("Failed to unpack trix container: %v", err) + } + fmt.Printf("Unpacked (original) payload:\n%s\n", decodedTrix.Payload) + + // 10. Verify the result + // To properly verify, we need to compact the indented JSON before comparing + var compactedPayload bytes.Buffer + if err := json.Compact(&compactedPayload, decodedTrix.Payload); err != nil { + log.Fatalf("Failed to compact final payload for verification: %v", err) + } + + if bytes.Equal(plaintext, compactedPayload.Bytes()) { + fmt.Println("\nSuccess! The message was decrypted and unpacked correctly.") + } else { + fmt.Println("\nFailure! The final payload does not match the original.") + } + fmt.Println() +} +``` diff --git a/docs/trix_format.md b/docs/docs/trix_format.md similarity index 100% rename from docs/trix_format.md rename to docs/docs/trix_format.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 0000000..1044c35 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,12 @@ +site_name: Enchantrix +theme: + name: material +nav: + - Home: index.md + - Trix File Format: trix_format.md + - Examples: + - Trix & Sigil Chaining: trix_and_sigils.md + - Hashing: hashing.md + - Checksums: checksums.md + - RSA: rsa.md + - Standalone Sigils: standalone_sigils.md diff --git a/go.work.sum b/go.work.sum index e4b0a5d..35caaa8 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,2 +1,8 @@ -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= diff --git a/pkg/crypt/crypt.go b/pkg/crypt/crypt.go index 7bb6d09..6591fba 100644 --- a/pkg/crypt/crypt.go +++ b/pkg/crypt/crypt.go @@ -160,4 +160,4 @@ func (s *Service) EncryptRSA(publicKey, data, label []byte) ([]byte, error) { func (s *Service) DecryptRSA(privateKey, ciphertext, label []byte) ([]byte, error) { s.ensureRSA() return s.rsa.Decrypt(privateKey, ciphertext, label) -} \ No newline at end of file +} diff --git a/pkg/crypt/crypt_test.go b/pkg/crypt/crypt_test.go index b9506d4..f4be621 100644 --- a/pkg/crypt/crypt_test.go +++ b/pkg/crypt/crypt_test.go @@ -31,11 +31,11 @@ func TestHash_Bad(t *testing.T) { func TestHash_Ugly(t *testing.T) { // Test with potentially problematic inputs testCases := []string{ - "", // Empty string - " ", // Whitespace - "\x00\x01\x02\x03\x04", // Null bytes + "", // Empty string + " ", // Whitespace + "\x00\x01\x02\x03\x04", // Null bytes strings.Repeat("a", 1024*1024), // Large payload (1MB) - "こんにちは", // Unicode characters + "こんにちは", // Unicode characters } for _, tc := range testCases { @@ -108,7 +108,6 @@ func TestFletcher64_Ugly(t *testing.T) { assert.NotEqual(t, uint64(0), service.Fletcher64(" "), "Checksum of space should not be 0") } - // --- RSA Tests --- func TestRSA_Good(t *testing.T) { diff --git a/pkg/crypt/std/rsa/rsa.go b/pkg/crypt/std/rsa/rsa.go index 5a19d3a..5470ea8 100644 --- a/pkg/crypt/std/rsa/rsa.go +++ b/pkg/crypt/std/rsa/rsa.go @@ -88,4 +88,4 @@ func (s *Service) Decrypt(privateKey, ciphertext, label []byte) ([]byte, error) } return plaintext, nil -} \ No newline at end of file +} diff --git a/pkg/trix/trix.go b/pkg/trix/trix.go index e0354b5..df4c1a1 100644 --- a/pkg/trix/trix.go +++ b/pkg/trix/trix.go @@ -30,8 +30,8 @@ var ( type Trix struct { Header map[string]interface{} Payload []byte - InSigils []string `json:"-"` // Ignore Sigils during JSON marshaling - OutSigils []string `json:"-"` // Ignore Sigils during JSON marshaling + InSigils []string `json:"-"` // Ignore Sigils during JSON marshaling + OutSigils []string `json:"-"` // Ignore Sigils during JSON marshaling ChecksumAlgo crypt.HashType `json:"-"` } diff --git a/pkg/trix/trix_test.go b/pkg/trix/trix_test.go index e49d0bf..0f8870f 100644 --- a/pkg/trix/trix_test.go +++ b/pkg/trix/trix_test.go @@ -117,10 +117,10 @@ func TestTrixEncodeDecode_Ugly(t *testing.T) { t.Run("CorruptedHeaderLength", func(t *testing.T) { // Manually construct a byte slice where the header length is larger than the actual data. var buf []byte - buf = append(buf, []byte(magicNumber)...) // Magic Number - buf = append(buf, byte(trix.Version)) // Version - buf = append(buf, []byte{0, 0, 3, 232}...) // BigEndian representation of 1000 - buf = append(buf, []byte("{}")...) // A minimal valid JSON header + buf = append(buf, []byte(magicNumber)...) // Magic Number + buf = append(buf, byte(trix.Version)) // Version + buf = append(buf, []byte{0, 0, 3, 232}...) // BigEndian representation of 1000 + buf = append(buf, []byte("{}")...) // A minimal valid JSON header buf = append(buf, []byte("payload")...) _, err := trix.Decode(buf, magicNumber, nil)