Cross-implementation research for Cryptash -- hash-based encrypt/decrypt using a single hash function as the only cryptographic primitive.
Cryptash is an encryption scheme built entirely on a hash function. No block ciphers, no key exchange protocols, no external crypto libraries. Any cryptographic hash function can serve as the foundation -- these implementations use SHA-256.
Given a hash function H, password, data, and parameters ivsz and macsz:
Encrypt:
- Generate
ivszbytes ofiv_outerandivszbytes ofiv_innerfrom random Key1 = H( iv_outer || password )MAC = H( iv_inner || data || Key1 ), truncated tomacszbytesprefix = iv_outer || MAC- Encrypt
iv_inner || datausing CBC mode withprefixandKey1as initial parameters:K[0] = H( prefix || Key1 )ct[i] = block[i] XOR K[i]K[i+1] = H( ct[i] || K[i] )
- Output:
prefix || ciphertext
Decrypt:
- Extract
iv_outerandMACfrom prefix Key1 = H( iv_outer || password )- Decrypt ciphertext using CBC mode with
prefixandKey1 - Verify
MACagainstH( decrypted || Key1 ), truncated tomacszbytes - Strip
iv_inner, return data
Properties:
- Outer IV is public, inner IV is encrypted -- effective IV space is
ivsz * 2 - MAC depends on data and Key1, then feeds back into keystream derivation
- Each CBC block key depends on the previous ciphertext block
- Minimum 3 hash calls per operation
| Language | Path | Hash source | Random source |
|---|---|---|---|
| C | C/ |
pure (sha256_pure.h) | /dev/urandom (lazy CSPRNG) |
| JavaScript | js/ |
pure (built-in) | crypto.getRandomValues |
| Go | go/ |
crypto/sha256 (stdlib) | crypto/rand (stdlib) |
| PHP | deemru/Cryptash | hash() (stdlib) | random_bytes() (stdlib) |
C and JS implementations have zero external dependencies.
All implementations are validated against shared test vector files and self-tests covering roundtrip, uniqueness, tamper detection, wrong password, truncation, and determinism.
# C
cd C && make test
# JavaScript
cd js && node test_sha256.mjs && node test_vectors.mjs && node selftest.mjs
# Go
cd go && go test ./...
# PHP (vector generation, requires deemru/Cryptash submodule)
cd php && php gen_vectors.phpiv = 16, mac = 16, typical single-threaded performance:
| Language | Plaintext | Encrypt | Decrypt |
|---|---|---|---|
| C | 16 B | 343 Kops (2.9 us) | 674 Kops (1.5 us) |
| C | 200 B | 122 Kops (8.2 us) | 171 Kops (5.9 us) |
| C | 1000 B | 34 Kops (30 us) | 41 Kops (25 us) |
| Go | 16 B | 492 Kops (2.0 us) | 566 Kops (1.8 us) |
| Go | 200 B | 137 Kops (7.3 us) | 143 Kops (7.0 us) |
| Go | 1000 B | 33 Kops (30 us) | 34 Kops (29 us) |
| PHP | 16 B | 233 Kops (4.3 us) | 497 Kops (2.0 us) |
| PHP | 200 B | 100 Kops (10 us) | 134 Kops (7.4 us) |
| PHP | 1000 B | 31 Kops (32 us) | 32 Kops (31 us) |
| JS | 16 B | 96 Kops (10 us) | 270 Kops (3.7 us) |
| JS | 200 B | 50 Kops (20 us) | 79 Kops (13 us) |
| JS | 1000 B | 17 Kops (58 us) | 20 Kops (50 us) |
With iv = 16 and mac = 16 (recommended):
- IV space: outer IV (16 bytes, public) + inner IV (16 bytes, encrypted) = 256 bits effective randomization
- MAC brute-force: MAC enters the keystream derivation, creating a fixed-point equation rather than a simple search. Experimental results show Poisson distribution (lambda = 1) for collisions, with ~37% of cases having zero collisions beyond the original
- Interdependence: all elements (outer IV, inner IV, MAC, keys, ciphertext) are interdependent -- changing any one invalidates all others. Attack on ciphertext reduces to preimage attack on the underlying hash function
Recommended parameters: iv = 16, mac = 16 (48 bytes overhead: outer IV + MAC + inner IV).
- deemru/Cryptash -- original PHP implementation