Posts  / #POST-221226
REDDIT

Looking for review of a deterministic encryption scheme for version-controlled Markdown

Y
Mar 4, 2026 · 23:53

I built a tool called [mdenc](https://github.com/yogh-io/mdenc) that encrypts Markdown files at paragraph level so they can be stored in git with meaningful diffs. The core idea: unchanged paragraphs produce identical ciphertext, so only edited paragraphs show up in version-control diffs.

There's a [live demo](https://yogh-io.github.io/mdenc/) where you can try it -- each paragraph is color-coded so you can see which chunks map to which ciphertext lines.

I'm a software engineer, not a cryptographer. I chose primitives that seemed appropriate and wrote a full spec, but I don't have the background to be confident I composed them correctly. I'm posting here because I'd genuinely like someone with more expertise to tell me what I got wrong.

**What it does:**

- Splits Markdown into paragraphs
- Encrypts each paragraph independently with XChaCha20-Poly1305
- Nonces are derived deterministically from the content, so same content + same key = same ciphertext
- A file-level HMAC seal detects reordering, truncation, and rollback
- Keys are derived from a password via scrypt and then split using HKDF

**What it intentionally leaks:** paragraph count, approximate sizes, which paragraphs changed between commits, repeated paragraphs within a file. This is a deliberate tradeoff for diffability.

**What it's for:** internal team docs in public git repos -- stuff that shouldn't be plaintext but isn't truly secret. The password is shared across the team. No forward secrecy, no key rotation mechanism. This is documented upfront in the [security model](https://github.com/yogh-io/mdenc/blob/main/SECURITY.md).

**Things I'm least sure about:**

- Deriving the nonce from HMAC-SHA256(key, plaintext) and truncating to 24 bytes -- is truncating HMAC output for use as a nonce problematic?
- The per-chunk authenticated data deliberately has no chunk index (so inserting a paragraph doesn't change surrounding ciphertext). Ordering is enforced by a separate HMAC seal instead. Is that a meaningful weakness?
- Using the same derived key for both the header HMAC and the file seal -- they operate over different inputs, but should I have separated them?

The full spec is here: [SPECIFICATION.md](https://github.com/yogh-io/mdenc/blob/main/SPECIFICATION.md). It covers the complete construction in detail. Crypto primitives come from the audited noble libraries. The protocol itself has not been reviewed -- that's why I'm here.

Post image