Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84e2ee5639 | |||
| 0f3af174b0 | |||
| 3f0a258c21 | |||
| 2a5aec7dce | |||
| 6c796df30a | |||
| 09ea57f146 | |||
| 0ae2423f19 | |||
| 3859e6492a | |||
| 0978d0323a | |||
| 0ea8b2dd32 | |||
| 12f92d2c96 | |||
| aea69148a8 | |||
| d537bc4948 | |||
| 42a8f5c358 | |||
| 7a30949ddd | |||
| eb8a5b6565 | |||
| d0a5628072 | |||
| 5498a2321e | |||
| fe2eb40ead | |||
| 0db1988d8f | |||
| 97530c8eb3 | |||
| a85c4ed55b | |||
| a9974c7e87 | |||
| 592d54728b | |||
| 21b3c4de52 | |||
| 3a854c3ccf | |||
| 877add0dbf | |||
| 482687cb68 | |||
| e35d94243e | |||
| e88e1b5d3d | |||
| 41ef97c43e | |||
| 7810e66114 | |||
| b4be05c34d | |||
| 1cb0ba935d | |||
| 8c8c873e73 | |||
| 692f65b7f0 | |||
| 1c4200a73a | |||
| 1c9e2ee527 | |||
| 8401e14ae0 | |||
| 0dbd81d1cc | |||
| f979789c11 | |||
| 498d7d31c4 | |||
| e58f05619e | |||
| 992b9349b3 | |||
| 1f4a1fb90f | |||
| c7fae1ad1d | |||
| 37bcb6a6d2 | |||
| 9ded0aed44 | |||
| 4442837ce8 | |||
| 31ee220558 | |||
| 0a25c13b65 | |||
| fd9d87c548 | |||
| c1aa29cd73 | |||
| 75e52d48dc | |||
| 28947a53a3 | |||
| 5a611a9dc0 | |||
| aff8bea0a2 | |||
| 864c0356da | |||
| 35175790e2 | |||
| 04ea4fb848 | |||
| 5c61ba7ea8 | |||
| a45b304d22 | |||
| 403d013224 | |||
| 82533d96e4 | |||
| 5b619384a1 | |||
| 12b9884572 | |||
| 83b60b5cc2 | |||
| 2d6546ab83 | |||
| c255185084 | |||
| 24800d69d5 | |||
| 7e50727163 | |||
| f118c23c60 | |||
| b149175f24 | |||
| 206e8042d8 | |||
| 2a5249d93c | |||
| 0e02eaee53 | |||
| e32eb8b2b1 | |||
| 0aecae0c5e | |||
| fa90e0eafd | |||
| 2a10d974b2 | |||
| 7e04896394 | |||
| 0cdf6e7804 | |||
| 268758a21b | |||
| 85ef39d283 | |||
| 2c864f1feb | |||
| ae0afcfffd | |||
| e45aa04b05 | |||
| 8e1fcdb108 | |||
| 29f4a67c1c | |||
| 146da4e883 | |||
| 3152a7777f | |||
| 68a2a0c252 | |||
| bb17b0a7be | |||
| 487432c399 | |||
| f521349cc0 | |||
| 4b822962bb |
29
.clinerules/workspace_rules.md
Normal file
29
.clinerules/workspace_rules.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
When building, use build.sh, not make.
|
||||||
|
|
||||||
|
Use it as follows: build.sh -m "useful comment on changes being made"
|
||||||
|
|
||||||
|
When making TUI menus, try to use the first leter of the command and the key to press to execute that command. For example, if the command is "Open file" try to use a keypress of "o" upper or lower case to signal to open the file. Use this instead of number keyed menus when possible. In the command, the letter should be underlined that signifies the command.
|
||||||
|
|
||||||
|
## Buffer Size Guidelines
|
||||||
|
|
||||||
|
### Path Handling
|
||||||
|
- Always use buffers of size 1024 or PATH_MAX (4096) for file paths
|
||||||
|
- When concatenating paths with snprintf, ensure buffer is at least 2x the expected maximum input
|
||||||
|
- Use safer path construction patterns that check lengths before concatenation
|
||||||
|
|
||||||
|
### String Formatting Safety
|
||||||
|
- Before using snprintf with dynamic strings, validate that buffer size >= sum of all input string lengths + format characters + 1
|
||||||
|
- Use strnlen() to check actual string lengths before formatting
|
||||||
|
- Consider using asprintf() for dynamic allocation when exact size is unknown
|
||||||
|
- Add length validation before snprintf calls
|
||||||
|
|
||||||
|
### Compiler Warning Prevention
|
||||||
|
- Always size string buffers generously (minimum 1024 for paths, 512 for general strings)
|
||||||
|
- Use buffer size calculations: `size >= strlen(str1) + strlen(str2) + format_overhead + 1`
|
||||||
|
- Add runtime length checks before snprintf operations
|
||||||
|
- Consider using safer alternatives like strlcpy/strlcat if available
|
||||||
|
|
||||||
|
### Code Patterns to Avoid
|
||||||
|
- Fixed-size buffers (512 bytes) for path operations where inputs could be 255+ bytes each
|
||||||
|
- Concatenating unchecked strings with snprintf
|
||||||
|
- Assuming maximum path component sizes without validation
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,6 @@
|
|||||||
|
otp
|
||||||
pads/
|
pads/
|
||||||
Gemini.md
|
Gemini.md
|
||||||
|
TropicOfCancer-HenryMiller.txt
|
||||||
|
|
||||||
|
# Auto-generated files (none currently)
|
||||||
|
|||||||
13
Makefile
13
Makefile
@@ -1,11 +1,18 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -std=c99
|
CFLAGS = -Wall -Wextra -std=c99
|
||||||
LIBS = -lssl -lcrypto
|
LIBS = -lm
|
||||||
|
LIBS_STATIC = -static -lm
|
||||||
TARGET = otp
|
TARGET = otp
|
||||||
SOURCE = otp.c
|
SOURCE = otp.c
|
||||||
|
CHACHA20_SOURCE = nostr_chacha20.c
|
||||||
|
|
||||||
|
# Default build target
|
||||||
$(TARGET): $(SOURCE)
|
$(TARGET): $(SOURCE)
|
||||||
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(LIBS)
|
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(CHACHA20_SOURCE) $(LIBS)
|
||||||
|
|
||||||
|
# Static linking target
|
||||||
|
static: $(SOURCE)
|
||||||
|
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(CHACHA20_SOURCE) $(LIBS_STATIC)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET) *.pad *.state
|
rm -f $(TARGET) *.pad *.state
|
||||||
@@ -16,4 +23,4 @@ install:
|
|||||||
uninstall:
|
uninstall:
|
||||||
sudo rm -f /usr/local/bin/$(TARGET)
|
sudo rm -f /usr/local/bin/$(TARGET)
|
||||||
|
|
||||||
.PHONY: clean install uninstall
|
.PHONY: clean install uninstall static
|
||||||
|
|||||||
466
README.md
466
README.md
@@ -1,273 +1,307 @@
|
|||||||
# OTP Cipher v2.0 - Enhanced One Time Pad Implementation
|
# OTP Cipher - One Time Pad Implementation
|
||||||
|
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
A secure one-time pad (OTP) cipher implementation in C.
|
||||||
|
|
||||||
|
## Why One-Time Pads
|
||||||
|
|
||||||
|
Nostr and much of the web runs on public key cryptography. Public key cryptography is great, but it is vulnerable. Cryptographers know this, and they know what it takes to attack it, so what they do is just make the keys large enough such that the system is resistant to attack given computers as they are today.
|
||||||
|
|
||||||
|
There is one type of cryptography, however, that is invulnerable to any type of attack in our universe, and that is known as a one-time pad.
|
||||||
|
|
||||||
|
One-time pads rely directly on the laws of physics and what it means for a number to be truly random.
|
||||||
|
|
||||||
|
If you take your secret message and mix it with truly random numbers, and don't use those random numbers again, then that message is unbreakable by any computer, no matter how powerful, quantum or not, forever.
|
||||||
|
|
||||||
|
In fact, one-time pads are so powerful that if you have data encrypted by a one-time pad located in a distant galaxy, and that data is not kept anywhere else, then by destroying the pad used for encryption in your galaxy, the data is wiped from the universe and can never be recovered.
|
||||||
|
|
||||||
|
## Advantages and Limitations
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
1. The pad must be shared between the parties wanting to use it.
|
||||||
|
2. The pad must be as long or longer than what you want to encrypt, and it can't be used a second time.
|
||||||
|
|
||||||
|
### Modern Advantages
|
||||||
|
|
||||||
|
While in the past, pad length might have been a problem, readily available USB drives in the terabytes make size less of a problem for many uses.
|
||||||
|
|
||||||
|
We are also becoming very accustomed to YubiKey authenticators in the USB ports of our computers. A small USB drive in our devices can now easily contain a key of greater length than all the text messages we would expect to send over a lifetime.
|
||||||
|
|
||||||
|
### Multi-Device Coordination
|
||||||
|
|
||||||
|
One of the problems to address is the fact that to use an OTP across several devices means that they have to coordinate to know when they are encrypting new plaintext and where to start in the key. Reusing the same section of the pad, while not necessarily fatal, degrades the encryption from its status as "Information Theoretically Secure".
|
||||||
|
|
||||||
|
To address this problem, we can use Nostr to share among devices the place in the pad that was last left off.
|
||||||
|
|
||||||
|
### Additional Benefits
|
||||||
|
|
||||||
|
One-time pads can be trivially encrypted and decrypted using pencil and paper, making them accessible even without electronic devices.
|
||||||
|
|
||||||
A comprehensive and user-friendly One Time Pad (OTP) cryptographic system implemented in C for Linux, supporting massive pad sizes up to 10TB+ with both interactive and command-line interfaces.
|
|
||||||
|
|
||||||
## New in Version 2.0 🚀
|
|
||||||
|
|
||||||
- **Interactive Menu System** - User-friendly menu-driven interface
|
|
||||||
- **Smart Size Parsing** - Supports K/KB/M/MB/G/GB/T/TB units
|
|
||||||
- **Partial Hash Matching** - Use hash prefixes or pad numbers for selection
|
|
||||||
- **Progress Indicators** - Real-time progress for large pad generation
|
|
||||||
- **10TB+ Support** - Generate massive pads for external drives
|
|
||||||
- **Enhanced Pad Management** - List, info, and usage statistics
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Cryptographically secure** random pad generation using `/dev/urandom`
|
- **Perfect Security**: Implements true one-time pad encryption with information-theoretic security
|
||||||
- **ASCII armor format** similar to PGP for encrypted messages
|
- **Text & File Encryption**: Supports both inline text and file encryption
|
||||||
- **Integrity verification** using SHA-256 hashing of pad files
|
- **Multiple Output Formats**: Binary (.otp) and ASCII armored (.otp.asc) file formats
|
||||||
- **State management** to prevent pad reuse
|
- **Keyboard Entropy**: Optional keyboard entropy collection for enhanced randomness
|
||||||
- **Interactive text encryption/decryption**
|
- **Short Command Flags**: Convenient single-character flags for all operations
|
||||||
- **Hash-based file naming** for content verification
|
- **Automatic Versioning**: Built-in semantic versioning with automatic patch increment
|
||||||
- **Read-only pad protection** prevents accidental corruption
|
- **Multiple Build Options**: Standard and static linking builds
|
||||||
|
- **Cross-Platform**: Works on Linux and other UNIX-like systems
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- OpenSSL development libraries (`libssl-dev` on Ubuntu/Debian)
|
|
||||||
- GCC compiler
|
|
||||||
|
|
||||||
### Install dependencies on Ubuntu/Debian:
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install libssl-dev build-essential
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- GCC compiler
|
||||||
|
- Git (for version tracking)
|
||||||
|
- Make
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Build Commands
|
||||||
|
|
||||||
|
Use the included build script for automatic versioning:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make
|
# Standard build (default)
|
||||||
|
./build.sh build
|
||||||
|
|
||||||
|
# Static linking build
|
||||||
|
./build.sh static
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
./build.sh clean
|
||||||
|
|
||||||
|
# Generate version files only
|
||||||
|
./build.sh version
|
||||||
|
|
||||||
|
# Install to system
|
||||||
|
./build.sh install
|
||||||
|
|
||||||
|
# Remove from system
|
||||||
|
./build.sh uninstall
|
||||||
|
|
||||||
|
# Show usage
|
||||||
|
./build.sh help
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create the `otp` executable.
|
### Traditional Make
|
||||||
|
|
||||||
## Usage Modes
|
You can also use make directly (without automatic versioning):
|
||||||
|
|
||||||
### Interactive Mode (Recommended)
|
```bash
|
||||||
|
make # Standard build
|
||||||
|
make static # Static linking
|
||||||
|
make clean # Clean artifacts
|
||||||
|
make install # Install to /usr/local/bin/
|
||||||
|
make uninstall # Remove from system
|
||||||
|
```
|
||||||
|
|
||||||
Simply run the program without arguments:
|
## Usage
|
||||||
|
|
||||||
|
### Interactive Mode
|
||||||
```bash
|
```bash
|
||||||
./otp
|
./otp
|
||||||
```
|
```
|
||||||
|
|
||||||
This launches a menu-driven interface:
|
|
||||||
```
|
|
||||||
=== OTP Cipher Interactive Mode ===
|
|
||||||
Version: OTP-CIPHER 2.0
|
|
||||||
|
|
||||||
=== Main Menu ===
|
|
||||||
1. Generate new pad
|
|
||||||
2. Encrypt message
|
|
||||||
3. Decrypt message
|
|
||||||
4. List available pads
|
|
||||||
5. Show pad information
|
|
||||||
6. Exit
|
|
||||||
```
|
|
||||||
|
|
||||||
### Command Line Mode
|
### Command Line Mode
|
||||||
|
|
||||||
For automation and scripting:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./otp generate <size> # Generate new pad
|
# Generate a new pad
|
||||||
./otp encrypt <pad_hash_prefix> # Encrypt text
|
|
||||||
./otp decrypt <pad_hash_prefix> # Decrypt message
|
|
||||||
./otp list # List available pads
|
|
||||||
```
|
|
||||||
|
|
||||||
## Smart Size Parsing
|
|
||||||
|
|
||||||
The system intelligently parses size specifications:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./otp generate 1024 # 1024 bytes
|
|
||||||
./otp generate 5MB # 5 megabytes
|
|
||||||
./otp generate 2GB # 2 gigabytes
|
|
||||||
./otp generate 10TB # 10 terabytes
|
|
||||||
./otp generate 1.5GB # 1.5 gigabytes (decimal supported)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Supported units:** K, KB, M, MB, G, GB, T, TB (case insensitive)
|
|
||||||
|
|
||||||
## Pad Selection
|
|
||||||
|
|
||||||
Multiple convenient ways to select pads:
|
|
||||||
|
|
||||||
1. **Full hash**: `./otp encrypt a1b2c3d4e5f6789012345678901234567890abcdef...`
|
|
||||||
2. **Hash prefix**: `./otp encrypt a1b2c3d4`
|
|
||||||
3. **Pad number**: `./otp encrypt 1` (from list output)
|
|
||||||
|
|
||||||
## Example Workflows
|
|
||||||
|
|
||||||
### Basic Usage
|
|
||||||
```bash
|
|
||||||
# Generate a 1GB pad
|
|
||||||
./otp generate 1GB
|
./otp generate 1GB
|
||||||
Generated pad: a1b2c3d4e5f6789...123456.pad (1.00 GB)
|
|
||||||
Pad hash: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
|
# Encrypt text (interactive input)
|
||||||
|
./otp encrypt <pad_hash_or_prefix>
|
||||||
|
|
||||||
|
# Decrypt message (interactive input)
|
||||||
|
./otp decrypt <pad_hash_or_prefix>
|
||||||
|
|
||||||
# List available pads
|
# List available pads
|
||||||
./otp list
|
./otp list
|
||||||
Available pads:
|
|
||||||
No. Hash (first 16 chars) Size Used
|
|
||||||
--- ------------------- ---------- ----------
|
|
||||||
1 a1b2c3d4e5f67890 1.00GB 0.0MB
|
|
||||||
|
|
||||||
# Encrypt using hash prefix
|
|
||||||
./otp encrypt a1b2
|
|
||||||
Enter text to encrypt: Secret message
|
|
||||||
-----BEGIN OTP MESSAGE-----
|
|
||||||
Version: OTP-CIPHER 2.0
|
|
||||||
Pad-Hash: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
|
|
||||||
Pad-Offset: 0
|
|
||||||
|
|
||||||
U2VjcmV0IG1lc3NhZ2U=
|
|
||||||
-----END OTP MESSAGE-----
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Large Scale Usage
|
## Version System Details
|
||||||
|
|
||||||
|
### Automatic Version Increment
|
||||||
|
Every build automatically increments the patch version:
|
||||||
|
- v0.1.0 → v0.1.1 → v0.1.2, etc.
|
||||||
|
- Creates git tags for each version
|
||||||
|
- Embeds detailed build information
|
||||||
|
|
||||||
|
### Manual Version Control
|
||||||
|
For major/minor releases, create tags manually:
|
||||||
```bash
|
```bash
|
||||||
# Generate a 5TB pad for external drive
|
# Feature release (minor bump)
|
||||||
./otp generate 5TB
|
git tag v0.2.0 # Next build: v0.2.1
|
||||||
Progress: 100.0% (85.2 MB/s, ETA: 0s)
|
|
||||||
Generated pad: f9e8d7c6b5a4932...654321.pad (5.00 TB)
|
|
||||||
|
|
||||||
# Use pad number for quick selection
|
# Breaking change (major bump)
|
||||||
./otp encrypt 1
|
git tag v1.0.0 # Next build: v1.0.1
|
||||||
Enter text to encrypt: Classified information
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive Mode Workflow
|
### Version Information Available
|
||||||
```bash
|
- Version number (major.minor.patch)
|
||||||
./otp
|
- Git commit hash and branch
|
||||||
# Select option 1 to generate
|
- Build date and time
|
||||||
# Enter size: 10GB
|
- Full version display with metadata
|
||||||
# Select option 2 to encrypt
|
|
||||||
# Choose pad from list
|
### Generated Files
|
||||||
# Enter your message
|
The build system automatically manages Git versioning by incrementing tags.
|
||||||
```
|
|
||||||
|
These files are excluded from git (.gitignore) and regenerated on each build.
|
||||||
|
|
||||||
## Security Features
|
## Security Features
|
||||||
|
|
||||||
### Perfect Forward Secrecy
|
- Uses `/dev/urandom` for cryptographically secure random number generation
|
||||||
Each message uses a unique portion of the pad that is never reused, ensuring perfect forward secrecy.
|
- Optional keyboard entropy mixing using simple XOR operations
|
||||||
|
- Custom 256-bit XOR checksum for pad identification (encrypted with pad data)
|
||||||
### Content-Based Integrity
|
- Read-only pad files to prevent accidental modification
|
||||||
- **SHA-256 file naming**: Pad files named by their hash ensure content verification
|
- State tracking to prevent pad reuse
|
||||||
- **Integrity checking**: Embedded hashes detect pad corruption/tampering
|
- **Zero external crypto dependencies** - completely self-contained implementation
|
||||||
- **Read-only protection**: Pad files automatically set to read-only after creation
|
|
||||||
|
|
||||||
### ASCII Armor Format
|
|
||||||
Messages use a PGP-like ASCII armor format:
|
|
||||||
```
|
|
||||||
-----BEGIN OTP MESSAGE-----
|
|
||||||
Version: OTP-CIPHER 2.0
|
|
||||||
Pad-Hash: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
|
|
||||||
Pad-Offset: 0
|
|
||||||
|
|
||||||
U2VjcmV0IG1lc3NhZ2U=
|
|
||||||
-----END OTP MESSAGE-----
|
|
||||||
```
|
|
||||||
|
|
||||||
### State Management
|
|
||||||
- **Automatic tracking**: Prevents pad reuse through state files
|
|
||||||
- **Portable state**: State stored separately from immutable pad data
|
|
||||||
- **Usage statistics**: Track pad consumption and remaining capacity
|
|
||||||
|
|
||||||
## File Structure
|
## File Structure
|
||||||
|
|
||||||
**Source Files:**
|
|
||||||
- `otp.c` - Complete implementation (850+ lines)
|
|
||||||
- `Makefile` - Build configuration
|
|
||||||
- `README.md` - This documentation
|
|
||||||
|
|
||||||
**Generated Files:**
|
|
||||||
- `otp` - Compiled executable
|
|
||||||
- `<hash>.pad` - Pad files (read-only, hash-named)
|
|
||||||
- `<hash>.state` - State files (writable, tracks usage)
|
|
||||||
|
|
||||||
## Advanced Features
|
|
||||||
|
|
||||||
### Progress Indicators
|
|
||||||
For large pads, see real-time generation progress:
|
|
||||||
```
|
```
|
||||||
Generating pad...
|
otp/
|
||||||
Progress: 45.2% (78.5 MB/s, ETA: 125s)
|
├── build.sh # Build script with automatic versioning
|
||||||
|
├── Makefile # Traditional make build system
|
||||||
|
├── otp.c # Main source code
|
||||||
|
├── README.md # This file
|
||||||
|
├── .gitignore # Git ignore rules
|
||||||
|
├── pads/ # OTP pad storage directory (created at runtime)
|
||||||
|
└── VERSION # Plain text version (generated)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pad Information
|
## File Formats
|
||||||
Detailed statistics for each pad:
|
|
||||||
|
### .otp File Format (Binary)
|
||||||
|
|
||||||
|
Binary encrypted files use a structured header format:
|
||||||
|
|
||||||
|
```
|
||||||
|
Offset | Size | Field | Description
|
||||||
|
-------|------|-------------------|----------------------------------
|
||||||
|
0 | 4 | Magic | "OTP\0" - File type identifier
|
||||||
|
4 | 2 | Version | Format version (currently 1)
|
||||||
|
6 | 32 | Pad Checksum | Binary pad checksum (32 bytes)
|
||||||
|
38 | 8 | Pad Offset | Offset in pad file (uint64_t)
|
||||||
|
46 | 4 | File Mode | Original file permissions (uint32_t)
|
||||||
|
50 | 8 | File Size | Original file size (uint64_t)
|
||||||
|
58 | var | Encrypted Data | XOR-encrypted file contents
|
||||||
|
```
|
||||||
|
|
||||||
|
### .otp.asc File Format (ASCII Armored)
|
||||||
|
|
||||||
|
ASCII armored files use the same format as encrypted text messages:
|
||||||
|
|
||||||
|
```
|
||||||
|
-----BEGIN OTP MESSAGE-----
|
||||||
|
Version: v0.2.15
|
||||||
|
Pad-ChkSum: <64-character-hex-checksum>
|
||||||
|
Pad-Offset: <decimal-offset-value>
|
||||||
|
|
||||||
|
<base64-encoded-encrypted-data>
|
||||||
|
-----END OTP MESSAGE-----
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** ASCII armored files do not preserve original file permissions metadata.
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Short Command Flags
|
||||||
```bash
|
```bash
|
||||||
./otp list
|
# Quick commands using short flags
|
||||||
No. Hash (first 16 chars) Size Used
|
./otp -g 1GB # Generate 1GB pad
|
||||||
--- ------------------- ---------- ----------
|
./otp -l # List available pads
|
||||||
1 a1b2c3d4e5f67890 5.00TB 2.1GB
|
./otp -e 1a2b "Hello world" # Encrypt text inline
|
||||||
2 f9e8d7c6b5a49321 1.00GB 0.5GB
|
./otp -d "-----BEGIN OTP..." # Decrypt message inline
|
||||||
|
|
||||||
|
# File operations
|
||||||
|
./otp -f document.pdf 1a2b # Encrypt file (binary)
|
||||||
|
./otp -f document.pdf 1a2b -a # Encrypt file (ASCII)
|
||||||
|
./otp -f document.pdf 1a2b -o secret.otp # Custom output name
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple Pad Management
|
### Text Encryption
|
||||||
- List all available pads
|
|
||||||
- Show detailed information per pad
|
|
||||||
- Track usage across multiple pads
|
|
||||||
- Quick selection by number or prefix
|
|
||||||
|
|
||||||
## Performance
|
|
||||||
|
|
||||||
### Size Limits
|
|
||||||
- **Theoretical maximum**: 18 exabytes (uint64_t limit)
|
|
||||||
- **Practical maximum**: Limited by available disk space
|
|
||||||
- **Tested up to**: 10TB+ on modern systems
|
|
||||||
- **Generation speed**: ~80-120 MB/s (system dependent)
|
|
||||||
|
|
||||||
### Memory Efficiency
|
|
||||||
- **Streaming operation**: Constant memory usage regardless of pad size
|
|
||||||
- **64KB buffers**: Efficient I/O without excessive memory consumption
|
|
||||||
- **Large file support**: Handles multi-terabyte pads efficiently
|
|
||||||
|
|
||||||
## Security Notes
|
|
||||||
|
|
||||||
⚠️ **Critical Security Requirements:**
|
|
||||||
|
|
||||||
1. **Never reuse pad data** - Automatic prevention through state tracking
|
|
||||||
2. **Secure pad distribution** - Use secure channels for pad sharing
|
|
||||||
3. **Physical security** - Protect pad files like encryption keys
|
|
||||||
4. **Verify integrity** - Always check pad hash verification during decryption
|
|
||||||
5. **Secure systems** - Generate pads on trusted systems with good entropy
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Local Installation
|
|
||||||
```bash
|
```bash
|
||||||
make install # Install to /usr/local/bin
|
# Interactive text encryption
|
||||||
make uninstall # Remove from system
|
./otp encrypt 1a2b3c
|
||||||
|
Enter text to encrypt: This is my secret message
|
||||||
|
# Outputs ASCII armored message
|
||||||
|
|
||||||
|
# Inline text encryption
|
||||||
|
./otp -e 1a2b3c "This is my secret message"
|
||||||
|
# Outputs ASCII armored message immediately
|
||||||
```
|
```
|
||||||
|
|
||||||
### Clean Up
|
### File Encryption
|
||||||
```bash
|
```bash
|
||||||
make clean # Remove compiled files and generated pads
|
# Binary format (preserves metadata)
|
||||||
|
./otp -f sensitive.doc a1b2c3
|
||||||
|
|
||||||
|
# ASCII armored format (text-safe)
|
||||||
|
./otp -f sensitive.doc a1b2c3 -a
|
||||||
|
|
||||||
|
# Custom output filename
|
||||||
|
./otp -f sensitive.doc a1b2c3 -o encrypted_document.otp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Technical Specifications
|
### Decryption
|
||||||
|
```bash
|
||||||
|
# Auto-detect format and pad from message/file
|
||||||
|
./otp -d encrypted.otp.asc
|
||||||
|
./otp -d "-----BEGIN OTP MESSAGE-----..."
|
||||||
|
|
||||||
- **Entropy source**: `/dev/urandom` (cryptographically secure)
|
# Interactive mode
|
||||||
- **Hash algorithm**: SHA-256 for integrity verification
|
./otp decrypt
|
||||||
- **Encoding**: Base64 for ciphertext representation
|
# Prompts for encrypted message input
|
||||||
- **File format**: ASCII armor with embedded metadata
|
```
|
||||||
- **Architecture**: Single C file, ~850 lines
|
|
||||||
- **Dependencies**: OpenSSL libcrypto
|
|
||||||
- **Platform**: Linux (easily portable)
|
|
||||||
|
|
||||||
## Theory
|
### Build and Version Tracking
|
||||||
|
```bash
|
||||||
|
$ ./build.sh build
|
||||||
|
[INFO] Incrementing version...
|
||||||
|
[INFO] Current version: v0.2.14
|
||||||
|
[INFO] New version: v0.2.15
|
||||||
|
[SUCCESS] Created new version tag: v0.2.15
|
||||||
|
[SUCCESS] Build completed successfully
|
||||||
|
|
||||||
A One Time Pad is theoretically unbreakable when implemented correctly with:
|
$ ./otp --help
|
||||||
- **Perfect randomness**: Cryptographically secure entropy
|
OTP Cipher - One Time Pad Implementation v0.2.15
|
||||||
- **Key length**: Equal to or greater than message length
|
Built on 2025-08-10 at 14:07:58 from commit ae0afcf on branch master
|
||||||
- **Single use**: Each pad portion used exactly once
|
```
|
||||||
- **Secure distribution**: Pads shared through secure channels
|
|
||||||
|
|
||||||
This implementation satisfies all requirements for perfect cryptographic security while providing modern usability features for practical deployment.
|
### Advanced Features
|
||||||
|
```bash
|
||||||
|
# Generate pad with keyboard entropy
|
||||||
|
./otp generate 5GB
|
||||||
|
# Follow prompts for keyboard entropy collection
|
||||||
|
|
||||||
## Version History
|
# Check pad usage
|
||||||
|
./otp -l
|
||||||
|
Available pads:
|
||||||
|
No. ChkSum (first 16 chars) Size Used % Used
|
||||||
|
--- ------------------- ---------- ---------- ------
|
||||||
|
1 97d9d82b5414a943 1.00GB 156B 0.0%
|
||||||
|
2 0c8e19fde996e683 1000B 248B 24.8%
|
||||||
|
|
||||||
- **v2.0**: Interactive mode, smart parsing, 10TB+ support, enhanced UX
|
# Show detailed pad information
|
||||||
- **v1.0**: Basic command-line implementation with hash-based naming
|
./otp
|
||||||
|
# Select "S" for show pad info, enter checksum or prefix
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project includes automatic versioning system based on the Generic Automatic Version Increment System.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
When contributing:
|
||||||
|
1. The version will automatically increment on builds
|
||||||
|
2. For major features, consider manually creating minor version tags
|
||||||
|
3. Generated version files (`src/version.*`, `VERSION`) should not be committed
|
||||||
|
|||||||
3
TODO.md
Normal file
3
TODO.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
## The pad menu in interactive encrypt mode gives numbers instead of checksum selection
|
||||||
228
build.sh
Executable file
228
build.sh
Executable file
@@ -0,0 +1,228 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
print_status() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||||
|
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||||
|
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||||
|
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# Global variable for commit message
|
||||||
|
COMMIT_MESSAGE=""
|
||||||
|
|
||||||
|
# Parse command line arguments for -m flag
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-m|--message)
|
||||||
|
COMMIT_MESSAGE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Keep other arguments for main logic
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Function to automatically increment version
|
||||||
|
increment_version() {
|
||||||
|
print_status "Incrementing version..."
|
||||||
|
|
||||||
|
# Check if we're in a git repository
|
||||||
|
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||||
|
print_warning "Not in a git repository - skipping version increment"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the highest version tag (not chronologically latest)
|
||||||
|
LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0")
|
||||||
|
if [[ -z "$LATEST_TAG" ]]; then
|
||||||
|
LATEST_TAG="v0.1.0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract version components (remove 'v' prefix)
|
||||||
|
VERSION=${LATEST_TAG#v}
|
||||||
|
|
||||||
|
# Parse major.minor.patch using regex
|
||||||
|
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||||
|
MAJOR=${BASH_REMATCH[1]}
|
||||||
|
MINOR=${BASH_REMATCH[2]}
|
||||||
|
PATCH=${BASH_REMATCH[3]}
|
||||||
|
else
|
||||||
|
print_error "Invalid version format in tag: $LATEST_TAG"
|
||||||
|
print_error "Expected format: v0.1.0"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increment patch version
|
||||||
|
NEW_PATCH=$((PATCH + 1))
|
||||||
|
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
|
||||||
|
|
||||||
|
print_status "Current version: $LATEST_TAG"
|
||||||
|
print_status "New version: $NEW_VERSION"
|
||||||
|
|
||||||
|
# Stage all changes
|
||||||
|
if git add . 2>/dev/null; then
|
||||||
|
print_success "Staged all changes"
|
||||||
|
else
|
||||||
|
print_warning "Failed to stage changes (maybe not a git repository)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle commit message - use global variable if set, otherwise prompt
|
||||||
|
if [[ -z "$COMMIT_MESSAGE" ]]; then
|
||||||
|
echo ""
|
||||||
|
print_status "Please enter a meaningful commit message for version $NEW_VERSION:"
|
||||||
|
echo -n "> "
|
||||||
|
read -r COMMIT_MESSAGE
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if user provided a message
|
||||||
|
if [[ -z "$COMMIT_MESSAGE" ]]; then
|
||||||
|
print_warning "No commit message provided. Using default message."
|
||||||
|
COMMIT_MESSAGE="Automatic version increment"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Commit changes with user-provided message
|
||||||
|
if git commit -m "Version $NEW_VERSION - $COMMIT_MESSAGE" 2>/dev/null; then
|
||||||
|
print_success "Committed changes for version $NEW_VERSION"
|
||||||
|
else
|
||||||
|
print_warning "Failed to commit changes (maybe no changes to commit or not a git repository)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create new git tag
|
||||||
|
if git tag "$NEW_VERSION" 2>/dev/null; then
|
||||||
|
print_success "Created new version tag: $NEW_VERSION"
|
||||||
|
|
||||||
|
# Push changes and tags to remote repository
|
||||||
|
if git push 2>/dev/null; then
|
||||||
|
print_success "Pushed changes to remote repository"
|
||||||
|
else
|
||||||
|
print_warning "Failed to push changes to remote repository"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git push --tags 2>/dev/null; then
|
||||||
|
print_success "Pushed tags to remote repository"
|
||||||
|
else
|
||||||
|
print_warning "Failed to push tags to remote repository"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_warning "Tag $NEW_VERSION already exists - using existing version"
|
||||||
|
NEW_VERSION=$LATEST_TAG
|
||||||
|
# Re-extract version components for existing tag
|
||||||
|
VERSION=${NEW_VERSION#v}
|
||||||
|
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||||
|
MAJOR=${BASH_REMATCH[1]}
|
||||||
|
MINOR=${BASH_REMATCH[2]}
|
||||||
|
NEW_PATCH=${BASH_REMATCH[3]}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Version updated to ${NEW_VERSION}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build functions
|
||||||
|
build_project() {
|
||||||
|
print_status "Cleaning previous build..."
|
||||||
|
make clean
|
||||||
|
increment_version
|
||||||
|
print_status "Building OTP project..."
|
||||||
|
make
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Build completed successfully"
|
||||||
|
else
|
||||||
|
print_error "Build failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_static() {
|
||||||
|
print_status "Cleaning previous build..."
|
||||||
|
make clean
|
||||||
|
increment_version
|
||||||
|
print_status "Building OTP project with static linking..."
|
||||||
|
make static
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Static build completed successfully"
|
||||||
|
else
|
||||||
|
print_error "Static build failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_project() {
|
||||||
|
print_status "Cleaning build artifacts..."
|
||||||
|
make clean
|
||||||
|
print_success "Clean completed"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_project() {
|
||||||
|
print_status "Installing OTP project..."
|
||||||
|
make install
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Installation completed"
|
||||||
|
else
|
||||||
|
print_error "Installation failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_project() {
|
||||||
|
print_status "Uninstalling OTP project..."
|
||||||
|
make uninstall
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Uninstallation completed"
|
||||||
|
else
|
||||||
|
print_error "Uninstallation failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
case "${1:-build}" in
|
||||||
|
build)
|
||||||
|
build_project
|
||||||
|
;;
|
||||||
|
static)
|
||||||
|
build_static
|
||||||
|
;;
|
||||||
|
clean)
|
||||||
|
clean_project
|
||||||
|
;;
|
||||||
|
install)
|
||||||
|
install_project
|
||||||
|
;;
|
||||||
|
uninstall)
|
||||||
|
uninstall_project
|
||||||
|
;;
|
||||||
|
version)
|
||||||
|
increment_version
|
||||||
|
print_status "Version tag updated"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "OTP Cipher Build Script"
|
||||||
|
echo "Usage: $0 [-m \"commit message\"] {build|static|clean|install|uninstall|version}"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -m, --message \"text\" - Specify commit message (skips interactive prompt)"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " build - Build project with automatic version increment (default)"
|
||||||
|
echo " static - Build with static linking"
|
||||||
|
echo " clean - Clean build artifacts"
|
||||||
|
echo " install - Install to system (requires build first)"
|
||||||
|
echo " uninstall - Remove from system"
|
||||||
|
echo " version - Update version tag only"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 build"
|
||||||
|
echo " $0 -m \"Fixed checksum parsing bug\" build"
|
||||||
|
echo " $0 --message \"Added new feature\" static"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
1
debug.c
Normal file
1
debug.c
Normal file
@@ -0,0 +1 @@
|
|||||||
|
int main() { printf("Testing direct filename: %d\n", strncmp("97d9d82b5414a9439102f3811fb90ab1d6368a00d33229a18b306476f9d04f82.pad", "97", 2)); return 0; }
|
||||||
1
files/o2.txt
Normal file
1
files/o2.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello, this is a test file for encryption!
|
||||||
BIN
files/out2.otp
Normal file
BIN
files/out2.otp
Normal file
Binary file not shown.
BIN
files/output.otp
Normal file
BIN
files/output.otp
Normal file
Binary file not shown.
1
files/test_decrypt.txt
Normal file
1
files/test_decrypt.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Test file content for decryption
|
||||||
BIN
files/test_decrypt.txt.otp
Normal file
BIN
files/test_decrypt.txt.otp
Normal file
Binary file not shown.
1
files/test_file.txt
Normal file
1
files/test_file.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello, this is a test file for encryption!
|
||||||
7
files/test_file.txt.otp.asc
Normal file
7
files/test_file.txt.otp.asc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-----BEGIN OTP MESSAGE-----
|
||||||
|
Version: v0.2.29
|
||||||
|
Pad-ChkSum: d0d4a489354348b08d8c7b324814d8c50010042e9da47f2c973f32a16a09101b
|
||||||
|
Pad-Offset: 57
|
||||||
|
|
||||||
|
05S8GfS0tFfczNMUz0xrieFGoPSREM4uo5QhFGoBCcOzjfTXTDMt3hRtAQ==
|
||||||
|
-----END OTP MESSAGE-----
|
||||||
BIN
files/test_files_dir.txt.otp
Normal file
BIN
files/test_files_dir.txt.otp
Normal file
Binary file not shown.
1
files/test_input.txt
Normal file
1
files/test_input.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is a test file for encryption.
|
||||||
8898
files/toc.txt
Executable file
8898
files/toc.txt
Executable file
File diff suppressed because it is too large
Load Diff
BIN
files/toc.txt.otp
Normal file
BIN
files/toc.txt.otp
Normal file
Binary file not shown.
163
nostr_chacha20.c
Normal file
163
nostr_chacha20.c
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* nostr_chacha20.c - ChaCha20 stream cipher implementation
|
||||||
|
*
|
||||||
|
* Implementation based on RFC 8439 "ChaCha20 and Poly1305 for IETF Protocols"
|
||||||
|
*
|
||||||
|
* This implementation is adapted from the RFC 8439 reference specification.
|
||||||
|
* It prioritizes correctness and clarity over performance optimization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nostr_chacha20.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ============================================================================
|
||||||
|
* UTILITY MACROS AND FUNCTIONS
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Left rotate a 32-bit value by n bits */
|
||||||
|
#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
|
||||||
|
|
||||||
|
/* Convert 4 bytes to 32-bit little-endian */
|
||||||
|
static uint32_t bytes_to_u32_le(const uint8_t *bytes) {
|
||||||
|
return ((uint32_t)bytes[0]) |
|
||||||
|
((uint32_t)bytes[1] << 8) |
|
||||||
|
((uint32_t)bytes[2] << 16) |
|
||||||
|
((uint32_t)bytes[3] << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert 32-bit to 4 bytes little-endian */
|
||||||
|
static void u32_to_bytes_le(uint32_t val, uint8_t *bytes) {
|
||||||
|
bytes[0] = (uint8_t)(val & 0xff);
|
||||||
|
bytes[1] = (uint8_t)((val >> 8) & 0xff);
|
||||||
|
bytes[2] = (uint8_t)((val >> 16) & 0xff);
|
||||||
|
bytes[3] = (uint8_t)((val >> 24) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ============================================================================
|
||||||
|
* CHACHA20 CORE FUNCTIONS
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d) {
|
||||||
|
state[a] += state[b];
|
||||||
|
state[d] ^= state[a];
|
||||||
|
state[d] = ROTLEFT(state[d], 16);
|
||||||
|
|
||||||
|
state[c] += state[d];
|
||||||
|
state[b] ^= state[c];
|
||||||
|
state[b] = ROTLEFT(state[b], 12);
|
||||||
|
|
||||||
|
state[a] += state[b];
|
||||||
|
state[d] ^= state[a];
|
||||||
|
state[d] = ROTLEFT(state[d], 8);
|
||||||
|
|
||||||
|
state[c] += state[d];
|
||||||
|
state[b] ^= state[c];
|
||||||
|
state[b] = ROTLEFT(state[b], 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chacha20_init_state(uint32_t state[16], const uint8_t key[32],
|
||||||
|
uint32_t counter, const uint8_t nonce[12]) {
|
||||||
|
/* ChaCha20 constants "expand 32-byte k" */
|
||||||
|
state[0] = 0x61707865;
|
||||||
|
state[1] = 0x3320646e;
|
||||||
|
state[2] = 0x79622d32;
|
||||||
|
state[3] = 0x6b206574;
|
||||||
|
|
||||||
|
/* Key (8 words) */
|
||||||
|
state[4] = bytes_to_u32_le(key + 0);
|
||||||
|
state[5] = bytes_to_u32_le(key + 4);
|
||||||
|
state[6] = bytes_to_u32_le(key + 8);
|
||||||
|
state[7] = bytes_to_u32_le(key + 12);
|
||||||
|
state[8] = bytes_to_u32_le(key + 16);
|
||||||
|
state[9] = bytes_to_u32_le(key + 20);
|
||||||
|
state[10] = bytes_to_u32_le(key + 24);
|
||||||
|
state[11] = bytes_to_u32_le(key + 28);
|
||||||
|
|
||||||
|
/* Counter (1 word) */
|
||||||
|
state[12] = counter;
|
||||||
|
|
||||||
|
/* Nonce (3 words) */
|
||||||
|
state[13] = bytes_to_u32_le(nonce + 0);
|
||||||
|
state[14] = bytes_to_u32_le(nonce + 4);
|
||||||
|
state[15] = bytes_to_u32_le(nonce + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void chacha20_serialize_state(const uint32_t state[16], uint8_t output[64]) {
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
u32_to_bytes_le(state[i], output + (i * 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int chacha20_block(const uint8_t key[32], uint32_t counter,
|
||||||
|
const uint8_t nonce[12], uint8_t output[64]) {
|
||||||
|
uint32_t state[16];
|
||||||
|
uint32_t initial_state[16];
|
||||||
|
|
||||||
|
/* Initialize state */
|
||||||
|
chacha20_init_state(state, key, counter, nonce);
|
||||||
|
|
||||||
|
/* Save initial state for later addition */
|
||||||
|
memcpy(initial_state, state, sizeof(initial_state));
|
||||||
|
|
||||||
|
/* Perform 20 rounds (10 iterations of the 8 quarter rounds) */
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
/* Column rounds */
|
||||||
|
chacha20_quarter_round(state, 0, 4, 8, 12);
|
||||||
|
chacha20_quarter_round(state, 1, 5, 9, 13);
|
||||||
|
chacha20_quarter_round(state, 2, 6, 10, 14);
|
||||||
|
chacha20_quarter_round(state, 3, 7, 11, 15);
|
||||||
|
|
||||||
|
/* Diagonal rounds */
|
||||||
|
chacha20_quarter_round(state, 0, 5, 10, 15);
|
||||||
|
chacha20_quarter_round(state, 1, 6, 11, 12);
|
||||||
|
chacha20_quarter_round(state, 2, 7, 8, 13);
|
||||||
|
chacha20_quarter_round(state, 3, 4, 9, 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add initial state back (prevents slide attacks) */
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
state[i] += initial_state[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Serialize to output bytes */
|
||||||
|
chacha20_serialize_state(state, output);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chacha20_encrypt(const uint8_t key[32], uint32_t counter,
|
||||||
|
const uint8_t nonce[12], const uint8_t* input,
|
||||||
|
uint8_t* output, size_t length) {
|
||||||
|
uint8_t keystream[CHACHA20_BLOCK_SIZE];
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
/* Generate keystream block */
|
||||||
|
int ret = chacha20_block(key, counter, nonce, keystream);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XOR with input to produce output */
|
||||||
|
size_t block_len = (length < CHACHA20_BLOCK_SIZE) ? length : CHACHA20_BLOCK_SIZE;
|
||||||
|
for (size_t i = 0; i < block_len; i++) {
|
||||||
|
output[offset + i] = input[offset + i] ^ keystream[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move to next block */
|
||||||
|
offset += block_len;
|
||||||
|
length -= block_len;
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
/* Check for counter overflow */
|
||||||
|
if (counter == 0) {
|
||||||
|
return -1; /* Counter wrapped around */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
115
nostr_chacha20.h
Normal file
115
nostr_chacha20.h
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* nostr_chacha20.h - ChaCha20 stream cipher implementation
|
||||||
|
*
|
||||||
|
* Implementation based on RFC 8439 "ChaCha20 and Poly1305 for IETF Protocols"
|
||||||
|
*
|
||||||
|
* This is a small, portable implementation for NIP-44 support in the NOSTR library.
|
||||||
|
* The implementation prioritizes correctness and simplicity over performance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NOSTR_CHACHA20_H
|
||||||
|
#define NOSTR_CHACHA20_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ============================================================================
|
||||||
|
* CONSTANTS AND DEFINITIONS
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CHACHA20_KEY_SIZE 32 /* 256 bits */
|
||||||
|
#define CHACHA20_NONCE_SIZE 12 /* 96 bits */
|
||||||
|
#define CHACHA20_BLOCK_SIZE 64 /* 512 bits */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ============================================================================
|
||||||
|
* CORE CHACHA20 FUNCTIONS
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChaCha20 quarter round operation
|
||||||
|
*
|
||||||
|
* Operates on four 32-bit words performing the core ChaCha20 quarter round:
|
||||||
|
* a += b; d ^= a; d <<<= 16;
|
||||||
|
* c += d; b ^= c; b <<<= 12;
|
||||||
|
* a += b; d ^= a; d <<<= 8;
|
||||||
|
* c += d; b ^= c; b <<<= 7;
|
||||||
|
*
|
||||||
|
* @param state[in,out] ChaCha state as 16 32-bit words
|
||||||
|
* @param a, b, c, d Indices into state array for quarter round
|
||||||
|
*/
|
||||||
|
void chacha20_quarter_round(uint32_t state[16], int a, int b, int c, int d);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChaCha20 block function
|
||||||
|
*
|
||||||
|
* Transforms a 64-byte input block using ChaCha20 algorithm with 20 rounds.
|
||||||
|
*
|
||||||
|
* @param key[in] 32-byte key
|
||||||
|
* @param counter[in] 32-bit block counter
|
||||||
|
* @param nonce[in] 12-byte nonce
|
||||||
|
* @param output[out] 64-byte output buffer
|
||||||
|
* @return 0 on success, negative on error
|
||||||
|
*/
|
||||||
|
int chacha20_block(const uint8_t key[32], uint32_t counter,
|
||||||
|
const uint8_t nonce[12], uint8_t output[64]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChaCha20 encryption/decryption
|
||||||
|
*
|
||||||
|
* Encrypts or decrypts data using ChaCha20 stream cipher.
|
||||||
|
* Since ChaCha20 is a stream cipher, encryption and decryption are the same operation.
|
||||||
|
*
|
||||||
|
* @param key[in] 32-byte key
|
||||||
|
* @param counter[in] Initial 32-bit counter value
|
||||||
|
* @param nonce[in] 12-byte nonce
|
||||||
|
* @param input[in] Input data to encrypt/decrypt
|
||||||
|
* @param output[out] Output buffer (can be same as input)
|
||||||
|
* @param length[in] Length of input data in bytes
|
||||||
|
* @return 0 on success, negative on error
|
||||||
|
*/
|
||||||
|
int chacha20_encrypt(const uint8_t key[32], uint32_t counter,
|
||||||
|
const uint8_t nonce[12], const uint8_t* input,
|
||||||
|
uint8_t* output, size_t length);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ============================================================================
|
||||||
|
* UTILITY FUNCTIONS
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize ChaCha20 state matrix
|
||||||
|
*
|
||||||
|
* Sets up the initial 16-word state matrix with constants, key, counter, and nonce.
|
||||||
|
*
|
||||||
|
* @param state[out] 16-word state array to initialize
|
||||||
|
* @param key[in] 32-byte key
|
||||||
|
* @param counter[in] 32-bit block counter
|
||||||
|
* @param nonce[in] 12-byte nonce
|
||||||
|
*/
|
||||||
|
void chacha20_init_state(uint32_t state[16], const uint8_t key[32],
|
||||||
|
uint32_t counter, const uint8_t nonce[12]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize ChaCha20 state to bytes
|
||||||
|
*
|
||||||
|
* Converts 16 32-bit words to 64 bytes in little-endian format.
|
||||||
|
*
|
||||||
|
* @param state[in] 16-word state array
|
||||||
|
* @param output[out] 64-byte output buffer
|
||||||
|
*/
|
||||||
|
void chacha20_serialize_state(const uint32_t state[16], uint8_t output[64]);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NOSTR_CHACHA20_H */
|
||||||
8
otp.code-workspace
Normal file
8
otp.code-workspace
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user