Merge pull request #14 from Snider/feat-refactor-crypt-service

feat: Implement Pre-Encryption Sigil Packing for Enhanced Security
This commit is contained in:
Snider 2025-10-31 02:41:48 +00:00 committed by GitHub
commit 3268e4a6e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 80 additions and 71 deletions

View file

@ -11,84 +11,78 @@ import (
)
func main() {
// 1. Original plaintext
// 1. Original plaintext and encryption key
plaintext := []byte("This is a super secret message!")
key := make([]byte, 32) // In a real application, use a secure key
for i := range key {
key[i] = 1
}
// 2. Encrypt the data using the chachapoly package
// The ciphertext from chachapoly includes the nonce.
ciphertext, err := chachapoly.Encrypt(plaintext, key)
// 2. Create a Trix container with the plaintext and attach sigils
trixContainer := &trix.Trix{
Header: map[string]interface{}{},
Payload: plaintext,
Sigils: []trix.Sigil{&trix.ReverseSigil{}},
}
// 3. Pack the Trix container to apply the sigil transformations
if err := trixContainer.Pack(); err != nil {
log.Fatalf("Failed to pack trix container: %v", err)
}
fmt.Printf("Packed (obfuscated) payload: %x\n", trixContainer.Payload)
// 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
// For the .trix header, we need to separate the nonce from the ciphertext.
// chacha20poly1305.NewX nonce size is 24 bytes.
// 5. Add encryption metadata to the header
nonce := ciphertext[:24]
actualCiphertext := ciphertext[24:]
// 3. Create a .trix container for the encrypted data
header := map[string]interface{}{
trixContainer.Header = map[string]interface{}{
"content_type": "application/octet-stream",
"encryption_algorithm": "chacha20poly1305",
"nonce": base64.StdEncoding.EncodeToString(nonce),
"created_at": time.Now().UTC().Format(time.RFC3339),
}
trixContainer := &trix.Trix{
Header: header,
Payload: actualCiphertext,
Sigils: []trix.Sigil{&trix.ReverseSigil{}},
}
// 4. Encode the .trix container into its binary format
magicNumber := "MyT1" // My Trix 1
// 6. Encode the .trix container into its binary format
magicNumber := "MyT1"
encodedTrix, err := trix.Encode(trixContainer, magicNumber)
if err != nil {
log.Fatalf("Failed to encode .trix container: %v", err)
}
fmt.Println("Successfully created .trix container.")
// 5. Decode the .trix container to retrieve the encrypted data
// --- DECODING ---
// 7. Decode the .trix container
decodedTrix, err := trix.Decode(encodedTrix, magicNumber)
if err != nil {
log.Fatalf("Failed to decode .trix container: %v", err)
}
// Manually apply the Out method of the sigil to restore the original payload.
restoredPayload, err := trixContainer.Sigils[0].Out(decodedTrix.Payload)
if err != nil {
log.Fatalf("Failed to apply sigil: %v", err)
}
decodedTrix.Payload = restoredPayload
// 6. Reassemble the ciphertext (nonce + payload) and decrypt
retrievedNonceStr, ok := decodedTrix.Header["nonce"].(string)
if !ok {
log.Fatalf("Nonce not found or not a string in header")
}
retrievedNonce, err := base64.StdEncoding.DecodeString(retrievedNonceStr)
if err != nil {
log.Fatalf("Failed to decode nonce: %v", err)
}
retrievedCiphertext := append(retrievedNonce, decodedTrix.Payload...)
decrypted, err := chachapoly.Decrypt(retrievedCiphertext, key)
// 8. Decrypt the payload
decryptedPayload, err := chachapoly.Decrypt(decodedTrix.Payload, key)
if err != nil {
log.Fatalf("Failed to decrypt: %v", err)
}
decodedTrix.Payload = decryptedPayload
// 7. Verify the result
fmt.Printf("Original plaintext: %s\n", plaintext)
fmt.Printf("Decrypted plaintext: %s\n", decrypted)
// 9. Unpack the Trix container to reverse the sigil transformations
decodedTrix.Sigils = trixContainer.Sigils // Re-attach sigils
if err := decodedTrix.Unpack(); err != nil {
log.Fatalf("Failed to unpack trix container: %v", err)
}
fmt.Printf("Unpacked (original) payload: %s\n", decodedTrix.Payload)
if string(plaintext) == string(decrypted) {
fmt.Println("\nSuccess! The message was decrypted correctly.")
// 10. Verify the result
if string(plaintext) == string(decodedTrix.Payload) {
fmt.Println("\nSuccess! The message was decrypted and unpacked correctly.")
} else {
fmt.Println("\nFailure! The decrypted message does not match the original.")
fmt.Println("\nFailure! The final payload does not match the original.")
}
}

View file

@ -39,19 +39,6 @@ func Encode(trix *Trix, magicNumber string) ([]byte, error) {
return nil, ErrMagicNumberLength
}
// Apply sigils to the payload before encoding
payload := trix.Payload
for _, sigil := range trix.Sigils {
if sigil == nil {
return nil, ErrNilSigil
}
var err error
payload, err = sigil.In(payload)
if err != nil {
return nil, err
}
}
headerBytes, err := json.Marshal(trix.Header)
if err != nil {
return nil, err
@ -81,7 +68,7 @@ func Encode(trix *Trix, magicNumber string) ([]byte, error) {
}
// Write Payload
if _, err := buf.Write(payload); err != nil {
if _, err := buf.Write(trix.Payload); err != nil {
return nil, err
}
@ -143,6 +130,37 @@ func Decode(data []byte, magicNumber string) (*Trix, error) {
}, nil
}
// Pack applies the In method of all attached sigils to the payload.
func (t *Trix) Pack() error {
for _, sigil := range t.Sigils {
if sigil == nil {
return ErrNilSigil
}
var err error
t.Payload, err = sigil.In(t.Payload)
if err != nil {
return err
}
}
return nil
}
// Unpack applies the Out method of all sigils in reverse order.
func (t *Trix) Unpack() error {
for i := len(t.Sigils) - 1; i >= 0; i-- {
sigil := t.Sigils[i]
if sigil == nil {
return ErrNilSigil
}
var err error
t.Payload, err = sigil.Out(t.Payload)
if err != nil {
return err
}
}
return nil
}
// ReverseSigil is an example Sigil that reverses the bytes of the payload.
type ReverseSigil struct{}

View file

@ -127,27 +127,24 @@ func (s *FailingSigil) Out(data []byte) ([]byte, error) {
return nil, s.err
}
func TestSigilPipeline_Good(t *testing.T) {
func TestPackUnpack_Good(t *testing.T) {
originalPayload := []byte("hello world")
trix := &Trix{
Header: map[string]interface{}{},
Payload: originalPayload,
Sigils: []Sigil{&ReverseSigil{}},
Sigils: []Sigil{&ReverseSigil{}, &ReverseSigil{}}, // Double reverse should be original
}
encoded, err := Encode(trix, "SIGL")
err := trix.Pack()
assert.NoError(t, err)
assert.Equal(t, originalPayload, trix.Payload) // Should be back to the original
decoded, err := Decode(encoded, "SIGL")
err = trix.Unpack()
assert.NoError(t, err)
// Manually apply the Out method to restore the original payload.
restoredPayload, err := trix.Sigils[0].Out(decoded.Payload)
assert.NoError(t, err)
assert.Equal(t, originalPayload, restoredPayload)
assert.Equal(t, originalPayload, trix.Payload) // Should be back to the original again
}
func TestSigilPipeline_Bad(t *testing.T) {
func TestPackUnpack_Bad(t *testing.T) {
expectedErr := errors.New("sigil failed")
trix := &Trix{
Header: map[string]interface{}{},
@ -155,12 +152,12 @@ func TestSigilPipeline_Bad(t *testing.T) {
Sigils: []Sigil{&ReverseSigil{}, &FailingSigil{err: expectedErr}},
}
_, err := Encode(trix, "FAIL")
err := trix.Pack()
assert.Error(t, err)
assert.Equal(t, expectedErr, err)
}
func TestSigilPipeline_Ugly(t *testing.T) {
func TestPackUnpack_Ugly(t *testing.T) {
t.Run("NilSigil", func(t *testing.T) {
trix := &Trix{
Header: map[string]interface{}{},
@ -168,7 +165,7 @@ func TestSigilPipeline_Ugly(t *testing.T) {
Sigils: []Sigil{nil},
}
_, err := Encode(trix, "UGLY")
err := trix.Pack()
assert.Error(t, err)
assert.Equal(t, ErrNilSigil, err)
})