From c1434a45ce054377c532746d3d325bf4af2d7cf2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:11:31 +0000 Subject: [PATCH 1/9] feat: Port crypt library from Core This commit ports the crypt library from the Core repository to the Enchantrix repository. It includes the following changes: - The project is now a Go module. - The `lthn` and `crypt` packages have been ported from the Core repository. - The PGP functionality has been commented out pending resolution of dependency issues. - The old Deno project has been moved to the `vault` directory. - The README has been updated to reflect the new project structure. --- README.md | 21 ++-- crypt.go | 148 +++++++++++++++++++++++ crypt_test.go | 48 ++++++++ go.mod | 14 +++ go.sum | 23 ++++ lthn/lthn.go | 61 ++++++++++ lthn/lthn_test.go | 18 +++ deno.json => vault/deno.json | 0 deps.ts => vault/deps.ts | 0 {lib => vault/lib}/entropy/quasi.test.ts | 0 {lib => vault/lib}/entropy/quasi.ts | 0 {lib => vault/lib}/log.ts | 0 {lib => vault/lib}/media/video/fmpeg.ts | 0 {lib => vault/lib}/parse/file.test.ts | 0 {lib => vault/lib}/parse/file.ts | 0 15 files changed, 322 insertions(+), 11 deletions(-) create mode 100644 crypt.go create mode 100644 crypt_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 lthn/lthn.go create mode 100644 lthn/lthn_test.go rename deno.json => vault/deno.json (100%) rename deps.ts => vault/deps.ts (100%) rename {lib => vault/lib}/entropy/quasi.test.ts (100%) rename {lib => vault/lib}/entropy/quasi.ts (100%) rename {lib => vault/lib}/log.ts (100%) rename {lib => vault/lib}/media/video/fmpeg.ts (100%) rename {lib => vault/lib}/parse/file.test.ts (100%) rename {lib => vault/lib}/parse/file.ts (100%) diff --git a/README.md b/README.md index ba906fd..c38f038 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # Enchantrix -The Little CryptoSuite that could. +Enchantrix is a Go-based encryption library for the Core framework, designed to provide a secure and easy-to-use framework for handling sensitive data in Web3 applications. It will feature Poly-ChaCha stream proxying and a custom `.trix` file format for encrypted data. + +## Test-Driven Development + +This project follows a strict Test-Driven Development (TDD) methodology. All new functionality must be accompanied by a comprehensive suite of tests. + +## Getting Started + +To get started with Enchantrix, you'll need to have Go installed. You can then run the tests using the following command: ```shell -deno test +go test ./... ``` - -This is used in Lethean, however; is not for DIRECT code-level public use, misuse of this software, can result in criminal charges, on you, not me. - -Do not edit, EXTEND or otherwise play with ANY variable, unless you UNDERSTAND to silicon, what you are doing. - -[Read Before Use](DISCLAIMER.md) you've been warned. - -- ffmpeg - `deno run --unstable --allow-read --allow-run https://github.com/Snider/Enchatrix/lib/media/video/fmpeg.ts` diff --git a/crypt.go b/crypt.go new file mode 100644 index 0000000..3ddd44b --- /dev/null +++ b/crypt.go @@ -0,0 +1,148 @@ +package crypt + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/binary" + "encoding/hex" + "strconv" + "strings" + + "github.com/Snider/Enchantrix/lthn" +) + +// HashType defines the supported hashing algorithms. +type HashType string + +const ( + LTHN HashType = "lthn" + SHA512 HashType = "sha512" + SHA256 HashType = "sha256" + SHA1 HashType = "sha1" + MD5 HashType = "md5" +) + +// --- Hashing --- + +// Hash computes a hash of the payload using the specified algorithm. +func Hash(lib HashType, payload string) string { + switch lib { + case LTHN: + return lthn.Hash(payload) + case SHA512: + hash := sha512.Sum512([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA1: + hash := sha1.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case MD5: + hash := md5.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA256: + fallthrough + default: + hash := sha256.Sum256([]byte(payload)) + return hex.EncodeToString(hash[:]) + } +} + +// --- Checksums --- + +// Luhn validates a number using the Luhn algorithm. +func Luhn(payload string) bool { + payload = strings.ReplaceAll(payload, " ", "") + sum := 0 + isSecond := false + for i := len(payload) - 1; i >= 0; i-- { + digit, err := strconv.Atoi(string(payload[i])) + if err != nil { + return false // Contains non-digit + } + + if isSecond { + digit = digit * 2 + if digit > 9 { + digit = digit - 9 + } + } + + sum += digit + isSecond = !isSecond + } + return sum%10 == 0 +} + +// Fletcher16 computes the Fletcher-16 checksum. +func Fletcher16(payload string) uint16 { + data := []byte(payload) + var sum1, sum2 uint16 + for _, b := range data { + sum1 = (sum1 + uint16(b)) % 255 + sum2 = (sum2 + sum1) % 255 + } + return (sum2 << 8) | sum1 +} + +// Fletcher32 computes the Fletcher-32 checksum. +func Fletcher32(payload string) uint32 { + data := []byte(payload) + if len(data)%2 != 0 { + data = append(data, 0) + } + + var sum1, sum2 uint32 + for i := 0; i < len(data); i += 2 { + val := binary.LittleEndian.Uint16(data[i : i+2]) + sum1 = (sum1 + uint32(val)) % 65535 + sum2 = (sum2 + sum1) % 65535 + } + return (sum2 << 16) | sum1 +} + +// Fletcher64 computes the Fletcher-64 checksum. +func Fletcher64(payload string) uint64 { + data := []byte(payload) + if len(data)%4 != 0 { + padding := 4 - (len(data) % 4) + data = append(data, make([]byte, padding)...) + } + + var sum1, sum2 uint64 + for i := 0; i < len(data); i += 4 { + val := binary.LittleEndian.Uint32(data[i : i+4]) + sum1 = (sum1 + uint64(val)) % 4294967295 + sum2 = (sum2 + sum1) % 4294967295 + } + return (sum2 << 32) | sum1 +} + +// --- PGP --- + +// @snider +// The PGP functions are commented out pending resolution of the dependency issues. +// +// import "io" +// import "github.com/Snider/Enchantrix/openpgp" +// +// // EncryptPGP encrypts data for a recipient, optionally signing it. +// func EncryptPGP(writer io.Writer, recipientPath, data string, signerPath, signerPassphrase *string) error { +// var buf bytes.Buffer +// err := openpgp.EncryptPGP(&buf, recipientPath, data, signerPath, signerPassphrase) +// if err != nil { +// return err +// } +// +// // Copy the encrypted data to the original writer. +// if _, err := writer.Write(buf.Bytes()); err != nil { +// return err +// } +// +// return nil +// } +// +// // DecryptPGP decrypts a PGP message, optionally verifying the signature. +// func DecryptPGP(recipientPath, message, passphrase string, signerPath *string) (string, error) { +// return openpgp.DecryptPGP(recipientPath, message, passphrase, signerPath) +// } diff --git a/crypt_test.go b/crypt_test.go new file mode 100644 index 0000000..b9ef1c4 --- /dev/null +++ b/crypt_test.go @@ -0,0 +1,48 @@ +package crypt + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + payload := "hello" + hash := Hash(LTHN, payload) + assert.NotEmpty(t, hash) +} + +func TestLuhn(t *testing.T) { + assert.True(t, Luhn("79927398713")) + assert.False(t, Luhn("79927398714")) +} + +func TestFletcher16(t *testing.T) { + assert.Equal(t, uint16(0xC8F0), Fletcher16("abcde")) + assert.Equal(t, uint16(0x2057), Fletcher16("abcdef")) + assert.Equal(t, uint16(0x0627), Fletcher16("abcdefgh")) +} + +func TestFletcher32(t *testing.T) { + expected := uint32(0xF04FC729) + actual := Fletcher32("abcde") + fmt.Printf("Fletcher32('abcde'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0x56502D2A) + actual = Fletcher32("abcdef") + fmt.Printf("Fletcher32('abcdef'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0xEBE19591) + actual = Fletcher32("abcdefgh") + fmt.Printf("Fletcher32('abcdefgh'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) +} + +func TestFletcher64(t *testing.T) { + assert.Equal(t, uint64(0xc8c6c527646362c6), Fletcher64("abcde")) + assert.Equal(t, uint64(0xc8c72b276463c8c6), Fletcher64("abcdef")) + assert.Equal(t, uint64(0x312e2b28cccac8c6), Fletcher64("abcdefgh")) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..adecc7f --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module github.com/Snider/Enchantrix + +go 1.25 + +require github.com/stretchr/testify v1.11.1 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..534aee1 --- /dev/null +++ b/go.sum @@ -0,0 +1,23 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lthn/lthn.go b/lthn/lthn.go new file mode 100644 index 0000000..1b6c97d --- /dev/null +++ b/lthn/lthn.go @@ -0,0 +1,61 @@ +package lthn + +import ( + "crypto/sha256" + "encoding/hex" +) + +// keyMap is the default character-swapping map used for the quasi-salting process. +var keyMap = map[rune]rune{ + 'o': '0', + 'l': '1', + 'e': '3', + 'a': '4', + 's': 'z', + 't': '7', + '0': 'o', + '1': 'l', + '3': 'e', + '4': 'a', + '7': 't', +} + +// SetKeyMap sets the key map for the notarisation process. +func SetKeyMap(newKeyMap map[rune]rune) { + keyMap = newKeyMap +} + +// GetKeyMap gets the current key map. +func GetKeyMap() map[rune]rune { + return keyMap +} + +// Hash creates a reproducible hash from a string. +func Hash(input string) string { + salt := createSalt(input) + hash := sha256.Sum256([]byte(input + salt)) + return hex.EncodeToString(hash[:]) +} + +// createSalt creates a quasi-salt from a string by reversing it and swapping characters. +func createSalt(input string) string { + if input == "" { + return "" + } + runes := []rune(input) + salt := make([]rune, len(runes)) + for i := 0; i < len(runes); i++ { + char := runes[len(runes)-1-i] + if replacement, ok := keyMap[char]; ok { + salt[i] = replacement + } else { + salt[i] = char + } + } + return string(salt) +} + +// Verify checks if an input string matches a given hash. +func Verifyf(input string, hash string) bool { + return Hash(input) == hash +} diff --git a/lthn/lthn_test.go b/lthn/lthn_test.go new file mode 100644 index 0000000..b0fbb7d --- /dev/null +++ b/lthn/lthn_test.go @@ -0,0 +1,18 @@ +package lthn + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + hash := Hash("hello") + assert.NotEmpty(t, hash) +} + +func TestVerify(t *testing.T) { + hash := Hash("hello") + assert.True(t, Verifyf("hello", hash)) + assert.False(t, Verifyf("world", hash)) +} diff --git a/deno.json b/vault/deno.json similarity index 100% rename from deno.json rename to vault/deno.json diff --git a/deps.ts b/vault/deps.ts similarity index 100% rename from deps.ts rename to vault/deps.ts diff --git a/lib/entropy/quasi.test.ts b/vault/lib/entropy/quasi.test.ts similarity index 100% rename from lib/entropy/quasi.test.ts rename to vault/lib/entropy/quasi.test.ts diff --git a/lib/entropy/quasi.ts b/vault/lib/entropy/quasi.ts similarity index 100% rename from lib/entropy/quasi.ts rename to vault/lib/entropy/quasi.ts diff --git a/lib/log.ts b/vault/lib/log.ts similarity index 100% rename from lib/log.ts rename to vault/lib/log.ts diff --git a/lib/media/video/fmpeg.ts b/vault/lib/media/video/fmpeg.ts similarity index 100% rename from lib/media/video/fmpeg.ts rename to vault/lib/media/video/fmpeg.ts diff --git a/lib/parse/file.test.ts b/vault/lib/parse/file.test.ts similarity index 100% rename from lib/parse/file.test.ts rename to vault/lib/parse/file.test.ts diff --git a/lib/parse/file.ts b/vault/lib/parse/file.ts similarity index 100% rename from lib/parse/file.ts rename to vault/lib/parse/file.ts From 8f469a4a31a0408d6df7a5d3ec3624076323adef Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:23:29 +0000 Subject: [PATCH 2/9] feat: Add GitHub Actions workflow This commit adds a GitHub Actions workflow to build and test the Go code on every push and pull request. It also corrects the Go version in the `go.mod` file to `1.22`. --- .github/workflows/go.yml | 24 ++++++++++++++++++++++++ go.mod | 5 +---- go.sum | 15 +-------------- 3 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..b1fe917 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,24 @@ +name: Go + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.22 + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... diff --git a/go.mod b/go.mod index adecc7f..7266149 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,11 @@ module github.com/Snider/Enchantrix -go 1.25 +go 1.22 require github.com/stretchr/testify v1.11.1 require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 534aee1..c4c1710 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,10 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From f59b6563eeb03d8e6646fe5070108dfb0b7f5449 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:47:40 +0000 Subject: [PATCH 3/9] feat: Update Go version and workflow This commit updates the Go version to 1.25 in the `go.mod` and `go.work` files. It also updates the GitHub Actions workflow to use the `go.work` file to determine the Go version and adds a step to set up `task`. --- .github/workflows/go.yml | 7 +++++-- go.mod | 6 ++++-- go.work | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 go.work diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b1fe917..d0e905b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -13,9 +13,12 @@ jobs: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.22 + go-version-file: 'go.work' + + - name: Setup Task + uses: arduino/setup-task@v1 - name: Build run: go build -v ./... diff --git a/go.mod b/go.mod index 7266149..467cf31 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,10 @@ module github.com/Snider/Enchantrix -go 1.22 +go 1.25 -require github.com/stretchr/testify v1.11.1 +require ( + github.com/stretchr/testify v1.11.1 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.work b/go.work new file mode 100644 index 0000000..4cb5c34 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.25 + +use . From d3f991edc5c8617db6f18c64d20258b3bdec10c7 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:59:39 +0000 Subject: [PATCH 4/9] fix: Remove Deno workflow This commit removes the Deno workflow file from the `.github/workflows` directory. --- .github/workflows/deno.yml | 35 ------ .github/workflows/go.yml | 27 +++++ README.md | 21 ++-- crypt.go | 148 +++++++++++++++++++++++ crypt_test.go | 48 ++++++++ go.mod | 13 ++ go.sum | 10 ++ go.work | 3 + lthn/lthn.go | 61 ++++++++++ lthn/lthn_test.go | 18 +++ deno.json => vault/deno.json | 0 deps.ts => vault/deps.ts | 0 {lib => vault/lib}/entropy/quasi.test.ts | 0 {lib => vault/lib}/entropy/quasi.ts | 0 {lib => vault/lib}/log.ts | 0 {lib => vault/lib}/media/video/fmpeg.ts | 0 {lib => vault/lib}/parse/file.test.ts | 0 {lib => vault/lib}/parse/file.ts | 0 18 files changed, 338 insertions(+), 46 deletions(-) delete mode 100644 .github/workflows/deno.yml create mode 100644 .github/workflows/go.yml create mode 100644 crypt.go create mode 100644 crypt_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 go.work create mode 100644 lthn/lthn.go create mode 100644 lthn/lthn_test.go rename deno.json => vault/deno.json (100%) rename deps.ts => vault/deps.ts (100%) rename {lib => vault/lib}/entropy/quasi.test.ts (100%) rename {lib => vault/lib}/entropy/quasi.ts (100%) rename {lib => vault/lib}/log.ts (100%) rename {lib => vault/lib}/media/video/fmpeg.ts (100%) rename {lib => vault/lib}/parse/file.test.ts (100%) rename {lib => vault/lib}/parse/file.ts (100%) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml deleted file mode 100644 index ae9cae0..0000000 --- a/.github/workflows/deno.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Deno Build - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-20.04, macos-11, windows-2019 ] - steps: - - name: Setup repo - uses: actions/checkout@v2 - - name: Setup Deno - uses: denoland/setup-deno@v1 - with: - deno-version: v1.x - # Check if the code is formatted according to Deno's default - # formatting conventions. - - if: matrix.os == 'ubuntu-20.04' - run: deno fmt --check - - # Scan the code for syntax errors and style issues. If - # you want to use a custom linter configuration you can add a configuration file with --config - - run: deno lint - - # This generates a report from the collected coverage in `deno test --coverage`. It is - # stored as a .lcov file which integrates well with services such as Codecov, Coveralls and Travis CI. - - name: Generate coverage report - if: matrix.os == 'ubuntu-20.04' - run: deno coverage --lcov cov > cov.lcov diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..d0e905b --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,27 @@ +name: Go + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: 'go.work' + + - name: Setup Task + uses: arduino/setup-task@v1 + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... diff --git a/README.md b/README.md index ba906fd..c38f038 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # Enchantrix -The Little CryptoSuite that could. +Enchantrix is a Go-based encryption library for the Core framework, designed to provide a secure and easy-to-use framework for handling sensitive data in Web3 applications. It will feature Poly-ChaCha stream proxying and a custom `.trix` file format for encrypted data. + +## Test-Driven Development + +This project follows a strict Test-Driven Development (TDD) methodology. All new functionality must be accompanied by a comprehensive suite of tests. + +## Getting Started + +To get started with Enchantrix, you'll need to have Go installed. You can then run the tests using the following command: ```shell -deno test +go test ./... ``` - -This is used in Lethean, however; is not for DIRECT code-level public use, misuse of this software, can result in criminal charges, on you, not me. - -Do not edit, EXTEND or otherwise play with ANY variable, unless you UNDERSTAND to silicon, what you are doing. - -[Read Before Use](DISCLAIMER.md) you've been warned. - -- ffmpeg - `deno run --unstable --allow-read --allow-run https://github.com/Snider/Enchatrix/lib/media/video/fmpeg.ts` diff --git a/crypt.go b/crypt.go new file mode 100644 index 0000000..3ddd44b --- /dev/null +++ b/crypt.go @@ -0,0 +1,148 @@ +package crypt + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/binary" + "encoding/hex" + "strconv" + "strings" + + "github.com/Snider/Enchantrix/lthn" +) + +// HashType defines the supported hashing algorithms. +type HashType string + +const ( + LTHN HashType = "lthn" + SHA512 HashType = "sha512" + SHA256 HashType = "sha256" + SHA1 HashType = "sha1" + MD5 HashType = "md5" +) + +// --- Hashing --- + +// Hash computes a hash of the payload using the specified algorithm. +func Hash(lib HashType, payload string) string { + switch lib { + case LTHN: + return lthn.Hash(payload) + case SHA512: + hash := sha512.Sum512([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA1: + hash := sha1.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case MD5: + hash := md5.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA256: + fallthrough + default: + hash := sha256.Sum256([]byte(payload)) + return hex.EncodeToString(hash[:]) + } +} + +// --- Checksums --- + +// Luhn validates a number using the Luhn algorithm. +func Luhn(payload string) bool { + payload = strings.ReplaceAll(payload, " ", "") + sum := 0 + isSecond := false + for i := len(payload) - 1; i >= 0; i-- { + digit, err := strconv.Atoi(string(payload[i])) + if err != nil { + return false // Contains non-digit + } + + if isSecond { + digit = digit * 2 + if digit > 9 { + digit = digit - 9 + } + } + + sum += digit + isSecond = !isSecond + } + return sum%10 == 0 +} + +// Fletcher16 computes the Fletcher-16 checksum. +func Fletcher16(payload string) uint16 { + data := []byte(payload) + var sum1, sum2 uint16 + for _, b := range data { + sum1 = (sum1 + uint16(b)) % 255 + sum2 = (sum2 + sum1) % 255 + } + return (sum2 << 8) | sum1 +} + +// Fletcher32 computes the Fletcher-32 checksum. +func Fletcher32(payload string) uint32 { + data := []byte(payload) + if len(data)%2 != 0 { + data = append(data, 0) + } + + var sum1, sum2 uint32 + for i := 0; i < len(data); i += 2 { + val := binary.LittleEndian.Uint16(data[i : i+2]) + sum1 = (sum1 + uint32(val)) % 65535 + sum2 = (sum2 + sum1) % 65535 + } + return (sum2 << 16) | sum1 +} + +// Fletcher64 computes the Fletcher-64 checksum. +func Fletcher64(payload string) uint64 { + data := []byte(payload) + if len(data)%4 != 0 { + padding := 4 - (len(data) % 4) + data = append(data, make([]byte, padding)...) + } + + var sum1, sum2 uint64 + for i := 0; i < len(data); i += 4 { + val := binary.LittleEndian.Uint32(data[i : i+4]) + sum1 = (sum1 + uint64(val)) % 4294967295 + sum2 = (sum2 + sum1) % 4294967295 + } + return (sum2 << 32) | sum1 +} + +// --- PGP --- + +// @snider +// The PGP functions are commented out pending resolution of the dependency issues. +// +// import "io" +// import "github.com/Snider/Enchantrix/openpgp" +// +// // EncryptPGP encrypts data for a recipient, optionally signing it. +// func EncryptPGP(writer io.Writer, recipientPath, data string, signerPath, signerPassphrase *string) error { +// var buf bytes.Buffer +// err := openpgp.EncryptPGP(&buf, recipientPath, data, signerPath, signerPassphrase) +// if err != nil { +// return err +// } +// +// // Copy the encrypted data to the original writer. +// if _, err := writer.Write(buf.Bytes()); err != nil { +// return err +// } +// +// return nil +// } +// +// // DecryptPGP decrypts a PGP message, optionally verifying the signature. +// func DecryptPGP(recipientPath, message, passphrase string, signerPath *string) (string, error) { +// return openpgp.DecryptPGP(recipientPath, message, passphrase, signerPath) +// } diff --git a/crypt_test.go b/crypt_test.go new file mode 100644 index 0000000..b9ef1c4 --- /dev/null +++ b/crypt_test.go @@ -0,0 +1,48 @@ +package crypt + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + payload := "hello" + hash := Hash(LTHN, payload) + assert.NotEmpty(t, hash) +} + +func TestLuhn(t *testing.T) { + assert.True(t, Luhn("79927398713")) + assert.False(t, Luhn("79927398714")) +} + +func TestFletcher16(t *testing.T) { + assert.Equal(t, uint16(0xC8F0), Fletcher16("abcde")) + assert.Equal(t, uint16(0x2057), Fletcher16("abcdef")) + assert.Equal(t, uint16(0x0627), Fletcher16("abcdefgh")) +} + +func TestFletcher32(t *testing.T) { + expected := uint32(0xF04FC729) + actual := Fletcher32("abcde") + fmt.Printf("Fletcher32('abcde'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0x56502D2A) + actual = Fletcher32("abcdef") + fmt.Printf("Fletcher32('abcdef'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0xEBE19591) + actual = Fletcher32("abcdefgh") + fmt.Printf("Fletcher32('abcdefgh'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) +} + +func TestFletcher64(t *testing.T) { + assert.Equal(t, uint64(0xc8c6c527646362c6), Fletcher64("abcde")) + assert.Equal(t, uint64(0xc8c72b276463c8c6), Fletcher64("abcdef")) + assert.Equal(t, uint64(0x312e2b28cccac8c6), Fletcher64("abcdefgh")) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..467cf31 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/Snider/Enchantrix + +go 1.25 + +require ( + github.com/stretchr/testify v1.11.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c4c1710 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +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/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= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.work b/go.work new file mode 100644 index 0000000..4cb5c34 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.25 + +use . diff --git a/lthn/lthn.go b/lthn/lthn.go new file mode 100644 index 0000000..1b6c97d --- /dev/null +++ b/lthn/lthn.go @@ -0,0 +1,61 @@ +package lthn + +import ( + "crypto/sha256" + "encoding/hex" +) + +// keyMap is the default character-swapping map used for the quasi-salting process. +var keyMap = map[rune]rune{ + 'o': '0', + 'l': '1', + 'e': '3', + 'a': '4', + 's': 'z', + 't': '7', + '0': 'o', + '1': 'l', + '3': 'e', + '4': 'a', + '7': 't', +} + +// SetKeyMap sets the key map for the notarisation process. +func SetKeyMap(newKeyMap map[rune]rune) { + keyMap = newKeyMap +} + +// GetKeyMap gets the current key map. +func GetKeyMap() map[rune]rune { + return keyMap +} + +// Hash creates a reproducible hash from a string. +func Hash(input string) string { + salt := createSalt(input) + hash := sha256.Sum256([]byte(input + salt)) + return hex.EncodeToString(hash[:]) +} + +// createSalt creates a quasi-salt from a string by reversing it and swapping characters. +func createSalt(input string) string { + if input == "" { + return "" + } + runes := []rune(input) + salt := make([]rune, len(runes)) + for i := 0; i < len(runes); i++ { + char := runes[len(runes)-1-i] + if replacement, ok := keyMap[char]; ok { + salt[i] = replacement + } else { + salt[i] = char + } + } + return string(salt) +} + +// Verify checks if an input string matches a given hash. +func Verifyf(input string, hash string) bool { + return Hash(input) == hash +} diff --git a/lthn/lthn_test.go b/lthn/lthn_test.go new file mode 100644 index 0000000..b0fbb7d --- /dev/null +++ b/lthn/lthn_test.go @@ -0,0 +1,18 @@ +package lthn + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + hash := Hash("hello") + assert.NotEmpty(t, hash) +} + +func TestVerify(t *testing.T) { + hash := Hash("hello") + assert.True(t, Verifyf("hello", hash)) + assert.False(t, Verifyf("world", hash)) +} diff --git a/deno.json b/vault/deno.json similarity index 100% rename from deno.json rename to vault/deno.json diff --git a/deps.ts b/vault/deps.ts similarity index 100% rename from deps.ts rename to vault/deps.ts diff --git a/lib/entropy/quasi.test.ts b/vault/lib/entropy/quasi.test.ts similarity index 100% rename from lib/entropy/quasi.test.ts rename to vault/lib/entropy/quasi.test.ts diff --git a/lib/entropy/quasi.ts b/vault/lib/entropy/quasi.ts similarity index 100% rename from lib/entropy/quasi.ts rename to vault/lib/entropy/quasi.ts diff --git a/lib/log.ts b/vault/lib/log.ts similarity index 100% rename from lib/log.ts rename to vault/lib/log.ts diff --git a/lib/media/video/fmpeg.ts b/vault/lib/media/video/fmpeg.ts similarity index 100% rename from lib/media/video/fmpeg.ts rename to vault/lib/media/video/fmpeg.ts diff --git a/lib/parse/file.test.ts b/vault/lib/parse/file.test.ts similarity index 100% rename from lib/parse/file.test.ts rename to vault/lib/parse/file.test.ts diff --git a/lib/parse/file.ts b/vault/lib/parse/file.ts similarity index 100% rename from lib/parse/file.ts rename to vault/lib/parse/file.ts From 085a8b3ba776df9ac65188b4c7a650b26ac190dc Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:21:38 +0000 Subject: [PATCH 5/9] feat: Add test coverage to workflow This commit adds a step to the GitHub Actions workflow to generate and upload a test coverage report. It also renames the `Verifyf` function in the `lthn` package to `Verify` to match the Core repository. --- .github/workflows/deno.yml | 35 ------ .github/workflows/go.yml | 33 +++++ README.md | 21 ++-- crypt.go | 148 +++++++++++++++++++++++ crypt_test.go | 48 ++++++++ go.mod | 13 ++ go.sum | 10 ++ go.work | 3 + lthn/lthn.go | 61 ++++++++++ lthn/lthn_test.go | 18 +++ deno.json => vault/deno.json | 0 deps.ts => vault/deps.ts | 0 {lib => vault/lib}/entropy/quasi.test.ts | 0 {lib => vault/lib}/entropy/quasi.ts | 0 {lib => vault/lib}/log.ts | 0 {lib => vault/lib}/media/video/fmpeg.ts | 0 {lib => vault/lib}/parse/file.test.ts | 0 {lib => vault/lib}/parse/file.ts | 0 18 files changed, 344 insertions(+), 46 deletions(-) delete mode 100644 .github/workflows/deno.yml create mode 100644 .github/workflows/go.yml create mode 100644 crypt.go create mode 100644 crypt_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 go.work create mode 100644 lthn/lthn.go create mode 100644 lthn/lthn_test.go rename deno.json => vault/deno.json (100%) rename deps.ts => vault/deps.ts (100%) rename {lib => vault/lib}/entropy/quasi.test.ts (100%) rename {lib => vault/lib}/entropy/quasi.ts (100%) rename {lib => vault/lib}/log.ts (100%) rename {lib => vault/lib}/media/video/fmpeg.ts (100%) rename {lib => vault/lib}/parse/file.test.ts (100%) rename {lib => vault/lib}/parse/file.ts (100%) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml deleted file mode 100644 index ae9cae0..0000000 --- a/.github/workflows/deno.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Deno Build - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-20.04, macos-11, windows-2019 ] - steps: - - name: Setup repo - uses: actions/checkout@v2 - - name: Setup Deno - uses: denoland/setup-deno@v1 - with: - deno-version: v1.x - # Check if the code is formatted according to Deno's default - # formatting conventions. - - if: matrix.os == 'ubuntu-20.04' - run: deno fmt --check - - # Scan the code for syntax errors and style issues. If - # you want to use a custom linter configuration you can add a configuration file with --config - - run: deno lint - - # This generates a report from the collected coverage in `deno test --coverage`. It is - # stored as a .lcov file which integrates well with services such as Codecov, Coveralls and Travis CI. - - name: Generate coverage report - if: matrix.os == 'ubuntu-20.04' - run: deno coverage --lcov cov > cov.lcov diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..86c90d0 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,33 @@ +name: Go + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: 'go.work' + + - name: Setup Task + uses: arduino/setup-task@v1 + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v -coverprofile=coverage.out ./... + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.out diff --git a/README.md b/README.md index ba906fd..c38f038 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # Enchantrix -The Little CryptoSuite that could. +Enchantrix is a Go-based encryption library for the Core framework, designed to provide a secure and easy-to-use framework for handling sensitive data in Web3 applications. It will feature Poly-ChaCha stream proxying and a custom `.trix` file format for encrypted data. + +## Test-Driven Development + +This project follows a strict Test-Driven Development (TDD) methodology. All new functionality must be accompanied by a comprehensive suite of tests. + +## Getting Started + +To get started with Enchantrix, you'll need to have Go installed. You can then run the tests using the following command: ```shell -deno test +go test ./... ``` - -This is used in Lethean, however; is not for DIRECT code-level public use, misuse of this software, can result in criminal charges, on you, not me. - -Do not edit, EXTEND or otherwise play with ANY variable, unless you UNDERSTAND to silicon, what you are doing. - -[Read Before Use](DISCLAIMER.md) you've been warned. - -- ffmpeg - `deno run --unstable --allow-read --allow-run https://github.com/Snider/Enchatrix/lib/media/video/fmpeg.ts` diff --git a/crypt.go b/crypt.go new file mode 100644 index 0000000..3ddd44b --- /dev/null +++ b/crypt.go @@ -0,0 +1,148 @@ +package crypt + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/binary" + "encoding/hex" + "strconv" + "strings" + + "github.com/Snider/Enchantrix/lthn" +) + +// HashType defines the supported hashing algorithms. +type HashType string + +const ( + LTHN HashType = "lthn" + SHA512 HashType = "sha512" + SHA256 HashType = "sha256" + SHA1 HashType = "sha1" + MD5 HashType = "md5" +) + +// --- Hashing --- + +// Hash computes a hash of the payload using the specified algorithm. +func Hash(lib HashType, payload string) string { + switch lib { + case LTHN: + return lthn.Hash(payload) + case SHA512: + hash := sha512.Sum512([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA1: + hash := sha1.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case MD5: + hash := md5.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA256: + fallthrough + default: + hash := sha256.Sum256([]byte(payload)) + return hex.EncodeToString(hash[:]) + } +} + +// --- Checksums --- + +// Luhn validates a number using the Luhn algorithm. +func Luhn(payload string) bool { + payload = strings.ReplaceAll(payload, " ", "") + sum := 0 + isSecond := false + for i := len(payload) - 1; i >= 0; i-- { + digit, err := strconv.Atoi(string(payload[i])) + if err != nil { + return false // Contains non-digit + } + + if isSecond { + digit = digit * 2 + if digit > 9 { + digit = digit - 9 + } + } + + sum += digit + isSecond = !isSecond + } + return sum%10 == 0 +} + +// Fletcher16 computes the Fletcher-16 checksum. +func Fletcher16(payload string) uint16 { + data := []byte(payload) + var sum1, sum2 uint16 + for _, b := range data { + sum1 = (sum1 + uint16(b)) % 255 + sum2 = (sum2 + sum1) % 255 + } + return (sum2 << 8) | sum1 +} + +// Fletcher32 computes the Fletcher-32 checksum. +func Fletcher32(payload string) uint32 { + data := []byte(payload) + if len(data)%2 != 0 { + data = append(data, 0) + } + + var sum1, sum2 uint32 + for i := 0; i < len(data); i += 2 { + val := binary.LittleEndian.Uint16(data[i : i+2]) + sum1 = (sum1 + uint32(val)) % 65535 + sum2 = (sum2 + sum1) % 65535 + } + return (sum2 << 16) | sum1 +} + +// Fletcher64 computes the Fletcher-64 checksum. +func Fletcher64(payload string) uint64 { + data := []byte(payload) + if len(data)%4 != 0 { + padding := 4 - (len(data) % 4) + data = append(data, make([]byte, padding)...) + } + + var sum1, sum2 uint64 + for i := 0; i < len(data); i += 4 { + val := binary.LittleEndian.Uint32(data[i : i+4]) + sum1 = (sum1 + uint64(val)) % 4294967295 + sum2 = (sum2 + sum1) % 4294967295 + } + return (sum2 << 32) | sum1 +} + +// --- PGP --- + +// @snider +// The PGP functions are commented out pending resolution of the dependency issues. +// +// import "io" +// import "github.com/Snider/Enchantrix/openpgp" +// +// // EncryptPGP encrypts data for a recipient, optionally signing it. +// func EncryptPGP(writer io.Writer, recipientPath, data string, signerPath, signerPassphrase *string) error { +// var buf bytes.Buffer +// err := openpgp.EncryptPGP(&buf, recipientPath, data, signerPath, signerPassphrase) +// if err != nil { +// return err +// } +// +// // Copy the encrypted data to the original writer. +// if _, err := writer.Write(buf.Bytes()); err != nil { +// return err +// } +// +// return nil +// } +// +// // DecryptPGP decrypts a PGP message, optionally verifying the signature. +// func DecryptPGP(recipientPath, message, passphrase string, signerPath *string) (string, error) { +// return openpgp.DecryptPGP(recipientPath, message, passphrase, signerPath) +// } diff --git a/crypt_test.go b/crypt_test.go new file mode 100644 index 0000000..b9ef1c4 --- /dev/null +++ b/crypt_test.go @@ -0,0 +1,48 @@ +package crypt + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + payload := "hello" + hash := Hash(LTHN, payload) + assert.NotEmpty(t, hash) +} + +func TestLuhn(t *testing.T) { + assert.True(t, Luhn("79927398713")) + assert.False(t, Luhn("79927398714")) +} + +func TestFletcher16(t *testing.T) { + assert.Equal(t, uint16(0xC8F0), Fletcher16("abcde")) + assert.Equal(t, uint16(0x2057), Fletcher16("abcdef")) + assert.Equal(t, uint16(0x0627), Fletcher16("abcdefgh")) +} + +func TestFletcher32(t *testing.T) { + expected := uint32(0xF04FC729) + actual := Fletcher32("abcde") + fmt.Printf("Fletcher32('abcde'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0x56502D2A) + actual = Fletcher32("abcdef") + fmt.Printf("Fletcher32('abcdef'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0xEBE19591) + actual = Fletcher32("abcdefgh") + fmt.Printf("Fletcher32('abcdefgh'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) +} + +func TestFletcher64(t *testing.T) { + assert.Equal(t, uint64(0xc8c6c527646362c6), Fletcher64("abcde")) + assert.Equal(t, uint64(0xc8c72b276463c8c6), Fletcher64("abcdef")) + assert.Equal(t, uint64(0x312e2b28cccac8c6), Fletcher64("abcdefgh")) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..467cf31 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module github.com/Snider/Enchantrix + +go 1.25 + +require ( + github.com/stretchr/testify v1.11.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c4c1710 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +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/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= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.work b/go.work new file mode 100644 index 0000000..4cb5c34 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.25 + +use . diff --git a/lthn/lthn.go b/lthn/lthn.go new file mode 100644 index 0000000..dec885e --- /dev/null +++ b/lthn/lthn.go @@ -0,0 +1,61 @@ +package lthn + +import ( + "crypto/sha256" + "encoding/hex" +) + +// keyMap is the default character-swapping map used for the quasi-salting process. +var keyMap = map[rune]rune{ + 'o': '0', + 'l': '1', + 'e': '3', + 'a': '4', + 's': 'z', + 't': '7', + '0': 'o', + '1': 'l', + '3': 'e', + '4': 'a', + '7': 't', +} + +// SetKeyMap sets the key map for the notarisation process. +func SetKeyMap(newKeyMap map[rune]rune) { + keyMap = newKeyMap +} + +// GetKeyMap gets the current key map. +func GetKeyMap() map[rune]rune { + return keyMap +} + +// Hash creates a reproducible hash from a string. +func Hash(input string) string { + salt := createSalt(input) + hash := sha256.Sum256([]byte(input + salt)) + return hex.EncodeToString(hash[:]) +} + +// createSalt creates a quasi-salt from a string by reversing it and swapping characters. +func createSalt(input string) string { + if input == "" { + return "" + } + runes := []rune(input) + salt := make([]rune, len(runes)) + for i := 0; i < len(runes); i++ { + char := runes[len(runes)-1-i] + if replacement, ok := keyMap[char]; ok { + salt[i] = replacement + } else { + salt[i] = char + } + } + return string(salt) +} + +// Verify checks if an input string matches a given hash. +func Verify(input string, hash string) bool { + return Hash(input) == hash +} diff --git a/lthn/lthn_test.go b/lthn/lthn_test.go new file mode 100644 index 0000000..3a04a1c --- /dev/null +++ b/lthn/lthn_test.go @@ -0,0 +1,18 @@ +package lthn + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + hash := Hash("hello") + assert.NotEmpty(t, hash) +} + +func TestVerify(t *testing.T) { + hash := Hash("hello") + assert.True(t, Verify("hello", hash)) + assert.False(t, Verify("world", hash)) +} diff --git a/deno.json b/vault/deno.json similarity index 100% rename from deno.json rename to vault/deno.json diff --git a/deps.ts b/vault/deps.ts similarity index 100% rename from deps.ts rename to vault/deps.ts diff --git a/lib/entropy/quasi.test.ts b/vault/lib/entropy/quasi.test.ts similarity index 100% rename from lib/entropy/quasi.test.ts rename to vault/lib/entropy/quasi.test.ts diff --git a/lib/entropy/quasi.ts b/vault/lib/entropy/quasi.ts similarity index 100% rename from lib/entropy/quasi.ts rename to vault/lib/entropy/quasi.ts diff --git a/lib/log.ts b/vault/lib/log.ts similarity index 100% rename from lib/log.ts rename to vault/lib/log.ts diff --git a/lib/media/video/fmpeg.ts b/vault/lib/media/video/fmpeg.ts similarity index 100% rename from lib/media/video/fmpeg.ts rename to vault/lib/media/video/fmpeg.ts diff --git a/lib/parse/file.test.ts b/vault/lib/parse/file.test.ts similarity index 100% rename from lib/parse/file.test.ts rename to vault/lib/parse/file.test.ts diff --git a/lib/parse/file.ts b/vault/lib/parse/file.ts similarity index 100% rename from lib/parse/file.ts rename to vault/lib/parse/file.ts From 63721e9811b677a43b941a92075f9267abf35c5c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 19:01:08 +0000 Subject: [PATCH 6/9] feat: Add ChaCha20-Poly1305 encryption This commit introduces ChaCha20-Poly1305 encryption functionality. It includes the following changes: - A new `chachapoly` package has been added. - The `chachapoly` package contains `Encrypt` and `Decrypt` functions. - The functionality is fully tested. - An `.ideas` directory has been created. --- .github/workflows/deno.yml | 35 ------ .github/workflows/go.yml | 33 +++++ README.md | 21 ++-- chachapoly/chachapoly.go | 40 ++++++ chachapoly/chachapoly_test.go | 23 ++++ crypt.go | 148 +++++++++++++++++++++++ crypt_test.go | 48 ++++++++ go.mod | 15 +++ go.sum | 14 +++ go.work | 3 + lthn/lthn.go | 61 ++++++++++ lthn/lthn_test.go | 18 +++ deno.json => vault/deno.json | 0 deps.ts => vault/deps.ts | 0 {lib => vault/lib}/entropy/quasi.test.ts | 0 {lib => vault/lib}/entropy/quasi.ts | 0 {lib => vault/lib}/log.ts | 0 {lib => vault/lib}/media/video/fmpeg.ts | 0 {lib => vault/lib}/parse/file.test.ts | 0 {lib => vault/lib}/parse/file.ts | 0 20 files changed, 413 insertions(+), 46 deletions(-) delete mode 100644 .github/workflows/deno.yml create mode 100644 .github/workflows/go.yml create mode 100644 chachapoly/chachapoly.go create mode 100644 chachapoly/chachapoly_test.go create mode 100644 crypt.go create mode 100644 crypt_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 go.work create mode 100644 lthn/lthn.go create mode 100644 lthn/lthn_test.go rename deno.json => vault/deno.json (100%) rename deps.ts => vault/deps.ts (100%) rename {lib => vault/lib}/entropy/quasi.test.ts (100%) rename {lib => vault/lib}/entropy/quasi.ts (100%) rename {lib => vault/lib}/log.ts (100%) rename {lib => vault/lib}/media/video/fmpeg.ts (100%) rename {lib => vault/lib}/parse/file.test.ts (100%) rename {lib => vault/lib}/parse/file.ts (100%) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml deleted file mode 100644 index ae9cae0..0000000 --- a/.github/workflows/deno.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Deno Build - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-20.04, macos-11, windows-2019 ] - steps: - - name: Setup repo - uses: actions/checkout@v2 - - name: Setup Deno - uses: denoland/setup-deno@v1 - with: - deno-version: v1.x - # Check if the code is formatted according to Deno's default - # formatting conventions. - - if: matrix.os == 'ubuntu-20.04' - run: deno fmt --check - - # Scan the code for syntax errors and style issues. If - # you want to use a custom linter configuration you can add a configuration file with --config - - run: deno lint - - # This generates a report from the collected coverage in `deno test --coverage`. It is - # stored as a .lcov file which integrates well with services such as Codecov, Coveralls and Travis CI. - - name: Generate coverage report - if: matrix.os == 'ubuntu-20.04' - run: deno coverage --lcov cov > cov.lcov diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..86c90d0 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,33 @@ +name: Go + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: 'go.work' + + - name: Setup Task + uses: arduino/setup-task@v1 + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v -coverprofile=coverage.out ./... + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.out diff --git a/README.md b/README.md index ba906fd..c38f038 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # Enchantrix -The Little CryptoSuite that could. +Enchantrix is a Go-based encryption library for the Core framework, designed to provide a secure and easy-to-use framework for handling sensitive data in Web3 applications. It will feature Poly-ChaCha stream proxying and a custom `.trix` file format for encrypted data. + +## Test-Driven Development + +This project follows a strict Test-Driven Development (TDD) methodology. All new functionality must be accompanied by a comprehensive suite of tests. + +## Getting Started + +To get started with Enchantrix, you'll need to have Go installed. You can then run the tests using the following command: ```shell -deno test +go test ./... ``` - -This is used in Lethean, however; is not for DIRECT code-level public use, misuse of this software, can result in criminal charges, on you, not me. - -Do not edit, EXTEND or otherwise play with ANY variable, unless you UNDERSTAND to silicon, what you are doing. - -[Read Before Use](DISCLAIMER.md) you've been warned. - -- ffmpeg - `deno run --unstable --allow-read --allow-run https://github.com/Snider/Enchatrix/lib/media/video/fmpeg.ts` diff --git a/chachapoly/chachapoly.go b/chachapoly/chachapoly.go new file mode 100644 index 0000000..f3086b8 --- /dev/null +++ b/chachapoly/chachapoly.go @@ -0,0 +1,40 @@ +package chachapoly + +import ( + "crypto/rand" + "fmt" + "io" + + "golang.org/x/crypto/chacha20poly1305" +) + +// Encrypt encrypts data using ChaCha20-Poly1305. +func Encrypt(plaintext []byte, key []byte) ([]byte, error) { + aead, err := chacha20poly1305.NewX(key) + if err != nil { + return nil, err + } + + nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(plaintext)+aead.Overhead()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + return aead.Seal(nonce, nonce, plaintext, nil), nil +} + +// Decrypt decrypts data using ChaCha20-Poly1305. +func Decrypt(ciphertext []byte, key []byte) ([]byte, error) { + aead, err := chacha20poly1305.NewX(key) + if err != nil { + return nil, err + } + + if len(ciphertext) < aead.NonceSize() { + return nil, fmt.Errorf("ciphertext too short") + } + + nonce, ciphertext := ciphertext[:aead.NonceSize()], ciphertext[aead.NonceSize():] + + return aead.Open(nil, nonce, ciphertext, nil) +} diff --git a/chachapoly/chachapoly_test.go b/chachapoly/chachapoly_test.go new file mode 100644 index 0000000..fb5ef73 --- /dev/null +++ b/chachapoly/chachapoly_test.go @@ -0,0 +1,23 @@ +package chachapoly + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEncryptDecrypt(t *testing.T) { + key := make([]byte, 32) + for i := range key { + key[i] = 1 + } + + plaintext := []byte("Hello, world!") + ciphertext, err := Encrypt(plaintext, key) + assert.NoError(t, err) + + decrypted, err := Decrypt(ciphertext, key) + assert.NoError(t, err) + + assert.Equal(t, plaintext, decrypted) +} diff --git a/crypt.go b/crypt.go new file mode 100644 index 0000000..3ddd44b --- /dev/null +++ b/crypt.go @@ -0,0 +1,148 @@ +package crypt + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/binary" + "encoding/hex" + "strconv" + "strings" + + "github.com/Snider/Enchantrix/lthn" +) + +// HashType defines the supported hashing algorithms. +type HashType string + +const ( + LTHN HashType = "lthn" + SHA512 HashType = "sha512" + SHA256 HashType = "sha256" + SHA1 HashType = "sha1" + MD5 HashType = "md5" +) + +// --- Hashing --- + +// Hash computes a hash of the payload using the specified algorithm. +func Hash(lib HashType, payload string) string { + switch lib { + case LTHN: + return lthn.Hash(payload) + case SHA512: + hash := sha512.Sum512([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA1: + hash := sha1.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case MD5: + hash := md5.Sum([]byte(payload)) + return hex.EncodeToString(hash[:]) + case SHA256: + fallthrough + default: + hash := sha256.Sum256([]byte(payload)) + return hex.EncodeToString(hash[:]) + } +} + +// --- Checksums --- + +// Luhn validates a number using the Luhn algorithm. +func Luhn(payload string) bool { + payload = strings.ReplaceAll(payload, " ", "") + sum := 0 + isSecond := false + for i := len(payload) - 1; i >= 0; i-- { + digit, err := strconv.Atoi(string(payload[i])) + if err != nil { + return false // Contains non-digit + } + + if isSecond { + digit = digit * 2 + if digit > 9 { + digit = digit - 9 + } + } + + sum += digit + isSecond = !isSecond + } + return sum%10 == 0 +} + +// Fletcher16 computes the Fletcher-16 checksum. +func Fletcher16(payload string) uint16 { + data := []byte(payload) + var sum1, sum2 uint16 + for _, b := range data { + sum1 = (sum1 + uint16(b)) % 255 + sum2 = (sum2 + sum1) % 255 + } + return (sum2 << 8) | sum1 +} + +// Fletcher32 computes the Fletcher-32 checksum. +func Fletcher32(payload string) uint32 { + data := []byte(payload) + if len(data)%2 != 0 { + data = append(data, 0) + } + + var sum1, sum2 uint32 + for i := 0; i < len(data); i += 2 { + val := binary.LittleEndian.Uint16(data[i : i+2]) + sum1 = (sum1 + uint32(val)) % 65535 + sum2 = (sum2 + sum1) % 65535 + } + return (sum2 << 16) | sum1 +} + +// Fletcher64 computes the Fletcher-64 checksum. +func Fletcher64(payload string) uint64 { + data := []byte(payload) + if len(data)%4 != 0 { + padding := 4 - (len(data) % 4) + data = append(data, make([]byte, padding)...) + } + + var sum1, sum2 uint64 + for i := 0; i < len(data); i += 4 { + val := binary.LittleEndian.Uint32(data[i : i+4]) + sum1 = (sum1 + uint64(val)) % 4294967295 + sum2 = (sum2 + sum1) % 4294967295 + } + return (sum2 << 32) | sum1 +} + +// --- PGP --- + +// @snider +// The PGP functions are commented out pending resolution of the dependency issues. +// +// import "io" +// import "github.com/Snider/Enchantrix/openpgp" +// +// // EncryptPGP encrypts data for a recipient, optionally signing it. +// func EncryptPGP(writer io.Writer, recipientPath, data string, signerPath, signerPassphrase *string) error { +// var buf bytes.Buffer +// err := openpgp.EncryptPGP(&buf, recipientPath, data, signerPath, signerPassphrase) +// if err != nil { +// return err +// } +// +// // Copy the encrypted data to the original writer. +// if _, err := writer.Write(buf.Bytes()); err != nil { +// return err +// } +// +// return nil +// } +// +// // DecryptPGP decrypts a PGP message, optionally verifying the signature. +// func DecryptPGP(recipientPath, message, passphrase string, signerPath *string) (string, error) { +// return openpgp.DecryptPGP(recipientPath, message, passphrase, signerPath) +// } diff --git a/crypt_test.go b/crypt_test.go new file mode 100644 index 0000000..b9ef1c4 --- /dev/null +++ b/crypt_test.go @@ -0,0 +1,48 @@ +package crypt + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + payload := "hello" + hash := Hash(LTHN, payload) + assert.NotEmpty(t, hash) +} + +func TestLuhn(t *testing.T) { + assert.True(t, Luhn("79927398713")) + assert.False(t, Luhn("79927398714")) +} + +func TestFletcher16(t *testing.T) { + assert.Equal(t, uint16(0xC8F0), Fletcher16("abcde")) + assert.Equal(t, uint16(0x2057), Fletcher16("abcdef")) + assert.Equal(t, uint16(0x0627), Fletcher16("abcdefgh")) +} + +func TestFletcher32(t *testing.T) { + expected := uint32(0xF04FC729) + actual := Fletcher32("abcde") + fmt.Printf("Fletcher32('abcde'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0x56502D2A) + actual = Fletcher32("abcdef") + fmt.Printf("Fletcher32('abcdef'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) + + expected = uint32(0xEBE19591) + actual = Fletcher32("abcdefgh") + fmt.Printf("Fletcher32('abcdefgh'): expected: %x, actual: %x\n", expected, actual) + assert.Equal(t, expected, actual) +} + +func TestFletcher64(t *testing.T) { + assert.Equal(t, uint64(0xc8c6c527646362c6), Fletcher64("abcde")) + assert.Equal(t, uint64(0xc8c72b276463c8c6), Fletcher64("abcdef")) + assert.Equal(t, uint64(0x312e2b28cccac8c6), Fletcher64("abcdefgh")) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..240d180 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/Snider/Enchantrix + +go 1.25 + +require ( + github.com/stretchr/testify v1.11.1 + golang.org/x/crypto v0.43.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.37.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cd3b418 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +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/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= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.work b/go.work new file mode 100644 index 0000000..4cb5c34 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.25 + +use . diff --git a/lthn/lthn.go b/lthn/lthn.go new file mode 100644 index 0000000..dec885e --- /dev/null +++ b/lthn/lthn.go @@ -0,0 +1,61 @@ +package lthn + +import ( + "crypto/sha256" + "encoding/hex" +) + +// keyMap is the default character-swapping map used for the quasi-salting process. +var keyMap = map[rune]rune{ + 'o': '0', + 'l': '1', + 'e': '3', + 'a': '4', + 's': 'z', + 't': '7', + '0': 'o', + '1': 'l', + '3': 'e', + '4': 'a', + '7': 't', +} + +// SetKeyMap sets the key map for the notarisation process. +func SetKeyMap(newKeyMap map[rune]rune) { + keyMap = newKeyMap +} + +// GetKeyMap gets the current key map. +func GetKeyMap() map[rune]rune { + return keyMap +} + +// Hash creates a reproducible hash from a string. +func Hash(input string) string { + salt := createSalt(input) + hash := sha256.Sum256([]byte(input + salt)) + return hex.EncodeToString(hash[:]) +} + +// createSalt creates a quasi-salt from a string by reversing it and swapping characters. +func createSalt(input string) string { + if input == "" { + return "" + } + runes := []rune(input) + salt := make([]rune, len(runes)) + for i := 0; i < len(runes); i++ { + char := runes[len(runes)-1-i] + if replacement, ok := keyMap[char]; ok { + salt[i] = replacement + } else { + salt[i] = char + } + } + return string(salt) +} + +// Verify checks if an input string matches a given hash. +func Verify(input string, hash string) bool { + return Hash(input) == hash +} diff --git a/lthn/lthn_test.go b/lthn/lthn_test.go new file mode 100644 index 0000000..3a04a1c --- /dev/null +++ b/lthn/lthn_test.go @@ -0,0 +1,18 @@ +package lthn + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHash(t *testing.T) { + hash := Hash("hello") + assert.NotEmpty(t, hash) +} + +func TestVerify(t *testing.T) { + hash := Hash("hello") + assert.True(t, Verify("hello", hash)) + assert.False(t, Verify("world", hash)) +} diff --git a/deno.json b/vault/deno.json similarity index 100% rename from deno.json rename to vault/deno.json diff --git a/deps.ts b/vault/deps.ts similarity index 100% rename from deps.ts rename to vault/deps.ts diff --git a/lib/entropy/quasi.test.ts b/vault/lib/entropy/quasi.test.ts similarity index 100% rename from lib/entropy/quasi.test.ts rename to vault/lib/entropy/quasi.test.ts diff --git a/lib/entropy/quasi.ts b/vault/lib/entropy/quasi.ts similarity index 100% rename from lib/entropy/quasi.ts rename to vault/lib/entropy/quasi.ts diff --git a/lib/log.ts b/vault/lib/log.ts similarity index 100% rename from lib/log.ts rename to vault/lib/log.ts diff --git a/lib/media/video/fmpeg.ts b/vault/lib/media/video/fmpeg.ts similarity index 100% rename from lib/media/video/fmpeg.ts rename to vault/lib/media/video/fmpeg.ts diff --git a/lib/parse/file.test.ts b/vault/lib/parse/file.test.ts similarity index 100% rename from lib/parse/file.test.ts rename to vault/lib/parse/file.test.ts diff --git a/lib/parse/file.ts b/vault/lib/parse/file.ts similarity index 100% rename from lib/parse/file.ts rename to vault/lib/parse/file.ts From 0da27d9280e6a863358f5b513172fb19991b48b1 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 30 Oct 2025 19:21:32 +0000 Subject: [PATCH 7/9] Apply suggestion from @coderabbitai[bot] Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- chachapoly/chachapoly.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chachapoly/chachapoly.go b/chachapoly/chachapoly.go index f3086b8..dcd3437 100644 --- a/chachapoly/chachapoly.go +++ b/chachapoly/chachapoly.go @@ -10,6 +10,9 @@ import ( // Encrypt encrypts data using ChaCha20-Poly1305. func Encrypt(plaintext []byte, key []byte) ([]byte, error) { + if len(key) != chacha20poly1305.KeySize { + return nil, fmt.Errorf("invalid key size: got %d bytes, want %d bytes", len(key), chacha20poly1305.KeySize) + } aead, err := chacha20poly1305.NewX(key) if err != nil { return nil, err From bbbbe6805268cd70a9fd5e06b8f402a07229546e Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 30 Oct 2025 19:21:44 +0000 Subject: [PATCH 8/9] Apply suggestion from @coderabbitai[bot] Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- chachapoly/chachapoly.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chachapoly/chachapoly.go b/chachapoly/chachapoly.go index dcd3437..3f26db8 100644 --- a/chachapoly/chachapoly.go +++ b/chachapoly/chachapoly.go @@ -28,6 +28,9 @@ func Encrypt(plaintext []byte, key []byte) ([]byte, error) { // Decrypt decrypts data using ChaCha20-Poly1305. func Decrypt(ciphertext []byte, key []byte) ([]byte, error) { + if len(key) != chacha20poly1305.KeySize { + return nil, fmt.Errorf("invalid key size: got %d bytes, want %d bytes", len(key), chacha20poly1305.KeySize) + } aead, err := chacha20poly1305.NewX(key) if err != nil { return nil, err From 67b9383432945195594e61a5a76c78395eadff21 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 30 Oct 2025 19:22:09 +0000 Subject: [PATCH 9/9] Update chachapoly/chachapoly.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- chachapoly/chachapoly.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chachapoly/chachapoly.go b/chachapoly/chachapoly.go index 3f26db8..60f356a 100644 --- a/chachapoly/chachapoly.go +++ b/chachapoly/chachapoly.go @@ -36,8 +36,9 @@ func Decrypt(ciphertext []byte, key []byte) ([]byte, error) { return nil, err } - if len(ciphertext) < aead.NonceSize() { - return nil, fmt.Errorf("ciphertext too short") + minLen := aead.NonceSize() + aead.Overhead() + if len(ciphertext) < minLen { + return nil, fmt.Errorf("ciphertext too short: got %d bytes, need at least %d bytes", len(ciphertext), minLen) } nonce, ciphertext := ciphertext[:aead.NonceSize()], ciphertext[aead.NonceSize():]