Skip to content

Conversation

@Snider
Copy link
Owner

@Snider Snider commented Dec 25, 2025

Implements ChaChaPolySigil that applies pre-obfuscation before sending
data to CPU encryption routines. This ensures raw plaintext is never
passed directly to encryption functions.

Key improvements:

  • XORObfuscator and ShuffleMaskObfuscator for pre-encryption transforms
  • Nonce is now properly embedded in ciphertext, not stored separately
    in headers (production-ready, not demo-style)
  • Trix crypto integration with EncryptPayload/DecryptPayload methods
  • Comprehensive test coverage following Good/Bad/Ugly pattern

Implements ChaChaPolySigil that applies pre-obfuscation before sending
data to CPU encryption routines. This ensures raw plaintext is never
passed directly to encryption functions.

Key improvements:
- XORObfuscator and ShuffleMaskObfuscator for pre-encryption transforms
- Nonce is now properly embedded in ciphertext, not stored separately
  in headers (production-ready, not demo-style)
- Trix crypto integration with EncryptPayload/DecryptPayload methods
- Comprehensive test coverage following Good/Bad/Ugly pattern
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features
    • Added ChaCha20-Poly1305 encryption for Trix payloads with multiple obfuscation strategies (XOR and ShuffleMask).
    • New methods to encrypt and decrypt payloads in-place with embedded nonces.
    • Helper methods to check encryption status and query encryption algorithm.
    • Convenient constructor to create encrypted Trix containers in a single operation.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

A new cryptographic layer is introduced across two packages. The enchantrix package receives ChaChaPolySigil, a ChaCha20-Poly1305 encryption implementation with pluggable pre-obfuscation strategies (XOR-based and shuffle-mask variants). The trix package integrates this via encryption/decryption methods, configuration handling, state tracking through headers, and a convenience constructor for encrypted containers.

Changes

Cohort / File(s) Summary
Enchantrix Encryption Core
pkg/enchantrix/crypto_sigil.go
Introduces ChaChaPolySigil type with constructor variants (basic and with custom obfuscator). Implements In() for obfuscation+encryption and Out() for decryption+deobfuscation. Defines PreObfuscator interface with two implementations: XORObfuscator (deterministic key-stream XOR) and ShuffleMaskObfuscator (entropy-derived permutation and byte masking). Includes nonce extraction utility and four public error variables for key, ciphertext, decryption, and configuration scenarios. Embeds nonce in ciphertext output.
Enchantrix Tests
pkg/enchantrix/crypto_sigil_test.go
Comprehensive test suite covering encryption/decryption cycles, empty and large payloads (1 MB), nonce variability, obfuscation determinism, negative cases (invalid keys, tampering, truncation), edge cases (nil inputs), integration integrity, and benchmarks for both obfuscators and sigil operations.
Trix Encryption Integration
pkg/trix/crypto.go
Extends Trix with EncryptPayload() and DecryptPayload() methods accepting CryptoConfig (32-byte key + obfuscator type). Adds IsEncrypted() and GetEncryptionAlgorithm() state helpers. Introduces NewEncryptedTrix() factory for one-step encrypted container creation. Updates header with encryption status, algorithm, obfuscator type, and timestamp; nonce embedded in ciphertext. Enforces key length, prevents double-encryption, and guards against decrypting non-encrypted payloads. Defaults to XOR obfuscator; supports shuffle-mask variant.
Trix Tests
pkg/trix/crypto_test.go
Validates encryption/decryption across multiple obfuscators and configurations, nil header handling, error cases (invalid keys, mismatched keys, re-encryption, wrong decryption state), NewEncryptedTrix constructor, header state verification, nonce embedding, plaintext safety, and end-to-end round-trip integrity including binary encoding/decoding.

Sequence Diagrams

sequenceDiagram
    participant Client
    participant Trix
    participant CryptoConfig
    participant ChaChaPolySigil
    participant PreObfuscator
    participant RNG as RNG<br/>(nonce)
    participant ChaCha as ChaCha20-<br/>Poly1305

    Client->>Trix: EncryptPayload(config)
    Trix->>Trix: Validate not encrypted
    Trix->>Trix: Select obfuscator<br/>(from config)
    Trix->>ChaChaPolySigil: NewChaChaPolySigilWithObfuscator<br/>(key, obfuscator)
    ChaChaPolySigil->>ChaChaPolySigil: Validate 32-byte key
    Trix->>ChaChaPolySigil: In(payload)
    ChaChaPolySigil->>PreObfuscator: Generate nonce via RNG
    ChaChaPolySigil->>PreObfuscator: Obfuscate(payload, nonce)
    PreObfuscator-->>ChaChaPolySigil: Obfuscated data
    ChaChaPolySigil->>ChaCha: Encrypt obfuscated data<br/>with nonce
    ChaCha-->>ChaChaPolySigil: Ciphertext
    ChaChaPolySigil->>ChaChaPolySigil: Prepend nonce to ciphertext
    ChaChaPolySigil-->>Trix: Encrypted payload (nonce + ct)
    Trix->>Trix: Update header<br/>(encrypted, algorithm,<br/>obfuscator, timestamp)
    Trix-->>Client: Success
Loading
sequenceDiagram
    participant Client
    participant Trix
    participant CryptoConfig
    participant ChaChaPolySigil
    participant PreObfuscator
    participant ChaCha as ChaCha20-<br/>Poly1305

    Client->>Trix: DecryptPayload(config)
    Trix->>Trix: Validate encrypted state<br/>from header
    Trix->>Trix: Read obfuscator from header<br/>(default XOR if missing)
    Trix->>ChaChaPolySigil: NewChaChaPolySigilWithObfuscator<br/>(key, obfuscator)
    ChaChaPolySigil->>ChaChaPolySigil: Validate 32-byte key
    Trix->>ChaChaPolySigil: Out(ciphertext)
    ChaChaPolySigil->>ChaChaPolySigil: Extract nonce from<br/>ciphertext prefix
    ChaChaPolySigil->>ChaCha: Decrypt (ct, nonce)
    ChaCha-->>ChaChaPolySigil: Plaintext
    ChaChaPolySigil->>PreObfuscator: Deobfuscate(plaintext, nonce)
    PreObfuscator-->>ChaChaPolySigil: Original payload
    ChaChaPolySigil-->>Trix: Decrypted payload
    Trix->>Trix: Clear encrypted flag<br/>in header
    Trix-->>Client: Success, payload restored
Loading

Poem

🐰 Whiskers twitching with delight
Encryption's here, both left and right,
With ChaCha singing, ciphers tight,
And nonces shuffled out of sight—
The burrows now are cryptographically tight! 🔐✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.48% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding an encryption sigil with pre-obfuscation capability, which aligns with the new ChaChaPolySigil implementation and obfuscator strategies introduced across the changeset.
Description check ✅ Passed The description is clearly related to the changeset, covering the ChaChaPolySigil implementation, pre-obfuscation strategies, nonce embedding, Trix integration, and test coverage patterns, all of which are present in the code changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/test-sigil-encryption-wdRbW

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8a3fb3 and afb1166.

📒 Files selected for processing (4)
  • pkg/enchantrix/crypto_sigil.go
  • pkg/enchantrix/crypto_sigil_test.go
  • pkg/trix/crypto.go
  • pkg/trix/crypto_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/enchantrix/crypto_sigil_test.go (1)
pkg/enchantrix/crypto_sigil.go (10)
  • NewChaChaPolySigil (218-231)
  • XORObfuscator (35-35)
  • ErrInvalidKey (15-15)
  • ErrDecryptionFailed (19-19)
  • ErrCiphertextTooShort (17-17)
  • ChaChaPolySigil (210-214)
  • ErrNoKeyConfigured (21-21)
  • ShuffleMaskObfuscator (92-92)
  • GetNonceFromCiphertext (330-338)
  • NewChaChaPolySigilWithObfuscator (234-243)
pkg/trix/crypto_test.go (2)
pkg/trix/trix.go (3)
  • Trix (39-45)
  • Encode (49-109)
  • Decode (114-187)
pkg/trix/crypto.go (12)
  • CryptoConfig (38-43)
  • AlgorithmChaCha20Poly1305 (30-30)
  • HeaderKeyEncrypted (21-21)
  • HeaderKeyAlgorithm (23-23)
  • ObfuscatorXOR (32-32)
  • HeaderKeyObfuscator (27-27)
  • HeaderKeyEncryptedAt (25-25)
  • ObfuscatorShuffleMask (34-34)
  • ErrNoEncryptionKey (12-12)
  • ErrAlreadyEncrypted (14-14)
  • ErrNotEncrypted (16-16)
  • NewEncryptedTrix (173-189)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (36)
pkg/enchantrix/crypto_sigil_test.go (10)

13-31: LGTM!

The mock readers are well-designed for testing: mockRandReader simulates error conditions, and deterministicReader provides reproducible entropy for deterministic testing of obfuscation behaviour.


35-135: LGTM!

Comprehensive test coverage for the happy path scenarios. The tests properly verify encryption/decryption round-trips, nonce uniqueness, and pre-obfuscation behaviour. Good use of require for setup assertions and assert for verifications.


137-209: LGTM!

Thorough negative test coverage including key validation, tampering detection, and error conditions. The tampered ciphertext test correctly modifies data after the nonce position (byte 30 > 24).


211-246: LGTM!

Edge cases for nil inputs and nil obfuscator are properly tested. The tests verify graceful handling without panics.


248-308: LGTM!

Good coverage for XORObfuscator including the symmetric round-trip property and edge cases. The empty entropy test is particularly valuable as it validates the key stream derivation still functions correctly.


310-381: LGTM!

Comprehensive test coverage for ShuffleMaskObfuscator. The determinism test is particularly important for verifying the shuffle algorithm produces consistent results with the same entropy.


383-404: LGTM!

Good coverage for GetNonceFromCiphertext including successful extraction and the error case for truncated ciphertext.


406-430: LGTM!

Good validation of NewChaChaPolySigilWithObfuscator factory function, including the fallback behaviour when a nil obfuscator is provided.


432-477: LGTM!

Valuable integration tests that verify security properties. The PlaintextNeverInOutput test provides good assurance that the pre-obfuscation layer is functioning correctly by checking both the full plaintext and distinctive substrings.


479-524: LGTM!

Well-structured benchmarks with proper use of b.ResetTimer() to exclude setup time. The 1024-byte data size provides a reasonable baseline for performance measurement.

pkg/trix/crypto.go (8)

1-17: LGTM!

Clean imports and well-defined sentinel errors with consistent trix: prefix for easy identification of error origin.


19-35: LGTM!

Well-defined constants with clear documentation. The algorithm identifier correctly specifies the XChaCha20-Poly1305 variant which uses 24-byte nonces.


37-43: LGTM!

Simple and clear configuration structure with appropriate documentation.


45-99: LGTM!

Solid implementation with proper key validation, double-encryption guard, defensive header initialisation, and clear metadata tracking. The UTC timestamp with RFC3339 format is good practice for interoperability.


101-148: LGTM!

Correct implementation that reads the obfuscator type from the header rather than the config, ensuring consistency with the obfuscator used during encryption. Preserving the algorithm metadata after decryption is useful for audit purposes.


150-157: LGTM!

Defensive implementation with proper nil check and type-safe header access.


159-169: LGTM!

Consistent defensive pattern with IsEncrypted, providing safe access to header values.


171-189: LGTM!

Clean convenience constructor that simplifies the common use case. Uses the default XOR obfuscator, which aligns with the documented behaviour.

pkg/trix/crypto_test.go (10)

1-10: LGTM!

Good use of external test package (trix_test) for black-box testing of the public API.


12-75: LGTM!

Comprehensive happy path tests with good verification of header metadata and the important security check that the nonce is not exposed in headers (Lines 41-42).


77-103: LGTM!

Good coverage of error conditions including nil config, invalid key size, and double-encryption prevention.


105-169: LGTM!

Thorough round-trip testing including the empty payload edge case.


171-243: LGTM!

Good error handling tests. The MissingObfuscatorHeader test is particularly valuable for ensuring backward compatibility when the obfuscator header is absent.


245-276: LGTM!

Adequate coverage of the NewEncryptedTrix convenience constructor including nil header handling and invalid key validation.


278-335: LGTM!

Comprehensive testing of the state query methods including the important edge case of wrong type values in headers.


337-379: LGTM!

Excellent end-to-end integration test covering the full lifecycle: encryption → binary encoding → decoding → decryption, with verification that custom header fields are preserved throughout.


381-416: LGTM!

Critical security test validating that the nonce is properly embedded in the ciphertext rather than exposed in headers, and that this property is preserved through encode/decode cycles.


418-437: LGTM!

Important security test ensuring the pre-obfuscation layer is effective at the Trix API level. The substring checks provide additional assurance against partial plaintext leakage.

pkg/enchantrix/crypto_sigil.go (8)

1-22: LGTM!

Appropriate imports for the cryptographic functionality. The sentinel errors are clearly defined with consistent enchantrix: prefix.


24-31: LGTM!

Clean interface design that enables pluggable obfuscation strategies with deterministic behaviour through the entropy parameter.


33-88: LGTM!

Well-implemented XOR obfuscator with SHA256-based key stream derivation. The counter-mode extension for generating arbitrary-length key streams is correctly implemented.


90-199: LGTM!

Correctly implemented shuffle-mask obfuscator. The Fisher-Yates shuffle uses deterministic randomness derived from entropy, and the order of operations (mask then shuffle for obfuscation, unshuffle then unmask for deobfuscation) is correctly reversed.


201-243: LGTM!

Good defensive coding with key copying to prevent external modification. The injectable randReader field enables deterministic testing without compromising production security.


245-282: LGTM!

Correct implementation of the encryption flow. The use of aead.Seal(nonce, nonce, obfuscated, nil) correctly prepends the nonce to the output. Pre-obfuscation using the nonce as entropy ensures deterministic reversibility during decryption.


284-325: LGTM!

Correct decryption implementation with proper minimum length validation to prevent short ciphertext attacks. The deobfuscation uses the same nonce as entropy, ensuring consistency with the obfuscation step.


327-338: LGTM!

Correct helper function with defensive copying and appropriate documentation noting it's for debugging/logging purposes only.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @Snider, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust and secure encryption layer to the system, primarily focusing on the implementation of a ChaChaPolySigil using the ChaCha20-Poly1305 algorithm. A key security enhancement is the addition of pre-obfuscation techniques, such as XOR and shuffle-masking, which ensure that raw plaintext is never directly exposed to the core encryption routines. Furthermore, the design prioritizes security best practices by embedding the encryption nonce directly within the ciphertext, moving away from less secure "demo-style" separate nonce storage. This new cryptographic functionality is seamlessly integrated into the Trix data container, providing methods for encrypting and decrypting payloads with appropriate metadata handling.

Highlights

  • ChaChaPolySigil Implementation: Introduces a new ChaChaPolySigil structure for ChaCha20-Poly1305 encryption, providing In (encrypt) and Out (decrypt) methods.
  • Pre-Obfuscation Layers: Adds PreObfuscator interface with two concrete implementations, XORObfuscator and ShuffleMaskObfuscator, to transform plaintext before encryption, enhancing security.
  • Nonce Handling: The encryption nonce is now embedded directly within the ciphertext, eliminating the need to store it separately in headers and aligning with production-ready practices.
  • Trix Crypto Integration: Integrates the new encryption capabilities into the Trix data container via EncryptPayload and DecryptPayload methods, along with CryptoConfig and header metadata.
  • Comprehensive Test Coverage: Includes extensive unit, integration, and benchmark tests for both the enchantrix sigil and trix crypto functionalities, following Good/Bad/Ugly patterns.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link

codecov bot commented Dec 25, 2025

Codecov Report

❌ Patch coverage is 93.13305% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/enchantrix/crypto_sigil.go 93.75% 5 Missing and 5 partials ⚠️
pkg/trix/crypto.go 91.78% 3 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust encryption layer with a pre-obfuscation step, which is a great security enhancement. The implementation correctly uses ChaCha20-Poly1305 and embeds the nonce within the ciphertext, following modern best practices. The code is well-structured and accompanied by an excellent and comprehensive test suite. I've identified a couple of areas for improvement: one is a performance issue in the ShuffleMaskObfuscator due to hashing within a loop, and the other is a small code simplification in the decryption logic to improve readability. Overall, this is a high-quality contribution.

Comment on lines +159 to +169
// Fisher-Yates shuffle with deterministic randomness
for i := length - 1; i > 0; i-- {
h.Reset()
h.Write(seed)
var iBytes [8]byte
binary.BigEndian.PutUint64(iBytes[:], uint64(i))
h.Write(iBytes[:])
jBytes := h.Sum(nil)
j := int(binary.BigEndian.Uint64(jBytes[:8]) % uint64(i+1))
perm[i], perm[j] = perm[j], perm[i]
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of generatePermutation re-calculates a SHA256 hash on every iteration of the loop. This is inefficient and can be a performance bottleneck for large inputs. Additionally, using the modulo operator % to determine the swap index j can introduce a slight statistical bias.

A more performant approach would be to generate a single stream of bytes upfront (similar to deriveKeyStream) and use that to source the values for the shuffle. This avoids expensive hash computations inside the loop.

For example, you could generate a stream of bytes and then use it like this:

// ... after generating seed ...

// Generate a stream of bytes to use for shuffling
streamLen := (length - 1) * 8 // 8 bytes per swap
keyStream := deriveStreamFromSeed(seed, streamLen) // A new helper function

// Fisher-Yates shuffle with deterministic randomness from the stream
for i := length - 1; i > 0; i-- {
    byteOffset := (length - 1 - i) * 8
    jBytes := keyStream[byteOffset : byteOffset+8]
    j := int(binary.BigEndian.Uint64(jBytes) % uint64(i+1))
    perm[i], perm[j] = perm[j], perm[i]
}

I also recommend refactoring the stream generation logic from XORObfuscator.deriveKeyStream and ShuffleMaskObfuscator.deriveMask into a shared helper function to promote code reuse.

Comment on lines +117 to +127
var obfuscator enchantrix.PreObfuscator
if obfType, ok := t.Header[HeaderKeyObfuscator].(string); ok {
switch obfType {
case ObfuscatorShuffleMask:
obfuscator = &enchantrix.ShuffleMaskObfuscator{}
default:
obfuscator = &enchantrix.XORObfuscator{}
}
} else {
obfuscator = &enchantrix.XORObfuscator{}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to determine which obfuscator to use during decryption can be simplified. The else block is redundant because the default case in the switch statement already handles the same logic (assigning XORObfuscator).

You can make this more concise by reading the header value and using a single switch with a default case to handle both missing values and unknown values.

	var obfuscator enchantrix.PreObfuscator
	obfType, _ := t.Header[HeaderKeyObfuscator].(string)
	switch obfType {
	case ObfuscatorShuffleMask:
		obfuscator = &enchantrix.ShuffleMaskObfuscator{}
	default:
		// Default to XORObfuscator if the header is missing, has the wrong type, or an unknown value.
		obfuscator = &enchantrix.XORObfuscator{}
	}

@Snider Snider merged commit 1e1dfee into main Dec 25, 2025
2 of 3 checks passed
@Snider Snider deleted the claude/test-sigil-encryption-wdRbW branch December 25, 2025 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants