Gem File Decryptor -
I wrote the decryptor in Rust. I wanted the memory safety guarantees, but mostly, I wanted the speed. If I had to brute-force the date format, Python’s overhead would be too slow.
fn attempt_decrypt(encrypted_data: &[u8], password_guess: &str) -> Result<Vec<u8>, Error>
// 1. Derive the key from the password guess
let key = derive_key_argon2(password_guess, SALT);
// 2. Initialize the cipher (AES-256-GCM was the guess)
let cipher = Cipher::aes_256_gcm(&key);
// 3. Attempt decryption
let decrypted = cipher.decrypt(IV, encrypted_data);
match decrypted
Ok(data) =>
// Check for valid UTF-8 or file signatures (PNG, PDF, etc.)
if looks_like_valid_file(&data)
Ok(data)
else
Err(Error::InvalidContent)
Err(e) => Err(e),
For days, the console output was a stream of InvalidContent errors. It is a maddening process. You stare at bytes that represent failure, looking for a pattern that implies success.
There is a specific kind of silence that falls over a terminal when a decryption fails. It’s not the violent crash of a segfault or the noisy stack trace of a syntax error. It is a quiet, dismissive false.
For the last week, that single word—false—was the wall I beat my head against. gem file decryptor
We deal in secrets. As developers, we are the architects of vaults. We build walls of AES-256 and moats of RSA keys, trusting implicitly in the mathematics of difficulty. But there is a haunting duality in cryptography: the very algorithms designed to protect data are often the most ruthless when it comes to destroying it.
This is the story of building a custom decryption engine for a proprietary encrypted archive—let’s call it the .gem format—not to break security, but to reclaim it. It is a story about the friction between human memory and mathematical certainty, and how reading binary is less like reading code and more like reading braille in a dark room.
In the Ruby programming world, a .gem file is a packaged library or application. It is essentially a tar archive containing: I wrote the decryptor in Rust
Before decrypting, identify:
Check with file command or a hex editor:
xxd encrypted.gem | head -n 5
Look for recognizable magic numbers or plaintext headers like GEM0, GEM1. For days, the console output was a stream
decipher = OpenSSL::Cipher.new('aes-256-gcm').decrypt decipher.key = key decipher.iv = iv decipher.auth_tag = tag decipher.auth_data = "" # Rails doesn't use additional auth data here
plaintext = decipher.update(ciphertext) + decipher.final
puts plaintext
Run it with:
ruby decrypt_gem_secrets.rb
If the master key is correct, you’ll see the raw YAML secrets. If it’s wrong, you’ll get an OpenSSL::Cipher::CipherError (authentication failure).