DEV Community

dialga-cmd
dialga-cmd

Posted on AI-assisted

I Asked Professional Cryptographers to Review My Security Library. Here's What They Found.

I'm a first-year computer science student in the BS degree program at IIT Madras. A few months ago, I built a Python library called GUN101 that encrypts PDF files. I was proud of it. I posted it on Security Stack Exchange and asked professionals to review it.

They tore it apart.

Not cruelly — but thoroughly. And every single criticism was correct. This is the story of what they found, what I did about it, and what I learned building a cryptographic library as a student with no formal security training.

What I Built (And What I Claimed)

GUN101's original README said things like "military-grade encryption" and "files remain uncrackable even if your password is stolen." It had five security layers. It looked impressive.

Here is what those layers actually were:

  • Layer 1 — Argon2id hashing. Genuinely good. This one earned its place.
  • Layer 2 — Dual-pass PBKDF2. I ran PBKDF2-SHA256 at 100,000 iterations, then fed the output into PBKDF2-SHA512 at 10,000 iterations. I thought two passes meant double the security.
  • Layer 3 — Nanosecond timestamp challenge. At encryption time, I captured the current nanosecond, hashed it, and mixed it into the final AES key. My thinking was that even with the password, an attacker would need to know the exact nanosecond of encryption.
  • Layer 4 — AES-256-GCM. Solid. The actual security of the system.
  • Layer 5 — Rate limiting. After 5 failed decryption attempts, the file locks for 15 minutes. State stored in a JSON file on disk.

I was pretty confident in this design. Then the professionals looked at it.

What They Actually Found

On the dual-pass PBKDF2:

The reviewer pointed out that PBKDF2 is already iterative. Running it twice with SHA-256 and then SHA-512 — both from the same SHA-2 family — provides no meaningful algorithmic diversity. If SHA-512 is compromised, SHA-256 almost certainly is too. The total work is roughly equivalent to one call with more iterations, but without any of the benefits I imagined.

They were right. I had added complexity without adding security.

On the timestamp challenge:

This one stung the most because I was most proud of it. The reviewer's point was simple and devastating: the ISO datetime string I was mixing into the key was stored in plaintext in the outer container. An attacker with the file and the password can read the timestamp directly. It is not a secret. It adds no entropy against someone who has both the file and the password.

There is a subtlety here — the sub-second components (nanoseconds, microseconds) were stored inside the encrypted metadata, not the outer container. So those were protected. But the second-precision timestamp that was being mixed into the key was fully visible. The challenge system provided tamper detection, which is real value, but my claim that it made the file "uncrackable even with the password" was simply false.

On the rate limiting:

One reviewer said it in the most direct way possible: an attacker takes your source code, comments out the rate limiting check, and runs unlimited guesses. Client-side rate limiting in a local Python library is not a security layer. It is a speed bump for casual users, nothing more.

On the README language:

"Military-grade encryption" is what the security community calls a red flag. It has no technical definition; it signals that the author is trying to impress rather than inform, and it makes knowledgeable readers trust the project less, not more. I had used it because it sounded credible. It had the opposite effect.

What I Did About It

I did not get defensive. I read every comment carefully and then rebuilt the library from scratch.

  • The key derivation is now a single Argon2id call. No dual PBKDF2. No timestamp mixing. Argon2id with 128MB memory cost, 4 iterations, and 4 parallel threads directly produces the 256-bit AES key. This is what the OWASP and NIST SP 800-63B both recommend. It is simpler, more auditable, and actually stronger than what I had before.
  • The timestamp challenge is gone from key derivation. I kept it as an integrity check because tamper detection is genuinely useful, but I removed it from the key derivation pipeline entirely and stopped claiming it added entropy.
  • The rate limiting is gone. I replaced the claim with an honest statement: the protection against brute force comes from Argon2id's computational cost, not from attempt counting. At the current parameters, each guess takes roughly 300-700ms on commodity hardware. That is the real defence.
  • The README no longer says "military-grade" anything.

Instead, it says:

"GUN101 encrypts files using AES-256-GCM. The key is derived from your password using Argon2id, which is resistant to GPU and ASIC-based brute-force attacks. The security of your encrypted file depends on the strength of your password."

That is true. The previous version was not.

What I Added That Was Not There Before

Rebuilding gave me the chance to add things I had not thought of originally.

  • Optional key file mode: The file now supports a separate 32-byte key file that can be stored on a USB stick or a different device. Key derivation becomes Argon2id(password + keyfile_bytes, salt). An attacker who steals the encrypted file and knows your password still cannot decrypt it without physical access to the key file. This is genuine two-factor protection — which is something the original Layer 3 was trying and failing to achieve.
  • Formal security documentation: I wrote a THREAT_MODEL.md that states explicitly what GUN101 protects against and what it does not. I wrote SECURITY.md explaining why every cryptographic decision was made. I wrote KNOWN_ATTACKS.md analysing ten named attack classes with verdicts. I wrote TEST_VECTORS.md with independently verifiable inputs and expected outputs so anyone can confirm the implementation is cryptographically correct without trusting the test suite.
  • 133 tests across four suites: Core security properties — salt uniqueness, ciphertext non-determinism, tamper detection across every container field, key file isolation, and Argon2id parameter enforcement. Edge case validation on cipher and KDF inputs. Fuzz testing with Hypothesis covering malformed JSON, missing fields, bad base64, and random mutations. Full CLI path safety coverage including symlink and path traversal rejection. 97% code coverage. Two tests are intentional platform skips — Windows-specific path safety tests that only run on case-insensitive filesystems.

The Bigger Thing I Learned

I thought I was building a security library. I was actually building something that looked like a security library. There is a meaningful difference.

Real security comes from simplicity and correctness, not from the number of layers. Every layer I added that did not have a clear, demonstrable cryptographic function was not neutral — it was actively harmful, because it added attack surface, added complexity for auditors, and made it harder to reason about what the system actually did.

The professionals did not tell me my library was bad. They told me it was doing more than it needed to, and less than it claimed. That is a subtle but important distinction.

Where GUN101 Is Now

The library is published on PyPI as three separate protocols:

  • gun101 — password-based encryption with optional key file two-factor mode (pip install gun101)
  • gun101-gkp — Ghost Key Protocol. RSA-4096 asymmetric encryption where the sender needs only the recipient's public token. No shared password. No prior communication (pip install gun101-gkp).
  • gun101-tpm — TPM 2.0 hardware-bound encryption. The AES key is sealed inside the machine's TPM chip. The file cannot be decrypted on any other hardware, even with the correct password (pip install gun101-tpm).

All three repos are open source. Check out the GitHub repositories below:

GitHub logo dialga-cmd / GUN101

A simple, secure file encryption CLI using AES-256-GCM and Argon2id. Encrypt and decrypt files with a password or a password + keyfile.

GUN-101

A simple, secure file encryption tool using AES-256-GCM and Argon2id.

CI PyPI version Python versions License: MIT OpenSSF Best Practices Security Policy

What problem does this solve?

If a laptop is lost, a cloud drive is breached, or a USB stick falls into the wrong hands, the files on it are exposed. GUN-101 protects files at rest: it encrypts them so that only someone with the correct password (and, optionally, a separate keyfile) can read them, and any tampering with an encrypted file is detected and rejected before it is opened.

Overview

GUN-101 encrypts files with authenticated encryption, providing confidentiality and integrity. It supports two modes:

  1. Password-only: Encryption key derived from password alone
  2. Password + keyfile: Two-factor protection requiring both password and a separate keyfile

The design prioritizes correctness and transparency over complexity or marketing claims.

The tool uses a versioned container format (currently v2.1) to allow for future improvements while maintaining backward compatibility with v2.0 encrypted files.


GitHub logo dialga-cmd / gun101-gkp

A passwordless, asymmetric file encryption library. Recipients share a public Identity Token; only their RSA-4096 private key can decrypt files sent to them. No shared secrets required.

GUN-101-GKP: Ghost Key Protocol

A passwordless asymmetric encryption library for file encryption using RSA-4096 and AES-256-GCM.

OpenSSF Best Practices CI License: MIT PyPI

Project website: https://dialga-cmd.github.io/gun101-gkp/

What is GUN-101-GKP?

GUN-101-GKP (Ghost Key Protocol) is a Python library that enables secure file encryption without shared secrets or passwords. The recipient generates an RSA-4096 key pair and shares only their public key (called an Identity Token). Anyone with this token can encrypt files for the recipient, but only the holder of the private key can decrypt them.

Who is it for?

  • Individuals who need to send sensitive files to a specific recipient without exchanging passwords or using a secure channel for key agreement.
  • Applications that require asymmetric encryption for file storage or transmission where the recipient's identity is known in advance.
  • Users who want a simple, stateless encryption scheme where the sender holds no long-term secrets.

What does it protect?

GUN-101-GKP provides confidentiality of file contents against attackers who…


GitHub logo dialga-cmd / gun101-tpm

Hardware-bound file encryption using TPM 2.0. Files are sealed to a specific machine's TPM chip, so decryption fails on any other device even with the correct password. Linux only.

GUN-101-TPM: Hardware-bound File Encryption

OpenSSF Best Practices OpenSSF Baseline

Platform Support

GUN-101-TPM is currently Linux-first. TPM 2.0 hardware binding is fully supported on Linux. Windows support is work in progress — a native TBS-based backend exists but its seal/unseal is still emulated and not yet provably hardware-bound. macOS is not yet supported.

  • Linux: Fully supported with /dev/tpm0 or /dev/tpmrm0
  • Windows: Work in progress — native TBS-backed backend (tbs.dll, x64/ARM64) with check-tpm and fingerprint support, but seal/unseal currently uses a software-emulated blob rather than genuine TPM binding
  • macOS: Not supported — most Mac hardware lacks TPM 2.0 chips; would require a Secure Enclave-based backend (planned for future)

Installation implications:

  • pip install gun101-tpm[tpm] installs cleanly on any OS via the conditional dependency tpm2-pytss>=2.3.0; sys_platform == 'linux' in pyproject.toml
  • On non-Linux OS, the runtime check _check_platform_supported() in tpm.py raises a clear RuntimeError without importing tpm2_pytss
  • The non-TPM GUN-101 modes (password-only) work cross-platform

The Original Review Thread

If you want to read the professional feedback in full, the Security Stack Exchange thread is still public. I am linking it not because I came out looking good — I did not, initially — but because I think the most valuable thing a student can do is show their work, including the wrong parts.

The criticisms were sharp, accurate, and given in good faith. I am grateful for them. The library is meaningfully better because of them.

I recently made a security protocol using python to encrypt PDFs in the best possible way I could have thought off. Its an official pip package as well, and can be installed using command pip install gun101.

I would like to ask the professionals here to check if there…


What edge cases or security challenges have you faced when building something outside your comfort zone? Let's discuss in the comments below! 👇

Top comments (0)