Compare commits
69 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 |
@@ -1,7 +1,29 @@
|
|||||||
|
|
||||||
When building, use build.sh, not make.
|
When building, use build.sh, not make.
|
||||||
|
|
||||||
Use it as follows: build.sh -m "useful comment on changes being made"
|
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.
|
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
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -3,7 +3,4 @@ pads/
|
|||||||
Gemini.md
|
Gemini.md
|
||||||
TropicOfCancer-HenryMiller.txt
|
TropicOfCancer-HenryMiller.txt
|
||||||
|
|
||||||
# Auto-generated version files
|
# Auto-generated files (none currently)
|
||||||
src/version.h
|
|
||||||
src/version.c
|
|
||||||
VERSION
|
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -1,22 +1,21 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -std=c99
|
CFLAGS = -Wall -Wextra -std=c99
|
||||||
LIBS =
|
LIBS = -lm
|
||||||
LIBS_STATIC = -static
|
LIBS_STATIC = -static -lm
|
||||||
TARGET = otp
|
TARGET = otp
|
||||||
SOURCE = otp.c
|
SOURCE = otp.c
|
||||||
VERSION_SOURCE = src/version.c
|
CHACHA20_SOURCE = nostr_chacha20.c
|
||||||
|
|
||||||
# Default build target
|
# Default build target
|
||||||
$(TARGET): $(SOURCE)
|
$(TARGET): $(SOURCE)
|
||||||
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(VERSION_SOURCE) $(LIBS)
|
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(CHACHA20_SOURCE) $(LIBS)
|
||||||
|
|
||||||
# Static linking target
|
# Static linking target
|
||||||
static: $(SOURCE)
|
static: $(SOURCE)
|
||||||
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(VERSION_SOURCE) $(LIBS_STATIC)
|
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(CHACHA20_SOURCE) $(LIBS_STATIC)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET) *.pad *.state
|
rm -f $(TARGET) *.pad *.state
|
||||||
rm -f src/version.h src/version.c VERSION
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
sudo cp $(TARGET) /usr/local/bin/
|
sudo cp $(TARGET) /usr/local/bin/
|
||||||
|
|||||||
62
README.md
62
README.md
@@ -1,6 +1,47 @@
|
|||||||
# OTP Cipher - One Time Pad Implementation
|
# OTP Cipher - One Time Pad Implementation
|
||||||
|
|
||||||
A secure one-time pad (OTP) cipher implementation in C with automatic versioning system.
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -13,15 +54,6 @@ A secure one-time pad (OTP) cipher implementation in C with automatic versioning
|
|||||||
- **Multiple Build Options**: Standard and static linking builds
|
- **Multiple Build Options**: Standard and static linking builds
|
||||||
- **Cross-Platform**: Works on Linux and other UNIX-like systems
|
- **Cross-Platform**: Works on Linux and other UNIX-like systems
|
||||||
|
|
||||||
## Version Information
|
|
||||||
|
|
||||||
This project uses an automatic versioning system that:
|
|
||||||
- Automatically increments the patch version on each build
|
|
||||||
- Embeds build timestamp, git commit hash, and branch information
|
|
||||||
- Creates git tags for version tracking
|
|
||||||
- Generates version header files with detailed build metadata
|
|
||||||
|
|
||||||
Current version can be viewed with: `./otp --help` or by running the interactive mode.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@@ -31,7 +63,7 @@ Current version can be viewed with: `./otp --help` or by running the interactive
|
|||||||
- Git (for version tracking)
|
- Git (for version tracking)
|
||||||
- Make
|
- Make
|
||||||
|
|
||||||
**Note: OpenSSL is no longer required! This implementation is now completely self-contained.**
|
|
||||||
|
|
||||||
### Build Commands
|
### Build Commands
|
||||||
|
|
||||||
@@ -119,10 +151,7 @@ git tag v1.0.0 # Next build: v1.0.1
|
|||||||
- Full version display with metadata
|
- Full version display with metadata
|
||||||
|
|
||||||
### Generated Files
|
### Generated Files
|
||||||
The build system automatically generates:
|
The build system automatically manages Git versioning by incrementing tags.
|
||||||
- `src/version.h` - Version constants and macros
|
|
||||||
- `src/version.c` - Version API functions
|
|
||||||
- `VERSION` - Plain text version number
|
|
||||||
|
|
||||||
These files are excluded from git (.gitignore) and regenerated on each build.
|
These files are excluded from git (.gitignore) and regenerated on each build.
|
||||||
|
|
||||||
@@ -144,9 +173,6 @@ otp/
|
|||||||
├── otp.c # Main source code
|
├── otp.c # Main source code
|
||||||
├── README.md # This file
|
├── README.md # This file
|
||||||
├── .gitignore # Git ignore rules
|
├── .gitignore # Git ignore rules
|
||||||
├── src/ # Generated version files (auto-created)
|
|
||||||
│ ├── version.h # Version header (generated)
|
|
||||||
│ └── version.c # Version implementation (generated)
|
|
||||||
├── pads/ # OTP pad storage directory (created at runtime)
|
├── pads/ # OTP pad storage directory (created at runtime)
|
||||||
└── VERSION # Plain text version (generated)
|
└── VERSION # Plain text version (generated)
|
||||||
```
|
```
|
||||||
|
|||||||
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
|
||||||
77
build.sh
77
build.sh
@@ -100,13 +100,13 @@ increment_version() {
|
|||||||
print_success "Created new version tag: $NEW_VERSION"
|
print_success "Created new version tag: $NEW_VERSION"
|
||||||
|
|
||||||
# Push changes and tags to remote repository
|
# Push changes and tags to remote repository
|
||||||
if git push ssh://ubuntu@laantungir.net:/home/ubuntu/git_repos/otp 2>/dev/null; then
|
if git push 2>/dev/null; then
|
||||||
print_success "Pushed changes to remote repository"
|
print_success "Pushed changes to remote repository"
|
||||||
else
|
else
|
||||||
print_warning "Failed to push changes to remote repository"
|
print_warning "Failed to push changes to remote repository"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if git push ssh://ubuntu@laantungir.net:/home/ubuntu/git_repos/otp --tags 2>/dev/null; then
|
if git push --tags 2>/dev/null; then
|
||||||
print_success "Pushed tags to remote repository"
|
print_success "Pushed tags to remote repository"
|
||||||
else
|
else
|
||||||
print_warning "Failed to push tags to remote repository"
|
print_warning "Failed to push tags to remote repository"
|
||||||
@@ -123,71 +123,7 @@ increment_version() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update VERSION file for compatibility
|
print_success "Version updated to ${NEW_VERSION}"
|
||||||
echo "${NEW_VERSION#v}" > VERSION
|
|
||||||
print_success "Updated VERSION file to ${NEW_VERSION#v}"
|
|
||||||
|
|
||||||
# Generate version.h header file
|
|
||||||
mkdir -p src
|
|
||||||
cat > src/version.h << EOF
|
|
||||||
/*
|
|
||||||
* Auto-Generated Version Header
|
|
||||||
* DO NOT EDIT THIS FILE MANUALLY - Generated by build script
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef VERSION_H
|
|
||||||
#define VERSION_H
|
|
||||||
|
|
||||||
#define VERSION_MAJOR ${MAJOR}
|
|
||||||
#define VERSION_MINOR ${MINOR}
|
|
||||||
#define VERSION_PATCH ${NEW_PATCH}
|
|
||||||
#define VERSION_STRING "${MAJOR}.${MINOR}.${NEW_PATCH}"
|
|
||||||
#define VERSION_TAG "${NEW_VERSION}"
|
|
||||||
|
|
||||||
/* Build information */
|
|
||||||
#define BUILD_DATE "$(date +%Y-%m-%d)"
|
|
||||||
#define BUILD_TIME "$(date +%H:%M:%S)"
|
|
||||||
#define BUILD_TIMESTAMP "$(date '+%Y-%m-%d %H:%M:%S')"
|
|
||||||
|
|
||||||
/* Git information */
|
|
||||||
#define GIT_HASH "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
|
|
||||||
#define GIT_BRANCH "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
|
|
||||||
|
|
||||||
/* Display versions */
|
|
||||||
#define VERSION_DISPLAY "${NEW_VERSION}"
|
|
||||||
#define VERSION_FULL_DISPLAY "${NEW_VERSION} ($(date '+%Y-%m-%d %H:%M:%S'), $(git rev-parse --short HEAD 2>/dev/null || echo 'unknown'))"
|
|
||||||
|
|
||||||
/* Version API functions */
|
|
||||||
const char* get_version(void);
|
|
||||||
const char* get_version_full(void);
|
|
||||||
const char* get_build_info(void);
|
|
||||||
|
|
||||||
#endif /* VERSION_H */
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Generate version.c implementation file
|
|
||||||
cat > src/version.c << EOF
|
|
||||||
/*
|
|
||||||
* Auto-Generated Version Implementation
|
|
||||||
* DO NOT EDIT THIS FILE MANUALLY - Generated by build script
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
const char* get_version(void) {
|
|
||||||
return VERSION_TAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get_version_full(void) {
|
|
||||||
return VERSION_FULL_DISPLAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get_build_info(void) {
|
|
||||||
return "Built on " BUILD_DATE " at " BUILD_TIME " from commit " GIT_HASH " on branch " GIT_BRANCH;
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
print_success "Generated version header files"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build functions
|
# Build functions
|
||||||
@@ -222,7 +158,6 @@ build_static() {
|
|||||||
clean_project() {
|
clean_project() {
|
||||||
print_status "Cleaning build artifacts..."
|
print_status "Cleaning build artifacts..."
|
||||||
make clean
|
make clean
|
||||||
rm -f VERSION src/version.h src/version.c
|
|
||||||
print_success "Clean completed"
|
print_success "Clean completed"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +202,7 @@ case "${1:-build}" in
|
|||||||
;;
|
;;
|
||||||
version)
|
version)
|
||||||
increment_version
|
increment_version
|
||||||
print_status "Version information generated"
|
print_status "Version tag updated"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "OTP Cipher Build Script"
|
echo "OTP Cipher Build Script"
|
||||||
@@ -279,10 +214,10 @@ case "${1:-build}" in
|
|||||||
echo "Commands:"
|
echo "Commands:"
|
||||||
echo " build - Build project with automatic version increment (default)"
|
echo " build - Build project with automatic version increment (default)"
|
||||||
echo " static - Build with static linking"
|
echo " static - Build with static linking"
|
||||||
echo " clean - Clean build artifacts and generated files"
|
echo " clean - Clean build artifacts"
|
||||||
echo " install - Install to system (requires build first)"
|
echo " install - Install to system (requires build first)"
|
||||||
echo " uninstall - Remove from system"
|
echo " uninstall - Remove from system"
|
||||||
echo " version - Generate version files only"
|
echo " version - Update version tag only"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Examples:"
|
echo "Examples:"
|
||||||
echo " $0 build"
|
echo " $0 build"
|
||||||
|
|||||||
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.
|
||||||
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 */
|
||||||
Reference in New Issue
Block a user