Compare commits

..

14 Commits

9 changed files with 1933 additions and 1159 deletions

5
.gitignore vendored
View File

@@ -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

View File

@@ -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/

View File

@@ -151,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.
@@ -176,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)
``` ```

25
TODO.md
View File

@@ -1,26 +1,3 @@
# TODO # TODO
## The pad menu in interactive encrypt mode gives numbers instead of checksum selection
## Change technique for adding keyboard entropy.
## Some of the processing seems similar, so maybe code could be more compact.
## Command line otp -e should go to default pad, and then comment after the fact that it used the default pad.
## There is the problem of the location of the pad revealing metadata about how many messages have been sent in the past, or at least the size of the messsages.
One solution could be to start the pad at a random location, and then wrap around, so an attacker could never tell the size of the past text sent. This helps. But then you have to store the start location, which you could do within the header of the pad along with the pad?
Or, better yet, assume the offset is a very large size, and use the pad itself to encrypt the offset.
## Take a look at how the file header is being handled.
## We have three different decrypt file functions
## Preferences directory and files look off. Should probably have ~/.otp as the default directory, and then in there we can have otp.conf, pads/
## Setup for multiple USB drives
## Change back in pad menu to exit

View File

@@ -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"

163
nostr_chacha20.c Normal file
View 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
View 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 */

2674
otp.c

File diff suppressed because it is too large Load Diff

BIN
otp.o Normal file

Binary file not shown.