Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5a1bd92c4 | |||
| 3ff91e7681 | |||
| 05f5d6ca4f | |||
| 33b34bf5a5 | |||
| 9d91ec912a | |||
| 7e431d98e9 | |||
| 90ffb25a2b | |||
| 79e43877a2 | |||
| abe87e865b | |||
| f87b2dbd8f | |||
| 1582c88be5 | |||
| 2ce3e823c5 | |||
| 4983edaaae | |||
| 66c6e3eea5 | |||
| b058911fb8 | |||
| a0ce6f3253 | |||
| 5e1de92454 | |||
| 55cf7b1937 | |||
| 3d990091eb | |||
| 232846e7ce | |||
| 860ec08d4f | |||
| 60276f5c97 | |||
| 8616e78547 | |||
| 8327ee125b | |||
| 5dadd948e6 | |||
| 02044f1054 | |||
| 194bd5fea5 | |||
| eb126bb663 | |||
| a4a4c0d8b2 | |||
| 0c4ef55a98 | |||
| 4a651da067 | |||
| 7b5db60d80 | |||
| 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 |
@@ -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
|
||||||
|
|||||||
27
.gitignore
vendored
27
.gitignore
vendored
@@ -1,9 +1,26 @@
|
|||||||
otp
|
# Build artifacts
|
||||||
|
build/
|
||||||
|
*.o
|
||||||
|
src/*.o
|
||||||
|
|
||||||
|
# Runtime directories
|
||||||
pads/
|
pads/
|
||||||
|
files/
|
||||||
|
|
||||||
|
# Personal files
|
||||||
Gemini.md
|
Gemini.md
|
||||||
TropicOfCancer-HenryMiller.txt
|
TropicOfCancer-HenryMiller.txt
|
||||||
|
.gitea_token
|
||||||
|
true_rng/
|
||||||
|
Trash/
|
||||||
|
|
||||||
# Auto-generated version files
|
# Test binaries
|
||||||
src/version.h
|
debug
|
||||||
src/version.c
|
test_swiftrng
|
||||||
VERSION
|
test_swiftrng_debug
|
||||||
|
test_swiftrng_detailed
|
||||||
|
test_truerng
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.pad
|
||||||
|
*.state
|
||||||
|
|||||||
1
.rooignore
Normal file
1
.rooignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
otp copy.c
|
||||||
@@ -1,361 +0,0 @@
|
|||||||
# Generic Automatic Version Increment System for Any Repository
|
|
||||||
|
|
||||||
Here's a generalized implementation guide for adding automatic versioning to any project:
|
|
||||||
|
|
||||||
## Core Concept
|
|
||||||
**Automatic patch version increment with each build** - Every build automatically increments the patch version: v0.1.0 → v0.1.1 → v0.1.2, etc.
|
|
||||||
|
|
||||||
## Implementation Steps
|
|
||||||
|
|
||||||
### 1. Add Version Increment Function to Build Script
|
|
||||||
Add this function to your build script (bash example):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Function to automatically increment version
|
|
||||||
increment_version() {
|
|
||||||
echo "[INFO] Incrementing version..."
|
|
||||||
|
|
||||||
# Check if we're in a git repository
|
|
||||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
||||||
echo "[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
|
|
||||||
echo "[ERROR] Invalid version format in tag: $LATEST_TAG"
|
|
||||||
echo "[ERROR] Expected format: v0.1.0"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increment patch version
|
|
||||||
NEW_PATCH=$((PATCH + 1))
|
|
||||||
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
|
|
||||||
|
|
||||||
echo "[INFO] Current version: $LATEST_TAG"
|
|
||||||
echo "[INFO] New version: $NEW_VERSION"
|
|
||||||
|
|
||||||
# Create new git tag
|
|
||||||
if git tag "$NEW_VERSION" 2>/dev/null; then
|
|
||||||
echo "[SUCCESS] Created new version tag: $NEW_VERSION"
|
|
||||||
else
|
|
||||||
echo "[WARNING] Tag $NEW_VERSION already exists - using existing version"
|
|
||||||
NEW_VERSION=$LATEST_TAG
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update VERSION file for compatibility
|
|
||||||
echo "${NEW_VERSION#v}" > VERSION
|
|
||||||
echo "[SUCCESS] Updated VERSION file to ${NEW_VERSION#v}"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Generate Version Header Files (For C/C++ Projects)
|
|
||||||
Add this to the increment_version function:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Generate version.h header file (adjust path as needed)
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Generate Version File for Other Languages
|
|
||||||
|
|
||||||
**Python (`src/__version__.py`):**
|
|
||||||
```bash
|
|
||||||
cat > src/__version__.py << EOF
|
|
||||||
"""Auto-generated version file"""
|
|
||||||
__version__ = "${MAJOR}.${MINOR}.${NEW_PATCH}"
|
|
||||||
__version_tag__ = "${NEW_VERSION}"
|
|
||||||
__build_date__ = "$(date +%Y-%m-%d)"
|
|
||||||
__build_time__ = "$(date +%H:%M:%S)"
|
|
||||||
__git_hash__ = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
|
|
||||||
__git_branch__ = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
**JavaScript/Node.js (update `package.json`):**
|
|
||||||
```bash
|
|
||||||
# Update package.json version field
|
|
||||||
if [ -f package.json ]; then
|
|
||||||
sed -i "s/\"version\": \".*\"/\"version\": \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" package.json
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
**Rust (update `Cargo.toml`):**
|
|
||||||
```bash
|
|
||||||
if [ -f Cargo.toml ]; then
|
|
||||||
sed -i "s/^version = \".*\"/version = \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" Cargo.toml
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
**Go (generate `version.go`):**
|
|
||||||
```bash
|
|
||||||
cat > version.go << EOF
|
|
||||||
// Auto-generated version file
|
|
||||||
package main
|
|
||||||
|
|
||||||
const (
|
|
||||||
VersionMajor = ${MAJOR}
|
|
||||||
VersionMinor = ${MINOR}
|
|
||||||
VersionPatch = ${NEW_PATCH}
|
|
||||||
VersionString = "${MAJOR}.${MINOR}.${NEW_PATCH}"
|
|
||||||
VersionTag = "${NEW_VERSION}"
|
|
||||||
BuildDate = "$(date +%Y-%m-%d)"
|
|
||||||
BuildTime = "$(date +%H:%M:%S)"
|
|
||||||
GitHash = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
|
|
||||||
GitBranch = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
|
|
||||||
)
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
**Java (generate `Version.java`):**
|
|
||||||
```bash
|
|
||||||
cat > src/main/java/Version.java << EOF
|
|
||||||
// Auto-generated version class
|
|
||||||
public class Version {
|
|
||||||
public static final int VERSION_MAJOR = ${MAJOR};
|
|
||||||
public static final int VERSION_MINOR = ${MINOR};
|
|
||||||
public static final int VERSION_PATCH = ${NEW_PATCH};
|
|
||||||
public static final String VERSION_STRING = "${MAJOR}.${MINOR}.${NEW_PATCH}";
|
|
||||||
public static final String VERSION_TAG = "${NEW_VERSION}";
|
|
||||||
public static final String BUILD_DATE = "$(date +%Y-%m-%d)";
|
|
||||||
public static final String BUILD_TIME = "$(date +%H:%M:%S)";
|
|
||||||
public static final String GIT_HASH = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')";
|
|
||||||
public static final String GIT_BRANCH = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')";
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Integrate into Build Targets
|
|
||||||
Call `increment_version` before your main build commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
build_library() {
|
|
||||||
increment_version
|
|
||||||
echo "[INFO] Building library..."
|
|
||||||
# Your actual build commands here
|
|
||||||
make clean && make
|
|
||||||
}
|
|
||||||
|
|
||||||
build_release() {
|
|
||||||
increment_version
|
|
||||||
echo "[INFO] Building release..."
|
|
||||||
# Your release build commands
|
|
||||||
}
|
|
||||||
|
|
||||||
build_package() {
|
|
||||||
increment_version
|
|
||||||
echo "[INFO] Building package..."
|
|
||||||
# Your packaging commands
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Update .gitignore
|
|
||||||
Add generated version files to `.gitignore`:
|
|
||||||
|
|
||||||
```gitignore
|
|
||||||
# Auto-generated version files
|
|
||||||
src/version.h
|
|
||||||
src/version.c
|
|
||||||
src/__version__.py
|
|
||||||
version.go
|
|
||||||
src/main/java/Version.java
|
|
||||||
VERSION
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Update Build System Files
|
|
||||||
|
|
||||||
**For Makefile projects:**
|
|
||||||
```makefile
|
|
||||||
# Add version.c to your source files
|
|
||||||
SOURCES = main.c utils.c version.c
|
|
||||||
```
|
|
||||||
|
|
||||||
**For CMake projects:**
|
|
||||||
```cmake
|
|
||||||
# Add version files to your target
|
|
||||||
target_sources(your_target PRIVATE src/version.c)
|
|
||||||
```
|
|
||||||
|
|
||||||
**For Node.js projects:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"build": "node build.js && increment_version",
|
|
||||||
"version": "node -e \"console.log(require('./package.json').version)\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Create Initial Version Tag
|
|
||||||
```bash
|
|
||||||
# Start with initial version
|
|
||||||
git tag v0.1.0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage Pattern
|
|
||||||
```bash
|
|
||||||
./build.sh # v0.1.0 → v0.1.1
|
|
||||||
./build.sh release # v0.1.1 → v0.1.2
|
|
||||||
./build.sh package # v0.1.2 → v0.1.3
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Version Control
|
|
||||||
|
|
||||||
### Major/Minor Version Bumps
|
|
||||||
```bash
|
|
||||||
# For feature releases (minor bump)
|
|
||||||
git tag v0.2.0 # Next build: v0.2.1
|
|
||||||
|
|
||||||
# For breaking changes (major bump)
|
|
||||||
git tag v1.0.0 # Next build: v1.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Version Reset
|
|
||||||
```bash
|
|
||||||
# Delete incorrect tags (if needed)
|
|
||||||
git tag -d v0.2.1
|
|
||||||
git push origin --delete v0.2.1 # If pushed to remote
|
|
||||||
|
|
||||||
# Create correct base version
|
|
||||||
git tag v0.2.0
|
|
||||||
|
|
||||||
# Next build will create v0.2.1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Build Script Template
|
|
||||||
```bash
|
|
||||||
#!/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"; }
|
|
||||||
|
|
||||||
# Insert increment_version function here
|
|
||||||
|
|
||||||
case "${1:-build}" in
|
|
||||||
build)
|
|
||||||
increment_version
|
|
||||||
print_status "Building project..."
|
|
||||||
# Your build commands
|
|
||||||
;;
|
|
||||||
clean)
|
|
||||||
print_status "Cleaning build artifacts..."
|
|
||||||
# Your clean commands
|
|
||||||
;;
|
|
||||||
test)
|
|
||||||
print_status "Running tests..."
|
|
||||||
# Your test commands (no version increment)
|
|
||||||
;;
|
|
||||||
release)
|
|
||||||
increment_version
|
|
||||||
print_status "Building release..."
|
|
||||||
# Your release commands
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 {build|clean|test|release}"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
1. **Zero maintenance** - No manual version editing
|
|
||||||
2. **Build traceability** - Every build has unique version + metadata
|
|
||||||
3. **Git integration** - Automatic version tags
|
|
||||||
4. **Language agnostic** - Adapt generation for any language
|
|
||||||
5. **CI/CD friendly** - Works in automated environments
|
|
||||||
6. **Rollback friendly** - Easy to revert to previous versions
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Version Not Incrementing
|
|
||||||
- Ensure you're in a git repository
|
|
||||||
- Check that git tags exist: `git tag --list`
|
|
||||||
- Verify tag format matches `v*.*.*` pattern
|
|
||||||
|
|
||||||
### Tag Already Exists
|
|
||||||
If a tag already exists, the build continues with existing version:
|
|
||||||
```
|
|
||||||
[WARNING] Tag v0.2.1 already exists - using existing version
|
|
||||||
```
|
|
||||||
|
|
||||||
### Missing Git Information
|
|
||||||
If git is unavailable, version files show "unknown" for git hash and branch.
|
|
||||||
46
HARDWARE_RNG_STATUS.md
Normal file
46
HARDWARE_RNG_STATUS.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
# Hardware RNG Implementation Status
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The OTP cipher application now includes comprehensive hardware Random Number Generator (RNG) device support with automatic detection, device identification, and graceful handling of different device types.
|
||||||
|
|
||||||
|
## Supported Devices
|
||||||
|
|
||||||
|
### ✅ Fully Supported (TrueRNG Family)
|
||||||
|
- **TrueRNG Original** (VID: 04d8, PID: f5fe)
|
||||||
|
- **TrueRNG Pro** (VID: 04d8, PID: 0aa0)
|
||||||
|
- **TrueRNG Pro V2** (VID: 04d8, PID: ebb5)
|
||||||
|
|
||||||
|
These devices work via serial port communication and are fully integrated into the entropy collection system.
|
||||||
|
|
||||||
|
### ⚠️ Detected but Not Supported (SwiftRNG Family)
|
||||||
|
- **SwiftRNG** (VID: 1fc9, PID: 8111)
|
||||||
|
|
||||||
|
SwiftRNG devices are detected and identified but cannot be used via serial port communication. They require the official SwiftRNG API with libusb-1.0 integration.
|
||||||
|
|
||||||
|
## Implementation Features
|
||||||
|
|
||||||
|
### Device Detection
|
||||||
|
- **Automatic scanning** of `/dev/ttyUSB*` and `/dev/ttyACM*` devices
|
||||||
|
- **VID/PID identification** via sysfs to distinguish device types
|
||||||
|
- **Multi-device support** with interactive selection menus
|
||||||
|
- **Real-time status indicators** showing device availability
|
||||||
|
|
||||||
|
### Device Communication
|
||||||
|
- **Optimized serial port configuration** for each device type
|
||||||
|
- **Timeout protection** to prevent hanging on unresponsive devices
|
||||||
|
- **Error handling** with clear diagnostic messages
|
||||||
|
- **Progress tracking** with speed estimation for large entropy collections
|
||||||
|
|
||||||
|
### Integration Points
|
||||||
|
- **Pad enhancement** via entropy addition to existing pads
|
||||||
|
- **Interactive menus** for device selection when multiple devices are present
|
||||||
|
- **Command-line support** for automated workflows
|
||||||
|
- **Graceful fallback** to other entropy sources when no hardware RNG is available
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
### Core Functions
|
||||||
|
- `detect_all_hardware_rng_devices()` - Scans and identifies all connected devices
|
||||||
|
- `collect_truerng_entropy_from_device()` - Collects entropy from TrueRNG devices
|
||||||
33
Makefile
33
Makefile
@@ -1,27 +1,32 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -std=c99
|
CFLAGS = -Wall -Wextra -std=c99 -Isrc
|
||||||
LIBS =
|
LIBS = -lm
|
||||||
LIBS_STATIC = -static
|
LIBS_STATIC = -static -lm
|
||||||
TARGET = otp
|
ARCH = $(shell uname -m)
|
||||||
SOURCE = otp.c
|
TARGET = build/otp-$(ARCH)
|
||||||
VERSION_SOURCE = src/version.c
|
SOURCES = $(wildcard src/*.c)
|
||||||
|
OBJS = $(SOURCES:.c=.o)
|
||||||
|
|
||||||
# Default build target
|
# Default build target
|
||||||
$(TARGET): $(SOURCE)
|
$(TARGET): $(OBJS)
|
||||||
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(VERSION_SOURCE) $(LIBS)
|
@mkdir -p build
|
||||||
|
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
|
||||||
|
|
||||||
# Static linking target
|
# Static linking target
|
||||||
static: $(SOURCE)
|
static: $(OBJS)
|
||||||
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE) $(VERSION_SOURCE) $(LIBS_STATIC)
|
@mkdir -p build
|
||||||
|
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LIBS_STATIC)
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET) *.pad *.state
|
rm -f $(OBJS) src/*.o build/otp-* *.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/otp
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
sudo rm -f /usr/local/bin/$(TARGET)
|
sudo rm -f /usr/local/bin/otp
|
||||||
|
|
||||||
.PHONY: clean install uninstall static
|
.PHONY: clean install uninstall static
|
||||||
|
|||||||
172
README.md
172
README.md
@@ -1,27 +1,61 @@
|
|||||||
# 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
|
||||||
|
|
||||||
- **Perfect Security**: Implements true one-time pad encryption with information-theoretic security
|
- **Perfect Security**: Implements true one-time pad encryption with information-theoretic security
|
||||||
- **Text & File Encryption**: Supports both inline text and file encryption
|
- **Text & File Encryption**: Supports both inline text and file encryption
|
||||||
- **Multiple Output Formats**: Binary (.otp) and ASCII armored (.otp.asc) file formats
|
- **Multiple Output Formats**: Binary (.otp) and ASCII armored (.otp.asc) file formats
|
||||||
|
- **Hardware RNG Support**: Direct entropy collection from TrueRNG USB devices with automatic detection
|
||||||
- **Keyboard Entropy**: Optional keyboard entropy collection for enhanced randomness
|
- **Keyboard Entropy**: Optional keyboard entropy collection for enhanced randomness
|
||||||
|
- **Modular Architecture**: Clean separation of concerns across multiple source modules
|
||||||
- **Short Command Flags**: Convenient single-character flags for all operations
|
- **Short Command Flags**: Convenient single-character flags for all operations
|
||||||
- **Automatic Versioning**: Built-in semantic versioning with automatic patch increment
|
- **Automatic Versioning**: Built-in semantic versioning with automatic patch increment
|
||||||
- **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 +65,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 +153,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.
|
||||||
|
|
||||||
@@ -140,17 +171,118 @@ These files are excluded from git (.gitignore) and regenerated on each build.
|
|||||||
```
|
```
|
||||||
otp/
|
otp/
|
||||||
├── build.sh # Build script with automatic versioning
|
├── build.sh # Build script with automatic versioning
|
||||||
├── Makefile # Traditional make build system
|
├── Makefile # Traditional make build system
|
||||||
├── otp.c # Main source code
|
├── otp.c # Legacy compatibility and global definitions
|
||||||
├── README.md # This file
|
├── README.md # This file
|
||||||
├── .gitignore # Git ignore rules
|
├── .gitignore # Git ignore rules
|
||||||
├── src/ # Generated version files (auto-created)
|
├── include/
|
||||||
│ ├── version.h # Version header (generated)
|
│ └── otp.h # Public API header with all function prototypes
|
||||||
│ └── version.c # Version implementation (generated)
|
├── src/
|
||||||
|
│ ├── main.c # Main application entry point and command line handling
|
||||||
|
│ ├── ui.c # Interactive user interface and menu system
|
||||||
|
│ ├── state.c # Global state management (pads directory, terminal dimensions)
|
||||||
|
│ ├── crypto.c # Core cryptographic operations (XOR, ChaCha20)
|
||||||
|
│ ├── pads.c # Pad management and file operations
|
||||||
|
│ ├── entropy.c # Entropy collection from various sources
|
||||||
|
│ ├── trng.c # Hardware RNG device detection and entropy collection
|
||||||
|
│ └── util.c # Utility functions and helpers
|
||||||
├── 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)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The OTP cipher uses a modular architecture with clean separation of concerns:
|
||||||
|
|
||||||
|
- **main.c**: Application entry point, command line parsing, and mode selection
|
||||||
|
- **ui.c**: Interactive user interface, menus, and terminal management
|
||||||
|
- **state.c**: Global state management (pads directory, terminal dimensions, preferences)
|
||||||
|
- **crypto.c**: Core cryptographic operations (XOR encryption, ChaCha20 entropy mixing)
|
||||||
|
- **pads.c**: Pad file management, checksums, and state tracking
|
||||||
|
- **entropy.c**: Entropy collection from keyboard, dice, and other sources
|
||||||
|
- **trng.c**: Hardware RNG device detection and entropy collection from USB devices
|
||||||
|
- **util.c**: Utility functions, file operations, and helper routines
|
||||||
|
|
||||||
|
All modules share a common header (`include/otp.h`) that defines the public API and data structures.
|
||||||
|
|
||||||
|
## Hardware RNG Device Support
|
||||||
|
|
||||||
|
The OTP cipher includes comprehensive support for hardware random number generators (RNGs) to enhance entropy quality for pad generation and entropy addition operations.
|
||||||
|
|
||||||
|
### Supported Devices
|
||||||
|
|
||||||
|
The system automatically detects and supports the following hardware RNG devices:
|
||||||
|
|
||||||
|
| Device | VID:PID | Status | Notes |
|
||||||
|
|--------|---------|--------|-------|
|
||||||
|
| **TrueRNG** | 04d8:f5fe | ✅ Working | Original TrueRNG device |
|
||||||
|
| **TrueRNG (Alt)** | 1fc9:8111 | ✅ Working | Alternative VID/PID combination |
|
||||||
|
| **TrueRNG Pro** | 04d8:f5fe | ✅ Working | Professional version |
|
||||||
|
| **TrueRNG Pro V2** | 04d8:f5fe | ✅ Working | Latest professional version |
|
||||||
|
|
||||||
|
### Device Detection
|
||||||
|
|
||||||
|
The system automatically scans `/dev/ttyACM*` ports and identifies hardware RNG devices by:
|
||||||
|
|
||||||
|
1. **USB VID/PID Detection**: Reading vendor and product IDs from sysfs
|
||||||
|
2. **Device Type Classification**: Identifying specific device variants
|
||||||
|
3. **Port Configuration**: Applying device-specific serial port settings
|
||||||
|
4. **Interactive Selection**: Presenting available devices for user selection
|
||||||
|
|
||||||
|
### Testing Hardware Devices
|
||||||
|
|
||||||
|
A comprehensive test script is included to verify hardware RNG functionality:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run hardware device tests
|
||||||
|
./test.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The test script performs:
|
||||||
|
- **Device Detection**: Scans for and identifies all connected hardware RNG devices
|
||||||
|
- **Connectivity Testing**: Verifies each device can be opened and read from
|
||||||
|
- **Configuration Testing**: Validates serial port configuration for each device type
|
||||||
|
- **Entropy Quality Analysis**: Measures Shannon entropy of collected random data
|
||||||
|
|
||||||
|
### Current Test Results
|
||||||
|
|
||||||
|
Based on testing with actual hardware devices:
|
||||||
|
|
||||||
|
**✅ Working Devices:**
|
||||||
|
- TrueRNG (Type 1): Full functionality confirmed
|
||||||
|
- TrueRNG Pro V2 (Type 3): Full functionality confirmed
|
||||||
|
|
||||||
|
- Device is detected and identified correctly
|
||||||
|
- Serial port configuration may need adjustment for this device variant
|
||||||
|
|
||||||
|
### Usage in Entropy Collection
|
||||||
|
|
||||||
|
When generating pads or adding entropy, the system will:
|
||||||
|
|
||||||
|
1. **Auto-detect** all connected hardware RNG devices
|
||||||
|
2. **Present a menu** of available devices if multiple are found
|
||||||
|
3. **Test connectivity** before beginning entropy collection
|
||||||
|
4. **Estimate completion time** based on device speed testing
|
||||||
|
5. **Collect entropy** with progress indicators and quality metrics
|
||||||
|
|
||||||
|
### Device Configuration
|
||||||
|
|
||||||
|
Each device type uses optimized serial port settings:
|
||||||
|
|
||||||
|
- **TrueRNG devices**: 3Mbps baud rate, 8N1, no flow control
|
||||||
|
- **Automatic timeout protection**: Prevents hanging on unresponsive devices
|
||||||
|
- **Error recovery**: Graceful handling of device disconnection during operation
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
|
||||||
|
If hardware RNG devices are not detected:
|
||||||
|
|
||||||
|
1. **Check USB connections**: Ensure devices are properly connected
|
||||||
|
2. **Verify permissions**: User must have access to `/dev/ttyACM*` devices
|
||||||
|
3. **Check device enumeration**: Use `lsusb` to verify USB device recognition
|
||||||
|
4. **Review sysfs entries**: Ensure VID/PID information is available in `/sys/bus/usb/devices/`
|
||||||
|
|
||||||
|
|
||||||
## File Formats
|
## File Formats
|
||||||
|
|
||||||
### .otp File Format (Binary)
|
### .otp File Format (Binary)
|
||||||
|
|||||||
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
|
||||||
299
build.sh
299
build.sh
@@ -16,18 +16,31 @@ print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|||||||
# Global variable for commit message
|
# Global variable for commit message
|
||||||
COMMIT_MESSAGE=""
|
COMMIT_MESSAGE=""
|
||||||
|
|
||||||
# Parse command line arguments for -m flag
|
# Parse command line arguments - check if first arg is a command, otherwise treat as commit message
|
||||||
while [[ $# -gt 0 ]]; do
|
COMMAND=""
|
||||||
case $1 in
|
if [[ $# -gt 0 ]]; then
|
||||||
-m|--message)
|
case "$1" in
|
||||||
COMMIT_MESSAGE="$2"
|
build|clean|install|uninstall)
|
||||||
shift 2
|
COMMAND="$1"
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# Keep other arguments for main logic
|
# First argument is not a command, so default to build and treat all args as commit message
|
||||||
break
|
COMMAND="build"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
else
|
||||||
|
# No arguments, default to build
|
||||||
|
COMMAND="build"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Any remaining arguments become the commit message
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ -z "$COMMIT_MESSAGE" ]]; then
|
||||||
|
COMMIT_MESSAGE="$arg"
|
||||||
|
else
|
||||||
|
COMMIT_MESSAGE="$COMMIT_MESSAGE $arg"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function to automatically increment version
|
# Function to automatically increment version
|
||||||
@@ -100,13 +113,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,106 +136,175 @@ increment_version() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update VERSION file for compatibility
|
# Update version strings in source code
|
||||||
echo "${NEW_VERSION#v}" > VERSION
|
update_source_version "$NEW_VERSION"
|
||||||
print_success "Updated VERSION file to ${NEW_VERSION#v}"
|
|
||||||
|
|
||||||
# Generate version.h header file
|
print_success "Version updated to ${NEW_VERSION}"
|
||||||
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) {
|
# Function to update version strings in source code
|
||||||
return VERSION_FULL_DISPLAY;
|
update_source_version() {
|
||||||
|
local NEW_VERSION="$1"
|
||||||
|
|
||||||
|
print_status "Updating version strings in source code..."
|
||||||
|
|
||||||
|
# Replace hardcoded version strings in src/otp.c with the current git tag
|
||||||
|
if [ -f "src/otp.c" ]; then
|
||||||
|
# Update main menu version
|
||||||
|
sed -i "s/OTP v[0-9]\+\.[0-9]\+\.[0-9]\+/OTP $NEW_VERSION/g" src/otp.c
|
||||||
|
# Update ASCII output version
|
||||||
|
sed -i "s/Version: v[0-9]\+\.[0-9]\+\.[0-9]\+/Version: $NEW_VERSION/g" src/otp.c
|
||||||
|
# Update usage/help text version
|
||||||
|
sed -i "s/Implementation v[0-9]\+\.[0-9]\+\.[0-9]\+/Implementation $NEW_VERSION/g" src/otp.c
|
||||||
|
|
||||||
|
print_success "Updated version strings in src/otp.c to $NEW_VERSION"
|
||||||
|
else
|
||||||
|
print_warning "src/otp.c not found - skipping version string updates"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* get_build_info(void) {
|
# Cross-platform build functions
|
||||||
return "Built on " BUILD_DATE " at " BUILD_TIME " from commit " GIT_HASH " on branch " GIT_BRANCH;
|
check_cross_compiler() {
|
||||||
}
|
if ! command -v aarch64-linux-gnu-gcc > /dev/null 2>&1; then
|
||||||
EOF
|
print_error "ARM64/AArch64 cross-compiler not found!"
|
||||||
|
print_error "Install with: sudo apt install gcc-aarch64-linux-gnu"
|
||||||
print_success "Generated version header files"
|
print_error "Or on other distros: gcc-cross-aarch64"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_release_asset() {
|
||||||
|
local api_url="$1"
|
||||||
|
local token="$2"
|
||||||
|
local version="$3"
|
||||||
|
local filename="$4"
|
||||||
|
local display_name="$5"
|
||||||
|
|
||||||
|
if [ ! -f "$filename" ]; then
|
||||||
|
print_warning "Binary $filename not found, skipping upload"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Uploading $filename as '$display_name' to release..."
|
||||||
|
|
||||||
|
# Get release ID first
|
||||||
|
local release_id=$(curl -s -H "Authorization: token $token" \
|
||||||
|
"$api_url/releases/tags/$version" | \
|
||||||
|
grep -o '"id":[0-9]*' | head -n1 | cut -d: -f2)
|
||||||
|
|
||||||
|
if [ -z "$release_id" ]; then
|
||||||
|
print_error "Could not get release ID for $version"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Upload the asset using multipart/form-data
|
||||||
|
curl -X POST "$api_url/releases/$release_id/assets" \
|
||||||
|
-H "Authorization: token $token" \
|
||||||
|
-F "attachment=@$filename;filename=$display_name"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Uploaded $filename as '$display_name' successfully"
|
||||||
|
else
|
||||||
|
print_warning "Failed to upload $filename"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_gitea_release() {
|
||||||
|
local version="$1"
|
||||||
|
|
||||||
|
# Read token from ~/.gitea_token
|
||||||
|
if [ ! -f "$HOME/.gitea_token" ]; then
|
||||||
|
print_error "No ~/.gitea_token found. Cannot create release."
|
||||||
|
print_error "Create ~/.gitea_token with your Gitea access token"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local token=$(cat "$HOME/.gitea_token" | tr -d '\n\r')
|
||||||
|
local api_url="https://git.laantungir.net/api/v1/repos/laantungir/otp"
|
||||||
|
|
||||||
|
print_status "Creating Gitea release for $version..."
|
||||||
|
|
||||||
|
# Create release
|
||||||
|
local response=$(curl -s -X POST "$api_url/releases" \
|
||||||
|
-H "Authorization: token $token" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"tag_name\": \"$version\", \"name\": \"$version\", \"body\": \"Automated release for $version\"}")
|
||||||
|
|
||||||
|
if echo "$response" | grep -q '"id"'; then
|
||||||
|
print_success "Created release $version"
|
||||||
|
|
||||||
|
# Upload binaries with descriptive names from build directory
|
||||||
|
upload_release_asset "$api_url" "$token" "$version" "build/otp-x86_64" "otp-${version}-linux-x86_64"
|
||||||
|
upload_release_asset "$api_url" "$token" "$version" "build/otp-arm64" "otp-${version}-linux-arm64"
|
||||||
|
else
|
||||||
|
print_warning "Release may already exist or creation failed"
|
||||||
|
print_status "Response: $response"
|
||||||
|
|
||||||
|
# Try to upload to existing release anyway
|
||||||
|
upload_release_asset "$api_url" "$token" "$version" "build/otp-x86_64" "otp-${version}-linux-x86_64"
|
||||||
|
upload_release_asset "$api_url" "$token" "$version" "build/otp-arm64" "otp-${version}-linux-arm64"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build functions
|
|
||||||
build_project() {
|
build_project() {
|
||||||
print_status "Cleaning previous build..."
|
print_status "Cleaning previous build..."
|
||||||
make clean
|
make clean
|
||||||
increment_version
|
increment_version
|
||||||
print_status "Building OTP project..."
|
|
||||||
make
|
# Check for cross-compiler
|
||||||
if [ $? -eq 0 ]; then
|
if ! check_cross_compiler; then
|
||||||
print_success "Build completed successfully"
|
print_warning "ARM64/AArch64 cross-compiler not available, building x86_64 only"
|
||||||
|
|
||||||
|
# Build x86_64 only
|
||||||
|
print_status "Building OTP project for x86_64..."
|
||||||
|
make CC=gcc ARCH=x86_64
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "x86_64 build completed successfully"
|
||||||
|
else
|
||||||
|
print_error "x86_64 build failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
print_error "Build failed"
|
# Build both architectures
|
||||||
return 1
|
print_status "Building OTP project for x86_64..."
|
||||||
|
make clean
|
||||||
|
make CC=gcc ARCH=x86_64
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "x86_64 build completed successfully"
|
||||||
|
else
|
||||||
|
print_error "x86_64 build failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "Building OTP project for ARM64/AArch64..."
|
||||||
|
make clean
|
||||||
|
make CC=aarch64-linux-gnu-gcc ARCH=arm64
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "ARM64/AArch64 build completed successfully"
|
||||||
|
else
|
||||||
|
print_error "ARM64/AArch64 build failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
# Create Gitea release with binaries
|
||||||
build_static() {
|
if [ -f "$HOME/.gitea_token" ]; then
|
||||||
print_status "Cleaning previous build..."
|
create_gitea_release "$NEW_VERSION"
|
||||||
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
|
else
|
||||||
print_error "Static build failed"
|
print_warning "No ~/.gitea_token found. Skipping release creation."
|
||||||
return 1
|
print_warning "Create ~/.gitea_token with your Gitea access token to enable releases."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
print_success "Build completed successfully"
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
# Remove build directory
|
||||||
|
rm -rf build
|
||||||
print_success "Clean completed"
|
print_success "Clean completed"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,13 +331,10 @@ uninstall_project() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Main script logic
|
# Main script logic
|
||||||
case "${1:-build}" in
|
case "$COMMAND" in
|
||||||
build)
|
build)
|
||||||
build_project
|
build_project
|
||||||
;;
|
;;
|
||||||
static)
|
|
||||||
build_static
|
|
||||||
;;
|
|
||||||
clean)
|
clean)
|
||||||
clean_project
|
clean_project
|
||||||
;;
|
;;
|
||||||
@@ -265,29 +344,33 @@ case "${1:-build}" in
|
|||||||
uninstall)
|
uninstall)
|
||||||
uninstall_project
|
uninstall_project
|
||||||
;;
|
;;
|
||||||
version)
|
|
||||||
increment_version
|
|
||||||
print_status "Version information generated"
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "OTP Cipher Build Script"
|
echo "OTP Cipher Build Script"
|
||||||
echo "Usage: $0 [-m \"commit message\"] {build|static|clean|install|uninstall|version}"
|
echo "Usage: $0 [command] [commit message]"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Options:"
|
echo "Arguments:"
|
||||||
echo " -m, --message \"text\" - Specify commit message (skips interactive prompt)"
|
echo " command - {build|clean|install|uninstall} (default: build)"
|
||||||
|
echo " commit message - Text to use as commit message (optional, skips interactive prompt)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Commands:"
|
echo "Commands:"
|
||||||
echo " build - Build project with automatic version increment (default)"
|
echo " build - Cross-compile for x86_64 and ARM64/AArch64 with automatic version increment (default)"
|
||||||
echo " static - Build with static linking"
|
echo " clean - Clean build artifacts and cross-compiled binaries"
|
||||||
echo " clean - Clean build artifacts and generated files"
|
|
||||||
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 ""
|
||||||
|
echo "Build Output:"
|
||||||
|
echo " build/otp-x86_64 - Native x86_64 binary"
|
||||||
|
echo " build/otp-arm64 - ARM64/AArch64 binary for Raspberry Pi (if cross-compiler available)"
|
||||||
|
echo ""
|
||||||
|
echo "Gitea Integration:"
|
||||||
|
echo " - Automatically creates releases with binaries if ~/.gitea_token exists"
|
||||||
|
echo " - Requires: ARM64 cross-compiler (gcc-aarch64-linux-gnu)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Examples:"
|
echo "Examples:"
|
||||||
echo " $0 build"
|
echo " $0"
|
||||||
echo " $0 -m \"Fixed checksum parsing bug\" build"
|
echo " $0 \"Fixed checksum parsing bug\""
|
||||||
echo " $0 --message \"Added new feature\" static"
|
echo " $0 build \"Added new feature\""
|
||||||
|
echo " $0 clean"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
1331
src/crypto.c
Normal file
1331
src/crypto.c
Normal file
File diff suppressed because it is too large
Load Diff
951
src/entropy.c
Normal file
951
src/entropy.c
Normal file
@@ -0,0 +1,951 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "nostr_chacha20.h"
|
||||||
|
#include "otp.h"
|
||||||
|
|
||||||
|
|
||||||
|
// In-place pad entropy addition using Chacha20 or direct XOR
|
||||||
|
int add_entropy_to_pad(const char* pad_chksum, const unsigned char* entropy_data,
|
||||||
|
size_t entropy_size, int display_progress) {
|
||||||
|
if (!pad_chksum || !entropy_data || entropy_size < 512) {
|
||||||
|
printf("Error: Invalid entropy data or insufficient entropy\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pad file path
|
||||||
|
char pad_path[1024];
|
||||||
|
char state_path[1024];
|
||||||
|
get_pad_path(pad_chksum, pad_path, state_path);
|
||||||
|
|
||||||
|
// Check if pad exists and get size
|
||||||
|
struct stat pad_stat;
|
||||||
|
if (stat(pad_path, &pad_stat) != 0) {
|
||||||
|
printf("Error: Pad file not found: %s\n", pad_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pad_size = pad_stat.st_size;
|
||||||
|
|
||||||
|
// Determine entropy addition method based on entropy size vs pad size
|
||||||
|
if (entropy_size >= pad_size) {
|
||||||
|
// Use direct XOR when entropy >= pad size
|
||||||
|
return add_entropy_direct_xor(pad_chksum, entropy_data, entropy_size, pad_size, display_progress);
|
||||||
|
} else {
|
||||||
|
// Use ChaCha20 when entropy < pad size
|
||||||
|
return add_entropy_chacha20(pad_chksum, entropy_data, entropy_size, pad_size, display_progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct XOR entropy addition for large entropy sources
|
||||||
|
int add_entropy_direct_xor(const char* pad_chksum, const unsigned char* entropy_data,
|
||||||
|
size_t entropy_size, uint64_t pad_size, int display_progress) {
|
||||||
|
// Get pad file path
|
||||||
|
char pad_path[1024];
|
||||||
|
char state_path[1024];
|
||||||
|
get_pad_path(pad_chksum, pad_path, state_path);
|
||||||
|
|
||||||
|
// Open pad file for read/write
|
||||||
|
FILE* pad_file = fopen(pad_path, "r+b");
|
||||||
|
if (!pad_file) {
|
||||||
|
printf("Error: Cannot open pad file for modification: %s\n", pad_path);
|
||||||
|
printf("Note: Pad files are read-only. Temporarily changing permissions...\n");
|
||||||
|
|
||||||
|
// Try to make writable temporarily
|
||||||
|
if (chmod(pad_path, S_IRUSR | S_IWUSR) != 0) {
|
||||||
|
printf("Error: Cannot change pad file permissions\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad_file = fopen(pad_path, "r+b");
|
||||||
|
if (!pad_file) {
|
||||||
|
printf("Error: Still cannot open pad file for modification\n");
|
||||||
|
// Restore read-only
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Adding entropy to pad using direct XOR...\n");
|
||||||
|
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size);
|
||||||
|
printf("Entropy size: %zu bytes\n", entropy_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process pad in chunks
|
||||||
|
unsigned char buffer[64 * 1024]; // 64KB chunks
|
||||||
|
size_t entropy_offset = 0;
|
||||||
|
uint64_t offset = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
|
||||||
|
while (offset < pad_size) {
|
||||||
|
size_t chunk_size = sizeof(buffer);
|
||||||
|
if (pad_size - offset < chunk_size) {
|
||||||
|
chunk_size = pad_size - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current pad data
|
||||||
|
if (fread(buffer, 1, chunk_size, pad_file) != chunk_size) {
|
||||||
|
printf("Error: Cannot read pad data at offset %lu\n", offset);
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR); // Restore read-only
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR with entropy data (wrap around if entropy smaller than pad)
|
||||||
|
for (size_t i = 0; i < chunk_size; i++) {
|
||||||
|
buffer[i] ^= entropy_data[entropy_offset % entropy_size];
|
||||||
|
entropy_offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek back and write modified data
|
||||||
|
if (fseek(pad_file, offset, SEEK_SET) != 0) {
|
||||||
|
printf("Error: Cannot seek to offset %lu\n", offset);
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(buffer, 1, chunk_size, pad_file) != chunk_size) {
|
||||||
|
printf("Error: Cannot write modified pad data\n");
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += chunk_size;
|
||||||
|
|
||||||
|
// Show progress for large pads
|
||||||
|
if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB
|
||||||
|
show_progress(offset, pad_size, start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pad_file);
|
||||||
|
|
||||||
|
// Restore read-only permissions
|
||||||
|
if (chmod(pad_path, S_IRUSR) != 0) {
|
||||||
|
printf("Warning: Cannot restore pad file to read-only\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
show_progress(pad_size, pad_size, start_time);
|
||||||
|
printf("\n✓ Entropy successfully added to pad using direct XOR\n");
|
||||||
|
printf("✓ Pad integrity maintained\n");
|
||||||
|
printf("✓ %zu bytes of entropy distributed across entire pad\n", entropy_size);
|
||||||
|
printf("✓ Pad restored to read-only mode\n");
|
||||||
|
|
||||||
|
// Update checksum after entropy addition
|
||||||
|
printf("\n🔄 Updating pad checksum...\n");
|
||||||
|
char new_chksum[65];
|
||||||
|
int checksum_result = update_pad_checksum_after_entropy(pad_chksum, new_chksum);
|
||||||
|
|
||||||
|
if (checksum_result == 0) {
|
||||||
|
printf("✓ Pad checksum updated successfully\n");
|
||||||
|
printf(" Old checksum: %.16s...\n", pad_chksum);
|
||||||
|
printf(" New checksum: %.16s...\n", new_chksum);
|
||||||
|
printf("✓ Pad files renamed to new checksum\n");
|
||||||
|
|
||||||
|
// Pause before returning to menu to let user see the success message
|
||||||
|
print_centered_header("Entropy Addition Complete", 1);
|
||||||
|
} else if (checksum_result == 2) {
|
||||||
|
printf("ℹ Checksum unchanged (unusual but not an error)\n");
|
||||||
|
} else {
|
||||||
|
printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n");
|
||||||
|
printf(" You may need to manually handle the checksum update\n");
|
||||||
|
return 1; // Report error despite successful entropy addition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChaCha20 entropy addition for smaller entropy sources
|
||||||
|
int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_data,
|
||||||
|
size_t entropy_size, uint64_t pad_size, int display_progress) {
|
||||||
|
// Derive Chacha20 key and nonce from entropy
|
||||||
|
unsigned char key[32], nonce[12];
|
||||||
|
if (derive_chacha20_params(entropy_data, entropy_size, key, nonce) != 0) {
|
||||||
|
printf("Error: Failed to derive Chacha20 parameters from entropy\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pad file path
|
||||||
|
char pad_path[1024];
|
||||||
|
char state_path[1024];
|
||||||
|
get_pad_path(pad_chksum, pad_path, state_path);
|
||||||
|
|
||||||
|
// Open pad file for read/write
|
||||||
|
FILE* pad_file = fopen(pad_path, "r+b");
|
||||||
|
if (!pad_file) {
|
||||||
|
printf("Error: Cannot open pad file for modification: %s\n", pad_path);
|
||||||
|
printf("Note: Pad files are read-only. Temporarily changing permissions...\n");
|
||||||
|
|
||||||
|
// Try to make writable temporarily
|
||||||
|
if (chmod(pad_path, S_IRUSR | S_IWUSR) != 0) {
|
||||||
|
printf("Error: Cannot change pad file permissions\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pad_file = fopen(pad_path, "r+b");
|
||||||
|
if (!pad_file) {
|
||||||
|
printf("Error: Still cannot open pad file for modification\n");
|
||||||
|
// Restore read-only
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Adding entropy to pad using Chacha20...\n");
|
||||||
|
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process pad in chunks
|
||||||
|
unsigned char buffer[64 * 1024]; // 64KB chunks
|
||||||
|
unsigned char keystream[64 * 1024];
|
||||||
|
uint64_t offset = 0;
|
||||||
|
uint32_t counter = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
|
||||||
|
while (offset < pad_size) {
|
||||||
|
size_t chunk_size = sizeof(buffer);
|
||||||
|
if (pad_size - offset < chunk_size) {
|
||||||
|
chunk_size = pad_size - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current pad data
|
||||||
|
if (fread(buffer, 1, chunk_size, pad_file) != chunk_size) {
|
||||||
|
printf("Error: Cannot read pad data at offset %lu\n", offset);
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR); // Restore read-only
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate keystream for this chunk
|
||||||
|
if (chacha20_encrypt(key, counter, nonce, buffer, keystream, chunk_size) != 0) {
|
||||||
|
printf("Error: Chacha20 keystream generation failed\n");
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR existing pad with keystream (adds entropy)
|
||||||
|
for (size_t i = 0; i < chunk_size; i++) {
|
||||||
|
buffer[i] ^= keystream[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek back and write modified data
|
||||||
|
if (fseek(pad_file, offset, SEEK_SET) != 0) {
|
||||||
|
printf("Error: Cannot seek to offset %lu\n", offset);
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(buffer, 1, chunk_size, pad_file) != chunk_size) {
|
||||||
|
printf("Error: Cannot write modified pad data\n");
|
||||||
|
fclose(pad_file);
|
||||||
|
chmod(pad_path, S_IRUSR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += chunk_size;
|
||||||
|
counter += (chunk_size + 63) / 64; // Round up for block count
|
||||||
|
|
||||||
|
// Show progress for large pads
|
||||||
|
if (display_progress && offset % (64 * 1024 * 1024) == 0) { // Every 64MB
|
||||||
|
show_progress(offset, pad_size, start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pad_file);
|
||||||
|
|
||||||
|
// Restore read-only permissions
|
||||||
|
if (chmod(pad_path, S_IRUSR) != 0) {
|
||||||
|
printf("Warning: Cannot restore pad file to read-only\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
show_progress(pad_size, pad_size, start_time);
|
||||||
|
printf("\n✓ Entropy successfully added to pad using Chacha20\n");
|
||||||
|
printf("✓ Pad integrity maintained\n");
|
||||||
|
printf("✓ %zu bytes of entropy distributed across entire pad\n", entropy_size);
|
||||||
|
printf("✓ Pad restored to read-only mode\n");
|
||||||
|
|
||||||
|
// Update checksum after entropy addition
|
||||||
|
printf("\n🔄 Updating pad checksum...\n");
|
||||||
|
char new_chksum[65];
|
||||||
|
int checksum_result = update_pad_checksum_after_entropy(pad_chksum, new_chksum);
|
||||||
|
|
||||||
|
if (checksum_result == 0) {
|
||||||
|
printf("✓ Pad checksum updated successfully\n");
|
||||||
|
printf(" Old checksum: %.16s...\n", pad_chksum);
|
||||||
|
printf(" New checksum: %.16s...\n", new_chksum);
|
||||||
|
printf("✓ Pad files renamed to new checksum\n");
|
||||||
|
|
||||||
|
// Pause before returning to menu to let user see the success message
|
||||||
|
print_centered_header("Entropy Addition Complete", 1);
|
||||||
|
} else if (checksum_result == 2) {
|
||||||
|
printf("ℹ Checksum unchanged (unusual but not an error)\n");
|
||||||
|
} else {
|
||||||
|
printf("⚠ Warning: Checksum update failed (entropy was added successfully)\n");
|
||||||
|
printf(" You may need to manually handle the checksum update\n");
|
||||||
|
return 1; // Report error despite successful entropy addition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced entropy collection with visual feedback
|
||||||
|
int collect_entropy_with_feedback(unsigned char* entropy_buffer, size_t target_bytes,
|
||||||
|
size_t* collected_bytes, int allow_early_exit) {
|
||||||
|
struct termios original_termios;
|
||||||
|
entropy_collection_state_t state = {0};
|
||||||
|
|
||||||
|
// Initialize state
|
||||||
|
state.target_bytes = target_bytes;
|
||||||
|
state.auto_complete_enabled = allow_early_exit;
|
||||||
|
state.collection_start_time = get_precise_time();
|
||||||
|
|
||||||
|
// Setup raw terminal
|
||||||
|
if (setup_raw_terminal(&original_termios) != 0) {
|
||||||
|
printf("Error: Cannot setup terminal for entropy collection\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear screen area for display
|
||||||
|
printf("\n\n\n\n\n\n");
|
||||||
|
printf("\033[2J\033[H"); // Clear screen and move to top
|
||||||
|
|
||||||
|
unsigned char entropy_block[16];
|
||||||
|
struct timespec timestamp;
|
||||||
|
uint32_t sequence_counter = 0;
|
||||||
|
char key;
|
||||||
|
unsigned char seen_keys[256] = {0};
|
||||||
|
|
||||||
|
*collected_bytes = 0;
|
||||||
|
|
||||||
|
while (state.collected_bytes < target_bytes) {
|
||||||
|
// Update display
|
||||||
|
state.quality_score = calculate_overall_quality(&state);
|
||||||
|
display_entropy_progress(&state);
|
||||||
|
|
||||||
|
// Non-blocking read
|
||||||
|
if (read(STDIN_FILENO, &key, 1) == 1) {
|
||||||
|
// Handle ESC key for early exit
|
||||||
|
if (key == 27 && allow_early_exit && state.collected_bytes >= 1024) {
|
||||||
|
break; // Early exit allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record keypress timing
|
||||||
|
double current_time = get_precise_time();
|
||||||
|
state.last_keypress_time = current_time;
|
||||||
|
|
||||||
|
// Update key histogram
|
||||||
|
state.key_histogram[(unsigned char)key]++;
|
||||||
|
|
||||||
|
// Get high precision timestamp
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, ×tamp);
|
||||||
|
|
||||||
|
// Create enhanced entropy block: [key][timestamp][sequence][quality_bits]
|
||||||
|
entropy_block[0] = key;
|
||||||
|
memcpy(&entropy_block[1], ×tamp.tv_sec, 8);
|
||||||
|
memcpy(&entropy_block[9], ×tamp.tv_nsec, 4);
|
||||||
|
memcpy(&entropy_block[13], &sequence_counter, 2);
|
||||||
|
entropy_block[15] = (unsigned char)(current_time * 1000) & 0xFF; // Sub-millisecond timing
|
||||||
|
|
||||||
|
// Add to entropy buffer
|
||||||
|
if (state.collected_bytes + 16 <= MAX_ENTROPY_BUFFER) {
|
||||||
|
memcpy(entropy_buffer + state.collected_bytes, entropy_block, 16);
|
||||||
|
state.collected_bytes += 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence_counter++;
|
||||||
|
|
||||||
|
// Track unique keys
|
||||||
|
if (!seen_keys[(unsigned char)key]) {
|
||||||
|
seen_keys[(unsigned char)key] = 1;
|
||||||
|
state.unique_keys++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No key available, just sleep and wait for keystrokes
|
||||||
|
usleep(10000); // 10ms delay - wait for keystrokes, don't add timing entropy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-complete at target if enabled
|
||||||
|
if (state.collected_bytes >= target_bytes) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final display update
|
||||||
|
state.quality_score = calculate_overall_quality(&state);
|
||||||
|
display_entropy_progress(&state);
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
double collection_time = get_precise_time() - state.collection_start_time;
|
||||||
|
printf("\n\n✓ Entropy collection complete!\n");
|
||||||
|
printf(" Collected: %zu bytes in %.1f seconds\n", state.collected_bytes, collection_time);
|
||||||
|
printf(" Quality: %d%% (Excellent: 80%%+, Good: 60%%+)\n", state.quality_score);
|
||||||
|
printf(" Unique keys: %zu\n", state.unique_keys);
|
||||||
|
|
||||||
|
// Restore terminal
|
||||||
|
restore_terminal(&original_termios);
|
||||||
|
|
||||||
|
*collected_bytes = state.collected_bytes;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chacha20 key derivation from collected entropy
|
||||||
|
int derive_chacha20_params(const unsigned char* entropy_data, size_t entropy_size,
|
||||||
|
unsigned char key[32], unsigned char nonce[12]) {
|
||||||
|
if (!entropy_data || entropy_size < 512 || !key || !nonce) {
|
||||||
|
return 1; // Error: insufficient entropy or null pointers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1: Generate base key from entropy using enhanced XOR checksum method
|
||||||
|
unsigned char enhanced_checksum[44]; // 32 key + 12 nonce
|
||||||
|
memset(enhanced_checksum, 0, 44);
|
||||||
|
|
||||||
|
// Mix entropy data similar to calculate_checksum but for 44 bytes
|
||||||
|
for (size_t i = 0; i < entropy_size; i++) {
|
||||||
|
unsigned char bucket = i % 44;
|
||||||
|
enhanced_checksum[bucket] ^= entropy_data[i] ^
|
||||||
|
((i >> 8) & 0xFF) ^
|
||||||
|
((i >> 16) & 0xFF) ^
|
||||||
|
((i >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Add system entropy for additional randomness
|
||||||
|
unsigned char system_entropy[32];
|
||||||
|
FILE* urandom = fopen("/dev/urandom", "rb");
|
||||||
|
if (!urandom) {
|
||||||
|
return 2; // Error: cannot access system entropy
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(system_entropy, 1, 32, urandom) != 32) {
|
||||||
|
fclose(urandom);
|
||||||
|
return 2; // Error: insufficient system entropy
|
||||||
|
}
|
||||||
|
fclose(urandom);
|
||||||
|
|
||||||
|
// Mix system entropy into derived key
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
enhanced_checksum[i] ^= system_entropy[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract key and nonce
|
||||||
|
memcpy(key, enhanced_checksum, 32);
|
||||||
|
memcpy(nonce, enhanced_checksum + 32, 12);
|
||||||
|
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file path and size information for entropy collection
|
||||||
|
int get_file_entropy_info(char* file_path, size_t max_path_len, size_t* file_size, int display_progress) {
|
||||||
|
if (display_progress) {
|
||||||
|
print_centered_header("File Entropy Collection", 0);
|
||||||
|
printf("Load entropy from binary file (.bin format)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Enter path to binary entropy file: ");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (!fgets(file_path, max_path_len, stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove newline
|
||||||
|
file_path[strcspn(file_path, "\n")] = 0;
|
||||||
|
|
||||||
|
// Check if file exists and get size
|
||||||
|
struct stat file_stat;
|
||||||
|
if (stat(file_path, &file_stat) != 0) {
|
||||||
|
printf("Error: File '%s' not found\n", file_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISREG(file_stat.st_mode)) {
|
||||||
|
printf("Error: '%s' is not a regular file\n", file_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*file_size = file_stat.st_size;
|
||||||
|
if (*file_size == 0) {
|
||||||
|
printf("Error: File is empty\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("✓ File found: %s\n", file_path);
|
||||||
|
printf(" Size: %zu bytes\n", *file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect entropy from binary file (legacy function for backward compatibility)
|
||||||
|
int collect_file_entropy(unsigned char* entropy_buffer, size_t target_bytes,
|
||||||
|
size_t* collected_bytes, int display_progress) {
|
||||||
|
char file_path[512];
|
||||||
|
size_t file_size;
|
||||||
|
|
||||||
|
// Get file path and size first
|
||||||
|
if (get_file_entropy_info(file_path, sizeof(file_path), &file_size, display_progress) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_size < target_bytes) {
|
||||||
|
printf("Warning: File size (%zu bytes) is smaller than target (%zu bytes)\n",
|
||||||
|
file_size, target_bytes);
|
||||||
|
printf("Will read available data and pad with zeros if necessary.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open file for reading
|
||||||
|
FILE* entropy_file = fopen(file_path, "rb");
|
||||||
|
if (!entropy_file) {
|
||||||
|
printf("Error: Cannot open file '%s' for reading\n", file_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Reading entropy from file...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read entropy data
|
||||||
|
size_t bytes_to_read = (file_size < target_bytes) ? file_size : target_bytes;
|
||||||
|
size_t bytes_read = fread(entropy_buffer, 1, bytes_to_read, entropy_file);
|
||||||
|
|
||||||
|
if (bytes_read != bytes_to_read) {
|
||||||
|
printf("Error: Failed to read %zu bytes from file (read %zu)\n",
|
||||||
|
bytes_to_read, bytes_read);
|
||||||
|
fclose(entropy_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(entropy_file);
|
||||||
|
|
||||||
|
// Pad with zeros if file was smaller than target
|
||||||
|
if (bytes_read < target_bytes) {
|
||||||
|
memset(entropy_buffer + bytes_read, 0, target_bytes - bytes_read);
|
||||||
|
*collected_bytes = target_bytes; // We padded to target size
|
||||||
|
} else {
|
||||||
|
*collected_bytes = bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("✓ File entropy collection complete!\n");
|
||||||
|
printf(" File: %s\n", file_path);
|
||||||
|
printf(" Read: %zu bytes\n", bytes_read);
|
||||||
|
printf(" Total: %zu bytes (padded to target if necessary)\n", *collected_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add file entropy directly to pad using streaming (for large files)
|
||||||
|
int add_file_entropy_streaming(const char* pad_chksum, const char* file_path, size_t file_size, int display_progress) {
|
||||||
|
// Get pad file path
|
||||||
|
char pad_path[1024];
|
||||||
|
char state_path[1024];
|
||||||
|
get_pad_path(pad_chksum, pad_path, state_path);
|
||||||
|
|
||||||
|
// Check if pad exists and get size
|
||||||
|
struct stat pad_stat;
|
||||||
|
if (stat(pad_path, &pad_stat) != 0) {
|
||||||
|
printf("Error: Pad file not found: %s\n", pad_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pad_size = pad_stat.st_size;
|
||||||
|
|
||||||
|
// Open entropy file for reading
|
||||||
|
FILE* entropy_file = fopen(file_path, "rb");
|
||||||
|
if (!entropy_file) {
|
||||||
|
printf("Error: Cannot open entropy file '%s' for reading\n", file_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open pad file for read/write
|
||||||
|
FILE* pad_file = fopen(pad_path, "r+b");
|
||||||
|
if (!pad_file) {
|
||||||
|
printf("Error: Cannot open pad file for modification: %s\n", pad_path);
|
||||||
|
fclose(entropy_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Adding entropy to pad using streaming direct XOR...\n");
|
||||||
|
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size);
|
||||||
|
printf("Entropy file: %.2f GB (%zu bytes)\n", (double)file_size / (1024.0*1024.0*1024.0), file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process in chunks
|
||||||
|
unsigned char pad_buffer[64 * 1024];
|
||||||
|
unsigned char entropy_buffer[64 * 1024];
|
||||||
|
uint64_t offset = 0;
|
||||||
|
size_t entropy_offset = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
|
||||||
|
while (offset < pad_size) {
|
||||||
|
size_t chunk_size = sizeof(pad_buffer);
|
||||||
|
if (pad_size - offset < chunk_size) {
|
||||||
|
chunk_size = pad_size - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current pad data
|
||||||
|
if (fread(pad_buffer, 1, chunk_size, pad_file) != chunk_size) {
|
||||||
|
printf("Error: Cannot read pad data at offset %lu\n", offset);
|
||||||
|
fclose(entropy_file);
|
||||||
|
fclose(pad_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read entropy data (wrap around if file smaller than pad)
|
||||||
|
size_t entropy_read = 0;
|
||||||
|
while (entropy_read < chunk_size) {
|
||||||
|
size_t to_read = chunk_size - entropy_read;
|
||||||
|
if (to_read > sizeof(entropy_buffer)) {
|
||||||
|
to_read = sizeof(entropy_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read_bytes = fread(entropy_buffer, 1, to_read, entropy_file);
|
||||||
|
if (read_bytes == 0) {
|
||||||
|
// Reached end of entropy file, wrap around
|
||||||
|
fseek(entropy_file, 0, SEEK_SET);
|
||||||
|
entropy_offset = 0;
|
||||||
|
read_bytes = fread(entropy_buffer, 1, to_read, entropy_file);
|
||||||
|
if (read_bytes == 0) {
|
||||||
|
printf("Error: Cannot read from entropy file\n");
|
||||||
|
fclose(entropy_file);
|
||||||
|
fclose(pad_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR this chunk
|
||||||
|
for (size_t i = 0; i < read_bytes; i++) {
|
||||||
|
pad_buffer[entropy_read + i] ^= entropy_buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
entropy_read += read_bytes;
|
||||||
|
entropy_offset += read_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek back and write modified data
|
||||||
|
if (fseek(pad_file, offset, SEEK_SET) != 0) {
|
||||||
|
printf("Error: Cannot seek to offset %lu\n", offset);
|
||||||
|
fclose(entropy_file);
|
||||||
|
fclose(pad_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(pad_buffer, 1, chunk_size, pad_file) != chunk_size) {
|
||||||
|
printf("Error: Cannot write modified pad data\n");
|
||||||
|
fclose(entropy_file);
|
||||||
|
fclose(pad_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += chunk_size;
|
||||||
|
|
||||||
|
// Show progress for large pads
|
||||||
|
if (display_progress && offset % (64 * 1024 * 1024) == 0) {
|
||||||
|
show_progress(offset, pad_size, start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(entropy_file);
|
||||||
|
fclose(pad_file);
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
show_progress(pad_size, pad_size, start_time);
|
||||||
|
printf("\n✓ Entropy successfully added to pad using streaming direct XOR\n");
|
||||||
|
printf("✓ Pad integrity maintained\n");
|
||||||
|
printf("✓ %zu bytes of entropy distributed across entire pad\n", file_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect entropy by source type with unified interface
|
||||||
|
int collect_entropy_by_source(entropy_source_t source, unsigned char* entropy_buffer,
|
||||||
|
size_t target_bytes, size_t* collected_bytes, int display_progress) {
|
||||||
|
switch (source) {
|
||||||
|
case ENTROPY_SOURCE_KEYBOARD:
|
||||||
|
return collect_entropy_with_feedback(entropy_buffer, target_bytes, collected_bytes, 1);
|
||||||
|
|
||||||
|
case ENTROPY_SOURCE_TRUERNG:
|
||||||
|
return collect_truerng_entropy(entropy_buffer, target_bytes, collected_bytes, display_progress);
|
||||||
|
|
||||||
|
case ENTROPY_SOURCE_DICE:
|
||||||
|
return collect_dice_entropy(entropy_buffer, target_bytes, collected_bytes, display_progress);
|
||||||
|
|
||||||
|
case ENTROPY_SOURCE_FILE:
|
||||||
|
return collect_file_entropy(entropy_buffer, target_bytes, collected_bytes, display_progress);
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Unknown entropy source\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect manual entropy from any printable character input
|
||||||
|
int collect_dice_entropy(unsigned char* entropy_buffer, size_t target_bytes,
|
||||||
|
size_t* collected_bytes, int display_progress) {
|
||||||
|
if (display_progress) {
|
||||||
|
print_centered_header("Manual Entropy Collection", 0);
|
||||||
|
printf("Enter any text, numbers, or symbols for entropy.\n");
|
||||||
|
printf("Target: %zu bytes (%zu characters needed)\n", target_bytes, target_bytes);
|
||||||
|
printf("Press Enter after each line, or 'done' when finished.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytes_written = 0;
|
||||||
|
|
||||||
|
char input[256];
|
||||||
|
|
||||||
|
while (bytes_written < target_bytes) {
|
||||||
|
if (display_progress) {
|
||||||
|
double percentage = (double)bytes_written / target_bytes * 100.0;
|
||||||
|
printf("Progress: %.1f%% (%zu/%zu bytes) - Enter text: ",
|
||||||
|
percentage, bytes_written, target_bytes);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fgets(input, sizeof(input), stdin)) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove newline
|
||||||
|
input[strcspn(input, "\n")] = 0;
|
||||||
|
|
||||||
|
// Check for done command
|
||||||
|
if (strcmp(input, "done") == 0 && bytes_written >= target_bytes / 2) {
|
||||||
|
break; // Allow early exit if we have at least half the target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each printable character as 8 bits of entropy
|
||||||
|
for (size_t i = 0; input[i] && bytes_written < target_bytes; i++) {
|
||||||
|
char c = input[i];
|
||||||
|
if (c >= 32 && c <= 126) { // Printable ASCII characters
|
||||||
|
entropy_buffer[bytes_written++] = (unsigned char)c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("\n✓ Manual entropy collection complete!\n");
|
||||||
|
printf(" Collected: %zu bytes from text input\n", bytes_written);
|
||||||
|
printf(" Entropy quality: 8 bits per character\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
*collected_bytes = bytes_written;
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_terminal(struct termios* original_termios) {
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, original_termios);
|
||||||
|
|
||||||
|
// Reset stdin to blocking
|
||||||
|
int flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
|
}
|
||||||
|
double get_precise_time(void) {
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return ts.tv_sec + ts.tv_nsec / 1000000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_progress_bar(double percentage, int width) {
|
||||||
|
int filled = (int)(percentage / 100.0 * width);
|
||||||
|
if (filled > width) filled = width;
|
||||||
|
|
||||||
|
printf("[");
|
||||||
|
for (int i = 0; i < filled; i++) {
|
||||||
|
printf("█");
|
||||||
|
}
|
||||||
|
for (int i = filled; i < width; i++) {
|
||||||
|
printf("░");
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_quality_bar(double quality, int width, const char* label) {
|
||||||
|
int filled = (int)(quality / 100.0 * width);
|
||||||
|
if (filled > width) filled = width;
|
||||||
|
|
||||||
|
// Color coding based on quality
|
||||||
|
const char* color;
|
||||||
|
if (quality >= 80) color = "\033[32m"; // Green
|
||||||
|
else if (quality >= 60) color = "\033[33m"; // Yellow
|
||||||
|
else color = "\033[31m"; // Red
|
||||||
|
|
||||||
|
printf("%s", color);
|
||||||
|
draw_progress_bar(quality, width);
|
||||||
|
printf("\033[0m %-10s", label); // Reset color
|
||||||
|
}
|
||||||
|
|
||||||
|
double calculate_timing_quality(const entropy_collection_state_t* state) {
|
||||||
|
// Analyze timing variance between keypresses
|
||||||
|
if (state->collected_bytes < 32) return 0.0; // Need minimum data
|
||||||
|
|
||||||
|
// Simplified timing quality based on collection rate and variation
|
||||||
|
double elapsed = get_precise_time() - state->collection_start_time;
|
||||||
|
if (elapsed < 0.1) return 0.0;
|
||||||
|
|
||||||
|
double rate = state->collected_bytes / elapsed;
|
||||||
|
|
||||||
|
// Optimal rate is around 50-200 bytes/second (moderate typing with good timing variance)
|
||||||
|
if (rate >= 50 && rate <= 200) return 90.0;
|
||||||
|
if (rate >= 20 && rate <= 500) return 70.0;
|
||||||
|
if (rate >= 10 && rate <= 1000) return 50.0;
|
||||||
|
return 30.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double calculate_variety_quality(const entropy_collection_state_t* state) {
|
||||||
|
// Analyze key variety and distribution
|
||||||
|
if (state->collected_bytes < 16) return 0.0;
|
||||||
|
|
||||||
|
// Calculate entropy from key histogram
|
||||||
|
double entropy = 0.0;
|
||||||
|
size_t total_keys = 0;
|
||||||
|
|
||||||
|
// Count total keypresses
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
total_keys += state->key_histogram[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_keys == 0) return 0.0;
|
||||||
|
|
||||||
|
// Calculate Shannon entropy
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
if (state->key_histogram[i] > 0) {
|
||||||
|
double p = (double)state->key_histogram[i] / total_keys;
|
||||||
|
entropy -= p * log2(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert entropy to quality score (0-100)
|
||||||
|
double max_entropy = log2(256); // Perfect entropy for 8-bit keyspace
|
||||||
|
double normalized_entropy = entropy / max_entropy;
|
||||||
|
|
||||||
|
// Scale based on unique keys as well
|
||||||
|
double unique_key_factor = (double)state->unique_keys / 50.0; // 50+ unique keys is excellent
|
||||||
|
if (unique_key_factor > 1.0) unique_key_factor = 1.0;
|
||||||
|
|
||||||
|
return (normalized_entropy * 70.0 + unique_key_factor * 30.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char calculate_overall_quality(const entropy_collection_state_t* state) {
|
||||||
|
double timing = calculate_timing_quality(state);
|
||||||
|
double variety = calculate_variety_quality(state);
|
||||||
|
|
||||||
|
// Simple collection progress bonus
|
||||||
|
double progress_bonus = (double)state->collected_bytes / state->target_bytes * 20.0;
|
||||||
|
if (progress_bonus > 20.0) progress_bonus = 20.0;
|
||||||
|
|
||||||
|
// Weighted average
|
||||||
|
double overall = (timing * 0.4 + variety * 0.4 + progress_bonus);
|
||||||
|
if (overall > 100.0) overall = 100.0;
|
||||||
|
|
||||||
|
return (unsigned char)overall;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_entropy_progress(const entropy_collection_state_t* state) {
|
||||||
|
// Calculate percentages
|
||||||
|
double progress = (double)state->collected_bytes / state->target_bytes * 100.0;
|
||||||
|
if (progress > 100.0) progress = 100.0;
|
||||||
|
|
||||||
|
double quality = state->quality_score;
|
||||||
|
double timing_quality = calculate_timing_quality(state);
|
||||||
|
double variety_quality = calculate_variety_quality(state);
|
||||||
|
|
||||||
|
// Clear previous output and redraw
|
||||||
|
printf("\033[2K\r"); // Clear line
|
||||||
|
printf("\033[A\033[2K\r"); // Move up and clear
|
||||||
|
printf("\033[A\033[2K\r"); // Move up and clear
|
||||||
|
printf("\033[A\033[2K\r"); // Move up and clear
|
||||||
|
printf("\033[A\033[2K\r"); // Move up and clear
|
||||||
|
printf("\033[A\033[2K\r"); // Move up and clear
|
||||||
|
|
||||||
|
// Header
|
||||||
|
printf("Adding Entropy to Pad - Target: %zu bytes\n\n", state->target_bytes);
|
||||||
|
|
||||||
|
// Main progress bar
|
||||||
|
printf("Progress: ");
|
||||||
|
draw_progress_bar(progress, 50);
|
||||||
|
printf(" %.1f%% (%zu/%zu bytes)\n", progress, state->collected_bytes, state->target_bytes);
|
||||||
|
|
||||||
|
// Quality indicators
|
||||||
|
printf("Quality: ");
|
||||||
|
draw_quality_bar(quality, 50, "OVERALL");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Timing: ");
|
||||||
|
draw_quality_bar(timing_quality, 50, "VARIED");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Keys: ");
|
||||||
|
draw_quality_bar(variety_quality, 50, "DIVERSE");
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Instructions
|
||||||
|
if (state->collected_bytes >= 1024 && state->auto_complete_enabled) {
|
||||||
|
printf("\nPress ESC to finish (minimum reached) or continue typing...");
|
||||||
|
} else if (state->collected_bytes < 1024) {
|
||||||
|
printf("\nType random keys... (%zu more bytes needed)", 1024 - state->collected_bytes);
|
||||||
|
} else {
|
||||||
|
printf("\nType random keys or press ESC when satisfied...");
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyboard entropy functions
|
||||||
|
int setup_raw_terminal(struct termios* original_termios) {
|
||||||
|
struct termios new_termios;
|
||||||
|
|
||||||
|
if (tcgetattr(STDIN_FILENO, original_termios) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_termios = *original_termios;
|
||||||
|
new_termios.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
new_termios.c_cc[VMIN] = 0;
|
||||||
|
new_termios.c_cc[VTIME] = 0;
|
||||||
|
|
||||||
|
if (tcsetattr(STDIN_FILENO, TCSANOW, &new_termios) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stdin to non-blocking
|
||||||
|
int flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||||
|
if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, original_termios);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
270
src/main.c
Normal file
270
src/main.c
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "otp.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
// Initialize terminal dimensions first
|
||||||
|
init_terminal_dimensions();
|
||||||
|
|
||||||
|
// Load preferences
|
||||||
|
load_preferences();
|
||||||
|
|
||||||
|
// Detect interactive mode: true when running with no arguments AND no piped input
|
||||||
|
int is_interactive = (argc == 1 && !has_stdin_data());
|
||||||
|
set_interactive_mode(is_interactive);
|
||||||
|
|
||||||
|
// Check for OTP thumb drive on startup
|
||||||
|
char otp_drive_path[512];
|
||||||
|
if (detect_otp_thumb_drive(otp_drive_path, sizeof(otp_drive_path))) {
|
||||||
|
// Only show messages in interactive mode
|
||||||
|
if (get_interactive_mode()) {
|
||||||
|
printf("Detected OTP thumb drive: %s\n", otp_drive_path);
|
||||||
|
printf("Using as default pads directory for this session.\n\n");
|
||||||
|
}
|
||||||
|
set_current_pads_dir(otp_drive_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_interactive_mode()) {
|
||||||
|
return interactive_mode();
|
||||||
|
} else {
|
||||||
|
return command_line_mode(argc, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int command_line_mode(int argc, char* argv[]) {
|
||||||
|
// Check for help flags first (only if we have arguments)
|
||||||
|
if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--h") == 0 ||
|
||||||
|
strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0 ||
|
||||||
|
strcmp(argv[1], "help") == 0)) {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no arguments but piped input, default to encrypt mode
|
||||||
|
if (argc == 1 && has_stdin_data()) {
|
||||||
|
char* piped_text = read_stdin_text();
|
||||||
|
if (piped_text) {
|
||||||
|
int result = pipe_mode(argc, argv, piped_text);
|
||||||
|
free(piped_text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1 && (strcmp(argv[1], "generate") == 0 || strcmp(argv[1], "-g") == 0)) {
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("Usage: %s generate|-g <size>\n", argv[0]);
|
||||||
|
printf("Size examples: 1024, 1GB, 5TB, 512MB\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
uint64_t size = parse_size_string(argv[2]);
|
||||||
|
if (size == 0) {
|
||||||
|
printf("Error: Invalid size format\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return generate_pad(size, 1); // Use simplified pad generation
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "encrypt") == 0 || strcmp(argv[1], "-e") == 0) {
|
||||||
|
// Check for piped input first
|
||||||
|
if (has_stdin_data()) {
|
||||||
|
char* piped_text = read_stdin_text();
|
||||||
|
if (piped_text) {
|
||||||
|
int result = pipe_mode(argc, argv, piped_text);
|
||||||
|
free(piped_text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < 2 || argc > 4) {
|
||||||
|
printf("Usage: %s encrypt|-e [pad_chksum_or_prefix] [text_to_encrypt]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if pad was specified or use default
|
||||||
|
const char* pad_identifier = NULL;
|
||||||
|
const char* text = NULL;
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
// Just -e, use default pad, no text (interactive)
|
||||||
|
pad_identifier = NULL;
|
||||||
|
text = NULL;
|
||||||
|
} else if (argc == 3) {
|
||||||
|
// Could be -e <pad> or -e <text> (using default pad)
|
||||||
|
// Check if default pad is available to determine interpretation
|
||||||
|
char* default_pad = get_default_pad_path();
|
||||||
|
if (default_pad) {
|
||||||
|
// Default pad available, treat argument as text
|
||||||
|
pad_identifier = NULL;
|
||||||
|
text = argv[2];
|
||||||
|
free(default_pad);
|
||||||
|
} else {
|
||||||
|
// No default pad, treat as pad identifier
|
||||||
|
pad_identifier = argv[2];
|
||||||
|
text = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// argc == 4: -e <pad> <text>
|
||||||
|
pad_identifier = argv[2];
|
||||||
|
text = argv[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If pad_identifier is NULL, we need to use default pad
|
||||||
|
if (pad_identifier == NULL) {
|
||||||
|
char* default_pad = get_default_pad_path();
|
||||||
|
if (default_pad) {
|
||||||
|
// Extract checksum from default pad path
|
||||||
|
char* filename = strrchr(default_pad, '/');
|
||||||
|
if (!filename) filename = default_pad;
|
||||||
|
else filename++; // Skip the '/'
|
||||||
|
|
||||||
|
// Extract checksum (remove .pad extension)
|
||||||
|
if (strlen(filename) >= 68 && strstr(filename, ".pad")) {
|
||||||
|
static char default_checksum[65];
|
||||||
|
strncpy(default_checksum, filename, 64);
|
||||||
|
default_checksum[64] = '\0';
|
||||||
|
pad_identifier = default_checksum;
|
||||||
|
}
|
||||||
|
free(default_pad);
|
||||||
|
|
||||||
|
// Call encrypt_text and return result
|
||||||
|
return encrypt_text(pad_identifier, text);
|
||||||
|
} else {
|
||||||
|
printf("Error: No default pad configured. Specify pad explicitly or configure default pad.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Explicit pad specified, normal operation
|
||||||
|
return encrypt_text(pad_identifier, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "decrypt") == 0 || strcmp(argv[1], "-d") == 0) {
|
||||||
|
if (argc == 2) {
|
||||||
|
// Check for piped input first
|
||||||
|
if (has_stdin_data()) {
|
||||||
|
// Piped decrypt mode - read stdin and decrypt silently
|
||||||
|
char* piped_message = read_stdin_text();
|
||||||
|
if (piped_message) {
|
||||||
|
int result = decrypt_text(NULL, piped_message);
|
||||||
|
free(piped_message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Interactive mode - no arguments needed
|
||||||
|
return decrypt_text(NULL, NULL);
|
||||||
|
}
|
||||||
|
else if (argc == 3) {
|
||||||
|
// Check if the argument looks like an encrypted message (starts with -----)
|
||||||
|
if (strncmp(argv[2], "-----BEGIN OTP MESSAGE-----", 27) == 0) {
|
||||||
|
// Inline decrypt with message only - use silent mode for command line
|
||||||
|
return decrypt_text(NULL, argv[2]);
|
||||||
|
} else {
|
||||||
|
// Check if it's a file (contains . or ends with known extensions)
|
||||||
|
if (strstr(argv[2], ".") != NULL) {
|
||||||
|
// Treat as file
|
||||||
|
return decrypt_file(argv[2], NULL);
|
||||||
|
} else {
|
||||||
|
// Interactive decrypt with pad hint (legacy support)
|
||||||
|
return decrypt_text(argv[2], NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (argc == 4) {
|
||||||
|
// Check for -o flag for output file
|
||||||
|
if (strcmp(argv[2], "-o") == 0) {
|
||||||
|
printf("Usage: %s decrypt|-d <input_file> [-o <output_file>]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
// Legacy format: pad_chksum and message, or file with output
|
||||||
|
// Use silent mode for command line when message is provided
|
||||||
|
return decrypt_text(argv[2], argv[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (argc == 5 && strcmp(argv[3], "-o") == 0) {
|
||||||
|
// File decryption with output: -d <input_file> -o <output_file>
|
||||||
|
return decrypt_file(argv[2], argv[4]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Usage: %s decrypt|-d [encrypted_message|file] [-o output_file]\n", argv[0]);
|
||||||
|
printf(" %s decrypt|-d [encrypted_message] (pad info from message)\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "-f") == 0) {
|
||||||
|
// File encryption mode: -f <input_file> <pad_prefix> [-a] [-o <output_file>]
|
||||||
|
if (argc < 4) {
|
||||||
|
printf("Usage: %s -f <input_file> <pad_prefix> [-a] [-o <output_file>]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* input_file = argv[2];
|
||||||
|
const char* pad_prefix = argv[3];
|
||||||
|
int ascii_armor = 0;
|
||||||
|
const char* output_file = NULL;
|
||||||
|
|
||||||
|
// Parse optional flags
|
||||||
|
for (int i = 4; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-a") == 0) {
|
||||||
|
ascii_armor = 1;
|
||||||
|
} else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
|
||||||
|
output_file = argv[++i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encrypt_file(pad_prefix, input_file, output_file, ascii_armor);
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[1], "list") == 0 || strcmp(argv[1], "-l") == 0) {
|
||||||
|
printf("Available pads:\n");
|
||||||
|
char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to exit)", PAD_FILTER_ALL, 0);
|
||||||
|
if (selected) {
|
||||||
|
free(selected);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage(const char* program_name) {
|
||||||
|
printf("OTP Cipher - One Time Pad Implementation v0.3.16\n");
|
||||||
|
printf("Built for testing entropy system\n");
|
||||||
|
printf("Usage:\n");
|
||||||
|
printf(" %s - Interactive mode\n", program_name);
|
||||||
|
printf(" %s generate|-g <size> - Generate new pad\n", program_name);
|
||||||
|
printf(" %s encrypt|-e [pad_checksum_prefix] [text] - Encrypt text\n", program_name);
|
||||||
|
printf(" %s decrypt|-d [encrypted_message] - Decrypt message\n", program_name);
|
||||||
|
printf(" %s -f <file> <pad_prefix> [-a] [-o <out>] - Encrypt file\n", program_name);
|
||||||
|
printf(" %s list|-l - List available pads\n", program_name);
|
||||||
|
printf("\nFile Operations:\n");
|
||||||
|
printf(" -f <file> <pad> - Encrypt file (binary .otp format)\n");
|
||||||
|
printf(" -f <file> <pad> -a - Encrypt file (ASCII .otp.asc format)\n");
|
||||||
|
printf(" -o <output> - Specify output filename\n");
|
||||||
|
printf("\nShort flags:\n");
|
||||||
|
printf(" -g generate -e encrypt -d decrypt -l list -f file\n");
|
||||||
|
printf("\nExamples:\n");
|
||||||
|
printf(" %s -e 1a2b3c \"Hello world\" - Encrypt inline text\n", program_name);
|
||||||
|
printf(" %s -f document.pdf 1a2b - Encrypt file (binary)\n", program_name);
|
||||||
|
printf(" %s -f document.pdf 1a2b -a - Encrypt file (ASCII)\n", program_name);
|
||||||
|
printf(" %s -f document.pdf 1a2b -o secret.otp - Encrypt with custom output\n", program_name);
|
||||||
|
printf(" %s -d \"-----BEGIN OTP MESSAGE-----...\" - Decrypt message/file\n", program_name);
|
||||||
|
printf(" %s -d encrypted.otp.asc - Decrypt ASCII file\n", program_name);
|
||||||
|
printf(" %s -g 1GB - Generate 1GB pad\n", program_name);
|
||||||
|
printf(" %s -l - List pads\n", program_name);
|
||||||
|
printf("\nSize examples: 1GB, 5TB, 512MB, 2048 (bytes)\n");
|
||||||
|
printf("Pad selection: Full chksum or prefix\n");
|
||||||
|
}
|
||||||
163
src/nostr_chacha20.c
Normal file
163
src/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
src/nostr_chacha20.h
Normal file
115
src/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 */
|
||||||
35
src/otp.c
Normal file
35
src/otp.c
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "nostr_chacha20.h"
|
||||||
|
#include "otp.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_INPUT_SIZE 4096
|
||||||
|
#define MAX_LINE_LENGTH 1024
|
||||||
|
#define MAX_HASH_LENGTH 65
|
||||||
|
#define PROGRESS_UPDATE_INTERVAL (64 * 1024 * 1024) // 64MB intervals
|
||||||
|
#define DEFAULT_PADS_DIR "pads"
|
||||||
|
#define FILES_DIR "files"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GLOBAL VARIABLES
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
char current_pads_dir[512] = DEFAULT_PADS_DIR;
|
||||||
338
src/otp.h
Normal file
338
src/otp.h
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
#ifndef OTP_H
|
||||||
|
#define OTP_H
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// OTP CIPHER - FUNCTION PROTOTYPES HEADER
|
||||||
|
// One Time Pad Implementation v0.2.109
|
||||||
|
//
|
||||||
|
// This header file contains all function prototypes extracted from otp.c
|
||||||
|
// Organized by functional categories for better maintainability
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
#define MAX_INPUT_SIZE 4096
|
||||||
|
#define MAX_LINE_LENGTH 1024
|
||||||
|
#define MAX_HASH_LENGTH 65
|
||||||
|
#define PROGRESS_UPDATE_INTERVAL (64 * 1024 * 1024) // 64MB intervals
|
||||||
|
#define DEFAULT_PADS_DIR "pads"
|
||||||
|
#define FILES_DIR "files"
|
||||||
|
#define MAX_ENTROPY_BUFFER (4 * 1024 * 1024) // 4MB entropy buffer for large operations
|
||||||
|
|
||||||
|
// Global variables - now managed through state module
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// STATE MANAGEMENT FUNCTIONS
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// State getters and setters
|
||||||
|
const char* get_current_pads_dir(void);
|
||||||
|
void set_current_pads_dir(const char* dir);
|
||||||
|
int get_interactive_mode(void);
|
||||||
|
void set_interactive_mode(int mode);
|
||||||
|
int get_terminal_width(void);
|
||||||
|
int get_terminal_height(void);
|
||||||
|
void set_terminal_dimensions(int width, int height);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TYPE DEFINITIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Decrypt operation modes for universal decrypt function
|
||||||
|
typedef enum {
|
||||||
|
DECRYPT_MODE_INTERACTIVE, // Interactive text decryption with prompts
|
||||||
|
DECRYPT_MODE_SILENT, // Silent text decryption (no prompts/labels)
|
||||||
|
DECRYPT_MODE_FILE_TO_TEXT, // File to text output with prompts
|
||||||
|
DECRYPT_MODE_FILE_TO_FILE // File to file output (binary)
|
||||||
|
} decrypt_mode_t;
|
||||||
|
|
||||||
|
// Pad filter types for selection functions
|
||||||
|
typedef enum {
|
||||||
|
PAD_FILTER_ALL, // Show all pads
|
||||||
|
PAD_FILTER_UNUSED_ONLY // Show only unused pads (0% usage)
|
||||||
|
} pad_filter_type_t;
|
||||||
|
|
||||||
|
// Enhanced entropy system state structure
|
||||||
|
typedef struct {
|
||||||
|
size_t target_bytes; // Target entropy to collect
|
||||||
|
size_t collected_bytes; // Bytes collected so far
|
||||||
|
size_t unique_keys; // Number of unique keys pressed
|
||||||
|
double collection_start_time; // Start timestamp
|
||||||
|
double last_keypress_time; // Last keypress timestamp
|
||||||
|
unsigned char quality_score; // Entropy quality (0-100)
|
||||||
|
int auto_complete_enabled; // Allow auto-complete at minimum
|
||||||
|
unsigned char key_histogram[256]; // Track key frequency
|
||||||
|
} entropy_collection_state_t;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CORE APPLICATION FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Main application entry points
|
||||||
|
int main(int argc, char* argv[]);
|
||||||
|
int interactive_mode(void);
|
||||||
|
int command_line_mode(int argc, char* argv[]);
|
||||||
|
int pipe_mode(int argc, char* argv[], const char* piped_text);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// INPUT/OUTPUT DETECTION FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Stdin detection functions
|
||||||
|
int has_stdin_data(void);
|
||||||
|
char* read_stdin_text(void);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PREFERENCES MANAGEMENT FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Configuration and preferences handling
|
||||||
|
int load_preferences(void);
|
||||||
|
int save_preferences(void);
|
||||||
|
char* get_preference(const char* key);
|
||||||
|
int set_preference(const char* key, const char* value);
|
||||||
|
char* get_default_pad_path(void);
|
||||||
|
int set_default_pad_path(const char* pad_path);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// HARDWARE DETECTION FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// OTP thumb drive detection function
|
||||||
|
int detect_otp_thumb_drive(char* otp_drive_path, size_t path_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// USB DRIVE MANAGEMENT FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// EXTERNAL TOOL INTEGRATION FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Editor and file manager functions
|
||||||
|
char* get_preferred_editor(void);
|
||||||
|
char* get_preferred_file_manager(void);
|
||||||
|
int launch_text_editor(const char* initial_content, char* result_buffer, size_t buffer_size);
|
||||||
|
int launch_file_manager(const char* start_directory, char* selected_file, size_t buffer_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CORE CRYPTOGRAPHIC OPERATIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Primary encryption/decryption functions
|
||||||
|
int generate_pad(uint64_t size_bytes, int show_progress);
|
||||||
|
int encrypt_text(const char* pad_identifier, const char* input_text);
|
||||||
|
int decrypt_text(const char* pad_identifier, const char* encrypted_message);
|
||||||
|
int encrypt_file(const char* pad_identifier, const char* input_file, const char* output_file, int ascii_armor);
|
||||||
|
int decrypt_file(const char* input_file, const char* output_file);
|
||||||
|
int decrypt_binary_file(FILE* input_fp, const char* output_file);
|
||||||
|
int decrypt_ascii_file(const char* input_file, const char* output_file);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ENHANCED ENTROPY SYSTEM FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Entropy source types
|
||||||
|
typedef enum {
|
||||||
|
ENTROPY_SOURCE_KEYBOARD = 1,
|
||||||
|
ENTROPY_SOURCE_DICE = 2,
|
||||||
|
ENTROPY_SOURCE_TRUERNG = 3,
|
||||||
|
ENTROPY_SOURCE_FILE = 4
|
||||||
|
} entropy_source_t;
|
||||||
|
|
||||||
|
// Terminal control for entropy collection
|
||||||
|
int setup_raw_terminal(struct termios* original_termios);
|
||||||
|
void restore_terminal(struct termios* original_termios);
|
||||||
|
|
||||||
|
// Entropy collection and feedback
|
||||||
|
int collect_entropy_with_feedback(unsigned char* entropy_buffer, size_t target_bytes,
|
||||||
|
size_t* collected_bytes, int allow_early_exit);
|
||||||
|
void display_entropy_progress(const entropy_collection_state_t* state);
|
||||||
|
void draw_progress_bar(double percentage, int width);
|
||||||
|
void draw_quality_bar(double quality, int width, const char* label);
|
||||||
|
|
||||||
|
// Hardware RNG Device Constants (lowercase to match sysfs output)
|
||||||
|
#define TRUERNG_VID "04d8"
|
||||||
|
#define TRUERNG_ORIGINAL_PID "f5fe"
|
||||||
|
#define TRUERNG_PRO_PID "0aa0"
|
||||||
|
#define TRUERNG_PRO_V2_PID "ebb5"
|
||||||
|
|
||||||
|
// Hardware RNG Device Type enumeration
|
||||||
|
typedef enum {
|
||||||
|
TRUERNG_ORIGINAL = 1,
|
||||||
|
TRUERNG_PRO = 2,
|
||||||
|
TRUERNG_PRO_V2 = 3
|
||||||
|
} hardware_rng_device_type_t;
|
||||||
|
|
||||||
|
// Hardware RNG device information structure
|
||||||
|
typedef struct {
|
||||||
|
char port_path[256]; // Device port path (e.g., /dev/ttyUSB0)
|
||||||
|
hardware_rng_device_type_t device_type; // Device type identifier
|
||||||
|
char friendly_name[64]; // Human-readable device name
|
||||||
|
int is_working; // 1 if device passes basic test, 0 otherwise
|
||||||
|
} hardware_rng_device_t;
|
||||||
|
|
||||||
|
// Hardware RNG device detection and selection functions
|
||||||
|
int detect_all_hardware_rng_devices(hardware_rng_device_t* devices, int max_devices, int* num_devices_found);
|
||||||
|
int test_hardware_rng_device(const hardware_rng_device_t* device);
|
||||||
|
int select_hardware_rng_device_interactive(hardware_rng_device_t* devices, int num_devices, hardware_rng_device_t* selected_device);
|
||||||
|
int find_truerng_port(char* port_path, size_t port_path_size, hardware_rng_device_type_t* device_type); // Legacy function for backward compatibility
|
||||||
|
|
||||||
|
// TrueRNG entropy collection functions (updated to match implementation)
|
||||||
|
int configure_rng_serial_port(int fd, hardware_rng_device_type_t device_type);
|
||||||
|
int setup_truerng_serial_port(const char* port_path);
|
||||||
|
int collect_truerng_entropy(unsigned char* entropy_buffer, size_t target_bytes, size_t* collected_bytes, int display_progress);
|
||||||
|
int collect_truerng_entropy_from_device(const hardware_rng_device_t* device, unsigned char* entropy_buffer,
|
||||||
|
size_t target_bytes, size_t* collected_bytes, int display_progress);
|
||||||
|
int collect_truerng_entropy_streaming_from_device(const hardware_rng_device_t* device, const char* pad_chksum,
|
||||||
|
size_t total_bytes, int display_progress, int entropy_mode);
|
||||||
|
const char* get_truerng_device_name(hardware_rng_device_type_t device_type);
|
||||||
|
int read_usb_device_info(const char* port_name, char* vid, char* pid);
|
||||||
|
|
||||||
|
// Dice entropy collection functions (updated to match implementation)
|
||||||
|
int collect_dice_entropy(unsigned char* entropy_buffer, size_t target_bytes, size_t* collected_bytes, int display_progress);
|
||||||
|
|
||||||
|
// File entropy collection functions
|
||||||
|
int get_file_entropy_info(char* file_path, size_t max_path_len, size_t* file_size, int display_progress);
|
||||||
|
int collect_file_entropy(unsigned char* entropy_buffer, size_t target_bytes, size_t* collected_bytes, int display_progress);
|
||||||
|
int add_file_entropy_streaming(const char* pad_chksum, const char* file_path, size_t file_size, int display_progress);
|
||||||
|
|
||||||
|
// Unified entropy collection interface (updated to match implementation)
|
||||||
|
int collect_entropy_by_source(entropy_source_t source, unsigned char* entropy_buffer, size_t target_bytes, size_t* collected_bytes, int display_progress);
|
||||||
|
|
||||||
|
// Entropy quality calculation
|
||||||
|
double calculate_timing_quality(const entropy_collection_state_t* state);
|
||||||
|
double calculate_variety_quality(const entropy_collection_state_t* state);
|
||||||
|
unsigned char calculate_overall_quality(const entropy_collection_state_t* state);
|
||||||
|
double get_precise_time(void);
|
||||||
|
|
||||||
|
// Entropy processing and application
|
||||||
|
int derive_chacha20_params(const unsigned char* entropy_data, size_t entropy_size,
|
||||||
|
unsigned char key[32], unsigned char nonce[12]);
|
||||||
|
int add_entropy_to_pad(const char* pad_chksum, const unsigned char* entropy_data,
|
||||||
|
size_t entropy_size, int show_progress);
|
||||||
|
int add_entropy_direct_xor(const char* pad_chksum, const unsigned char* entropy_data,
|
||||||
|
size_t entropy_size, uint64_t pad_size, int display_progress);
|
||||||
|
int add_entropy_chacha20(const char* pad_chksum, const unsigned char* entropy_data,
|
||||||
|
size_t entropy_size, uint64_t pad_size, int display_progress);
|
||||||
|
int handle_add_entropy_to_pad(const char* pad_chksum);
|
||||||
|
|
||||||
|
// Enhanced entropy system helper functions
|
||||||
|
int update_pad_checksum_after_entropy(const char* old_chksum, char* new_chksum);
|
||||||
|
int rename_pad_files_safely(const char* old_chksum, const char* new_chksum);
|
||||||
|
int is_pad_unused(const char* pad_chksum);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// DIRECTORY MANAGEMENT FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Directory handling and path management
|
||||||
|
int ensure_pads_directory(void);
|
||||||
|
void get_pad_path(const char* chksum, char* pad_path, char* state_path);
|
||||||
|
const char* get_files_directory(void);
|
||||||
|
void get_default_file_path(const char* filename, char* result_path, size_t result_size);
|
||||||
|
void get_directory_display(const char* file_path, char* result, size_t result_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UTILITY FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// General utility and helper functions
|
||||||
|
uint64_t parse_size_string(const char* size_str);
|
||||||
|
char* find_pad_by_prefix(const char* prefix);
|
||||||
|
int show_pad_info(const char* chksum);
|
||||||
|
void show_progress(uint64_t current, uint64_t total, time_t start_time);
|
||||||
|
void format_time_remaining(double seconds, char* buffer, size_t buffer_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// FILE OPERATIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// File state and checksum operations
|
||||||
|
int read_state_offset(const char* pad_chksum, uint64_t* offset);
|
||||||
|
int write_state_offset(const char* pad_chksum, uint64_t offset);
|
||||||
|
int calculate_checksum(const char* filename, char* checksum_hex);
|
||||||
|
int calculate_checksum_with_progress(const char* filename, char* checksum_hex, int display_progress, uint64_t file_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UNIVERSAL CORE FUNCTIONS FOR CODE CONSOLIDATION
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Consolidated cryptographic operations
|
||||||
|
int universal_xor_operation(const unsigned char* data, size_t data_len,
|
||||||
|
const unsigned char* pad_data, unsigned char* result);
|
||||||
|
int parse_ascii_message(const char* message, char* chksum, uint64_t* offset, char* base64_data);
|
||||||
|
int load_pad_data(const char* pad_chksum, uint64_t offset, size_t length, unsigned char** pad_data);
|
||||||
|
int generate_ascii_armor(const char* chksum, uint64_t offset, const unsigned char* encrypted_data,
|
||||||
|
size_t data_length, char** ascii_output);
|
||||||
|
int validate_pad_integrity(const char* pad_path, const char* expected_chksum);
|
||||||
|
|
||||||
|
// Universal decrypt function - consolidates all decrypt operations
|
||||||
|
int universal_decrypt(const char* input_data, const char* output_target, decrypt_mode_t mode);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// BASE64 ENCODING FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Custom base64 implementation
|
||||||
|
char* custom_base64_encode(const unsigned char* input, int length);
|
||||||
|
unsigned char* custom_base64_decode(const char* input, int* output_length);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TERMINAL UI FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Terminal dimension and UI functions
|
||||||
|
void init_terminal_dimensions(void);
|
||||||
|
void print_centered_header(const char* text, int pause_before_clear);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MENU SYSTEM FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Interactive menu interface functions
|
||||||
|
void show_main_menu(void);
|
||||||
|
int handle_generate_menu(void);
|
||||||
|
int handle_encrypt_menu(void);
|
||||||
|
int handle_decrypt_menu(void);
|
||||||
|
int handle_pads_menu(void);
|
||||||
|
int handle_text_encrypt(void);
|
||||||
|
int handle_file_encrypt(void);
|
||||||
|
int handle_verify_pad(const char* pad_chksum);
|
||||||
|
int handle_delete_pad(const char* pad_chksum);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ENHANCED INPUT FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Advanced input handling
|
||||||
|
int get_filename_with_default(const char* prompt, const char* default_path, char* result, size_t result_size);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PAD SELECTION FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Unified pad selection interface
|
||||||
|
char* select_pad_interactive(const char* title, const char* prompt, pad_filter_type_t filter_type, int allow_cancel);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// USAGE AND HELP FUNCTIONS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Help and usage display
|
||||||
|
void print_usage(const char* program_name);
|
||||||
|
|
||||||
|
#endif // OTP_H
|
||||||
1582
src/pads.c
Normal file
1582
src/pads.c
Normal file
File diff suppressed because it is too large
Load Diff
45
src/state.c
Normal file
45
src/state.c
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "otp.h"
|
||||||
|
|
||||||
|
// Global state variables
|
||||||
|
static char current_pads_dir[512] = DEFAULT_PADS_DIR;
|
||||||
|
static int is_interactive_mode = 0;
|
||||||
|
|
||||||
|
// Terminal dimensions (moved from ui.c to state.c for global access)
|
||||||
|
static int terminal_width = 80; // Default fallback width
|
||||||
|
static int terminal_height = 24; // Default fallback height
|
||||||
|
|
||||||
|
// Getters and setters for global state
|
||||||
|
|
||||||
|
const char* get_current_pads_dir(void) {
|
||||||
|
return current_pads_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_current_pads_dir(const char* dir) {
|
||||||
|
if (dir) {
|
||||||
|
strncpy(current_pads_dir, dir, sizeof(current_pads_dir) - 1);
|
||||||
|
current_pads_dir[sizeof(current_pads_dir) - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_interactive_mode(void) {
|
||||||
|
return is_interactive_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_interactive_mode(int mode) {
|
||||||
|
is_interactive_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_terminal_width(void) {
|
||||||
|
return terminal_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_terminal_height(void) {
|
||||||
|
return terminal_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_terminal_dimensions(int width, int height) {
|
||||||
|
terminal_width = width;
|
||||||
|
terminal_height = height;
|
||||||
|
}
|
||||||
572
src/trng.c
Normal file
572
src/trng.c
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "nostr_chacha20.h"
|
||||||
|
#include "otp.h"
|
||||||
|
|
||||||
|
// Basic TrueRNG entropy collection function
|
||||||
|
int collect_truerng_entropy(unsigned char* entropy_buffer, size_t target_bytes, size_t* collected_bytes, int display_progress) {
|
||||||
|
hardware_rng_device_t devices[10];
|
||||||
|
int num_devices_found = 0;
|
||||||
|
|
||||||
|
// Detect available TrueRNG devices
|
||||||
|
if (detect_all_hardware_rng_devices(devices, 10, &num_devices_found) != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to detect hardware RNG devices\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_devices_found == 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("No hardware RNG devices found.\n");
|
||||||
|
printf("\nSupported devices:\n");
|
||||||
|
printf(" - TrueRNG Original (VID: %s, PID: %s)\n", TRUERNG_VID, TRUERNG_ORIGINAL_PID);
|
||||||
|
printf(" - TrueRNG Pro (VID: %s, PID: %s)\n", TRUERNG_VID, TRUERNG_PRO_PID);
|
||||||
|
printf(" - TrueRNG Pro V2 (VID: %s, PID: %s)\n", TRUERNG_VID, TRUERNG_PRO_V2_PID);
|
||||||
|
printf("\nPlease connect a TrueRNG device and try again.\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first available device
|
||||||
|
hardware_rng_device_t* selected_device = &devices[0];
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Using device: %s\n", selected_device->friendly_name);
|
||||||
|
printf("Collecting %zu bytes of entropy...\n", target_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect entropy from the device
|
||||||
|
int result = collect_truerng_entropy_from_device(selected_device, entropy_buffer, target_bytes, collected_bytes, display_progress);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to collect entropy from TrueRNG device\n");
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("✓ Successfully collected %zu bytes of entropy from TrueRNG device\n", *collected_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming entropy collection directly to pad file
|
||||||
|
int collect_truerng_entropy_streaming_from_device(const hardware_rng_device_t* device, const char* pad_chksum,
|
||||||
|
size_t total_bytes, int display_progress, int entropy_mode) {
|
||||||
|
(void)entropy_mode; // Suppress unused parameter warning
|
||||||
|
if (!device || !pad_chksum || total_bytes == 0) {
|
||||||
|
return 1; // Invalid parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get pad file path
|
||||||
|
char pad_path[1024];
|
||||||
|
char state_path[1024];
|
||||||
|
get_pad_path(pad_chksum, pad_path, state_path);
|
||||||
|
|
||||||
|
// Check if pad exists and get size
|
||||||
|
struct stat pad_stat;
|
||||||
|
if (stat(pad_path, &pad_stat) != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Pad file not found: %s\n", pad_path);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pad_size = pad_stat.st_size;
|
||||||
|
if (total_bytes > pad_size) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Requested entropy (%zu bytes) exceeds pad size (%lu bytes)\n", total_bytes, pad_size);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the RNG device
|
||||||
|
int device_fd = open(device->port_path, O_RDONLY | O_NOCTTY);
|
||||||
|
if (device_fd < 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot open RNG device %s: %s\n", device->port_path, strerror(errno));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure serial port for this device type
|
||||||
|
if (configure_rng_serial_port(device_fd, device->device_type) != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to configure serial port for %s\n", device->friendly_name);
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard delay for TrueRNG devices
|
||||||
|
usleep(100000); // 100ms
|
||||||
|
|
||||||
|
// Open pad file for read/write
|
||||||
|
FILE* pad_file = fopen(pad_path, "r+b");
|
||||||
|
if (!pad_file) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot open pad file for modification: %s\n", pad_path);
|
||||||
|
printf("Reason: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
// Provide additional diagnostics
|
||||||
|
if (errno == EROFS) {
|
||||||
|
printf("The filesystem appears to be read-only. Check if the drive is mounted read-only.\n");
|
||||||
|
} else if (errno == EACCES) {
|
||||||
|
printf("Permission denied. Check file permissions and mount options.\n");
|
||||||
|
} else if (errno == ENOENT) {
|
||||||
|
printf("File not found. The pad file may have been moved or deleted.\n");
|
||||||
|
} else if (errno == EISDIR) {
|
||||||
|
printf("Path is a directory, not a file.\n");
|
||||||
|
} else {
|
||||||
|
printf("This may be due to filesystem limitations or mount options.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nTroubleshooting suggestions:\n");
|
||||||
|
printf("1. Ensure the external drive is mounted read-write: mount -o remount,rw /media/teknari/OTP_01\n");
|
||||||
|
printf("2. Check file permissions: ls -la '%s'\n", pad_path);
|
||||||
|
printf("3. Verify the drive supports the required operations\n");
|
||||||
|
printf("4. Try copying the pad to local storage, enhancing it, then copying back\n");
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Streaming entropy from %s to pad...\n", device->friendly_name);
|
||||||
|
printf("Pad size: %.2f GB (%lu bytes)\n", (double)pad_size / (1024.0*1024.0*1024.0), pad_size);
|
||||||
|
printf("Enhancing entire pad with hardware entropy\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process pad in chunks
|
||||||
|
unsigned char buffer[64 * 1024]; // 64KB chunks
|
||||||
|
size_t bytes_processed = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
int error_occurred = 0;
|
||||||
|
|
||||||
|
while (bytes_processed < total_bytes && !error_occurred) {
|
||||||
|
size_t chunk_size = sizeof(buffer);
|
||||||
|
if (total_bytes - bytes_processed < chunk_size) {
|
||||||
|
chunk_size = total_bytes - bytes_processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read entropy from device
|
||||||
|
ssize_t entropy_read = read(device_fd, buffer, chunk_size);
|
||||||
|
if (entropy_read < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue; // Interrupted, try again
|
||||||
|
}
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to read from TrueRNG device: %s\n", strerror(errno));
|
||||||
|
printf("Device may have been disconnected during operation.\n");
|
||||||
|
}
|
||||||
|
error_occurred = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entropy_read == 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: TrueRNG device returned no data (device disconnected?)\n");
|
||||||
|
}
|
||||||
|
error_occurred = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read current pad data at this position
|
||||||
|
if (fseek(pad_file, bytes_processed, SEEK_SET) != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot seek to position %zu in pad file\n", bytes_processed);
|
||||||
|
}
|
||||||
|
error_occurred = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char pad_data[64 * 1024];
|
||||||
|
size_t pad_read = fread(pad_data, 1, entropy_read, pad_file);
|
||||||
|
if (pad_read != (size_t)entropy_read) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot read pad data at position %zu\n", bytes_processed);
|
||||||
|
}
|
||||||
|
error_occurred = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XOR entropy with existing pad data
|
||||||
|
for (size_t i = 0; i < (size_t)entropy_read; i++) {
|
||||||
|
pad_data[i] ^= buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek back and write modified data
|
||||||
|
if (fseek(pad_file, bytes_processed, SEEK_SET) != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot seek back to position %zu in pad file\n", bytes_processed);
|
||||||
|
}
|
||||||
|
error_occurred = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(pad_data, 1, entropy_read, pad_file) != (size_t)entropy_read) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot write modified pad data\n");
|
||||||
|
}
|
||||||
|
error_occurred = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_processed += entropy_read;
|
||||||
|
|
||||||
|
// Show progress for large pads
|
||||||
|
if (display_progress && bytes_processed % (64 * 1024 * 1024) == 0) { // Every 64MB
|
||||||
|
show_progress(bytes_processed, total_bytes, start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(device_fd);
|
||||||
|
fclose(pad_file);
|
||||||
|
|
||||||
|
if (error_occurred) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
show_progress(total_bytes, total_bytes, start_time);
|
||||||
|
printf("\n✓ Successfully streamed %zu bytes of hardware entropy to pad\n", bytes_processed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect all available hardware RNG devices
|
||||||
|
int detect_all_hardware_rng_devices(hardware_rng_device_t* devices, int max_devices, int* num_devices_found) {
|
||||||
|
*num_devices_found = 0;
|
||||||
|
|
||||||
|
// Scan /dev directory for serial devices (ttyUSB*, ttyACM*)
|
||||||
|
DIR* dev_dir = opendir("/dev");
|
||||||
|
if (!dev_dir) {
|
||||||
|
return 1; // Error opening /dev
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent* entry;
|
||||||
|
while ((entry = readdir(dev_dir)) != NULL && *num_devices_found < max_devices) {
|
||||||
|
// Check for serial device patterns
|
||||||
|
if (strncmp(entry->d_name, "ttyUSB", 6) == 0 || strncmp(entry->d_name, "ttyACM", 6) == 0) {
|
||||||
|
char device_path[512]; // Increased buffer size to prevent truncation
|
||||||
|
int ret = snprintf(device_path, sizeof(device_path), "/dev/%s", entry->d_name);
|
||||||
|
if (ret >= (int)sizeof(device_path)) {
|
||||||
|
continue; // Skip if path would be truncated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a TrueRNG/SwiftRNG device by reading VID/PID
|
||||||
|
char vid[5], pid[5];
|
||||||
|
if (read_usb_device_info(device_path, vid, pid) == 0) {
|
||||||
|
hardware_rng_device_type_t device_type = 0;
|
||||||
|
|
||||||
|
// Check against known TrueRNG VID/PID combinations
|
||||||
|
if (strcmp(vid, TRUERNG_VID) == 0 && strcmp(pid, TRUERNG_ORIGINAL_PID) == 0) {
|
||||||
|
device_type = TRUERNG_ORIGINAL;
|
||||||
|
} else if (strcmp(vid, TRUERNG_VID) == 0 && strcmp(pid, TRUERNG_PRO_PID) == 0) {
|
||||||
|
device_type = TRUERNG_PRO;
|
||||||
|
} else if (strcmp(vid, TRUERNG_VID) == 0 && strcmp(pid, TRUERNG_PRO_V2_PID) == 0) {
|
||||||
|
device_type = TRUERNG_PRO_V2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_type != 0) {
|
||||||
|
// Found a TrueRNG/SwiftRNG device
|
||||||
|
hardware_rng_device_t* device = &devices[*num_devices_found];
|
||||||
|
|
||||||
|
strncpy(device->port_path, device_path, sizeof(device->port_path) - 1);
|
||||||
|
device->device_type = device_type;
|
||||||
|
strncpy(device->friendly_name, get_truerng_device_name(device_type), sizeof(device->friendly_name) - 1);
|
||||||
|
|
||||||
|
// Assume device is working if VID/PID matches (no test needed)
|
||||||
|
device->is_working = 1;
|
||||||
|
|
||||||
|
(*num_devices_found)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dev_dir);
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure serial port for different RNG device types
|
||||||
|
int configure_rng_serial_port(int fd, hardware_rng_device_type_t device_type) {
|
||||||
|
(void)device_type; // Suppress unused parameter warning - all TrueRNG devices use same config
|
||||||
|
struct termios tty;
|
||||||
|
|
||||||
|
if (tcgetattr(fd, &tty) != 0) {
|
||||||
|
return 1; // Error getting terminal attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrueRNG configuration - traditional serial settings
|
||||||
|
// TrueRNG devices: 115200 baud, 8N1, no flow control
|
||||||
|
cfsetospeed(&tty, B115200);
|
||||||
|
cfsetispeed(&tty, B115200);
|
||||||
|
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
|
||||||
|
tty.c_cflag |= CLOCAL | CREAD; // ignore modem controls, enable reading
|
||||||
|
tty.c_cflag &= ~(PARENB | PARODD); // no parity
|
||||||
|
tty.c_cflag &= ~CSTOPB; // 1 stop bit
|
||||||
|
tty.c_cflag &= ~CRTSCTS; // no hardware flow control
|
||||||
|
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // no software flow control
|
||||||
|
tty.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw mode
|
||||||
|
tty.c_oflag &= ~OPOST; // raw output
|
||||||
|
|
||||||
|
// Set timeouts for TrueRNG
|
||||||
|
tty.c_cc[VMIN] = 1; // read at least 1 character
|
||||||
|
tty.c_cc[VTIME] = 10; // 1 second timeout
|
||||||
|
|
||||||
|
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||||
|
return 1; // Error setting terminal attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush any existing data
|
||||||
|
tcflush(fd, TCIOFLUSH);
|
||||||
|
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect entropy from a specific TrueRNG device
|
||||||
|
int collect_truerng_entropy_from_device(const hardware_rng_device_t* device, unsigned char* entropy_buffer,
|
||||||
|
size_t target_bytes, size_t* collected_bytes, int display_progress) {
|
||||||
|
if (!device || !entropy_buffer || !collected_bytes || target_bytes == 0) {
|
||||||
|
return 1; // Invalid parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Open the TrueRNG device
|
||||||
|
int device_fd = open(device->port_path, O_RDONLY | O_NOCTTY);
|
||||||
|
if (device_fd < 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Cannot open RNG device %s: %s\n", device->port_path, strerror(errno));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure serial port for this device type
|
||||||
|
if (configure_rng_serial_port(device_fd, device->device_type) != 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to configure serial port for %s\n", device->friendly_name);
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard delay for TrueRNG devices
|
||||||
|
usleep(100000); // 100ms
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Collecting %zu bytes from %s...\n", target_bytes, device->friendly_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read entropy data with timeout protection
|
||||||
|
size_t total_read = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
time_t last_progress_time = start_time;
|
||||||
|
|
||||||
|
while (total_read < target_bytes) {
|
||||||
|
// Check for overall timeout (5 minutes max for large collections)
|
||||||
|
time_t current_time = time(NULL);
|
||||||
|
if (difftime(current_time, start_time) > 300) { // 5 minutes timeout
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Collection timeout - device may be unresponsive\n");
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t remaining = target_bytes - total_read;
|
||||||
|
size_t chunk_size = (remaining > 4096) ? 4096 : remaining; // Read in 4KB chunks
|
||||||
|
|
||||||
|
ssize_t bytes_read = read(device_fd, entropy_buffer + total_read, chunk_size);
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue; // Interrupted, try again
|
||||||
|
}
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
// Timeout occurred, check if we have enough data for a test
|
||||||
|
if (total_read > 0 && target_bytes > 1024) {
|
||||||
|
// For testing purposes, we have enough data
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// For small collections, this is an error
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Device read timeout - no data received\n");
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: Failed to read from TrueRNG device: %s\n", strerror(errno));
|
||||||
|
printf("Device may have been disconnected.\n");
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_read == 0) {
|
||||||
|
// End of data - this shouldn't happen for RNG devices
|
||||||
|
if (total_read == 0) {
|
||||||
|
if (display_progress) {
|
||||||
|
printf("Error: TrueRNG device returned no data (device disconnected or misconfigured?)\n");
|
||||||
|
}
|
||||||
|
close(device_fd);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
// We have some data, might be enough for testing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total_read += bytes_read;
|
||||||
|
|
||||||
|
// Show progress
|
||||||
|
if (display_progress && (total_read % 1024 == 0 || difftime(current_time, last_progress_time) >= 1)) {
|
||||||
|
show_progress(total_read, target_bytes, start_time);
|
||||||
|
last_progress_time = current_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(device_fd);
|
||||||
|
|
||||||
|
if (display_progress) {
|
||||||
|
show_progress(target_bytes, target_bytes, start_time);
|
||||||
|
printf("\n✓ Successfully collected %zu bytes from TrueRNG device\n", total_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
*collected_bytes = total_read;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read USB device VID/PID information from sysfs
|
||||||
|
int read_usb_device_info(const char* device_path, char* vid, char* pid) {
|
||||||
|
// Extract device name from path (e.g., /dev/ttyUSB0 -> ttyUSB0)
|
||||||
|
const char* device_name = strrchr(device_path, '/');
|
||||||
|
if (!device_name) device_name = device_path;
|
||||||
|
else device_name++; // Skip the '/'
|
||||||
|
|
||||||
|
// Construct sysfs path for USB device info
|
||||||
|
char sysfs_path[256];
|
||||||
|
snprintf(sysfs_path, sizeof(sysfs_path), "/sys/class/tty/%s/device/../idVendor", device_name);
|
||||||
|
|
||||||
|
FILE* vid_file = fopen(sysfs_path, "r");
|
||||||
|
if (!vid_file) {
|
||||||
|
return 1; // Cannot read VID
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fscanf(vid_file, "%4s", vid) != 1) {
|
||||||
|
fclose(vid_file);
|
||||||
|
return 1; // Cannot parse VID
|
||||||
|
}
|
||||||
|
fclose(vid_file);
|
||||||
|
|
||||||
|
// Read PID
|
||||||
|
snprintf(sysfs_path, sizeof(sysfs_path), "/sys/class/tty/%s/device/../idProduct", device_name);
|
||||||
|
FILE* pid_file = fopen(sysfs_path, "r");
|
||||||
|
if (!pid_file) {
|
||||||
|
return 1; // Cannot read PID
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fscanf(pid_file, "%4s", pid) != 1) {
|
||||||
|
fclose(pid_file);
|
||||||
|
return 1; // Cannot parse PID
|
||||||
|
}
|
||||||
|
fclose(pid_file);
|
||||||
|
|
||||||
|
return 0; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get friendly name for hardware RNG device type
|
||||||
|
const char* get_truerng_device_name(hardware_rng_device_type_t device_type) {
|
||||||
|
switch (device_type) {
|
||||||
|
case TRUERNG_ORIGINAL:
|
||||||
|
return "TrueRNG";
|
||||||
|
case TRUERNG_PRO:
|
||||||
|
return "TrueRNG Pro";
|
||||||
|
case TRUERNG_PRO_V2:
|
||||||
|
return "TrueRNG Pro V2";
|
||||||
|
default:
|
||||||
|
return "Unknown Hardware RNG Device";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if a hardware RNG device is working by attempting to read from it
|
||||||
|
int test_hardware_rng_device(const hardware_rng_device_t* device) {
|
||||||
|
int fd = open(device->port_path, O_RDONLY | O_NONBLOCK);
|
||||||
|
if (fd < 0) {
|
||||||
|
return 1; // Cannot open device
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read a small amount of data
|
||||||
|
unsigned char test_buffer[16];
|
||||||
|
ssize_t bytes_read = read(fd, test_buffer, sizeof(test_buffer));
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (bytes_read <= 0) {
|
||||||
|
return 1; // Cannot read from device
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; // Device appears to be working
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive device selection for hardware RNG
|
||||||
|
int select_hardware_rng_device_interactive(hardware_rng_device_t* devices, int num_devices, hardware_rng_device_t* selected_device) {
|
||||||
|
if (num_devices == 0) {
|
||||||
|
printf("No hardware RNG devices found.\n");
|
||||||
|
return 1; // No devices available
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_devices == 1) {
|
||||||
|
// Only one device, use it automatically
|
||||||
|
*selected_device = devices[0];
|
||||||
|
printf("Using %s (%s)\n\n", devices[0].friendly_name, devices[0].port_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple devices - let user choose
|
||||||
|
printf("\nAvailable Hardware RNG Devices:\n");
|
||||||
|
for (int i = 0; i < num_devices; i++) {
|
||||||
|
printf("%d. %s (%s)\n",
|
||||||
|
i + 1,
|
||||||
|
devices[i].friendly_name,
|
||||||
|
devices[i].port_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nSelect device (1-%d): ", num_devices);
|
||||||
|
|
||||||
|
char input[10];
|
||||||
|
if (fgets(input, sizeof(input), stdin) == NULL) {
|
||||||
|
return 1; // Input error
|
||||||
|
}
|
||||||
|
|
||||||
|
int choice = atoi(input);
|
||||||
|
if (choice < 1 || choice > num_devices) {
|
||||||
|
printf("Invalid selection.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*selected_device = devices[choice - 1];
|
||||||
|
printf("Selected: %s (%s)\n", selected_device->friendly_name, selected_device->port_path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
503
src/ui.c
Normal file
503
src/ui.c
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#define _DEFAULT_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "otp.h"
|
||||||
|
|
||||||
|
// Initialize terminal dimensions
|
||||||
|
void init_terminal_dimensions(void) {
|
||||||
|
struct winsize ws;
|
||||||
|
|
||||||
|
// Try to get actual terminal size
|
||||||
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0 && ws.ws_row > 0) {
|
||||||
|
set_terminal_dimensions(ws.ws_col, ws.ws_row);
|
||||||
|
}
|
||||||
|
// If ioctl fails, keep the default values (80x24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print centered header with = padding, screen clearing, and optional pause
|
||||||
|
void print_centered_header(const char* text, int pause_before_clear) {
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
// Phase 1: Pause if requested
|
||||||
|
if (pause_before_clear) {
|
||||||
|
printf("\nPress Enter to continue...");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
// Wait for Enter key
|
||||||
|
int c;
|
||||||
|
while ((c = getchar()) != '\n' && c != EOF) {
|
||||||
|
// Consume any extra characters until newline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Clear screen using terminal height
|
||||||
|
for (int i = 0; i < get_terminal_height(); i++) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 3: Display centered header (existing logic)
|
||||||
|
int text_len = strlen(text);
|
||||||
|
int available_width = get_terminal_width();
|
||||||
|
|
||||||
|
// Ensure minimum spacing: at least 1 space on each side
|
||||||
|
int min_required = text_len + 4; // text + " " + text + " " (spaces around text)
|
||||||
|
|
||||||
|
if (available_width < min_required) {
|
||||||
|
// Terminal too narrow - just print the text with minimal formatting
|
||||||
|
printf("=== %s ===\n", text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate padding
|
||||||
|
int total_padding = available_width - text_len - 2; // -2 for spaces around text
|
||||||
|
int left_padding = total_padding / 2;
|
||||||
|
int right_padding = total_padding - left_padding;
|
||||||
|
|
||||||
|
// Print the header
|
||||||
|
for (int i = 0; i < left_padding; i++) {
|
||||||
|
printf("=");
|
||||||
|
}
|
||||||
|
printf(" %s ", text);
|
||||||
|
for (int i = 0; i < right_padding; i++) {
|
||||||
|
printf("=");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive mode main loop
|
||||||
|
int interactive_mode(void) {
|
||||||
|
char input[10];
|
||||||
|
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
|
||||||
|
while (1) {
|
||||||
|
show_main_menu();
|
||||||
|
|
||||||
|
if (!fgets(input, sizeof(input), stdin)) {
|
||||||
|
printf("Goodbye!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char choice = toupper(input[0]);
|
||||||
|
|
||||||
|
switch (choice) {
|
||||||
|
case 'T':
|
||||||
|
handle_text_encrypt();
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
handle_file_encrypt();
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
handle_decrypt_menu();
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
handle_pads_menu();
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
case 'Q':
|
||||||
|
printf("Goodbye!\n");
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
printf("Invalid choice. Please try again.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_main_menu(void) {
|
||||||
|
printf("\n");
|
||||||
|
print_centered_header("Main Menu - OTP v0.3.16", 0);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf(" \033[4mT\033[0mext encrypt\n"); //TEXT ENCRYPT
|
||||||
|
printf(" \033[4mF\033[0mile encrypt\n"); //FILE ENCRYPT
|
||||||
|
printf(" \033[4mD\033[0mecrypt\n"); //DECRYPT
|
||||||
|
printf(" \033[4mP\033[0mads\n"); //PADS
|
||||||
|
printf(" E\033[4mx\033[0mit\n"); //EXIT
|
||||||
|
printf("\nSelect option: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_generate_menu(void) {
|
||||||
|
printf("\n");
|
||||||
|
print_centered_header("Generate New Pad", 0);
|
||||||
|
printf("Enter pad size (examples: 1GB, 5TB, 512MB, 2048): ");
|
||||||
|
|
||||||
|
char size_input[64];
|
||||||
|
if (!fgets(size_input, sizeof(size_input), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_input[strcspn(size_input, "\n")] = 0;
|
||||||
|
uint64_t size = parse_size_string(size_input);
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
printf("Error: Invalid size format\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double size_gb = (double)size / (1024.0 * 1024.0 * 1024.0);
|
||||||
|
printf("Generating %.2f GB pad...\n", size_gb);
|
||||||
|
printf("Note: Use 'Add entropy' in Pads menu to enhance randomness after creation.\n");
|
||||||
|
|
||||||
|
return generate_pad(size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_encrypt_menu(void) {
|
||||||
|
printf("\n");
|
||||||
|
print_centered_header("Encrypt Data", 0);
|
||||||
|
|
||||||
|
printf("Available pads:\n");
|
||||||
|
char* selected = select_pad_interactive("Available pads:", "Select pad (or press Enter to continue)", PAD_FILTER_ALL, 0);
|
||||||
|
int pad_count = 1; // Assume at least 1 pad if function returned
|
||||||
|
if (selected) {
|
||||||
|
free(selected);
|
||||||
|
} else {
|
||||||
|
pad_count = 0;
|
||||||
|
}
|
||||||
|
if (pad_count == 0) {
|
||||||
|
printf("No pads available. Generate a pad first.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask user to choose between text and file encryption
|
||||||
|
printf("\nSelect encryption type:\n");
|
||||||
|
printf(" 1. Text message\n");
|
||||||
|
printf(" 2. File\n");
|
||||||
|
printf("Enter choice (1-2): ");
|
||||||
|
|
||||||
|
char choice_input[10];
|
||||||
|
if (!fgets(choice_input, sizeof(choice_input), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int choice = atoi(choice_input);
|
||||||
|
|
||||||
|
if (choice == 1) {
|
||||||
|
// Text encryption - use unified pad selection
|
||||||
|
char* selected_pad = select_pad_interactive("Select Pad for Text Encryption",
|
||||||
|
"Select pad (by prefix)",
|
||||||
|
PAD_FILTER_ALL, 1);
|
||||||
|
if (!selected_pad) {
|
||||||
|
printf("Text encryption cancelled.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = encrypt_text(selected_pad, NULL); // NULL for interactive mode
|
||||||
|
free(selected_pad);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else if (choice == 2) {
|
||||||
|
// File encryption
|
||||||
|
printf("\nFile selection options:\n");
|
||||||
|
printf(" 1. Type file path directly\n");
|
||||||
|
printf(" 2. Use file manager\n");
|
||||||
|
printf("Enter choice (1-2): ");
|
||||||
|
|
||||||
|
char file_choice[10];
|
||||||
|
char input_file[512];
|
||||||
|
|
||||||
|
if (!fgets(file_choice, sizeof(file_choice), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (atoi(file_choice) == 2) {
|
||||||
|
// Use file manager
|
||||||
|
if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) {
|
||||||
|
printf("Falling back to manual file path entry.\n");
|
||||||
|
printf("Enter input file path: ");
|
||||||
|
if (!fgets(input_file, sizeof(input_file), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
input_file[strcspn(input_file, "\n")] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Direct file path input
|
||||||
|
printf("Enter input file path: ");
|
||||||
|
if (!fgets(input_file, sizeof(input_file), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
input_file[strcspn(input_file, "\n")] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
if (access(input_file, R_OK) != 0) {
|
||||||
|
printf("Error: File '%s' not found or cannot be read\n", input_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use unified pad selection
|
||||||
|
char* selected_pad = select_pad_interactive("Select Pad for File Encryption",
|
||||||
|
"Select pad (by prefix)",
|
||||||
|
PAD_FILTER_ALL, 1);
|
||||||
|
if (!selected_pad) {
|
||||||
|
printf("File encryption cancelled.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask for output format
|
||||||
|
printf("\nSelect output format:\n");
|
||||||
|
printf(" 1. Binary (.otp) - preserves file permissions\n");
|
||||||
|
printf(" 2. ASCII (.otp.asc) - text-safe format\n");
|
||||||
|
printf("Enter choice (1-2): ");
|
||||||
|
|
||||||
|
char format_input[10];
|
||||||
|
if (!fgets(format_input, sizeof(format_input), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ascii_armor = (atoi(format_input) == 2) ? 1 : 0;
|
||||||
|
|
||||||
|
// Generate default output filename with files directory and use enhanced input function
|
||||||
|
char default_output[1024]; // Increased size to prevent truncation warnings
|
||||||
|
char temp_default[1024];
|
||||||
|
|
||||||
|
// Generate base filename with appropriate extension
|
||||||
|
if (ascii_armor) {
|
||||||
|
snprintf(temp_default, sizeof(temp_default), "%s.otp.asc", input_file);
|
||||||
|
} else {
|
||||||
|
snprintf(temp_default, sizeof(temp_default), "%s.otp", input_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply files directory default path
|
||||||
|
get_default_file_path(temp_default, default_output, sizeof(default_output));
|
||||||
|
|
||||||
|
char output_file[512];
|
||||||
|
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* output_filename = output_file;
|
||||||
|
|
||||||
|
int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor);
|
||||||
|
free(selected_pad);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Invalid choice. Please enter 1 or 2.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_decrypt_menu(void) {
|
||||||
|
printf("\n");
|
||||||
|
print_centered_header("Smart Decrypt", 0);
|
||||||
|
printf("Enter encrypted data (paste ASCII armor), file path, or press Enter to browse files:\n");
|
||||||
|
|
||||||
|
char input_line[MAX_LINE_LENGTH];
|
||||||
|
if (!fgets(input_line, sizeof(input_line), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove newline
|
||||||
|
input_line[strcspn(input_line, "\n")] = 0;
|
||||||
|
|
||||||
|
if (strlen(input_line) == 0) {
|
||||||
|
// Empty input - launch file manager to browse for files
|
||||||
|
char selected_file[512];
|
||||||
|
if (launch_file_manager(get_files_directory(), selected_file, sizeof(selected_file)) != 0) {
|
||||||
|
printf("Error: Could not launch file manager\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate smart default output filename with files directory and use enhanced input function
|
||||||
|
char temp_default[512];
|
||||||
|
char default_output[512];
|
||||||
|
strncpy(temp_default, selected_file, sizeof(temp_default) - 1);
|
||||||
|
temp_default[sizeof(temp_default) - 1] = '\0';
|
||||||
|
|
||||||
|
// Remove common encrypted extensions to get a better default
|
||||||
|
if (strstr(temp_default, ".otp.asc")) {
|
||||||
|
// Replace .otp.asc with original extension or no extension
|
||||||
|
char* ext_pos = strstr(temp_default, ".otp.asc");
|
||||||
|
*ext_pos = '\0';
|
||||||
|
} else if (strstr(temp_default, ".otp")) {
|
||||||
|
// Replace .otp with original extension or no extension
|
||||||
|
char* ext_pos = strstr(temp_default, ".otp");
|
||||||
|
*ext_pos = '\0';
|
||||||
|
} else {
|
||||||
|
// No recognized encrypted extension, add .decrypted suffix
|
||||||
|
strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply files directory default path
|
||||||
|
get_default_file_path(temp_default, default_output, sizeof(default_output));
|
||||||
|
|
||||||
|
char output_file[512];
|
||||||
|
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return decrypt_file(selected_file, output_file);
|
||||||
|
}
|
||||||
|
else if (strncmp(input_line, "-----BEGIN OTP MESSAGE-----", 27) == 0) {
|
||||||
|
// Looks like ASCII armor - collect the full message
|
||||||
|
char full_message[MAX_INPUT_SIZE * 4] = {0};
|
||||||
|
strcat(full_message, input_line);
|
||||||
|
strcat(full_message, "\n");
|
||||||
|
|
||||||
|
printf("Continue pasting the message (end with -----END OTP MESSAGE-----):\n");
|
||||||
|
|
||||||
|
char line[MAX_LINE_LENGTH];
|
||||||
|
while (fgets(line, sizeof(line), stdin)) {
|
||||||
|
strncat(full_message, line, sizeof(full_message) - strlen(full_message) - 1);
|
||||||
|
if (strstr(line, "-----END OTP MESSAGE-----")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decrypt_text(NULL, full_message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check if it looks like a file path
|
||||||
|
if (access(input_line, R_OK) == 0) {
|
||||||
|
// It's a valid file - decrypt it with enhanced input for output filename
|
||||||
|
char temp_default[512];
|
||||||
|
char default_output[512];
|
||||||
|
strncpy(temp_default, input_line, sizeof(temp_default) - 1);
|
||||||
|
temp_default[sizeof(temp_default) - 1] = '\0';
|
||||||
|
|
||||||
|
// Remove common encrypted extensions to get a better default
|
||||||
|
if (strstr(temp_default, ".otp.asc")) {
|
||||||
|
// Replace .otp.asc with original extension or no extension
|
||||||
|
char* ext_pos = strstr(temp_default, ".otp.asc");
|
||||||
|
*ext_pos = '\0';
|
||||||
|
} else if (strstr(temp_default, ".otp")) {
|
||||||
|
// Replace .otp with original extension or no extension
|
||||||
|
char* ext_pos = strstr(temp_default, ".otp");
|
||||||
|
*ext_pos = '\0';
|
||||||
|
} else {
|
||||||
|
// No recognized encrypted extension, add .decrypted suffix
|
||||||
|
strncat(temp_default, ".decrypted", sizeof(temp_default) - strlen(temp_default) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply files directory default path
|
||||||
|
get_default_file_path(temp_default, default_output, sizeof(default_output));
|
||||||
|
|
||||||
|
char output_file[512];
|
||||||
|
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return decrypt_file(input_line, output_file);
|
||||||
|
} else {
|
||||||
|
printf("Input not recognized as ASCII armor or valid file path.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_text_encrypt(void) {
|
||||||
|
printf("\n");
|
||||||
|
print_centered_header("Text Encrypt", 0);
|
||||||
|
|
||||||
|
// Launch text editor directly
|
||||||
|
char text_buffer[MAX_INPUT_SIZE];
|
||||||
|
if (launch_text_editor(NULL, text_buffer, sizeof(text_buffer)) != 0) {
|
||||||
|
printf("Error: Could not launch text editor\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(text_buffer) == 0) {
|
||||||
|
printf("No text entered - canceling encryption\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use unified pad selection
|
||||||
|
char* selected_pad = select_pad_interactive("Select Pad for Text Encryption",
|
||||||
|
"Select pad (by prefix)",
|
||||||
|
PAD_FILTER_ALL, 1);
|
||||||
|
if (!selected_pad) {
|
||||||
|
printf("Text encryption cancelled.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = encrypt_text(selected_pad, text_buffer);
|
||||||
|
free(selected_pad);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_file_encrypt(void) {
|
||||||
|
printf("\n");
|
||||||
|
print_centered_header("File Encrypt", 0);
|
||||||
|
|
||||||
|
// Launch file manager directly
|
||||||
|
char input_file[512];
|
||||||
|
if (launch_file_manager(".", input_file, sizeof(input_file)) != 0) {
|
||||||
|
printf("Error: Could not launch file manager\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
if (access(input_file, R_OK) != 0) {
|
||||||
|
printf("Error: File '%s' not found or cannot be read\n", input_file);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use unified pad selection
|
||||||
|
char* selected_pad = select_pad_interactive("Select Pad for File Encryption",
|
||||||
|
"Select pad (by prefix)",
|
||||||
|
PAD_FILTER_ALL, 1);
|
||||||
|
if (!selected_pad) {
|
||||||
|
printf("File encryption cancelled.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask for output format
|
||||||
|
printf("\nSelect output format:\n");
|
||||||
|
printf(" 1. Binary (.otp) - preserves file permissions\n");
|
||||||
|
printf(" 2. ASCII (.otp.asc) - text-safe format\n");
|
||||||
|
printf("Enter choice (1-2): ");
|
||||||
|
|
||||||
|
char format_input[10];
|
||||||
|
if (!fgets(format_input, sizeof(format_input), stdin)) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ascii_armor = (atoi(format_input) == 2) ? 1 : 0;
|
||||||
|
|
||||||
|
// Generate default output filename
|
||||||
|
char default_output[1024]; // Increased buffer size to prevent truncation warnings
|
||||||
|
if (ascii_armor) {
|
||||||
|
snprintf(default_output, sizeof(default_output), "%s.otp.asc", input_file);
|
||||||
|
} else {
|
||||||
|
snprintf(default_output, sizeof(default_output), "%s.otp", input_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use enhanced input function for output filename
|
||||||
|
char output_file[512];
|
||||||
|
if (get_filename_with_default("Output filename:", default_output, output_file, sizeof(output_file)) != 0) {
|
||||||
|
printf("Error: Failed to read input\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* output_filename = output_file;
|
||||||
|
|
||||||
|
int result = encrypt_file(selected_pad, input_file, output_filename, ascii_armor);
|
||||||
|
free(selected_pad);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
1041
src/util.c
Normal file
1041
src/util.c
Normal file
File diff suppressed because it is too large
Load Diff
27
test.sh
27
test.sh
@@ -1,27 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "Testing OTP Cipher Implementation"
|
|
||||||
echo "================================="
|
|
||||||
|
|
||||||
# Test 1: Generate a pad
|
|
||||||
echo "Test 1: Generating pad..."
|
|
||||||
./otp generate test 2
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test 2: Check if files were created
|
|
||||||
echo "Test 2: Checking generated files..."
|
|
||||||
ls -la test.pad test.state
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test 3: Test encryption
|
|
||||||
echo "Test 3: Testing encryption..."
|
|
||||||
echo "Secret Message" | ./otp encrypt test > encrypted_output.txt
|
|
||||||
cat encrypted_output.txt
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test 4: Test decryption
|
|
||||||
echo "Test 4: Testing decryption..."
|
|
||||||
cat encrypted_output.txt | ./otp decrypt test
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "Tests completed!"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
This is a test file for OTP encryption.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
-----BEGIN OTP MESSAGE-----
|
|
||||||
Version: v0.2.15
|
|
||||||
Pad-ChkSum: 0c8e19fde996e683fdbd348d1052eec168ffe6f67a88bb1278d0d02e9341b87b
|
|
||||||
Pad-Offset: 210
|
|
||||||
|
|
||||||
mMIm7iVtUO6NbXbskMxtydI/A16UXEQUGTcIya/8Dja6PB3EC0MLdw==
|
|
||||||
-----END OTP MESSAGE-----
|
|
||||||
Binary file not shown.
23
tests/temp_device_test.c
Normal file
23
tests/temp_device_test.c
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "include/otp.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
|
||||||
|
hardware_rng_device_t devices[10];
|
||||||
|
int num_devices_found = 0;
|
||||||
|
|
||||||
|
if (detect_all_hardware_rng_devices(devices, 10, &num_devices_found) != 0) {
|
||||||
|
printf("ERROR: Device detection failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("DEVICES_FOUND:%d\n", num_devices_found);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_devices_found; i++) {
|
||||||
|
printf("DEVICE:%d:%s:%d:%s\n", i, devices[i].port_path, devices[i].device_type, devices[i].friendly_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
499
tests/test.sh
Executable file
499
tests/test.sh
Executable file
@@ -0,0 +1,499 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Hardware RNG Device Testing Script
|
||||||
|
# Tests all three detected hardware RNG devices for functionality
|
||||||
|
# Author: OTP Cipher Implementation
|
||||||
|
# Version: 1.0
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
echo "========================================================================"
|
||||||
|
echo "Hardware RNG Device Testing Script - OTP Cipher v0.3.16"
|
||||||
|
echo "========================================================================"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Test counters
|
||||||
|
TOTAL_TESTS=0
|
||||||
|
PASSED_TESTS=0
|
||||||
|
FAILED_TESTS=0
|
||||||
|
|
||||||
|
# Function to print test results
|
||||||
|
print_result() {
|
||||||
|
local test_name="$1"
|
||||||
|
local result="$2"
|
||||||
|
local details="$3"
|
||||||
|
|
||||||
|
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||||
|
|
||||||
|
if [ "$result" = "PASS" ]; then
|
||||||
|
echo -e "${GREEN}✓ PASS${NC}: $test_name"
|
||||||
|
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||||
|
elif [ "$result" = "FAIL" ]; then
|
||||||
|
echo -e "${RED}✗ FAIL${NC}: $test_name"
|
||||||
|
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||||
|
if [ -n "$details" ]; then
|
||||||
|
echo -e " ${RED}Error:${NC} $details"
|
||||||
|
fi
|
||||||
|
elif [ "$result" = "SKIP" ]; then
|
||||||
|
echo -e "${YELLOW}⚠ SKIP${NC}: $test_name"
|
||||||
|
if [ -n "$details" ]; then
|
||||||
|
echo -e " ${YELLOW}Reason:${NC} $details"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test device detection
|
||||||
|
test_device_detection() {
|
||||||
|
echo -e "${BLUE}=== Device Detection Tests ===${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Test 1: Check if devices are detected
|
||||||
|
echo "Scanning for hardware RNG devices..."
|
||||||
|
|
||||||
|
# Create a temporary test program to check device detection
|
||||||
|
cat > temp_device_test.c << 'EOF'
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "include/otp.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
(void)argc; (void)argv; // Suppress unused parameter warnings
|
||||||
|
|
||||||
|
hardware_rng_device_t devices[10];
|
||||||
|
int num_devices_found = 0;
|
||||||
|
|
||||||
|
if (detect_all_hardware_rng_devices(devices, 10, &num_devices_found) != 0) {
|
||||||
|
printf("ERROR: Device detection failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("DEVICES_FOUND:%d\n", num_devices_found);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_devices_found; i++) {
|
||||||
|
printf("DEVICE:%d:%s:%d:%s\n", i, devices[i].port_path, devices[i].device_type, devices[i].friendly_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Compile the test program
|
||||||
|
if gcc -Wall -Wextra -std=c99 -Iinclude -o temp_device_test temp_device_test.c src/trng.o src/util.o src/state.o src/pads.o src/crypto.o src/entropy.o src/ui.o nostr_chacha20.o -lm 2>/dev/null; then
|
||||||
|
# Run device detection
|
||||||
|
DEVICE_OUTPUT=$(./temp_device_test 2>/dev/null)
|
||||||
|
DEVICE_COUNT=$(echo "$DEVICE_OUTPUT" | grep "DEVICES_FOUND:" | cut -d: -f2)
|
||||||
|
|
||||||
|
if [ "$DEVICE_COUNT" -gt 0 ]; then
|
||||||
|
print_result "Hardware RNG device detection" "PASS" "Found $DEVICE_COUNT devices"
|
||||||
|
|
||||||
|
# Parse device information
|
||||||
|
echo "$DEVICE_OUTPUT" | grep "DEVICE:" | while IFS=: read -r prefix index port_path device_type friendly_name; do
|
||||||
|
echo " Device $index: $friendly_name at $port_path (Type: $device_type)"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Store device info for later tests
|
||||||
|
echo "$DEVICE_OUTPUT" > temp_devices.txt
|
||||||
|
else
|
||||||
|
print_result "Hardware RNG device detection" "FAIL" "No devices found"
|
||||||
|
echo " Expected devices: TrueRNG, SwiftRNG variants"
|
||||||
|
echo " Check USB connections and device permissions"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -f temp_device_test temp_device_test.c
|
||||||
|
else
|
||||||
|
print_result "Device detection compilation" "FAIL" "Could not compile test program"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test individual device connectivity
|
||||||
|
test_device_connectivity() {
|
||||||
|
echo -e "${BLUE}=== Device Connectivity Tests ===${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ ! -f temp_devices.txt ]; then
|
||||||
|
print_result "Device connectivity tests" "SKIP" "No devices detected in previous test"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEVICE_COUNT=$(grep "DEVICES_FOUND:" temp_devices.txt | cut -d: -f2)
|
||||||
|
|
||||||
|
if [ "$DEVICE_COUNT" -eq 0 ]; then
|
||||||
|
print_result "Device connectivity tests" "SKIP" "No devices available for testing"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test each detected device
|
||||||
|
grep "DEVICE:" temp_devices.txt | while IFS=: read -r prefix index port_path device_type friendly_name; do
|
||||||
|
echo "Testing device: $friendly_name at $port_path"
|
||||||
|
|
||||||
|
# Create device-specific test
|
||||||
|
cat > temp_connectivity_test.c << 'EOF'
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "include/otp.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 4) {
|
||||||
|
printf("Usage: %s <port_path> <device_type> <friendly_name>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hardware_rng_device_t device;
|
||||||
|
snprintf(device.port_path, sizeof(device.port_path), "%s", argv[1]);
|
||||||
|
device.device_type = atoi(argv[2]);
|
||||||
|
snprintf(device.friendly_name, sizeof(device.friendly_name), "%s", argv[3]);
|
||||||
|
device.is_working = 0;
|
||||||
|
|
||||||
|
// Test with small buffer (1KB) and short timeout
|
||||||
|
const size_t test_bytes = 1024;
|
||||||
|
unsigned char* test_buffer = malloc(test_bytes);
|
||||||
|
if (!test_buffer) {
|
||||||
|
printf("ERROR: Cannot allocate test buffer\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t collected = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
|
||||||
|
printf("TESTING:%s\n", device.friendly_name);
|
||||||
|
|
||||||
|
// Test device connectivity with timeout
|
||||||
|
int result = collect_truerng_entropy_from_device(&device, test_buffer, test_bytes, &collected, 0);
|
||||||
|
|
||||||
|
time_t end_time = time(NULL);
|
||||||
|
double test_duration = difftime(end_time, start_time);
|
||||||
|
|
||||||
|
if (result == 0 && collected > 0) {
|
||||||
|
double speed_kbps = (collected / 1024.0) / (test_duration > 0 ? test_duration : 1.0);
|
||||||
|
printf("SUCCESS:%zu:%0.2f:%0.2f\n", collected, test_duration, speed_kbps);
|
||||||
|
} else {
|
||||||
|
printf("FAILED:%d:%zu:%0.2f\n", result, collected, test_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(test_buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Compile and run connectivity test
|
||||||
|
if gcc -Wall -Wextra -std=c99 -Iinclude -o temp_connectivity_test temp_connectivity_test.c src/trng.o src/util.o src/state.o src/pads.o src/crypto.o src/entropy.o src/ui.o nostr_chacha20.o -lm 2>/dev/null; then
|
||||||
|
|
||||||
|
# Run test with timeout to prevent hanging
|
||||||
|
CONNECTIVITY_OUTPUT=$(timeout 30s ./temp_connectivity_test "$port_path" "$device_type" "$friendly_name" 2>/dev/null || echo "TIMEOUT")
|
||||||
|
|
||||||
|
if echo "$CONNECTIVITY_OUTPUT" | grep -q "SUCCESS:"; then
|
||||||
|
# Parse success output
|
||||||
|
SUCCESS_LINE=$(echo "$CONNECTIVITY_OUTPUT" | grep "SUCCESS:")
|
||||||
|
COLLECTED=$(echo "$SUCCESS_LINE" | cut -d: -f2)
|
||||||
|
DURATION=$(echo "$SUCCESS_LINE" | cut -d: -f3)
|
||||||
|
SPEED=$(echo "$SUCCESS_LINE" | cut -d: -f4)
|
||||||
|
|
||||||
|
print_result "Device connectivity: $friendly_name" "PASS" "Collected ${COLLECTED} bytes in ${DURATION}s (${SPEED} KB/s)"
|
||||||
|
|
||||||
|
elif echo "$CONNECTIVITY_OUTPUT" | grep -q "FAILED:"; then
|
||||||
|
# Parse failure output
|
||||||
|
FAILED_LINE=$(echo "$CONNECTIVITY_OUTPUT" | grep "FAILED:")
|
||||||
|
ERROR_CODE=$(echo "$FAILED_LINE" | cut -d: -f2)
|
||||||
|
COLLECTED=$(echo "$FAILED_LINE" | cut -d: -f3)
|
||||||
|
|
||||||
|
print_result "Device connectivity: $friendly_name" "FAIL" "Error code $ERROR_CODE, collected $COLLECTED bytes"
|
||||||
|
|
||||||
|
elif echo "$CONNECTIVITY_OUTPUT" | grep -q "TIMEOUT"; then
|
||||||
|
print_result "Device connectivity: $friendly_name" "FAIL" "Test timed out after 30 seconds"
|
||||||
|
|
||||||
|
else
|
||||||
|
print_result "Device connectivity: $friendly_name" "FAIL" "Unexpected test output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
print_result "Device connectivity test compilation: $friendly_name" "FAIL" "Could not compile test program"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -f temp_connectivity_test temp_connectivity_test.c
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test device configuration
|
||||||
|
test_device_configuration() {
|
||||||
|
echo -e "${BLUE}=== Device Configuration Tests ===${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ ! -f temp_devices.txt ]; then
|
||||||
|
print_result "Device configuration tests" "SKIP" "No devices detected"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test serial port configuration for each device type
|
||||||
|
grep "DEVICE:" temp_devices.txt | while IFS=: read -r prefix index port_path device_type friendly_name; do
|
||||||
|
echo "Testing serial configuration for: $friendly_name"
|
||||||
|
|
||||||
|
# Create configuration test
|
||||||
|
cat > temp_config_test.c << 'EOF'
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "include/otp.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("Usage: %s <port_path> <device_type>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* port_path = argv[1];
|
||||||
|
hardware_rng_device_type_t device_type = atoi(argv[2]);
|
||||||
|
|
||||||
|
printf("TESTING_CONFIG:%s:%d\n", port_path, device_type);
|
||||||
|
|
||||||
|
// Test opening the device
|
||||||
|
int fd = open(port_path, O_RDONLY | O_NOCTTY | O_NONBLOCK);
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("FAILED:Cannot open device\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test configuring the serial port
|
||||||
|
int config_result = configure_rng_serial_port(fd, device_type);
|
||||||
|
|
||||||
|
if (config_result == 0) {
|
||||||
|
printf("SUCCESS:Serial port configured successfully\n");
|
||||||
|
} else {
|
||||||
|
printf("FAILED:Serial port configuration failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Compile and run configuration test
|
||||||
|
if gcc -Wall -Wextra -std=c99 -Iinclude -o temp_config_test temp_config_test.c src/trng.o src/util.o src/state.o src/ui.o -lm 2>/dev/null; then
|
||||||
|
|
||||||
|
CONFIG_OUTPUT=$(./temp_config_test "$port_path" "$device_type" 2>/dev/null || echo "ERROR")
|
||||||
|
|
||||||
|
if echo "$CONFIG_OUTPUT" | grep -q "SUCCESS:"; then
|
||||||
|
print_result "Serial configuration: $friendly_name" "PASS" "Port configured successfully"
|
||||||
|
elif echo "$CONFIG_OUTPUT" | grep -q "FAILED:"; then
|
||||||
|
REASON=$(echo "$CONFIG_OUTPUT" | grep "FAILED:" | cut -d: -f2)
|
||||||
|
print_result "Serial configuration: $friendly_name" "FAIL" "$REASON"
|
||||||
|
else
|
||||||
|
print_result "Serial configuration: $friendly_name" "FAIL" "Unexpected configuration result"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
print_result "Configuration test compilation: $friendly_name" "FAIL" "Could not compile test program"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
# Clean up
|
||||||
|
rm -f temp_config_test temp_config_test.c
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test entropy quality
|
||||||
|
test_entropy_quality() {
|
||||||
|
echo -e "${BLUE}=== Entropy Quality Tests ===${NC}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ ! -f temp_devices.txt ]; then
|
||||||
|
print_result "Entropy quality tests" "SKIP" "No devices detected"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test entropy quality for working devices
|
||||||
|
grep "DEVICE:" temp_devices.txt | while IFS=: read -r prefix index port_path device_type friendly_name; do
|
||||||
|
echo "Testing entropy quality for: $friendly_name"
|
||||||
|
|
||||||
|
# Create entropy quality test
|
||||||
|
cat > temp_quality_test.c << 'EOF'
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "include/otp.h"
|
||||||
|
|
||||||
|
// Simple entropy quality check
|
||||||
|
double calculate_byte_entropy(unsigned char* data, size_t length) {
|
||||||
|
int counts[256] = {0};
|
||||||
|
double entropy = 0.0;
|
||||||
|
|
||||||
|
// Count byte frequencies
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
counts[data[i]]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate Shannon entropy
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
if (counts[i] > 0) {
|
||||||
|
double p = (double)counts[i] / length;
|
||||||
|
entropy -= p * log2(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entropy;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 4) {
|
||||||
|
printf("Usage: %s <port_path> <device_type> <friendly_name>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hardware_rng_device_t device;
|
||||||
|
snprintf(device.port_path, sizeof(device.port_path), "%s", argv[1]);
|
||||||
|
device.device_type = atoi(argv[2]);
|
||||||
|
snprintf(device.friendly_name, sizeof(device.friendly_name), "%s", argv[3]);
|
||||||
|
|
||||||
|
// Test with 4KB sample
|
||||||
|
const size_t test_bytes = 4096;
|
||||||
|
unsigned char* test_buffer = malloc(test_bytes);
|
||||||
|
if (!test_buffer) {
|
||||||
|
printf("ERROR: Cannot allocate test buffer\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t collected = 0;
|
||||||
|
printf("TESTING_QUALITY:%s\n", device.friendly_name);
|
||||||
|
|
||||||
|
// Collect entropy sample
|
||||||
|
int result = collect_truerng_entropy_from_device(&device, test_buffer, test_bytes, &collected, 0);
|
||||||
|
|
||||||
|
if (result == 0 && collected >= 1024) { // Need at least 1KB for meaningful analysis
|
||||||
|
double entropy = calculate_byte_entropy(test_buffer, collected);
|
||||||
|
double entropy_percentage = (entropy / 8.0) * 100.0; // Max entropy is 8 bits per byte
|
||||||
|
|
||||||
|
if (entropy_percentage >= 95.0) {
|
||||||
|
printf("EXCELLENT:%0.2f:%zu\n", entropy_percentage, collected);
|
||||||
|
} else if (entropy_percentage >= 85.0) {
|
||||||
|
printf("GOOD:%0.2f:%zu\n", entropy_percentage, collected);
|
||||||
|
} else if (entropy_percentage >= 70.0) {
|
||||||
|
printf("FAIR:%0.2f:%zu\n", entropy_percentage, collected);
|
||||||
|
} else {
|
||||||
|
printf("POOR:%0.2f:%zu\n", entropy_percentage, collected);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("FAILED:%d:%zu\n", result, collected);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(test_buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Compile and run quality test
|
||||||
|
if gcc -Wall -Wextra -std=c99 -Iinclude -o temp_quality_test temp_quality_test.c src/trng.o src/util.o src/state.o src/ui.o -lm 2>/dev/null; then
|
||||||
|
|
||||||
|
QUALITY_OUTPUT=$(timeout 30s ./temp_quality_test "$port_path" "$device_type" "$friendly_name" 2>/dev/null || echo "TIMEOUT")
|
||||||
|
|
||||||
|
if echo "$QUALITY_OUTPUT" | grep -q "EXCELLENT:"; then
|
||||||
|
QUALITY_LINE=$(echo "$QUALITY_OUTPUT" | grep "EXCELLENT:")
|
||||||
|
PERCENTAGE=$(echo "$QUALITY_LINE" | cut -d: -f2)
|
||||||
|
BYTES=$(echo "$QUALITY_LINE" | cut -d: -f3)
|
||||||
|
print_result "Entropy quality: $friendly_name" "PASS" "Excellent quality (${PERCENTAGE}% entropy, ${BYTES} bytes)"
|
||||||
|
|
||||||
|
elif echo "$QUALITY_OUTPUT" | grep -q "GOOD:"; then
|
||||||
|
QUALITY_LINE=$(echo "$QUALITY_OUTPUT" | grep "GOOD:")
|
||||||
|
PERCENTAGE=$(echo "$QUALITY_LINE" | cut -d: -f2)
|
||||||
|
BYTES=$(echo "$QUALITY_LINE" | cut -d: -f3)
|
||||||
|
print_result "Entropy quality: $friendly_name" "PASS" "Good quality (${PERCENTAGE}% entropy, ${BYTES} bytes)"
|
||||||
|
|
||||||
|
elif echo "$QUALITY_OUTPUT" | grep -q "FAIR:"; then
|
||||||
|
QUALITY_LINE=$(echo "$QUALITY_OUTPUT" | grep "FAIR:")
|
||||||
|
PERCENTAGE=$(echo "$QUALITY_LINE" | cut -d: -f2)
|
||||||
|
BYTES=$(echo "$QUALITY_LINE" | cut -d: -f3)
|
||||||
|
print_result "Entropy quality: $friendly_name" "PASS" "Fair quality (${PERCENTAGE}% entropy, ${BYTES} bytes)"
|
||||||
|
|
||||||
|
elif echo "$QUALITY_OUTPUT" | grep -q "POOR:"; then
|
||||||
|
QUALITY_LINE=$(echo "$QUALITY_OUTPUT" | grep "POOR:")
|
||||||
|
PERCENTAGE=$(echo "$QUALITY_LINE" | cut -d: -f2)
|
||||||
|
BYTES=$(echo "$QUALITY_LINE" | cut -d: -f3)
|
||||||
|
print_result "Entropy quality: $friendly_name" "FAIL" "Poor quality (${PERCENTAGE}% entropy, ${BYTES} bytes)"
|
||||||
|
|
||||||
|
elif echo "$QUALITY_OUTPUT" | grep -q "FAILED:"; then
|
||||||
|
print_result "Entropy quality: $friendly_name" "FAIL" "Could not collect sufficient entropy for analysis"
|
||||||
|
|
||||||
|
elif echo "$QUALITY_OUTPUT" | grep -q "TIMEOUT"; then
|
||||||
|
print_result "Entropy quality: $friendly_name" "FAIL" "Quality test timed out"
|
||||||
|
|
||||||
|
else
|
||||||
|
print_result "Entropy quality: $friendly_name" "FAIL" "Unexpected quality test output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
print_result "Quality test compilation: $friendly_name" "FAIL" "Could not compile test program"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
# Clean up
|
||||||
|
rm -f temp_quality_test temp_quality_test.c
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to print final summary
|
||||||
|
print_summary() {
|
||||||
|
echo
|
||||||
|
echo "========================================================================"
|
||||||
|
echo -e "${BLUE}Test Summary${NC}"
|
||||||
|
echo "========================================================================"
|
||||||
|
echo "Total tests run: $TOTAL_TESTS"
|
||||||
|
echo -e "Passed: ${GREEN}$PASSED_TESTS${NC}"
|
||||||
|
echo -e "Failed: ${RED}$FAILED_TESTS${NC}"
|
||||||
|
echo -e "Success rate: $(( (PASSED_TESTS * 100) / (TOTAL_TESTS > 0 ? TOTAL_TESTS : 1) ))%"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ $FAILED_TESTS -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}🎉 All tests passed! Hardware RNG devices are working correctly.${NC}"
|
||||||
|
elif [ $PASSED_TESTS -gt $FAILED_TESTS ]; then
|
||||||
|
echo -e "${YELLOW}⚠ Some tests failed, but most devices are working.${NC}"
|
||||||
|
echo "Check the failed tests above for specific device issues."
|
||||||
|
else
|
||||||
|
echo -e "${RED}❌ Multiple test failures detected.${NC}"
|
||||||
|
echo "Hardware RNG devices may have connectivity or configuration issues."
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main test execution
|
||||||
|
main() {
|
||||||
|
echo "Starting comprehensive hardware RNG device testing..."
|
||||||
|
echo "This will test device detection, connectivity, configuration, and entropy quality."
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Ensure the OTP binary exists
|
||||||
|
if [ ! -f "./otp" ]; then
|
||||||
|
echo -e "${RED}Error: OTP binary not found. Please run 'make' first.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run all test suites
|
||||||
|
test_device_detection
|
||||||
|
test_device_connectivity
|
||||||
|
test_device_configuration
|
||||||
|
test_entropy_quality
|
||||||
|
|
||||||
|
# Clean up temporary files
|
||||||
|
rm -f temp_devices.txt
|
||||||
|
|
||||||
|
# Print final summary
|
||||||
|
print_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
55
tests/test_truerng.c
Normal file
55
tests/test_truerng.c
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "include/otp.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
(void)argc; (void)argv; // Suppress unused parameter warnings
|
||||||
|
|
||||||
|
hardware_rng_device_t device;
|
||||||
|
snprintf(device.port_path, sizeof(device.port_path), "/dev/ttyUSB0");
|
||||||
|
device.device_type = TRUERNG_ORIGINAL;
|
||||||
|
snprintf(device.friendly_name, sizeof(device.friendly_name), "TrueRNG");
|
||||||
|
device.is_working = 1;
|
||||||
|
|
||||||
|
printf("Debug: Device type set to: %d (TRUERNG_ORIGINAL should be %d)\n", device.device_type, TRUERNG_ORIGINAL);
|
||||||
|
printf("Debug: Comparison result: device.device_type == SWIFTRNG is %s\n",
|
||||||
|
(device.device_type == SWIFTRNG) ? "TRUE" : "FALSE");
|
||||||
|
|
||||||
|
// Test with small buffer (1KB) and short timeout
|
||||||
|
const size_t test_bytes = 1024;
|
||||||
|
unsigned char* test_buffer = malloc(test_bytes);
|
||||||
|
if (!test_buffer) {
|
||||||
|
printf("ERROR: Cannot allocate test buffer\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t collected = 0;
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
|
||||||
|
printf("Testing TrueRNG device at %s...\n", device.port_path);
|
||||||
|
|
||||||
|
// Test device connectivity with timeout
|
||||||
|
int result = collect_truerng_entropy_from_device(&device, test_buffer, test_bytes, &collected, 1);
|
||||||
|
|
||||||
|
time_t end_time = time(NULL);
|
||||||
|
double test_duration = difftime(end_time, start_time);
|
||||||
|
|
||||||
|
if (result == 0 && collected > 0) {
|
||||||
|
double speed_kbps = (collected / 1024.0) / (test_duration > 0 ? test_duration : 1.0);
|
||||||
|
printf("SUCCESS: Collected %zu bytes in %.2f seconds (%.2f KB/s)\n", collected, test_duration, speed_kbps);
|
||||||
|
|
||||||
|
// Show first few bytes as hex
|
||||||
|
printf("First 16 bytes: ");
|
||||||
|
for (int i = 0; i < 16 && i < (int)collected; i++) {
|
||||||
|
printf("%02x ", test_buffer[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
printf("FAILED: Error code %d, collected %zu bytes in %.2f seconds\n", result, collected, test_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(test_buffer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
1
true_rng
Submodule
1
true_rng
Submodule
Submodule true_rng added at 52ed7af980
Reference in New Issue
Block a user