Skip to content

Repository files navigation

TimePad

Build and tests License: MIT C11 libsodium Platforms Status

A C11 replacement for the Python numbers-station experiments in TimePad_Spying. It provides authenticated file encryption and a decimal one-time-pad experiment for research and education. No Python runtime or scripts are required.

Created and maintained by Renan Rocha (valinux).

Setup and how-tos · How it works · Case study · Security · Contributing · Changelog

No implementation is guaranteed “unbreakable.” The normal encode/decode commands use libsodium's XChaCha20-Poly1305 authenticated encryption. The explicit otp-* commands demonstrate decimal pads without authentication. Both preserve every input byte, including UTF-8, whitespace, and binary data.

Mode Purpose Authentication Secret material
encode / decode Encrypt and recover files, up to 64 MiB XChaCha20-Poly1305 tag One 32-byte key; a new random nonce is generated for each encryption.
otp-encode / otp-decode Study decimal pads, up to 1 MiB None A new secret pad with three digits per input byte, never reused for another encryption.

How it works

TimePad authenticated encryption: a shared secret key and a fresh nonce encrypt a local file; the receiver verifies authentication before writing the recovered file.

keygen creates a secret key file. encode uses that key and a fresh random nonce to produce a binary file containing a header, ciphertext, and authentication tag. decode verifies authenticity before writing the original bytes back to a new file. Senders and recipients arrange key distribution separately; TimePad does not transmit files or provide a messaging service.

See the illustrated explanation for the complete flow, the role of nonces, a worked decimal example, and the security limitations.

Build

Requirements: macOS or Linux, a C11 compiler, CMake 3.16+, pkg-config, and libsodium 1.0.18+. Native Windows is not currently supported.

git clone https://github.com/valinux/TimePad_Spying.git
cd TimePad_Spying
# macOS (Homebrew)
brew install cmake pkg-config libsodium

# Debian/Ubuntu alternative
sudo apt-get install build-essential cmake pkg-config libsodium-dev
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failure

If pkg-config cannot find a Homebrew installation, run: export PKG_CONFIG_PATH="$(brew --prefix libsodium)/lib/pkgconfig" and configure again.

Encode and decode

Run in a private directory. Every command takes file paths, never literal keys or message contents. Outputs must be new paths; existing files are not replaced.

umask 077
printf 'Meet me at 09:00.\n' > message.txt
./build/timepad keygen research.key
./build/timepad encode research.key message.txt message.tpd
./build/timepad decode research.key message.tpd recovered.txt
cmp message.txt recovered.txt

The key is 32 random binary bytes; it is not a password. Keep it secret and arrange its transfer separately through an already trusted confidential channel. A lost key cannot be recovered. encode generates a fresh random 24-byte nonce for each message; this mode supports multiple messages per key. Use a new key after a suspected compromise.

Decryption verifies the complete message before creating output. A wrong key, modified header/ciphertext/tag, truncation, or appended data causes failure. Inputs are limited to 64 MiB of plaintext, processed in memory. Encryption adds 48 bytes. Empty files are supported.

Files created by the program have mode 0600. Keys and pads must be owned by the current user without group/other permission bits; use chmod 600 FILE after a transfer if necessary. Inputs must be regular files, and final-component symlinks are rejected. Success returns 0, operational/input failure 1, and invalid command syntax 2. Diagnostics go to stderr; secrets are never printed.

Decimal pad experiment

./build/timepad otp-encode message.txt research.pad numbers.txt
./build/timepad otp-decode research.pad numbers.txt recovered-otp.txt
cmp message.txt recovered-otp.txt

Each byte becomes three decimal digits: A becomes 065, a newline 010, and a zero byte 000. A fresh secret digit is added modulo 10 to each message digit; decoding subtracts it. Ciphertext and pad contain three ASCII digits per original byte. Decoding accepts ASCII whitespace between digits. Plaintext is limited to 1 MiB, and each grouped numeric input to 6 MiB, containing at most 3 MiB of digits. Unequal pad/ciphertext lengths, non-digit data, incomplete triples, and decoded values above 255 are rejected. No newline is added or removed from plaintext.

Pad digits come from libsodium's unbiased cryptographic random generator. Pads must remain secret and must never be reused to encrypt another message. otp-encode always creates a new pad and refuses an existing pad path. Copies made elsewhere cannot be tracked or prevented. Decoding does not delete a pad. The program saves the pad before publishing ciphertext; if the second write fails, discard the unused pad reported by the diagnostic.

This mode has no tamper detection. A wrong pad or changed ciphertext can produce plausible, incorrect plaintext. Input validation is not authentication. An ideal OTP's perfect-secrecy proof assumes independent truly uniform pad symbols, a secret pad as long as the numeric message, and exactly one encryption per pad. This program uses an operating-system-backed cryptographic generator; it does not claim information-theoretic security for its generated pads. Use encode/decode when message authentication matters.

Verification and research notes

cmake -S . -B build-sanitize -DCMAKE_BUILD_TYPE=Debug -DTIMEPAD_SANITIZERS=ON
cmake --build build-sanitize --parallel
ctest --test-dir build-sanitize --output-on-failure

The C tests cover binary round trips, every single-bit change in a sample encrypted envelope, wrong keys, every truncation of that envelope, extra bytes, size limits, the documented wire format, decimal arithmetic, and invalid inputs. Shell integration tests cover file permissions, symlinks, FIFOs, collisions, concurrent writers, and failure without publishing decoded output. Checks remain enabled in release builds. GitHub Actions is configured for macOS and Linux.

Optional fuzzing requires a Clang distribution with the libFuzzer runtime:

cmake -S . -B build-fuzz -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \
  -DTIMEPAD_FUZZ=ON -DBUILD_TESTING=OFF
cmake --build build-fuzz --parallel
./build-fuzz/fuzz_timepad -max_total_time=30 -max_len=4096

Read the case study for the original defects, format specification, design choices, and security limitations. The new formats are intentionally incompatible with the Python scripts; their source remains in Git history. The per-message word dictionary, JSON files, and Fernet dependency have been retired.

License and responsibility

Copyright © 2026 Renan Rocha. TimePad is released under the MIT License, including its disclaimer of warranty and limitation of liability.

Users are responsible for their own conduct, compliance with applicable laws, required authorizations, and protection of their keys and data. To the fullest extent permitted by applicable law, the authors and copyright holders disclaim liability for use or misuse as stated in the license. No claim of unbreakable encryption, certification, or independent security audit is made.

Read the responsibility and liability notice. Research and education describe the project's purpose; they do not add use restrictions to the MIT License. External dependencies retain their own licenses; see third-party notices.

Maintainers preparing a release can use the GitHub setup guide for repository topics, description, security reporting, and publication steps.

About

C11 cryptography research CLI: authenticated file encryption with libsodium and a decimal one-time-pad experiment.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages