Compare commits

..

32 Commits

Author SHA1 Message Date
Your Name
d5350d7c30 v0.4.10 - api 2025-10-05 14:35:09 -04:00
Your Name
c63fd04c92 v0.4.9 - Working on dm admin 2025-10-04 19:04:12 -04:00
Your Name
64b418a551 v0.4.8 - Implement web server functionality for embedded admin interface - serve HTML/CSS/JS from /api/ endpoint with proper MIME types, CORS headers, and performance optimizations 2025-10-04 12:45:35 -04:00
Your Name
36c9c84047 v0.4.7 - Implement NIP-70 Protected Events - Add protected event support with authentication checks, comprehensive testing, and relay metadata protection 2025-10-03 06:44:27 -04:00
Your Name
88b4aaa301 v0.4.6 - Implement NIP-50 search functionality with LIKE-based content and tag searching 2025-10-03 05:43:49 -04:00
Your Name
eac4c227c9 v0.4.5 - Fix NIP-45 COUNT test to account for existing relay events and handle replaceable events correctly 2025-10-03 05:19:39 -04:00
Your Name
d5eb7d4a55 v0.4.4 - Just waking up 2025-10-03 04:52:40 -04:00
Your Name
80b15e16e2 v0.4.3 - feat: Implement dynamic configuration updates without restart
- Add cache refresh mechanism for config updates
- Implement selective re-initialization for NIP-11 relay info changes
- Categorize configs as dynamic vs restart-required using requires_restart field
- Enhance admin API responses with restart requirement information
- Add comprehensive test for dynamic config updates
- Update documentation for dynamic configuration capabilities

Most relay settings can now be updated via admin API without requiring restart, improving operational flexibility while maintaining stability for critical changes.
2025-10-02 15:53:26 -04:00
Your Name
cfacedbb1a v0.4.2 - Implement NIP-11 Relay Information Document with event-based configuration - make relay info dynamically configurable via admin API 2025-10-02 11:38:28 -04:00
Your Name
c3bab033ed v0.4.1 - Fixed startup bug 2025-10-01 17:23:50 -04:00
Your Name
524f9bd84f Last push before major bug fixes 2025-10-01 14:53:20 -04:00
Your Name
4658ede9d6 feat: Implement auth rules enforcement and fix subscription filtering issues
- **Auth Rules Implementation**: Added blacklist/whitelist enforcement in websockets.c
  - Events are now checked against auth_rules table before acceptance
  - Blacklist blocks specific pubkeys, whitelist enables allow-only mode
  - Made check_database_auth_rules() public for cross-module access

- **Subscription Filtering Fixes**:
  - Added missing 'ids' filter support in SQL query building
  - Fixed test expectations to not require exact event counts for kind filters
  - Improved filter validation and error handling

- **Ephemeral Events Compliance**:
  - Modified SQL queries to exclude kinds 20000-29999 from historical queries
  - Maintains broadcasting to active subscribers while preventing storage/retrieval
  - Ensures NIP-01 compliance for ephemeral event handling

- **Comprehensive Testing**:
  - Created white_black_test.sh with full blacklist/whitelist functionality testing
  - Tests verify blocked posting for blacklisted users
  - Tests verify whitelist-only mode when whitelist rules exist
  - Includes proper auth rule clearing between test phases

- **Code Quality**:
  - Added proper function declarations to websockets.h
  - Improved error handling and logging throughout
  - Enhanced test script with clear pass/fail reporting
2025-09-30 15:17:59 -04:00
Your Name
f7b463aca1 Fixing whitelist and blacklist functionality 2025-09-30 15:02:49 -04:00
Your Name
c1a6e92b1d v0.3.19 - last save before major refactoring 2025-09-30 10:47:11 -04:00
Your Name
eefb0e427e v0.3.18 - index.html improvements 2025-09-30 07:51:23 -04:00
Your Name
c23d81b740 v0.3.17 - Embedded login button 2025-09-30 06:47:09 -04:00
Your Name
6dac231040 v0.3.16 - Admin system getting better 2025-09-30 05:32:23 -04:00
Your Name
6fd3e531c3 v0.3.15 - How can administration take so long 2025-09-27 15:50:42 -04:00
Your Name
c1c05991cf v0.3.14 - I think the admin api is finally working 2025-09-27 14:08:45 -04:00
Your Name
ab378e14d1 v0.3.13 - Working on admin system 2025-09-27 13:32:21 -04:00
Your Name
c0f9bf9ef5 v0.3.12 - Working through auth still 2025-09-25 17:33:38 -04:00
Your Name
bc6a7b3f20 Working on API 2025-09-25 16:35:16 -04:00
Your Name
036b0823b9 v0.3.11 - Working on admin api 2025-09-25 11:25:50 -04:00
Your Name
be99595bde v0.3.10 - . 2025-09-24 10:49:48 -04:00
Your Name
01836a4b4c v0.3.9 - API work 2025-09-21 15:53:03 -04:00
Your Name
9f3b3dd773 v0.3.8 - safety push 2025-09-18 10:18:15 -04:00
Your Name
3210b9e752 v0.3.7 - working on cinfig api 2025-09-16 15:52:27 -04:00
Your Name
2d66b8bf1d . 2025-09-15 20:34:00 -04:00
Your Name
f3d6afead1 v0.3.5 - nip42 implemented 2025-09-13 08:49:09 -04:00
Your Name
1690b58c67 v0.3.4 - Implement secure relay private key storage
- Add relay_seckey table for secure private key storage
- Implement store_relay_private_key() and get_relay_private_key() functions
- Remove relay private key from public configuration events (kind 33334)
- Update first-time startup sequence to store keys securely after DB init
- Add proper validation and error handling for private key operations
- Fix timing issue where private key storage was attempted before DB initialization
- Security improvement: relay private keys no longer exposed in public events
2025-09-07 07:35:51 -04:00
Your Name
2e8eda5c67 v0.3.3 - Fix function naming consistency: rename find_existing_nrdb_files to find_existing_db_files
- Update function declaration in config.h
- Update function definition in config.c
- Update function calls in config.c and main.c
- Maintain consistency with .db file extension naming convention

This resolves the inconsistency between database file extension (.db) and function names (nrdb)
2025-09-07 06:58:50 -04:00
Your Name
74a4dc2533 v0.3.2 - Implement -p/--port CLI option for first-time startup port override
- Add cli_options_t structure for extensible command line options
- Implement port override in create_default_config_event()
- Update main() with robust CLI parsing and validation
- Add comprehensive help text documenting first-time only behavior
- Ensure CLI options only affect initial configuration event creation
- Maintain event-based configuration architecture for ongoing operation
- Include comprehensive error handling and input validation
- Add documentation in CLI_PORT_OVERRIDE_IMPLEMENTATION.md

Tested: First-time startup uses CLI port, subsequent startups use database config
2025-09-07 06:54:56 -04:00
853 changed files with 153941 additions and 3565 deletions

4
.gitignore vendored
View File

@@ -8,3 +8,7 @@ src/version.h
dev-config/ dev-config/
db/ db/
copy_executable_local.sh copy_executable_local.sh
nostr_login_lite/
style_guide/
nostr-tools

298
.roo/architect/AGENTS.md Normal file
View File

@@ -0,0 +1,298 @@
# AGENTS.md - AI Agent Integration Guide for Architect Mode
**Project-Specific Information for AI Agents Working with C-Relay in Architect Mode**
## Critical Architecture Understanding
### System Architecture Overview
C-Relay implements a **unique event-based configuration architecture** that fundamentally differs from traditional Nostr relays:
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ WebSocket │ │ Configuration │ │ Database │
│ + HTTP │◄──►│ Event System │◄──►│ (SQLite) │
│ (Port 8888) │ │ (Kind 33334) │ │ Schema v4 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ nostr_core_lib │ │ Admin Key │ │ Event Storage │
│ (Crypto/Sigs) │ │ Management │ │ + Subscriptions │
└─────────────────┘ └──────────────────┘ └─────────────────┘
```
### Core Architectural Principles
#### 1. Event-Driven Configuration
**Design Philosophy**: Configuration as cryptographically signed events rather than files
- **Benefits**: Auditability, remote management, tamper-evidence
- **Trade-offs**: Complexity in configuration changes, admin key management burden
- **Implementation**: Kind 33334 events stored in same database as relay events
#### 2. Identity-Based Database Naming
**Design Philosophy**: Database file named by relay's generated public key
- **Benefits**: Prevents database conflicts, enables multi-relay deployments
- **Trade-offs**: Cannot predict database filename, complicates backup strategies
- **Implementation**: `<relay_pubkey>.db` created in build/ directory
#### 3. Single-Binary Deployment
**Design Philosophy**: All functionality embedded in one executable
- **Benefits**: Simple deployment, no external dependencies to manage
- **Trade-offs**: Larger binary size, harder to modularize
- **Implementation**: SQL schema embedded as header file, nostr_core_lib as submodule
#### 4. Dual-Protocol Support
**Design Philosophy**: WebSocket (Nostr) and HTTP (NIP-11) on same port
- **Benefits**: Simplified port management, reduced infrastructure complexity
- **Trade-offs**: Protocol detection overhead, libwebsockets dependency
- **Implementation**: Request routing based on HTTP headers and upgrade requests
## Architectural Decision Analysis
### Configuration System Design
**Traditional Approach vs C-Relay:**
```
Traditional: C-Relay:
config.json → kind 33334 events
ENV variables → cryptographically signed tags
File watching → database polling/restart
```
**Implications for Extensions:**
- Configuration changes require event signing capabilities
- No hot-reloading without architectural changes
- Admin key loss = complete database reset required
### Database Architecture Decisions
**Schema Design Philosophy:**
- **Event Tags as JSON**: Separate table with JSON column instead of normalized relations
- **Application-Level Filtering**: NIP-40 expiration handled in C, not SQL
- **Embedded Schema**: Version 4 schema compiled into binary
**Scaling Considerations:**
- SQLite suitable for small-to-medium relays (< 10k concurrent connections)
- Single-writer limitation of SQLite affects write-heavy workloads
- JSON tag storage optimizes for read performance over write normalization
### Memory Management Architecture
**Thread Safety Model:**
- Global subscription manager with mutex protection
- Per-client subscription limits enforced in memory
- WebSocket connection state managed by libwebsockets
**Resource Management:**
- JSON objects use reference counting (jansson library)
- String duplication pattern for configuration values
- Automatic cleanup on client disconnect
## Architectural Extension Points
### Adding New Configuration Options
**Required Changes:**
1. Update [`default_config_event.h`](src/default_config_event.h) template
2. Add parsing logic in [`config.c`](src/config.c) `load_config_from_database()`
3. Add global config struct field in [`config.h`](src/config.h)
4. Update documentation in [`docs/configuration_guide.md`](docs/configuration_guide.md)
### Adding New NIP Support
**Integration Pattern:**
1. Event validation in [`request_validator.c`](src/request_validator.c)
2. Protocol handling in [`main.c`](src/main.c) WebSocket callback
3. Database storage considerations in schema
4. Add test in `tests/` directory
### Scaling Architecture
**Current Limitations:**
- Single process, no horizontal scaling
- SQLite single-writer bottleneck
- Memory-based subscription management
**Potential Extensions:**
- Redis for subscription state sharing
- PostgreSQL for better concurrent write performance
- Load balancer for read scaling with multiple instances
## Deployment Architecture Patterns
### Development Deployment
```
Developer Machine:
├── ./make_and_restart_relay.sh
├── build/c_relay_x86
├── build/<relay_pubkey>.db
└── relay.log
```
### Production SystemD Deployment
```
/opt/c-relay/:
├── c_relay_x86
├── <relay_pubkey>.db
├── systemd service (c-relay.service)
└── c-relay user isolation
```
### Container Deployment Architecture
```
Container:
├── Multi-stage build (deps + binary)
├── Volume mount for database persistence
├── Health checks via NIP-11 endpoint
└── Signal handling for graceful shutdown
```
### Reverse Proxy Architecture
```
Internet → Nginx/HAProxy → C-Relay
├── WebSocket upgrade handling
├── SSL termination
└── Rate limiting
```
## Security Architecture Considerations
### Key Management Design
**Admin Key Security Model:**
- Generated once, displayed once, never stored
- Required for all configuration changes
- Loss requires complete database reset
**Relay Identity Model:**
- Separate keypair for relay identity
- Public key used for database naming
- Private key never exposed to clients
### Event Validation Pipeline
```
WebSocket Input → JSON Parse → Schema Validate → Signature Verify → Store
↓ ↓ ↓
reject reject reject success
```
### Attack Surface Analysis
**Network Attack Vectors:**
- WebSocket connection flooding (mitigated by libwebsockets limits)
- JSON parsing attacks (handled by jansson library bounds checking)
- SQLite injection (prevented by prepared statements)
**Configuration Attack Vectors:**
- Admin key compromise (complete relay control)
- Event signature forgery (prevented by nostr_core_lib validation)
- Replay attacks (event timestamp validation required)
## Non-Obvious Architectural Considerations
### Database Evolution Strategy
**Current Limitations:**
- Schema changes require database recreation
- No migration system for configuration events
- Version 4 schema embedded in binary
**Future Architecture Needs:**
- Schema versioning and migration system
- Backward compatibility for configuration events
- Database backup/restore procedures
### Configuration Event Lifecycle
**Event Flow:**
```
Admin Signs Event → WebSocket Submit → Validate → Store → Restart Required
↓ ↓ ↓
Signature Check Database Config Reload
```
**Architectural Implications:**
- No hot configuration reloading
- Configuration changes require planned downtime
- Event ordering matters for multiple simultaneous changes
### Cross-Architecture Deployment
**Build System Architecture:**
- Auto-detection of host architecture
- Cross-compilation support for ARM64
- Architecture-specific binary outputs
**Deployment Implications:**
- Binary must match target architecture
- Dependencies must be available for target architecture
- Debug tooling architecture-specific
### Performance Architecture Characteristics
**Bottlenecks:**
1. **SQLite Write Performance**: Single writer limitation
2. **JSON Parsing**: Per-event parsing overhead
3. **Signature Validation**: Cryptographic operations per event
4. **Memory Management**: JSON object lifecycle management
**Optimization Points:**
- Prepared statement reuse
- Connection pooling for concurrent reads
- Event batching for bulk operations
- Subscription indexing strategies
### Integration Architecture Patterns
**Monitoring Integration:**
- NIP-11 endpoint for health checks
- Log file monitoring for operational metrics
- Database query monitoring for performance
- Process monitoring for resource usage
**Backup Architecture:**
- Database file backup (SQLite file copy)
- Configuration event export/import
- Admin key secure storage (external to relay)
### Future Extension Architectures
**Multi-Relay Coordination:**
- Database sharding by event kind
- Cross-relay event synchronization
- Distributed configuration management
**Plugin Architecture Possibilities:**
- Event processing pipeline hooks
- Custom validation plugins
- External authentication providers
**Scaling Architecture Options:**
- Read replicas with PostgreSQL migration
- Event stream processing with message queues
- Microservice decomposition (auth, storage, validation)
## Architectural Anti-Patterns to Avoid
1. **Configuration File Addition**: Breaks event-based config paradigm
2. **Direct Database Modification**: Bypasses signature validation
3. **Hard-Coded Ports**: Conflicts with auto-fallback system
4. **Schema Modifications**: Requires database recreation
5. **Admin Key Storage**: Violates security model
6. **Blocking Operations**: Interferes with WebSocket event loop
7. **Memory Leaks**: JSON objects must be properly reference counted
8. **Thread Unsafe Operations**: Global state requires proper synchronization
## Architecture Decision Records (Implicit)
### Decision: Event-Based Configuration
**Context**: Traditional config files vs. cryptographic auditability
**Decision**: Store configuration as signed Nostr events
**Consequences**: Complex configuration changes, enhanced security, remote management capability
### Decision: SQLite Database
**Context**: Database choice for relay storage
**Decision**: Embedded SQLite with JSON tag storage
**Consequences**: Simple deployment, single-writer limitation, application-level filtering
### Decision: Single Binary Deployment
**Context**: Dependency management vs. deployment simplicity
**Decision**: Embed all dependencies and schema in binary
**Consequences**: Larger binary, simple deployment, version coupling
### Decision: Dual Protocol Support
**Context**: WebSocket for Nostr, HTTP for NIP-11
**Decision**: Same port serves both protocols
**Consequences**: Simplified deployment, protocol detection overhead, libwebsockets dependency
These architectural decisions form the foundation of C-Relay's unique approach to Nostr relay implementation and should be carefully considered when planning extensions or modifications.
**
[Response interrupted by a tool use result. Only one tool may be used at a time and should be placed at the end of the message.]

View File

@@ -2,4 +2,6 @@
description: "Brief description of what this command does" description: "Brief description of what this command does"
--- ---
Run build_and_push.sh, and supply a good git commit message. Run build_and_push.sh, and supply a good git commit message. For example:
./build_and_push.sh "Fixed the bug with nip05 implementation"

1
.rooignore Normal file
View File

@@ -0,0 +1 @@
src/embedded_web_content.c

152
AGENTS.md Normal file
View File

@@ -0,0 +1,152 @@
# AGENTS.md - AI Agent Integration Guide
**Project-Specific Information for AI Agents Working with C-Relay**
## Critical Build Commands
### Primary Build Command
```bash
./make_and_restart_relay.sh
```
**Never use `make` directly.** The project requires the custom restart script which:
- Handles database preservation/cleanup based on flags
- Manages architecture-specific binary detection (x86/ARM64)
- Performs automatic process cleanup and port management
- Starts relay in background with proper logging
### Architecture-Specific Binary Outputs
- **x86_64**: `./build/c_relay_x86`
- **ARM64**: `./build/c_relay_arm64`
- **Other**: `./build/c_relay_$(ARCH)`
### Database File Naming Convention
- **Format**: `<relay_pubkey>.db` (NOT `.nrdb` as shown in docs)
- **Location**: Created in `build/` directory during execution
- **Cleanup**: Use `--preserve-database` flag to retain between builds
## Critical Integration Issues
### Event-Based Configuration System
- **No traditional config files** - all configuration stored in config table
- Admin private key shown **only once** on first startup
- Configuration changes require cryptographically signed events
- Database path determined by generated relay pubkey
### First-Time Startup Sequence
1. Relay generates admin keypair and relay keypair
2. Creates database file with relay pubkey as filename
3. Stores default configuration in config table
4. **CRITICAL**: Admin private key displayed once and never stored on disk
### Port Management
- Default port 8888 with automatic fallback (8889, 8890, etc.)
- Script performs port availability checking before libwebsockets binding
- Process cleanup includes force-killing processes on port 8888
### Database Schema Dependencies
- Uses embedded SQL schema (`sql_schema.h`)
- Schema version 4 with JSON tag storage
- **Critical**: Event expiration filtering done at application level, not SQL level
### Admin API Event Structure
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [
["p", "<relay_pubkey>"]
]
}
```
**Configuration Commands** (encrypted in content):
- `["relay_description", "My Relay"]`
- `["max_subscriptions_per_client", "25"]`
- `["pow_min_difficulty", "16"]`
**Auth Rule Commands** (encrypted in content):
- `["blacklist", "pubkey", "hex_pubkey_value"]`
- `["whitelist", "pubkey", "hex_pubkey_value"]`
**Query Commands** (encrypted in content):
- `["auth_query", "all"]`
- `["system_command", "system_status"]`
### Process Management
```bash
# Kill existing relay processes
pkill -f "c_relay_"
# Check running processes
ps aux | grep c_relay_
# Force kill port binding
fuser -k 8888/tcp
```
### Cross-Compilation Specifics
- ARM64 requires explicit dependency installation: `make install-arm64-deps`
- Uses `aarch64-linux-gnu-gcc` with specific library paths
- PKG_CONFIG_PATH must be set for ARM64: `/usr/lib/aarch64-linux-gnu/pkgconfig`
### Testing Integration
- Tests expect relay running on default port
- Use `tests/quick_error_tests.sh` for validation
- Event configuration tests: `tests/event_config_tests.sh`
### SystemD Integration Considerations
- Service runs as `c-relay` user in `/opt/c-relay`
- Database files created in WorkingDirectory automatically
- No environment variables needed (event-based config)
- Resource limits: 65536 file descriptors, 4096 processes
### Development vs Production Differences
- Development: `make_and_restart_relay.sh` (default database cleanup)
- Production: `make_and_restart_relay.sh --preserve-database`
- Debug build requires manual gdb attachment to architecture-specific binary
### Critical File Dependencies
- `nostr_core_lib/` submodule must be initialized and built first
- Version header auto-generated from git tags: `src/version.h`
- Schema embedded in binary from `src/sql_schema.h`
### WebSocket Protocol Specifics
- Supports both WebSocket (Nostr protocol) and HTTP (NIP-11)
- NIP-11 requires `Accept: application/nostr+json` header
- CORS headers automatically added for NIP-11 compliance
### Memory Management Notes
- Persistent subscription system with thread-safe global manager
- Per-session subscription limits enforced
- Event filtering done at C level, not SQL level for NIP-40 expiration
### Configuration Override Behavior
- CLI port override only affects first-time startup
- After database creation, all config comes from events
- Database path cannot be changed after initialization
## Non-Obvious Pitfalls
1. **Database Lock Issues**: Script handles SQLite locking by killing existing processes first
2. **Port Race Conditions**: Pre-check + libwebsockets binding can still fail due to timing
3. **Key Loss**: Admin private key loss requires complete database deletion and restart
4. **Architecture Detection**: Build system auto-detects but cross-compilation requires manual setup
5. **Event Storage**: Ephemeral events (kind 20000-29999) accepted but not stored
6. **Signature Validation**: All events validated with `nostr_verify_event_signature()` from nostr_core_lib
## Quick Debugging Commands
```bash
# Check relay status
ps aux | grep c_relay_ && netstat -tln | grep 8888
# View logs
tail -f relay.log
# Test WebSocket connection
wscat -c ws://localhost:8888
# Test NIP-11 endpoint
curl -H "Accept: application/nostr+json" http://localhost:8888
# Find database files
find . -name "*.db" -type f

View File

@@ -9,7 +9,7 @@ LIBS = -lsqlite3 -lwebsockets -lz -ldl -lpthread -lm -L/usr/local/lib -lsecp256k
BUILD_DIR = build BUILD_DIR = build
# Source files # Source files
MAIN_SRC = src/main.c src/config.c MAIN_SRC = src/main.c src/config.c src/dm_admin.c src/request_validator.c src/nip009.c src/nip011.c src/nip013.c src/nip040.c src/nip042.c src/websockets.c src/subscriptions.c src/api.c src/embedded_web_content.c
NOSTR_CORE_LIB = nostr_core_lib/libnostr_core_x64.a NOSTR_CORE_LIB = nostr_core_lib/libnostr_core_x64.a
# Architecture detection # Architecture detection
@@ -36,10 +36,16 @@ $(NOSTR_CORE_LIB):
@echo "Building nostr_core_lib..." @echo "Building nostr_core_lib..."
cd nostr_core_lib && ./build.sh cd nostr_core_lib && ./build.sh
# Generate version.h from git tags # Update main.h version information (requires main.h to exist)
src/version.h: src/main.h:
@if [ -d .git ]; then \ @if [ ! -f src/main.h ]; then \
echo "Generating version.h from git tags..."; \ echo "ERROR: src/main.h not found!"; \
echo "Please ensure src/main.h exists with relay metadata."; \
echo "Copy from a backup or create manually with proper relay configuration."; \
exit 1; \
fi; \
if [ -d .git ]; then \
echo "Updating main.h version information from git tags..."; \
RAW_VERSION=$$(git describe --tags --always 2>/dev/null || echo "unknown"); \ RAW_VERSION=$$(git describe --tags --always 2>/dev/null || echo "unknown"); \
if echo "$$RAW_VERSION" | grep -q "^v[0-9]"; then \ if echo "$$RAW_VERSION" | grep -q "^v[0-9]"; then \
CLEAN_VERSION=$$(echo "$$RAW_VERSION" | sed 's/^v//' | cut -d- -f1); \ CLEAN_VERSION=$$(echo "$$RAW_VERSION" | sed 's/^v//' | cut -d- -f1); \
@@ -51,54 +57,34 @@ src/version.h:
VERSION="v0.0.0"; \ VERSION="v0.0.0"; \
MAJOR=0; MINOR=0; PATCH=0; \ MAJOR=0; MINOR=0; PATCH=0; \
fi; \ fi; \
echo "/* Auto-generated version information */" > src/version.h; \ echo "Updating version information in existing main.h..."; \
echo "#ifndef VERSION_H" >> src/version.h; \ sed -i "s/#define VERSION \".*\"/#define VERSION \"$$VERSION\"/g" src/main.h; \
echo "#define VERSION_H" >> src/version.h; \ sed -i "s/#define VERSION_MAJOR [0-9]*/#define VERSION_MAJOR $$MAJOR/g" src/main.h; \
echo "" >> src/version.h; \ sed -i "s/#define VERSION_MINOR [0-9]*/#define VERSION_MINOR $$MINOR/g" src/main.h; \
echo "#define VERSION \"$$VERSION\"" >> src/version.h; \ sed -i "s/#define VERSION_PATCH [0-9]*/#define VERSION_PATCH $$PATCH/g" src/main.h; \
echo "#define VERSION_MAJOR $$MAJOR" >> src/version.h; \ echo "Updated main.h version to: $$VERSION"; \
echo "#define VERSION_MINOR $$MINOR" >> src/version.h; \
echo "#define VERSION_PATCH $$PATCH" >> src/version.h; \
echo "" >> src/version.h; \
echo "#endif /* VERSION_H */" >> src/version.h; \
echo "Generated version.h with clean version: $$VERSION"; \
elif [ ! -f src/version.h ]; then \
echo "Git not available and version.h missing, creating fallback version.h..."; \
VERSION="v0.0.0"; \
echo "/* Auto-generated version information */" > src/version.h; \
echo "#ifndef VERSION_H" >> src/version.h; \
echo "#define VERSION_H" >> src/version.h; \
echo "" >> src/version.h; \
echo "#define VERSION \"$$VERSION\"" >> src/version.h; \
echo "#define VERSION_MAJOR 0" >> src/version.h; \
echo "#define VERSION_MINOR 0" >> src/version.h; \
echo "#define VERSION_PATCH 0" >> src/version.h; \
echo "" >> src/version.h; \
echo "#endif /* VERSION_H */" >> src/version.h; \
echo "Created fallback version.h with version: $$VERSION"; \
else \ else \
echo "Git not available, preserving existing version.h"; \ echo "Git not available, preserving existing main.h version information"; \
fi fi
# Force version.h regeneration (useful for development) # Update main.h version information (requires existing main.h)
force-version: force-version:
@echo "Force regenerating version.h..." @echo "Force updating main.h version information..."
@rm -f src/version.h @$(MAKE) src/main.h
@$(MAKE) src/version.h
# Build the relay # Build the relay
$(TARGET): $(BUILD_DIR) src/version.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) $(TARGET): $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB)
@echo "Compiling C-Relay for architecture: $(ARCH)" @echo "Compiling C-Relay for architecture: $(ARCH)"
$(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) -o $(TARGET) $(NOSTR_CORE_LIB) $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) -o $(TARGET) $(NOSTR_CORE_LIB) $(LIBS)
@echo "Build complete: $(TARGET)" @echo "Build complete: $(TARGET)"
# Build for specific architectures # Build for specific architectures
x86: $(BUILD_DIR) src/version.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) x86: $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB)
@echo "Building C-Relay for x86_64..." @echo "Building C-Relay for x86_64..."
$(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) -o $(BUILD_DIR)/c_relay_x86 $(NOSTR_CORE_LIB) $(LIBS) $(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) -o $(BUILD_DIR)/c_relay_x86 $(NOSTR_CORE_LIB) $(LIBS)
@echo "Build complete: $(BUILD_DIR)/c_relay_x86" @echo "Build complete: $(BUILD_DIR)/c_relay_x86"
arm64: $(BUILD_DIR) src/version.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB) arm64: $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(NOSTR_CORE_LIB)
@echo "Cross-compiling C-Relay for ARM64..." @echo "Cross-compiling C-Relay for ARM64..."
@if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \ @if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \
echo "ERROR: ARM64 cross-compiler not found."; \ echo "ERROR: ARM64 cross-compiler not found."; \
@@ -171,7 +157,6 @@ init-db:
# Clean build artifacts # Clean build artifacts
clean: clean:
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)
rm -f src/version.h
@echo "Clean complete" @echo "Clean complete"
# Clean everything including nostr_core_lib # Clean everything including nostr_core_lib
@@ -210,6 +195,6 @@ help:
@echo " make check-toolchain # Check what compilers are available" @echo " make check-toolchain # Check what compilers are available"
@echo " make test # Run tests" @echo " make test # Run tests"
@echo " make init-db # Set up database" @echo " make init-db # Set up database"
@echo " make force-version # Force regenerate version.h from git" @echo " make force-version # Force regenerate main.h from git"
.PHONY: all x86 arm64 test init-db clean clean-all install-deps install-cross-tools install-arm64-deps check-toolchain help force-version .PHONY: all x86 arm64 test init-db clean clean-all install-deps install-cross-tools install-arm64-deps check-toolchain help force-version

556
README.md
View File

@@ -1,267 +1,8 @@
# C Nostr Relay - Event-Based Configuration System # C-Nostr Relay
A high-performance Nostr relay implemented in C with SQLite backend, featuring a revolutionary **zero-configuration** approach using event-based configuration management. A high-performance Nostr relay implemented in C with SQLite backend, featuring a revolutionary **zero-configuration** approach using event-based configuration management.
## 🌟 Key Features ## Supported NIPs
- **🔧 Zero Configuration**: No config files or command line arguments needed
- **🔑 Event-Based Config**: All settings stored as kind 33334 Nostr events
- **🚀 Real-Time Updates**: Configuration changes applied instantly via WebSocket
- **🛡️ Cryptographic Security**: Configuration events cryptographically signed and validated
- **📊 SQLite Backend**: High-performance event storage with optimized schema
- **🔄 Auto Key Generation**: Secure admin and relay keypairs generated on first startup
- **💾 Database Per Relay**: Each relay instance uses `<relay_pubkey>.nrdb` database naming
## 🚀 Quick Start
### 1. Build the Relay
```bash
git clone <repository-url>
cd c-relay
git submodule update --init --recursive
make
```
### 2. Start the Relay
```bash
./build/c_relay_x86
```
**That's it!** No configuration files, no command line arguments needed.
### 3. Save Your Admin Keys (IMPORTANT!)
On first startup, the relay will display:
```
=================================================================
IMPORTANT: SAVE THIS ADMIN PRIVATE KEY SECURELY!
=================================================================
Admin Private Key: f8491814ea288260dad2ab52c09b3b037e75e83e8b24feb9bdc328423922be44
Admin Public Key: 07fc2cdd8bdc0c60eefcc9e37e67fef88206bc84fadb894c283b006554ac687b
Relay Private Key: a1b2c3d4e5f6...
Relay Public Key: 1a2b3c4d5e6f...
Database: dc9a93fd0ffba7041f6df0602e5021913a42fcaf6dbf40f43ecdc011177b4d94.nrdb
=================================================================
```
⚠️ **Save the admin private key securely** - it's needed to update relay configuration and is only displayed once!
## 📋 System Requirements
- **OS**: Linux, macOS, or Windows (WSL)
- **Dependencies**:
- SQLite 3
- libwebsockets
- OpenSSL/LibreSSL
- libsecp256k1
- libcurl
- zlib
## 🏗️ Event-Based Configuration System
### How It Works
Traditional Nostr relays require configuration files, environment variables, or command line arguments. This relay uses a **revolutionary approach**:
1. **First-Time Startup**: Generates cryptographically secure admin and relay keypairs
2. **Database Creation**: Creates `<relay_pubkey>.nrdb` database file
3. **Default Configuration**: Creates initial kind 33334 configuration event with sensible defaults
4. **Real-Time Updates**: Administrators send new kind 33334 events to update configuration
5. **Instant Application**: Changes are applied immediately without restart
### Configuration Updates
To update relay configuration, send a signed kind 33334 event:
```json
{
"kind": 33334,
"content": "C Nostr Relay Configuration",
"tags": [
["d", "<relay_pubkey>"],
["relay_description", "My awesome Nostr relay"],
["max_subscriptions_per_client", "25"],
["pow_min_difficulty", "16"],
["nip40_expiration_enabled", "true"]
],
"created_at": 1234567890,
"pubkey": "<admin_pubkey>",
"id": "...",
"sig": "..."
}
```
Send this event to your relay via WebSocket, and changes are applied instantly.
### Configurable Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| `relay_description` | Relay description (NIP-11) | "C Nostr Relay" |
| `relay_contact` | Admin contact info | "" |
| `max_subscriptions_per_client` | Max subscriptions per client | "25" |
| `max_total_subscriptions` | Total subscription limit | "5000" |
| `pow_min_difficulty` | NIP-13 PoW difficulty | "0" |
| `pow_mode` | PoW validation mode | "optional" |
| `nip40_expiration_enabled` | Enable NIP-40 expiration | "true" |
| `nip40_expiration_strict` | Strict expiration mode | "false" |
| `max_message_length` | Max message size | "65536" |
| `max_event_tags` | Max tags per event | "2000" |
| `max_content_length` | Max content length | "65536" |
## 🔧 Deployment
### Manual Installation
```bash
# Build the relay
make
# Run directly
./build/c_relay_x86
```
### SystemD Service (Recommended)
```bash
# Install as system service
sudo systemd/install-service.sh
# Start the service
sudo systemctl start c-relay
# Enable auto-start on boot
sudo systemctl enable c-relay
# View logs
sudo journalctl -u c-relay -f
```
See [`systemd/README.md`](systemd/README.md) for detailed deployment documentation.
### Docker (Coming Soon)
Docker support is planned for future releases.
## 📊 Database Schema
The relay uses an optimized SQLite schema (version 4) with these key features:
- **Event-based storage**: All Nostr events in single `events` table
- **JSON tags support**: Native JSON storage for event tags
- **Performance optimized**: Multiple indexes for fast queries
- **Subscription logging**: Optional detailed subscription analytics
- **Auto-cleanup**: Automatic ephemeral event cleanup
- **Replaceable events**: Proper handling of replaceable/addressable events
## 🛡️ Security Features
- **Cryptographic validation**: All configuration events cryptographically verified
- **Admin-only config**: Only authorized admin pubkey can update configuration
- **Signature verification**: Uses `nostr_verify_event_signature()` for validation
- **Event structure validation**: Complete event structure validation
- **Secure key generation**: Uses `/dev/urandom` for cryptographically secure keys
- **No secrets storage**: Admin private key never stored on disk
## 🔌 Network Configuration
- **Default Port**: 8888 (WebSocket)
- **Protocol**: WebSocket with Nostr message format
- **Endpoints**:
- `ws://localhost:8888` - WebSocket relay
- `http://localhost:8888` - NIP-11 relay information (HTTP GET)
## 🏃‍♂️ Usage Examples
### Connect with a Nostr Client
```javascript
const relay = new WebSocket('ws://localhost:8888');
relay.send(JSON.stringify(["REQ", "sub1", {"kinds": [1], "limit": 10}]));
```
### Update Configuration (using `nostrtool` or similar)
```bash
# Create configuration event with nostrtool
nostrtool event --kind 33334 --content "Updated config" \
--tag d <relay_pubkey> \
--tag relay_description "My updated relay" \
--private-key <admin_private_key>
# Send to relay
nostrtool send ws://localhost:8888 <event_json>
```
## 📈 Monitoring and Analytics
### View Relay Status
```bash
# Check if relay is running
ps aux | grep c_relay
# Check network port
netstat -tln | grep 8888
# View recent logs
tail -f relay.log
```
### Database Analytics
```bash
# Connect to relay database
sqlite3 <relay_pubkey>.nrdb
# View relay statistics
SELECT * FROM event_stats;
# View configuration events
SELECT * FROM configuration_events;
# View recent events
SELECT * FROM recent_events LIMIT 10;
```
## 🧪 Testing
### Run Error Handling Tests
```bash
# Comprehensive test suite
tests/event_config_tests.sh
# Quick validation tests
tests/quick_error_tests.sh
```
### Manual Testing
```bash
# Test WebSocket connection
wscat -c ws://localhost:8888
# Test NIP-11 information
curl http://localhost:8888
```
## 🔧 Development
### Build from Source
```bash
git clone <repository-url>
cd c-relay
git submodule update --init --recursive
make clean && make
```
### Debug Build
```bash
make debug
gdb ./build/c_relay_x86
```
### Contributing
1. Fork the repository
2. Create a feature branch
3. Make changes with tests
4. Submit a pull request
## 📜 Supported NIPs
<!-- <!--
NOTE FOR ASSISTANTS: When updating the NIPs checklist below, ONLY change [ ] to [x] to mark as complete. NOTE FOR ASSISTANTS: When updating the NIPs checklist below, ONLY change [ ] to [x] to mark as complete.
@@ -276,68 +17,267 @@ Do NOT modify the formatting, add emojis, or change the text. Keep the simple fo
- [x] NIP-20: Command Results - [x] NIP-20: Command Results
- [x] NIP-33: Parameterized Replaceable Events - [x] NIP-33: Parameterized Replaceable Events
- [x] NIP-40: Expiration Timestamp - [x] NIP-40: Expiration Timestamp
- [ ] NIP-42: Authentication of clients to relays - [x] NIP-42: Authentication of clients to relays
- [ ] NIP-45: Counting results - [x] NIP-45: Counting results
- [ ] NIP-50: Keywords filter - [x] NIP-50: Keywords filter
- [ ] NIP-70: Protected Events - [x] NIP-70: Protected Events
## 🆘 Troubleshooting ## Web Admin Interface
### Common Issues C-Relay includes a **built-in web-based administration interface** accessible at `http://localhost:8888/api/`. The interface provides:
**Relay won't start** - **Real-time Configuration Management**: View and edit all relay settings through a web UI
```bash - **Database Statistics Dashboard**: Monitor event counts, storage usage, and performance metrics
# Check for port conflicts - **Auth Rules Management**: Configure whitelist/blacklist rules for pubkeys
netstat -tln | grep 8888 - **NIP-42 Authentication**: Secure access using your Nostr identity
- **Event-Based Updates**: All changes are applied as cryptographically signed Nostr events
# Check permissions The web interface serves embedded static files with no external dependencies and includes proper CORS headers for browser compatibility.
ls -la build/c_relay_x86
# Check dependencies ## Administrator API
ldd build/c_relay_x86
C-Relay uses an innovative **event-based administration system** where all configuration and management commands are sent as signed Nostr events using the admin private key generated during first startup. All admin commands use **NIP-44 encrypted command arrays** for security and compatibility.
### Authentication
All admin commands require signing with the admin private key displayed during first-time startup. **Save this key securely** - it cannot be recovered and is needed for all administrative operations.
### Event Structure
All admin commands use the same unified event structure with NIP-44 encrypted content:
**Admin Command Event:**
```json
{
"id": "event_id",
"pubkey": "admin_public_key",
"created_at": 1234567890,
"kind": 23456,
"content": "AqHBUgcM7dXFYLQuDVzGwMST1G8jtWYyVvYxXhVGEu4nAb4LVw...",
"tags": [
["p", "relay_public_key"]
],
"sig": "event_signature"
}
``` ```
**Lost admin private key** The `content` field contains a NIP-44 encrypted JSON array representing the command.
- If you lose the admin private key, you cannot update configuration
- You must delete the database file and restart (loses all events)
- The relay will generate new keys on first startup
**Database corruption** **Admin Response Event:**
```bash ```json
# Check database integrity ["EVENT", "temp_sub_id", {
sqlite3 <relay_pubkey>.nrdb "PRAGMA integrity_check;" "id": "response_event_id",
"pubkey": "relay_public_key",
# If corrupted, remove database (loses all events) "created_at": 1234567890,
rm <relay_pubkey>.nrdb* "kind": 23457,
./build/c_relay_x86 # Will create fresh database "content": "BpKCVhfN8eYtRmPqSvWxZnMkL2gHjUiOp3rTyEwQaS5dFg...",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
``` ```
**Configuration not updating** The `content` field contains a NIP-44 encrypted JSON response object.
- Ensure configuration events are properly signed
- Check that admin pubkey matches the one from first startup
- Verify WebSocket connection is active
- Check relay logs for validation errors
## 📄 License ### Admin Commands
This project is licensed under the MIT License - see the LICENSE file for details. All commands are sent as NIP-44 encrypted JSON arrays in the event content. The following table lists all available commands:
## 🤝 Support | Command Type | Command Format | Description |
|--------------|----------------|-------------|
| **Configuration Management** |
| `config_update` | `["config_update", [{"key": "auth_enabled", "value": "true", "data_type": "boolean", "category": "auth"}, {"key": "relay_description", "value": "My Relay", "data_type": "string", "category": "relay"}, ...]]` | Update relay configuration parameters (supports multiple updates) |
| `config_query` | `["config_query", "all"]` | Query all configuration parameters |
| **Auth Rules Management** |
| `auth_add_blacklist` | `["blacklist", "pubkey", "abc123..."]` | Add pubkey to blacklist |
| `auth_add_whitelist` | `["whitelist", "pubkey", "def456..."]` | Add pubkey to whitelist |
| `auth_delete_rule` | `["delete_auth_rule", "blacklist", "pubkey", "abc123..."]` | Delete specific auth rule |
| `auth_query_all` | `["auth_query", "all"]` | Query all auth rules |
| `auth_query_type` | `["auth_query", "whitelist"]` | Query specific rule type |
| `auth_query_pattern` | `["auth_query", "pattern", "abc123..."]` | Query specific pattern |
| **System Commands** |
| `system_clear_auth` | `["system_command", "clear_all_auth_rules"]` | Clear all auth rules |
| `system_status` | `["system_command", "system_status"]` | Get system status |
| `stats_query` | `["stats_query"]` | Get comprehensive database statistics |
- **Issues**: Report bugs and feature requests on GitHub ### Available Configuration Keys
- **Documentation**: See `docs/` directory for technical details
- **Deployment**: See `systemd/README.md` for production deployment
- **Community**: Join the Nostr development community
## 🚀 Future Roadmap **Basic Relay Settings:**
- `relay_name`: Relay name (displayed in NIP-11)
- `relay_description`: Relay description text
- `relay_contact`: Contact information
- `relay_software`: Software URL
- `relay_version`: Software version
- `supported_nips`: Comma-separated list of supported NIP numbers (e.g., "1,2,4,9,11,12,13,15,16,20,22,33,40,42")
- `language_tags`: Comma-separated list of supported language tags (e.g., "en,es,fr" or "*" for all)
- `relay_countries`: Comma-separated list of supported country codes (e.g., "US,CA,MX" or "*" for all)
- `posting_policy`: Posting policy URL or text
- `payments_url`: Payment URL for premium features
- `max_connections`: Maximum concurrent connections
- `max_subscriptions_per_client`: Max subscriptions per client
- `max_event_tags`: Maximum tags per event
- `max_content_length`: Maximum event content length
- [ ] Docker containerization **Authentication & Access Control:**
- [ ] NIP-42 authentication support - `auth_enabled`: Enable whitelist/blacklist auth rules (`true`/`false`)
- [ ] Advanced analytics dashboard - `nip42_auth_required`: Enable NIP-42 cryptographic authentication (`true`/`false`)
- [ ] Clustering support for high availability - `nip42_auth_required_kinds`: Event kinds requiring NIP-42 auth (comma-separated)
- [ ] Performance optimizations - `nip42_challenge_timeout`: NIP-42 challenge expiration seconds
- [ ] Additional NIP implementations
**Proof of Work & Validation:**
- `pow_min_difficulty`: Minimum proof-of-work difficulty
- `nip40_expiration_enabled`: Enable event expiration (`true`/`false`)
### Dynamic Configuration Updates
C-Relay supports **dynamic configuration updates** without requiring a restart for most settings. Configuration parameters are categorized as either **dynamic** (can be updated immediately) or **restart-required** (require relay restart to take effect).
**Dynamic Configuration Parameters (No Restart Required):**
- All relay information (NIP-11) settings: `relay_name`, `relay_description`, `relay_contact`, `relay_software`, `relay_version`, `supported_nips`, `language_tags`, `relay_countries`, `posting_policy`, `payments_url`
- Authentication settings: `auth_enabled`, `nip42_auth_required`, `nip42_auth_required_kinds`, `nip42_challenge_timeout`
- Subscription limits: `max_subscriptions_per_client`, `max_total_subscriptions`
- Event validation limits: `max_event_tags`, `max_content_length`, `max_message_length`
- Proof of Work settings: `pow_min_difficulty`, `pow_mode`
- Event expiration settings: `nip40_expiration_enabled`, `nip40_expiration_strict`, `nip40_expiration_filter`, `nip40_expiration_grace_period`
**Restart-Required Configuration Parameters:**
- Connection settings: `max_connections`, `relay_port`
- Database and core system settings
When updating configuration, the admin API response will indicate whether a restart is required for each parameter. Dynamic updates take effect immediately and are reflected in NIP-11 relay information documents without restart.
### Response Format
All admin commands return **signed EVENT responses** via WebSocket following standard Nostr protocol. Responses use JSON content with structured data.
#### Response Examples
**Success Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"status\": \"success\", \"message\": \"Operation completed successfully\", \"timestamp\": 1234567890}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
**Error Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"status\": \"error\", \"error\": \"invalid configuration value\", \"timestamp\": 1234567890}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
**Auth Rules Query Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"auth_rules_all\", \"total_results\": 2, \"timestamp\": 1234567890, \"data\": [{\"rule_type\": \"blacklist\", \"pattern_type\": \"pubkey\", \"pattern_value\": \"abc123...\", \"action\": \"allow\"}]}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
**Configuration Query Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"config_all\", \"total_results\": 27, \"timestamp\": 1234567890, \"data\": [{\"key\": \"auth_enabled\", \"value\": \"false\", \"data_type\": \"boolean\", \"category\": \"auth\", \"description\": \"Enable NIP-42 authentication\"}, {\"key\": \"relay_description\", \"value\": \"My Relay\", \"data_type\": \"string\", \"category\": \"relay\", \"description\": \"Relay description text\"}]}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
**Configuration Update Success Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"total_results\": 2, \"timestamp\": 1234567890, \"status\": \"success\", \"data\": [{\"key\": \"auth_enabled\", \"value\": \"true\", \"status\": \"updated\"}, {\"key\": \"relay_description\", \"value\": \"My Updated Relay\", \"status\": \"updated\"}]}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
**Configuration Update Error Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"status\": \"error\", \"error\": \"field validation failed: invalid port number '99999' (must be 1-65535)\", \"timestamp\": 1234567890}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
**Database Statistics Query Response:**
```json
["EVENT", "temp_sub_id", {
"id": "response_event_id",
"pubkey": "relay_public_key",
"created_at": 1234567890,
"kind": 23457,
"content": "nip44 encrypted:{\"query_type\": \"stats_query\", \"timestamp\": 1234567890, \"database_size_bytes\": 1048576, \"total_events\": 15432, \"database_created_at\": 1234567800, \"latest_event_at\": 1234567890, \"event_kinds\": [{\"kind\": 1, \"count\": 12000, \"percentage\": 77.8}, {\"kind\": 0, \"count\": 2500, \"percentage\": 16.2}], \"time_stats\": {\"total\": 15432, \"last_24h\": 234, \"last_7d\": 1456, \"last_30d\": 5432}, \"top_pubkeys\": [{\"pubkey\": \"abc123...\", \"event_count\": 1234, \"percentage\": 8.0}, {\"pubkey\": \"def456...\", \"event_count\": 987, \"percentage\": 6.4}]}",
"tags": [
["p", "admin_public_key"]
],
"sig": "response_event_signature"
}]
```
## Direct Messaging Admin System
In addition to the above admin API, c-relay allows the administrator to direct message the relay to get information or control some settings. As long as the administrator is signed in with any nostr client that allows sending nip-17 direct messages (DMs), they can control the relay.
The is possible because the relay is a full nostr citizen with it's own private and public key.
**Available DM commands**
The intent is not to be strict in the formatting of the DM. So for example if the relay receives any DM from the administrator with the words "stats" or "statistics" in it, it will respond to the administrator with a reply DM with the current relay statistics.
- `stats`|`statistics`: Relay statistics
- `config`|`configuration`: Relay configuration
---
**The C Nostr Relay represents the future of Nostr infrastructure - zero configuration, event-based management, and cryptographically secure administration.**

455
api/index.css Normal file
View File

@@ -0,0 +1,455 @@
:root {
/* Core Variables (7) */
--primary-color: #000000;
--secondary-color: #ffffff;
--accent-color: #ff0000;
--muted-color: #dddddd;
--border-color: var(--muted-color);
--font-family: "Courier New", Courier, monospace;
--border-radius: 15px;
--border-width: 1px;
/* Floating Tab Variables (8) */
--tab-bg-logged-out: #ffffff;
--tab-bg-logged-in: #ffffff;
--tab-bg-opacity-logged-out: 0.9;
--tab-bg-opacity-logged-in: 0.2;
--tab-color-logged-out: #000000;
--tab-color-logged-in: #ffffff;
--tab-border-logged-out: #000000;
--tab-border-logged-in: #ff0000;
--tab-border-opacity-logged-out: 1.0;
--tab-border-opacity-logged-in: 0.1;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-family);
background-color: var(--secondary-color);
color: var(--primary-color);
/* line-height: 1.4; */
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
h1 {
border-bottom: var(--border-width) solid var(--border-color);
padding-bottom: 10px;
margin-bottom: 30px;
font-weight: normal;
font-size: 24px;
font-family: var(--font-family);
color: var(--primary-color);
}
h2 {
font-weight: normal;
padding-left: 10px;
font-size: 16px;
font-family: var(--font-family);
color: var(--primary-color);
}
.section {
background: var(--secondary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
padding: 20px;
margin-bottom: 20px;
}
.input-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
font-size: 14px;
font-family: var(--font-family);
color: var(--primary-color);
}
input,
textarea,
select {
width: 100%;
padding: 8px;
background: var(--secondary-color);
color: var(--primary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
font-family: var(--font-family);
font-size: 14px;
box-sizing: border-box;
transition: all 0.2s ease;
}
input:focus,
textarea:focus,
select:focus {
border-color: var(--accent-color);
outline: none;
}
button {
width: 100%;
padding: 8px;
background: var(--secondary-color);
color: var(--primary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
font-family: var(--font-family);
font-size: 14px;
cursor: pointer;
margin: 5px 0;
font-weight: bold;
transition: all 0.2s ease;
}
button:hover {
border-color: var(--accent-color);
}
button:active {
background: var(--accent-color);
color: var(--secondary-color);
}
button:disabled {
background-color: #ccc;
color: var(--muted-color);
cursor: not-allowed;
border-color: #ccc;
}
.status {
padding: 10px;
margin: 10px 0;
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
font-weight: bold;
font-family: var(--font-family);
transition: all 0.2s ease;
}
.status.connected {
background-color: var(--primary-color);
color: var(--secondary-color);
}
.status.disconnected {
background-color: var(--secondary-color);
color: var(--primary-color);
}
.status.authenticated {
background-color: var(--primary-color);
color: var(--secondary-color);
}
.status.error {
background-color: var(--secondary-color);
color: var(--primary-color);
border-color: var(--accent-color);
}
.config-table {
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
overflow: hidden;
}
.config-table th,
.config-table td {
border: 0.1px solid var(--muted-color);
padding: 4px;
text-align: left;
font-family: var(--font-family);
font-size: 10px;
}
.config-table-container {
overflow-x: auto;
max-width: 100%;
}
.config-table th {
font-weight: bold;
}
.config-table tr:hover {
background-color: var(--muted-color);
}
.json-display {
background-color: var(--secondary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
padding: 10px;
font-family: var(--font-family);
font-size: 12px;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
margin: 10px 0;
}
.log-panel {
height: 200px;
overflow-y: auto;
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
padding: 10px;
font-size: 12px;
background-color: var(--secondary-color);
font-family: var(--font-family);
}
.log-entry {
margin-bottom: 5px;
border-bottom: 1px solid var(--muted-color);
padding-bottom: 5px;
}
.log-timestamp {
font-weight: bold;
font-family: var(--font-family);
}
.inline-buttons {
display: flex;
gap: 10px;
}
.inline-buttons button {
flex: 1;
}
.user-info {
padding: 10px;
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
margin: 10px 0;
background-color: var(--secondary-color);
}
.user-info-container {
display: flex;
align-items: flex-start;
gap: 20px;
}
.user-details {
flex: 1;
}
.login-logout-btn {
width: auto;
min-width: 120px;
padding: 12px 16px;
background: var(--secondary-color);
color: var(--primary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
font-family: var(--font-family);
font-size: 14px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s ease;
margin: 0;
flex-shrink: 0;
}
.login-logout-btn:hover {
border-color: var(--accent-color);
}
.login-logout-btn:active {
background: var(--accent-color);
color: var(--secondary-color);
}
.login-logout-btn.logout-state {
background: var(--accent-color);
color: var(--secondary-color);
border-color: var(--accent-color);
}
.login-logout-btn.logout-state:hover {
background: var(--primary-color);
border-color: var(--border-color);
}
.user-pubkey {
font-family: var(--font-family);
font-size: 12px;
word-break: break-all;
margin: 5px 0;
}
.hidden {
display: none;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: var(--border-width) solid var(--border-color);
padding-bottom: 10px;
}
.auth-rules-controls {
margin-bottom: 15px;
}
.section-header .status {
margin: 0;
padding: 5px 10px;
min-width: auto;
font-size: 12px;
}
/* Auth Rule Input Sections Styling */
.auth-rule-section {
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
padding: 15px;
margin: 15px 0;
background-color: var(--secondary-color);
}
.auth-rule-section h3 {
margin: 0 0 10px 0;
font-size: 14px;
font-weight: bold;
border-left: 4px solid var(--border-color);
padding-left: 8px;
font-family: var(--font-family);
color: var(--primary-color);
}
.auth-rule-section p {
margin: 0 0 15px 0;
font-size: 13px;
color: var(--muted-color);
font-family: var(--font-family);
}
.rule-status {
margin-top: 10px;
padding: 8px;
border: var(--border-width) solid var(--muted-color);
border-radius: var(--border-radius);
font-size: 12px;
min-height: 20px;
background-color: var(--secondary-color);
font-family: var(--font-family);
transition: all 0.2s ease;
}
.rule-status.success {
border-color: #4CAF50;
background-color: #E8F5E8;
color: #2E7D32;
}
.rule-status.error {
border-color: var(--accent-color);
background-color: #FFEBEE;
color: #C62828;
}
.rule-status.warning {
border-color: #FF9800;
background-color: #FFF3E0;
color: #E65100;
}
.warning-box {
border: var(--border-width) solid #FF9800;
border-radius: var(--border-radius);
background-color: #FFF3E0;
padding: 10px;
margin: 10px 0;
font-size: 13px;
color: #E65100;
font-family: var(--font-family);
}
.warning-box strong {
color: #D84315;
}
#login-section {
text-align: center;
padding: 20px;
}
/* Floating tab styles */
.floating-tab {
font-family: var(--font-family);
border-radius: var(--border-radius);
border: var(--border-width) solid;
transition: all 0.2s ease;
}
.floating-tab--logged-out {
background: rgba(255, 255, 255, var(--tab-bg-opacity-logged-out));
color: var(--tab-color-logged-out);
border-color: rgba(0, 0, 0, var(--tab-border-opacity-logged-out));
}
.floating-tab--logged-in {
background: rgba(0, 0, 0, var(--tab-bg-opacity-logged-in));
color: var(--tab-color-logged-in);
border-color: rgba(255, 0, 0, var(--tab-border-opacity-logged-in));
}
.transition {
transition: all 0.2s ease;
}
/* Main Sections Wrapper */
.main-sections-wrapper {
display: flex;
flex-wrap: wrap;
gap: var(--border-width);
margin-bottom: 20px;
}
.flex-section {
flex: 1;
min-width: 300px;
}
@media (max-width: 700px) {
body {
padding: 10px;
}
.inline-buttons {
flex-direction: column;
}
h1 {
font-size: 20px;
}
h2 {
font-size: 14px;
}
}

337
api/index.html Normal file
View File

@@ -0,0 +1,337 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>C-Relay Admin API</title>
<link rel="stylesheet" href="/api/index.css">
</head>
<body>
<h1>C-RELAY ADMIN API</h1>
<!-- Main Sections Wrapper -->
<div class="main-sections-wrapper">
<!-- Persistent Authentication Header - Always Visible -->
<div id="persistent-auth-container" class="section flex-section">
<div class="user-info-container">
<button type="button" id="login-logout-btn" class="login-logout-btn">LOGIN</button>
<div class="user-details" id="persistent-user-details" style="display: none;">
<div><strong>Name:</strong> <span id="persistent-user-name">Loading...</span></div>
<div><strong>Public Key:</strong>
<div class="user-pubkey" id="persistent-user-pubkey">Loading...</div>
</div>
<div><strong>About:</strong> <span id="persistent-user-about">Loading...</span></div>
</div>
</div>
</div>
<!-- Login Section -->
<div id="login-section" class="flex-section">
<div class="section">
<h2>NOSTR AUTHENTICATION</h2>
<p id="login-instructions">Please login with your Nostr identity to access the admin interface.</p>
<!-- nostr-lite login UI will be injected here -->
</div>
</div>
<!-- Relay Connection Section -->
<div id="relay-connection-section" class="flex-section">
<div class="section">
<h2>RELAY CONNECTION</h2>
<div class="input-group">
<label for="relay-connection-url">Relay URL:</label>
<input type="text" id="relay-connection-url" value="ws://localhost:8888"
placeholder="ws://localhost:8888 or wss://relay.example.com">
</div>
<div class="input-group">
<label for="relay-pubkey-manual">Relay Pubkey (if not available via NIP-11):</label>
<input type="text" id="relay-pubkey-manual" placeholder="64-character hex pubkey"
pattern="[0-9a-fA-F]{64}" title="64-character hexadecimal public key">
</div>
<div class="inline-buttons">
<button type="button" id="connect-relay-btn">CONNECT TO RELAY</button>
<button type="button" id="disconnect-relay-btn" disabled>DISCONNECT</button>
<button type="button" id="test-websocket-btn" disabled>TEST WEBSOCKET</button>
</div>
<div class="status disconnected" id="relay-connection-status">NOT CONNECTED</div>
<!-- Relay Information Display -->
<div id="relay-info-display" class="hidden">
<h3>Relay Information (NIP-11)</h3>
<table class="config-table" id="relay-info-table">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
</tr>
</thead>
<tbody id="relay-info-table-body">
</tbody>
</table>
</div>
</div>
</div>
</div> <!-- End Main Sections Wrapper -->
<!-- Testing Section -->
<div id="div_config" class="section flex-section" style="display: none;">
<h2>RELAY CONFIGURATION</h2>
<div id="config-display" class="hidden">
<div id="config-view-mode">
<div class="config-table-container">
<table class="config-table" id="config-table">
<thead>
<tr>
<th>Parameter</th>
<th>Value</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="config-table-body">
</tbody>
</table>
</div>
<div class="inline-buttons">
<button type="button" id="edit-config-btn">EDIT CONFIGURATION</button>
<button type="button" id="copy-config-btn">COPY CONFIGURATION</button>
<button type="button" id="fetch-config-btn">REFRESH</button>
</div>
</div>
<div id="config-edit-mode" class="hidden">
<h3>Edit Configuration</h3>
<div id="config-form" class="section">
<!-- Dynamic form will be generated here -->
</div>
<div class="inline-buttons">
<button type="button" id="save-config-btn">SAVE & PUBLISH</button>
<button type="button" id="cancel-edit-btn">CANCEL</button>
</div>
</div>
</div>
</div>
<!-- Auth Rules Management - Moved after configuration -->
<div class="section flex-section" id="authRulesSection" style="display: none;">
<div class="section-header">
<h2>AUTH RULES MANAGEMENT</h2>
</div>
<!-- Auth Rules Table -->
<div id="authRulesTableContainer" style="display: none;">
<table class="config-table" id="authRulesTable">
<thead>
<tr>
<th>Rule Type</th>
<th>Pattern Type</th>
<th>Pattern Value</th>
<th>Action</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="authRulesTableBody">
</tbody>
</table>
</div>
<!-- Simplified Auth Rule Input Section -->
<div id="authRuleInputSections" style="display: block;">
<!-- Combined Pubkey Auth Rule Section -->
<div class="input-group">
<label for="authRulePubkey">Pubkey (nsec or hex):</label>
<input type="text" id="authRulePubkey" placeholder="nsec1... or 64-character hex pubkey">
</div>
<div id="whitelistWarning" class="warning-box" style="display: none;">
<strong>⚠️ WARNING:</strong> Adding whitelist rules changes relay behavior to whitelist-only
mode.
Only whitelisted users will be able to interact with the relay.
</div>
<div class="inline-buttons">
<button type="button" id="addWhitelistBtn" onclick="addWhitelistRule()">ADD TO
WHITELIST</button>
<button type="button" id="addBlacklistBtn" onclick="addBlacklistRule()">ADD TO
BLACKLIST</button>
<button type="button" id="refreshAuthRulesBtn">REFRESH</button>
</div>
</div>
</div>
<!-- DATABASE STATISTICS Section -->
<div class="section">
<div class="section-header">
<h2>DATABASE STATISTICS</h2>
</div>
<!-- Database Overview Table -->
<div class="input-group">
<label>Database Overview:</label>
<div class="config-table-container">
<table class="config-table" id="stats-overview-table">
<thead>
<tr>
<th>Metric</th>
<th>Value</th>
<th>Description</th>
</tr>
</thead>
<tbody id="stats-overview-table-body">
<tr>
<td>Database Size</td>
<td id="db-size">-</td>
<td>Current database file size</td>
</tr>
<tr>
<td>Total Events</td>
<td id="total-events">-</td>
<td>Total number of events stored</td>
</tr>
<tr>
<td>Oldest Event</td>
<td id="oldest-event">-</td>
<td>Timestamp of oldest event</td>
</tr>
<tr>
<td>Newest Event</td>
<td id="newest-event">-</td>
<td>Timestamp of newest event</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Event Kind Distribution Table -->
<div class="input-group">
<label>Event Kind Distribution:</label>
<div class="config-table-container">
<table class="config-table" id="stats-kinds-table">
<thead>
<tr>
<th>Event Kind</th>
<th>Count</th>
<th>Percentage</th>
</tr>
</thead>
<tbody id="stats-kinds-table-body">
<tr>
<td colspan="3" style="text-align: center; font-style: italic;">No data loaded</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Time-based Statistics Table -->
<div class="input-group">
<label>Time-based Statistics:</label>
<div class="config-table-container">
<table class="config-table" id="stats-time-table">
<thead>
<tr>
<th>Period</th>
<th>Events</th>
<th>Description</th>
</tr>
</thead>
<tbody id="stats-time-table-body">
<tr>
<td>Last 24 Hours</td>
<td id="events-24h">-</td>
<td>Events in the last day</td>
</tr>
<tr>
<td>Last 7 Days</td>
<td id="events-7d">-</td>
<td>Events in the last week</td>
</tr>
<tr>
<td>Last 30 Days</td>
<td id="events-30d">-</td>
<td>Events in the last month</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Top Pubkeys Table -->
<div class="input-group">
<label>Top Pubkeys by Event Count:</label>
<div class="config-table-container">
<table class="config-table" id="stats-pubkeys-table">
<thead>
<tr>
<th>Rank</th>
<th>Pubkey</th>
<th>Event Count</th>
<th>Percentage</th>
</tr>
</thead>
<tbody id="stats-pubkeys-table-body">
<tr>
<td colspan="4" style="text-align: center; font-style: italic;">No data loaded</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Refresh Button -->
<div class="input-group">
<button type="button" id="refresh-stats-btn">REFRESH STATISTICS</button>
</div>
</div>
<!-- Load the official nostr-tools bundle first -->
<!-- <script src="https://laantungir.net/nostr-login-lite/nostr.bundle.js"></script> -->
<script src="/api/nostr.bundle.js"></script>
<!-- Load NOSTR_LOGIN_LITE main library -->
<!-- <script src="https://laantungir.net/nostr-login-lite/nostr-lite.js"></script> -->
<script src="/api/nostr-lite.js"></script>
<script src="/api/index.js"></script>
</body>
</html>

3268
api/index.js Normal file

File diff suppressed because it is too large Load Diff

4282
api/nostr-lite.js Normal file

File diff suppressed because it is too large Load Diff

11534
api/nostr.bundle.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -139,11 +139,11 @@ compile_project() {
print_warning "Clean failed or no Makefile found" print_warning "Clean failed or no Makefile found"
fi fi
# Force regenerate version.h to pick up new tags # Force regenerate main.h to pick up new tags
if make force-version > /dev/null 2>&1; then if make force-version > /dev/null 2>&1; then
print_success "Regenerated version.h" print_success "Regenerated main.h"
else else
print_warning "Failed to regenerate version.h" print_warning "Failed to regenerate main.h"
fi fi
# Compile the project # Compile the project

8
c-relay.code-workspace Normal file
View File

@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}

View File

@@ -1 +0,0 @@
Only README.md will remain

3
deploy_local.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
cp build/c_relay_x86 ~/Storage/c_relay/crelay

View File

@@ -0,0 +1,295 @@
# NIP-42 Authentication Implementation
## Overview
This relay implements NIP-42 (Authentication of clients to relays) providing granular authentication controls for event submission and subscription operations. The implementation supports both challenge-response authentication and per-connection state management.
## Architecture
### Core Components
1. **Per-Session Authentication State** (`struct per_session_data`)
- `authenticated`: Boolean flag indicating authentication status
- `authenticated_pubkey[65]`: Hex-encoded public key of authenticated user
- `active_challenge[65]`: Current authentication challenge
- `challenge_created`: Timestamp when challenge was generated
- `challenge_expires`: Challenge expiration timestamp
- `nip42_auth_required_events`: Whether auth is required for EVENT submission
- `nip42_auth_required_subscriptions`: Whether auth is required for REQ operations
- `auth_challenge_sent`: Flag indicating if challenge has been sent
2. **Challenge Management** (via `request_validator.c`)
- `nostr_nip42_generate_challenge()`: Generates cryptographically secure challenges
- `nostr_nip42_verify_auth_event()`: Validates signed authentication events
- Challenge storage and cleanup with expiration handling
3. **WebSocket Protocol Integration**
- AUTH message handling in `nostr_relay_callback()`
- Challenge generation and transmission
- Authentication verification and session state updates
## Configuration Options
### Event-Based Configuration
NIP-42 authentication is configured using kind 33334 configuration events with the following tags:
| Tag | Description | Default | Values |
|-----|-------------|---------|--------|
| `nip42_auth_required_events` | Require auth for EVENT submission | `false` | `true`/`false` |
| `nip42_auth_required_subscriptions` | Require auth for REQ operations | `false` | `true`/`false` |
### Example Configuration Event
```json
{
"kind": 33334,
"content": "C Nostr Relay Configuration",
"tags": [
["d", "<relay_pubkey>"],
["nip42_auth_required_events", "true"],
["nip42_auth_required_subscriptions", "false"],
["relay_description", "Authenticated Nostr Relay"]
],
"created_at": 1640995200,
"pubkey": "<admin_pubkey>",
"id": "<event_id>",
"sig": "<signature>"
}
```
## Authentication Flow
### 1. Challenge Generation
When authentication is required and client is not authenticated:
```
Client -> Relay: ["EVENT", <event>] (unauthenticated)
Relay -> Client: ["AUTH", <challenge>]
```
The challenge is a 64-character hex string generated using cryptographically secure random numbers.
### 2. Authentication Response
Client creates and signs an authentication event (kind 22242):
```json
{
"kind": 22242,
"content": "",
"tags": [
["relay", "ws://relay.example.com"],
["challenge", "<challenge_from_relay>"]
],
"created_at": <current_timestamp>,
"pubkey": "<client_pubkey>",
"id": "<event_id>",
"sig": "<signature>"
}
```
Client sends this event back to relay:
```
Client -> Relay: ["AUTH", <signed_auth_event>]
```
### 3. Verification and Session Update
The relay:
1. Validates the authentication event signature
2. Verifies the challenge matches the one sent
3. Checks challenge expiration (default: 10 minutes)
4. Updates session state with authenticated public key
5. Sends confirmation notice
```
Relay -> Client: ["NOTICE", "NIP-42 authentication successful"]
```
## Granular Authentication Controls
### Separate Controls for Events vs Subscriptions
The implementation provides separate authentication requirements:
- **Event Submission**: Control whether clients must authenticate to publish events
- **Subscription Access**: Control whether clients must authenticate to create subscriptions
This allows flexible relay policies:
- **Public Read, Authenticated Write**: `events=true, subscriptions=false`
- **Fully Authenticated**: `events=true, subscriptions=true`
- **Public Access**: `events=false, subscriptions=false` (default)
- **Authenticated Read Only**: `events=false, subscriptions=true`
### Per-Connection State
Each WebSocket connection maintains its own authentication state:
- Authentication persists for the lifetime of the connection
- Challenges expire after 10 minutes
- Session cleanup on connection close
## Security Features
### Challenge Security
- 64-character hexadecimal challenges (256 bits of entropy)
- Cryptographically secure random generation
- Challenge expiration to prevent replay attacks
- One-time use challenges
### Event Validation
- Complete signature verification using secp256k1
- Event ID validation
- Challenge-response binding verification
- Timestamp validation with configurable tolerance
### Session Management
- Thread-safe per-session state management
- Automatic cleanup on disconnection
- Challenge expiration handling
## Client Integration
### Using nak Client
```bash
# Generate keypair
PRIVKEY=$(nak key --gen)
PUBKEY=$(nak key --pub $PRIVKEY)
# Connect and authenticate automatically
nak event -k 1 --content "Authenticated message" --sec $PRIVKEY --relay ws://localhost:8888
# nak handles NIP-42 authentication automatically when required
```
### Manual WebSocket Integration
```javascript
const ws = new WebSocket('ws://localhost:8888');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message[0] === 'AUTH') {
const challenge = message[1];
// Create auth event (kind 22242)
const authEvent = {
kind: 22242,
content: "",
tags: [
["relay", "ws://localhost:8888"],
["challenge", challenge]
],
created_at: Math.floor(Date.now() / 1000),
pubkey: clientPubkey,
// ... calculate id and signature
};
// Send auth response
ws.send(JSON.stringify(["AUTH", authEvent]));
}
};
// Send event (may trigger AUTH challenge)
ws.send(JSON.stringify(["EVENT", myEvent]));
```
## Administration
### Enabling Authentication
1. **Get Admin Private Key**: Extract from relay startup logs (shown once)
2. **Create Configuration Event**: Use nak or custom tooling
3. **Publish Configuration**: Send to relay with admin signature
```bash
# Enable auth for events only
nak event -k 33334 \
--content "C Nostr Relay Configuration" \
--tag "d=$RELAY_PUBKEY" \
--tag "nip42_auth_required_events=true" \
--tag "nip42_auth_required_subscriptions=false" \
--sec $ADMIN_PRIVKEY \
--relay ws://localhost:8888
```
### Monitoring Authentication
- Check relay logs for authentication events
- Monitor `NOTICE` messages for auth status
- Use `get_settings.sh` script to view current configuration
```bash
./get_settings.sh
```
## Troubleshooting
### Common Issues
1. **Challenge Expiration**
- Default: 10 minutes
- Client must respond within expiration window
- Generate new challenge for expired attempts
2. **Signature Verification Failures**
- Verify event structure matches NIP-42 specification
- Check challenge value matches exactly
- Ensure proper secp256k1 signature generation
3. **Configuration Not Applied**
- Verify admin private key is correct
- Check configuration event signature
- Ensure relay pubkey in 'd' tag matches relay
### Debug Commands
```bash
# Check supported NIPs
curl -H "Accept: application/nostr+json" http://localhost:8888 | jq .supported_nips
# View current configuration
nak req -k 33334 ws://localhost:8888 | jq .
# Test authentication flow
./tests/42_nip_test.sh
```
## Performance Considerations
- Challenge generation: ~1ms overhead per unauthenticated connection
- Authentication verification: ~2-5ms per auth event
- Memory overhead: ~200 bytes per connection for auth state
- Database impact: Configuration events cached, minimal query overhead
## Integration with Other NIPs
### NIP-01 (Basic Protocol)
- AUTH messages integrated into standard WebSocket flow
- Compatible with existing EVENT/REQ/CLOSE message handling
### NIP-11 (Relay Information)
- NIP-42 advertised in `supported_nips` array
- Authentication requirements reflected in relay metadata
### NIP-20 (Command Results)
- OK responses include authentication-related error messages
- NOTICE messages provide authentication status updates
## Future Extensions
### Potential Enhancements
- Role-based authentication (admin, user, read-only)
- Time-based access controls
- Rate limiting based on authentication status
- Integration with external authentication providers
### Configuration Extensions
- Per-kind authentication requirements
- Whitelist/blacklist integration
- Custom challenge expiration times
- Authentication logging and metrics

460
docs/admin_api_plan.md Normal file
View File

@@ -0,0 +1,460 @@
# C-Relay Administrator API Implementation Plan
## Problem Analysis
### Current Issues Identified:
1. **Schema Mismatch**: Storage system (config.c) vs Validation system (request_validator.c) use different column names and values
2. **Missing API Endpoint**: No way to clear auth_rules table for testing
3. **Configuration Gap**: Auth rules enforcement may not be properly enabled
4. **Documentation Gap**: Admin API commands not documented
### Root Cause: Auth Rules Schema Inconsistency
**Current Schema (sql_schema.h lines 140-150):**
```sql
CREATE TABLE auth_rules (
rule_type TEXT CHECK (rule_type IN ('whitelist', 'blacklist')),
pattern_type TEXT CHECK (pattern_type IN ('pubkey', 'hash')),
pattern_value TEXT,
action TEXT CHECK (action IN ('allow', 'deny')),
active INTEGER DEFAULT 1
);
```
**Storage Implementation (config.c):**
- Stores: `rule_type='blacklist'`, `pattern_type='pubkey'`, `pattern_value='hex'`, `action='allow'`
**Validation Implementation (request_validator.c):**
- Queries: `rule_type='pubkey_blacklist'`, `rule_target='hex'`, `operation='event'`, `enabled=1`
**MISMATCH**: Validator looks for non-existent columns and wrong rule_type values!
## Proposed Solution Architecture
### Phase 1: API Documentation & Standardization
#### Admin API Commands (via WebSocket with admin private key)
**Kind 23456: Unified Admin API (Ephemeral)**
- Configuration management: Update relay settings, limits, authentication policies
- Auth rules: Add/remove/query whitelist/blacklist rules
- System commands: clear rules, status, cache management
- **Unified Format**: All commands use NIP-44 encrypted content with `["p", "relay_pubkey"]` tags
- **Command Types**:
- Configuration: `["config_key", "config_value"]`
- Auth rules: `["rule_type", "pattern_type", "pattern_value"]`
- Queries: `["auth_query", "filter"]` or `["system_command", "command_name"]`
- **Security**: All admin commands use NIP-44 encryption for privacy and security
#### Configuration Commands (using Kind 23456)
1. **Update Configuration**:
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [["p", "relay_pubkey"]]
}
```
*Encrypted content contains:* `["relay_description", "My Relay"]`
2. **Query System Status**:
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [["p", "relay_pubkey"]]
}
```
*Encrypted content contains:* `["system_command", "system_status"]`
#### Auth Rules and System Commands (using Kind 23456)
1. **Clear All Auth Rules**:
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [["p", "relay_pubkey"]]
}
```
*Encrypted content contains:* `["system_command", "clear_all_auth_rules"]`
2. **Query All Auth Rules**:
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [["p", "relay_pubkey"]]
}
```
*Encrypted content contains:* `["auth_query", "all"]`
3. **Add Blacklist Rule**:
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [["p", "relay_pubkey"]]
}
```
*Encrypted content contains:* `["blacklist", "pubkey", "deadbeef1234abcd..."]`
### Phase 2: Auth Rules Schema Alignment
#### Option A: Fix Validator to Match Schema (RECOMMENDED)
**Update request_validator.c:**
```sql
-- OLD (broken):
WHERE rule_type = 'pubkey_blacklist' AND rule_target = ? AND operation = ? AND enabled = 1
-- NEW (correct):
WHERE rule_type = 'blacklist' AND pattern_type = 'pubkey' AND pattern_value = ? AND active = 1
```
**Benefits:**
- Matches actual database schema
- Simpler rule_type values ('blacklist' vs 'pubkey_blacklist')
- Uses existing columns (pattern_value vs rule_target)
- Consistent with storage implementation
#### Option B: Update Schema to Match Validator (NOT RECOMMENDED)
Would require changing schema, migration scripts, and storage logic.
### Phase 3: Implementation Priority
#### High Priority (Critical for blacklist functionality):
1. Fix request_validator.c schema mismatch
2. Ensure auth_required configuration is enabled
3. Update tests to use unified ephemeral event kind (23456)
4. Test blacklist enforcement
#### Medium Priority (Enhanced Admin Features):
1. **Implement NIP-44 Encryption Support**:
- Detect NIP-44 encrypted content for Kind 23456 events
- Parse `encrypted_tags` field from content JSON
- Decrypt using admin privkey and relay pubkey
- Process decrypted tags as normal commands
2. Add clear_all_auth_rules system command
3. Add auth rule query functionality (both standard and encrypted modes)
4. Add configuration discovery (list available config keys)
5. Enhanced error reporting in admin API
6. Conflict resolution (same pubkey in whitelist + blacklist)
#### Security Priority (NIP-44 Implementation):
1. **Encryption Detection Logic**: Check for empty tags + encrypted_tags field
2. **Key Pair Management**: Use admin private key + relay public key for NIP-44
3. **Backward Compatibility**: Support both standard and encrypted modes
4. **Error Handling**: Graceful fallback if decryption fails
5. **Performance**: Cache decrypted results to avoid repeated decryption
#### Low Priority (Documentation & Polish):
1. Complete README.md API documentation
2. Example usage scripts
3. Admin client tools
### Phase 4: Expected API Structure
#### README.md Documentation Format:
```markdown
# C-Relay Administrator API
## Authentication
All admin commands require signing with the admin private key generated during first startup.
## Unified Admin API (Kind 23456 - Ephemeral)
Update relay configuration parameters or query available settings.
**Configuration Update Event:**
```json
{
"kind": 23456,
"content": "base64_nip44_encrypted_command_array",
"tags": [["p", "relay_pubkey"]]
}
```
*Encrypted content contains:* `["relay_description", "My Relay Description"]`
**Auth Rules Management:**
**Add Rule Event:**
```json
{
"kind": 23456,
"content": "{\"action\":\"add\",\"description\":\"Block malicious user\"}",
"tags": [
["blacklist", "pubkey", "deadbeef1234..."]
]
}
```
**Remove Rule Event:**
```json
{
"kind": 23456,
"content": "{\"action\":\"remove\",\"description\":\"Unblock user\"}",
"tags": [
["blacklist", "pubkey", "deadbeef1234..."]
]
}
```
**Query All Auth Rules:**
```json
{
"kind": 23456,
"content": "{\"query\":\"list_auth_rules\",\"description\":\"Get all rules\"}",
"tags": [
["auth_query", "all"]
]
}
```
**Query Whitelist Rules Only:**
```json
{
"kind": 23456,
"content": "{\"query\":\"list_auth_rules\",\"description\":\"Get whitelist\"}",
"tags": [
["auth_query", "whitelist"]
]
}
```
**Check Specific Pattern:**
```json
{
"kind": 23456,
"content": "{\"query\":\"check_pattern\",\"description\":\"Check if pattern exists\"}",
"tags": [
["auth_query", "pattern", "deadbeef1234..."]
]
}
```
## System Management (Kind 23456 - Ephemeral)
System administration commands using the same kind as auth rules.
**Clear All Auth Rules:**
```json
{
"kind": 23456,
"content": "{\"action\":\"clear_all\",\"description\":\"Clear all auth rules\"}",
"tags": [
["system_command", "clear_all_auth_rules"]
]
}
```
**System Status:**
```json
{
"kind": 23456,
"content": "{\"action\":\"system_status\",\"description\":\"Get system status\"}",
"tags": [
["system_command", "system_status"]
]
}
```
## Response Format
All admin commands return JSON responses via WebSocket:
**Success Response:**
```json
["OK", "event_id", true, "success_message"]
```
**Error Response:**
```json
["OK", "event_id", false, "error_message"]
```
## Configuration Keys
- `relay_description`: Relay description text
- `relay_contact`: Contact information
- `auth_enabled`: Enable authentication system
- `max_connections`: Maximum concurrent connections
- `pow_min_difficulty`: Minimum proof-of-work difficulty
- ... (full list of config keys)
## Examples
### Enable Authentication & Add Blacklist
```bash
# 1. Enable auth system
nak event -k 23456 --content "base64_nip44_encrypted_command" \
-t "auth_enabled=true" \
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
# 2. Add user to blacklist
nak event -k 23456 --content '{"action":"add","description":"Spam user"}' \
-t "blacklist=pubkey;$SPAM_USER_PUBKEY" \
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
# 3. Query all auth rules
nak event -k 23456 --content '{"query":"list_auth_rules","description":"Get all rules"}' \
-t "auth_query=all" \
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
# 4. Clear all rules for testing
nak event -k 23456 --content '{"action":"clear_all","description":"Clear all rules"}' \
-t "system_command=clear_all_auth_rules" \
--sec $ADMIN_PRIVKEY | nak event ws://localhost:8888
```
## Expected Response Formats
### Configuration Query Response
```json
["EVENT", "subscription_id", {
"kind": 23457,
"content": "base64_nip44_encrypted_response",
"tags": [["p", "admin_pubkey"]]
}]
```
### Current Config Response
```json
["EVENT", "subscription_id", {
"kind": 23457,
"content": "base64_nip44_encrypted_response",
"tags": [["p", "admin_pubkey"]]
}]
```
### Auth Rules Query Response
```json
["EVENT", "subscription_id", {
"kind": 23456,
"content": "{\"auth_rules\": [{\"rule_type\": \"blacklist\", \"pattern_type\": \"pubkey\", \"pattern_value\": \"deadbeef...\"}, {\"rule_type\": \"whitelist\", \"pattern_type\": \"pubkey\", \"pattern_value\": \"cafebabe...\"}]}",
"tags": [["response_type", "auth_rules_list"], ["query_type", "all"]]
}]
```
### Pattern Check Response
```json
["EVENT", "subscription_id", {
"kind": 23456,
"content": "{\"pattern_exists\": true, \"rule_type\": \"blacklist\", \"pattern_value\": \"deadbeef...\"}",
"tags": [["response_type", "pattern_check"], ["pattern", "deadbeef..."]]
}]
```
## Implementation Steps
1. **Document API** (this file) ✅
2. **Update to ephemeral event kinds** ✅
3. **Fix request_validator.c** schema mismatch
4. **Update tests** to use unified Kind 23456
5. **Add auth rule query functionality**
6. **Add configuration discovery feature**
7. **Test blacklist functionality**
8. **Add remaining system commands**
## Testing Plan
1. Fix schema mismatch and test basic blacklist
2. Add clear_auth_rules and test table cleanup
3. Test whitelist/blacklist conflict scenarios
4. Test all admin API commands end-to-end
5. Update integration tests
This plan addresses the immediate blacklist issue while establishing a comprehensive admin API framework for future expansion.
## NIP-44 Encryption Implementation Details
### Server-Side Detection Logic
```c
// In admin event processing function
bool is_encrypted_command(struct nostr_event *event) {
// Check if Kind 23456 with NIP-44 encrypted content
if (event->kind == 23456 &&
event->tags_count == 0) {
return true;
}
return false;
}
cJSON *decrypt_admin_tags(struct nostr_event *event) {
cJSON *content_json = cJSON_Parse(event->content);
if (!content_json) return NULL;
cJSON *encrypted_tags = cJSON_GetObjectItem(content_json, "encrypted_tags");
if (!encrypted_tags) {
cJSON_Delete(content_json);
return NULL;
}
// Decrypt using NIP-44 with admin pubkey and relay privkey
char *decrypted = nip44_decrypt(
cJSON_GetStringValue(encrypted_tags),
admin_pubkey, // Shared secret with admin
relay_private_key // Our private key
);
cJSON *decrypted_tags = cJSON_Parse(decrypted);
free(decrypted);
cJSON_Delete(content_json);
return decrypted_tags; // Returns tag array: [["key1", "val1"], ["key2", "val2"]]
}
```
### Admin Event Processing Flow
1. **Receive Event**: Kind 23456 with admin signature
2. **Check Mode**: Empty tags = encrypted, populated tags = standard
3. **Decrypt if Needed**: Extract and decrypt `encrypted_tags` from content
4. **Process Commands**: Use decrypted/standard tags for command processing
5. **Execute**: Same logic for both modes after tag extraction
6. **Respond**: Standard response format (optionally encrypt response)
### Security Benefits
- **Command Privacy**: Admin operations invisible in event tags
- **Replay Protection**: NIP-44 includes timestamp/randomness
- **Key Management**: Uses existing admin/relay key pair
- **Backward Compatible**: Standard mode still works
- **Performance**: Only decrypt when needed (empty tags detection)
### NIP-44 Library Integration
The relay will need to integrate a NIP-44 encryption/decryption library:
```c
// Required NIP-44 functions
char* nip44_encrypt(const char* plaintext, const char* sender_privkey, const char* recipient_pubkey);
char* nip44_decrypt(const char* ciphertext, const char* recipient_privkey, const char* sender_pubkey);
```
### Implementation Priority (Updated)
#### Phase 1: Core Infrastructure (Complete)
- [x] Event-based admin authentication system
- [x] Kind 23456 (Unified Admin API) processing
- [x] Basic configuration parameter updates
- [x] Auth rule add/remove/clear functionality
- [x] Updated to ephemeral event kinds
- [x] Designed NIP-44 encryption support
#### Phase 2: NIP-44 Encryption Support (Next Priority)
- [ ] **Add NIP-44 library dependency** to project
- [ ] **Implement encryption detection logic** (`is_encrypted_command()`)
- [ ] **Add decrypt_admin_tags() function** with NIP-44 support
- [ ] **Update admin command processing** to handle both modes
- [ ] **Test encrypted admin commands** end-to-end
#### Phase 3: Enhanced Features
- [ ] **Auth rule query functionality** (both standard and encrypted modes)
- [ ] **Configuration discovery API** (list available config keys)
- [ ] **Enhanced error messages** with encryption status
- [ ] **Performance optimization** (caching, async decrypt)
#### Phase 4: Schema Fixes (Critical)
- [ ] **Fix request_validator.c** schema mismatch
- [ ] **Enable blacklist enforcement** with encrypted commands
- [ ] **Update tests** to use both standard and encrypted modes
This enhanced admin API provides enterprise-grade security while maintaining ease of use for basic operations.

View File

@@ -6,6 +6,7 @@ Complete guide for deploying, configuring, and managing the C Nostr Relay with e
- [Quick Start](#quick-start) - [Quick Start](#quick-start)
- [Installation](#installation) - [Installation](#installation)
- [Web Admin Interface](#web-admin-interface)
- [Configuration Management](#configuration-management) - [Configuration Management](#configuration-management)
- [Administration](#administration) - [Administration](#administration)
- [Monitoring](#monitoring) - [Monitoring](#monitoring)
@@ -43,7 +44,8 @@ Admin Public Key: 68394d08ab87f936a42ff2deb15a84fbdfbe0996ee0eb20cda064aae67328
### 3. Connect Clients ### 3. Connect Clients
Your relay is now available at: Your relay is now available at:
- **WebSocket**: `ws://localhost:8888` - **WebSocket**: `ws://localhost:8888`
- **NIP-11 Info**: `http://localhost:8888` - **NIP-11 Info**: `http://localhost:8888` (with `Accept: application/nostr+json` header)
- **Web Admin Interface**: `http://localhost:8888/api/` (serves embedded admin interface)
## Installation ## Installation
@@ -211,6 +213,38 @@ Send this to your relay via WebSocket, and changes are applied immediately.
| `nip40_expiration_filter` | Filter expired events | "true" | "true", "false" | | `nip40_expiration_filter` | Filter expired events | "true" | "true", "false" |
| `nip40_expiration_grace_period` | Grace period (seconds) | "300" | 0-86400 | | `nip40_expiration_grace_period` | Grace period (seconds) | "300" | 0-86400 |
## Web Admin Interface
The relay includes a built-in web-based administration interface that provides a user-friendly way to manage your relay without command-line tools.
### Accessing the Interface
1. **Open your browser** and navigate to: `http://localhost:8888/api/`
2. **Authenticate** using your Nostr identity (the admin interface uses NIP-42 authentication)
3. **Manage configuration** through the web interface
### Features
- **Real-time Configuration**: View and edit all relay settings
- **Database Statistics**: Monitor event counts, storage usage, and performance metrics
- **Auth Rules Management**: Configure whitelist/blacklist rules for pubkeys
- **Relay Connection Testing**: Verify WebSocket connectivity and NIP-11 information
- **Event-Based Updates**: All changes are applied as signed Nostr events
### Security Notes
- The web interface requires NIP-42 authentication with your admin pubkey
- All configuration changes are cryptographically signed
- The interface serves embedded static files (no external dependencies)
- CORS headers are included for proper browser operation
### Browser Compatibility
The admin interface works with modern browsers that support:
- WebSocket connections
- ES6 JavaScript features
- Modern CSS Grid and Flexbox layouts
## Administration ## Administration
### Viewing Current Configuration ### Viewing Current Configuration

128
embed_web_files.sh Executable file
View File

@@ -0,0 +1,128 @@
#!/bin/bash
# Script to embed web files into C headers for the C-Relay admin interface
# Converts HTML, CSS, and JS files from api/ directory into C byte arrays
set -e
echo "Embedding web files into C headers..."
# Output directory for generated headers
OUTPUT_DIR="src"
mkdir -p "$OUTPUT_DIR"
# Function to convert a file to C byte array
file_to_c_array() {
local input_file="$1"
local array_name="$2"
local output_file="$3"
# Get file size
local file_size=$(stat -c%s "$input_file" 2>/dev/null || stat -f%z "$input_file" 2>/dev/null || echo "0")
echo "// Auto-generated from $input_file" >> "$output_file"
echo "static const unsigned char ${array_name}_data[] = {" >> "$output_file"
# Convert file to hex bytes
hexdump -v -e '1/1 "0x%02x,"' "$input_file" >> "$output_file"
echo "};" >> "$output_file"
echo "static const size_t ${array_name}_size = $file_size;" >> "$output_file"
echo "" >> "$output_file"
}
# Generate the header file
HEADER_FILE="$OUTPUT_DIR/embedded_web_content.h"
echo "// Auto-generated embedded web content header" > "$HEADER_FILE"
echo "// Do not edit manually - generated by embed_web_files.sh" >> "$HEADER_FILE"
echo "" >> "$HEADER_FILE"
echo "#ifndef EMBEDDED_WEB_CONTENT_H" >> "$HEADER_FILE"
echo "#define EMBEDDED_WEB_CONTENT_H" >> "$HEADER_FILE"
echo "" >> "$HEADER_FILE"
echo "#include <stddef.h>" >> "$HEADER_FILE"
echo "" >> "$HEADER_FILE"
# Generate the C file
SOURCE_FILE="$OUTPUT_DIR/embedded_web_content.c"
echo "// Auto-generated embedded web content" > "$SOURCE_FILE"
echo "// Do not edit manually - generated by embed_web_files.sh" >> "$SOURCE_FILE"
echo "" >> "$SOURCE_FILE"
echo "#include \"embedded_web_content.h\"" >> "$SOURCE_FILE"
echo "#include <string.h>" >> "$SOURCE_FILE"
echo "" >> "$SOURCE_FILE"
# Process each web file
declare -A file_map
# Find all web files
for file in api/*.html api/*.css api/*.js; do
if [ -f "$file" ]; then
# Get filename without path
basename=$(basename "$file")
# Create C identifier from filename
c_name=$(echo "$basename" | sed 's/[^a-zA-Z0-9_]/_/g' | sed 's/^_//')
# Determine content type
case "$file" in
*.html) content_type="text/html" ;;
*.css) content_type="text/css" ;;
*.js) content_type="application/javascript" ;;
*) content_type="text/plain" ;;
esac
echo "Processing $file -> ${c_name}"
# No extern declarations needed - data is accessed through get_embedded_file()
# Add to source
file_to_c_array "$file" "$c_name" "$SOURCE_FILE"
# Store mapping for lookup function
file_map["/$basename"]="$c_name:$content_type"
if [ "$basename" = "index.html" ]; then
file_map["/"]="$c_name:$content_type"
fi
fi
done
# Generate lookup function
echo "// Embedded file lookup function" >> "$HEADER_FILE"
echo "typedef struct {" >> "$HEADER_FILE"
echo " const char *path;" >> "$HEADER_FILE"
echo " const unsigned char *data;" >> "$HEADER_FILE"
echo " size_t size;" >> "$HEADER_FILE"
echo " const char *content_type;" >> "$HEADER_FILE"
echo "} embedded_file_t;" >> "$HEADER_FILE"
echo "" >> "$HEADER_FILE"
echo "embedded_file_t *get_embedded_file(const char *path);" >> "$HEADER_FILE"
echo "" >> "$HEADER_FILE"
echo "#endif // EMBEDDED_WEB_CONTENT_H" >> "$HEADER_FILE"
# Generate lookup function implementation
echo "// File mapping" >> "$SOURCE_FILE"
echo "static embedded_file_t embedded_files[] = {" >> "$SOURCE_FILE"
for path in "${!file_map[@]}"; do
entry="${file_map[$path]}"
c_name="${entry%:*}"
content_type="${entry#*:}"
echo " {\"$path\", ${c_name}_data, ${c_name}_size, \"$content_type\"}," >> "$SOURCE_FILE"
done
echo " {NULL, NULL, 0, NULL} // Sentinel" >> "$SOURCE_FILE"
echo "};" >> "$SOURCE_FILE"
echo "" >> "$SOURCE_FILE"
echo "embedded_file_t *get_embedded_file(const char *path) {" >> "$SOURCE_FILE"
echo " for (int i = 0; embedded_files[i].path != NULL; i++) {" >> "$SOURCE_FILE"
echo " if (strcmp(path, embedded_files[i].path) == 0) {" >> "$SOURCE_FILE"
echo " return &embedded_files[i];" >> "$SOURCE_FILE"
echo " }" >> "$SOURCE_FILE"
echo " }" >> "$SOURCE_FILE"
echo " return NULL;" >> "$SOURCE_FILE"
echo "}" >> "$SOURCE_FILE"
echo "Web file embedding complete. Generated:" >&2
echo " $HEADER_FILE" >&2
echo " $SOURCE_FILE" >&2

19
get_settings.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# get_settings.sh - Query relay configuration events using nak
# Uses admin test key to query kind 33334 configuration events
# Test key configuration
ADMIN_PRIVATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
ADMIN_PUBLIC_KEY="6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3"
RELAY_PUBLIC_KEY="4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"
RELAY_URL="ws://localhost:8888"
echo "Querying configuration events (kind 33334) from relay at $RELAY_URL"
echo "Using admin public key: $ADMIN_PUBLIC_KEY"
echo "Looking for relay config: $RELAY_PUBLIC_KEY"
echo ""
# Query for kind 33334 configuration events
# These events contain the relay configuration with d-tag matching the relay pubkey
nak req -k 33334 "$RELAY_URL" | jq .

View File

@@ -8,13 +8,69 @@ echo "=== C Nostr Relay Build and Restart Script ==="
# Parse command line arguments # Parse command line arguments
PRESERVE_DATABASE=false PRESERVE_DATABASE=false
HELP=false HELP=false
USE_TEST_KEYS=false
ADMIN_KEY=""
RELAY_KEY=""
PORT_OVERRIDE=""
# Key validation function
validate_hex_key() {
local key="$1"
local key_type="$2"
if [ ${#key} -ne 64 ]; then
echo "ERROR: $key_type key must be exactly 64 characters"
return 1
fi
if ! [[ "$key" =~ ^[0-9a-fA-F]{64}$ ]]; then
echo "ERROR: $key_type key must contain only hex characters (0-9, a-f, A-F)"
return 1
fi
return 0
}
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
--preserve-database|-p) -a|--admin-key)
if [ -z "$2" ]; then
echo "ERROR: Admin key option requires a value"
HELP=true
shift
else
ADMIN_KEY="$2"
shift 2
fi
;;
-r|--relay-key)
if [ -z "$2" ]; then
echo "ERROR: Relay key option requires a value"
HELP=true
shift
else
RELAY_KEY="$2"
shift 2
fi
;;
-p|--port)
if [ -z "$2" ]; then
echo "ERROR: Port option requires a value"
HELP=true
shift
else
PORT_OVERRIDE="$2"
shift 2
fi
;;
-d|--preserve-database)
PRESERVE_DATABASE=true PRESERVE_DATABASE=true
shift shift
;; ;;
--test-keys|-t)
USE_TEST_KEYS=true
shift
;;
--help|-h) --help|-h)
HELP=true HELP=true
shift shift
@@ -27,13 +83,38 @@ while [[ $# -gt 0 ]]; do
esac esac
done done
# Validate custom keys if provided
if [ -n "$ADMIN_KEY" ]; then
if ! validate_hex_key "$ADMIN_KEY" "Admin"; then
exit 1
fi
fi
if [ -n "$RELAY_KEY" ]; then
if ! validate_hex_key "$RELAY_KEY" "Relay"; then
exit 1
fi
fi
# Validate port if provided
if [ -n "$PORT_OVERRIDE" ]; then
if ! [[ "$PORT_OVERRIDE" =~ ^[0-9]+$ ]] || [ "$PORT_OVERRIDE" -lt 1 ] || [ "$PORT_OVERRIDE" -gt 65535 ]; then
echo "ERROR: Port must be a number between 1 and 65535"
exit 1
fi
fi
# Show help # Show help
if [ "$HELP" = true ]; then if [ "$HELP" = true ]; then
echo "Usage: $0 [OPTIONS]" echo "Usage: $0 [OPTIONS]"
echo "" echo ""
echo "Options:" echo "Options:"
echo " --preserve-database, -p Keep existing database files (don't delete for fresh start)" echo " -a, --admin-key <hex> 64-character hex admin private key"
echo " --help, -h Show this help message" echo " -r, --relay-key <hex> 64-character hex relay private key"
echo " -p, --port <port> Custom port override (default: 8888)"
echo " --preserve-database Keep existing database files (don't delete for fresh start)"
echo " --test-keys, -t Use deterministic test keys for development (admin: all 'a's, relay: all '1's)"
echo " --help, -h Show this help message"
echo "" echo ""
echo "Event-Based Configuration:" echo "Event-Based Configuration:"
echo " This relay now uses event-based configuration stored directly in the database." echo " This relay now uses event-based configuration stored directly in the database."
@@ -41,9 +122,14 @@ if [ "$HELP" = true ]; then
echo " Database file: <relay_pubkey>.db (created automatically)" echo " Database file: <relay_pubkey>.db (created automatically)"
echo "" echo ""
echo "Examples:" echo "Examples:"
echo " $0 # Fresh start with new keys (default)" echo " $0 # Fresh start with random keys"
echo " $0 -p # Preserve existing database and keys" echo " $0 -a <admin-hex> -r <relay-hex> # Use custom keys"
echo " $0 -a <admin-hex> -p 9000 # Custom admin key on port 9000"
echo " $0 --preserve-database # Preserve existing database and keys"
echo " $0 --test-keys # Use test keys for consistent development"
echo " $0 -t --preserve-database # Use test keys and preserve database"
echo "" echo ""
echo "Key Format: Keys must be exactly 64 hexadecimal characters (0-9, a-f, A-F)"
echo "Default behavior: Deletes existing database files to start fresh with new keys" echo "Default behavior: Deletes existing database files to start fresh with new keys"
echo " for development purposes" echo " for development purposes"
exit 0 exit 0
@@ -112,25 +198,54 @@ fi
echo "Build successful. Proceeding with relay restart..." echo "Build successful. Proceeding with relay restart..."
# Kill existing relay if running # Kill existing relay if running - start aggressive immediately
echo "Stopping any existing relay servers..." echo "Stopping any existing relay servers..."
pkill -f "c_relay_" 2>/dev/null
sleep 2 # Give time for shutdown
# Check if port is still bound # Get all relay processes and kill them immediately with -9
if lsof -i :8888 >/dev/null 2>&1; then RELAY_PIDS=$(pgrep -f "c_relay_" || echo "")
echo "Port 8888 still in use, force killing..." if [ -n "$RELAY_PIDS" ]; then
fuser -k 8888/tcp 2>/dev/null || echo "No process on port 8888" echo "Force killing relay processes immediately: $RELAY_PIDS"
kill -9 $RELAY_PIDS 2>/dev/null
else
echo "No existing relay processes found"
fi fi
# Get any remaining processes # Ensure port 8888 is completely free with retry loop
REMAINING_PIDS=$(pgrep -f "c_relay_" || echo "") echo "Ensuring port 8888 is available..."
if [ -n "$REMAINING_PIDS" ]; then for attempt in {1..15}; do
echo "Force killing remaining processes: $REMAINING_PIDS" if ! lsof -i :8888 >/dev/null 2>&1; then
kill -9 $REMAINING_PIDS 2>/dev/null echo "Port 8888 is now free"
break
fi
echo "Attempt $attempt: Port 8888 still in use, force killing..."
# Kill anything using port 8888
fuser -k 8888/tcp 2>/dev/null || true
# Double-check for any remaining relay processes
REMAINING_PIDS=$(pgrep -f "c_relay_" || echo "")
if [ -n "$REMAINING_PIDS" ]; then
echo "Killing remaining relay processes: $REMAINING_PIDS"
kill -9 $REMAINING_PIDS 2>/dev/null || true
fi
sleep 2
if [ $attempt -eq 15 ]; then
echo "ERROR: Could not free port 8888 after 15 attempts"
echo "Current processes using port:"
lsof -i :8888 2>/dev/null || echo "No process details available"
echo "You may need to manually kill processes or reboot"
exit 1
fi
done
# Final safety check - ensure no relay processes remain
FINAL_PIDS=$(pgrep -f "c_relay_" || echo "")
if [ -n "$FINAL_PIDS" ]; then
echo "Final cleanup: killing processes $FINAL_PIDS"
kill -9 $FINAL_PIDS 2>/dev/null || true
sleep 1 sleep 1
else
echo "No existing relay found"
fi fi
# Clean up PID file # Clean up PID file
@@ -144,10 +259,38 @@ echo "Database will be initialized automatically on startup if needed"
echo "Starting relay server..." echo "Starting relay server..."
echo "Debug: Current processes: $(ps aux | grep 'c_relay_' | grep -v grep || echo 'None')" echo "Debug: Current processes: $(ps aux | grep 'c_relay_' | grep -v grep || echo 'None')"
# Build command line arguments for relay binary
RELAY_ARGS=""
if [ -n "$ADMIN_KEY" ]; then
RELAY_ARGS="$RELAY_ARGS -a $ADMIN_KEY"
echo "Using custom admin key: ${ADMIN_KEY:0:16}..."
fi
if [ -n "$RELAY_KEY" ]; then
RELAY_ARGS="$RELAY_ARGS -r $RELAY_KEY"
echo "Using custom relay key: ${RELAY_KEY:0:16}..."
fi
if [ -n "$PORT_OVERRIDE" ]; then
RELAY_ARGS="$RELAY_ARGS -p $PORT_OVERRIDE"
echo "Using custom port: $PORT_OVERRIDE"
fi
# Change to build directory before starting relay so database files are created there # Change to build directory before starting relay so database files are created there
cd build cd build
# Start relay in background and capture its PID (no command line arguments needed) # Start relay in background and capture its PID
./$(basename $BINARY_PATH) > ../relay.log 2>&1 & if [ "$USE_TEST_KEYS" = true ]; then
echo "Using deterministic test keys for development..."
./$(basename $BINARY_PATH) -a 6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3 -r 1111111111111111111111111111111111111111111111111111111111111111 --strict-port > ../relay.log 2>&1 &
elif [ -n "$RELAY_ARGS" ]; then
echo "Starting relay with custom configuration..."
./$(basename $BINARY_PATH) $RELAY_ARGS --strict-port > ../relay.log 2>&1 &
else
# No command line arguments needed for random key generation
echo "Starting relay with random key generation..."
./$(basename $BINARY_PATH) --strict-port > ../relay.log 2>&1 &
fi
RELAY_PID=$! RELAY_PID=$!
# Change back to original directory # Change back to original directory
cd .. cd ..
@@ -161,7 +304,34 @@ sleep 3
if ps -p "$RELAY_PID" >/dev/null 2>&1; then if ps -p "$RELAY_PID" >/dev/null 2>&1; then
echo "Relay started successfully!" echo "Relay started successfully!"
echo "PID: $RELAY_PID" echo "PID: $RELAY_PID"
echo "WebSocket endpoint: ws://127.0.0.1:8888"
# Wait for relay to fully initialize and detect the actual port it's using
sleep 2
# Extract actual port from relay logs
ACTUAL_PORT=""
if [ -f relay.log ]; then
# Look for the success message with actual port
ACTUAL_PORT=$(grep "WebSocket relay started on ws://127.0.0.1:" relay.log 2>/dev/null | tail -1 | sed -n 's/.*ws:\/\/127\.0\.0\.1:\([0-9]*\).*/\1/p')
# If we couldn't find the port in logs, try to detect from netstat
if [ -z "$ACTUAL_PORT" ]; then
ACTUAL_PORT=$(netstat -tln 2>/dev/null | grep -E ":888[0-9]" | head -1 | sed -n 's/.*:\([0-9]*\).*/\1/p')
fi
fi
# Display the actual endpoint
if [ -n "$ACTUAL_PORT" ]; then
if [ "$ACTUAL_PORT" = "8888" ]; then
echo "WebSocket endpoint: ws://127.0.0.1:$ACTUAL_PORT"
else
echo "WebSocket endpoint: ws://127.0.0.1:$ACTUAL_PORT (fell back from port 8888)"
fi
else
echo "WebSocket endpoint: ws://127.0.0.1:8888 (port detection failed - check logs)"
fi
echo "HTTP endpoint: http://127.0.0.1:${ACTUAL_PORT:-8888}"
echo "Log file: relay.log" echo "Log file: relay.log"
echo "" echo ""

3
nip_11_curl.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
curl -H "Accept: application/nostr+json" http://localhost:8888/

153
node_modules/.package-lock.json generated vendored Normal file
View File

@@ -0,0 +1,153 @@
{
"name": "c-relay",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@noble/ciphers": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"license": "MIT"
},
"node_modules/@scure/bip32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
"license": "MIT",
"dependencies": {
"@noble/curves": "~1.1.0",
"@noble/hashes": "~1.3.1",
"@scure/base": "~1.1.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip32/node_modules/@noble/curves": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.3.1"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@scure/bip39": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "~1.3.0",
"@scure/base": "~1.1.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/nostr-tools": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.17.0.tgz",
"integrity": "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w==",
"license": "Unlicense",
"dependencies": {
"@noble/ciphers": "^0.5.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.1",
"@scure/base": "1.1.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1",
"nostr-wasm": "0.1.0"
},
"peerDependencies": {
"typescript": ">=5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/nostr-wasm": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
"license": "MIT"
},
"node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

22
node_modules/@noble/ciphers/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

655
node_modules/@noble/ciphers/README.md generated vendored Normal file
View File

@@ -0,0 +1,655 @@
# noble-ciphers
Auditable & minimal JS implementation of Salsa20, ChaCha and AES.
- 🔒 Auditable
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness: property-based, cross-library and Wycheproof vectors
- 💼 AES: ECB, CBC, CTR, CFB, GCM, SIV (nonce misuse-resistant)
- 💃 Salsa20, ChaCha, XSalsa20, XChaCha, Poly1305, ChaCha8, ChaCha12
- 🥈 Two AES implementations: choose between friendly webcrypto wrapper and pure JS one
- 🪶 45KB (8KB gzipped) for everything, 10KB (3KB gzipped) for ChaCha build
For discussions, questions and support, visit
[GitHub Discussions](https://github.com/paulmillr/noble-ciphers/discussions)
section of the repository.
### This library belongs to _noble_ cryptography
> **noble cryptography** — high-security, easily auditable set of contained cryptographic libraries and tools.
- Zero or minimal dependencies
- Highly readable TypeScript / JS code
- PGP-signed releases and transparent NPM builds
- All libraries:
[ciphers](https://github.com/paulmillr/noble-ciphers),
[curves](https://github.com/paulmillr/noble-curves),
[hashes](https://github.com/paulmillr/noble-hashes),
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
4kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
[ed25519](https://github.com/paulmillr/noble-ed25519)
- [Check out homepage](https://paulmillr.com/noble/)
for reading resources, documentation and apps built with noble
## Usage
> npm install @noble/ciphers
We support all major platforms and runtimes.
For [Deno](https://deno.land), ensure to use
[npm specifier](https://deno.land/manual@v1.28.0/node/npm_specifiers).
For React Native, you may need a
[polyfill for getRandomValues](https://github.com/LinusU/react-native-get-random-values).
A standalone file
[noble-ciphers.js](https://github.com/paulmillr/noble-ciphers/releases) is also available.
```js
// import * from '@noble/ciphers'; // Error: use sub-imports, to ensure small app size
import { xchacha20poly1305 } from '@noble/ciphers/chacha';
// import { xchacha20poly1305 } from 'npm:@noble/ciphers@0.5.0/chacha'; // Deno
```
- [Examples](#examples)
- [Encrypt with XChaCha20-Poly1305](#encrypt-with-xchacha20-poly1305)
- [Encrypt with AES-256-GCM](#encrypt-with-aes-256-gcm)
- [Use existing key instead of a new one](#use-existing-key-instead-of-a-new-one)
- [Encrypt without nonce](#encrypt-without-nonce)
- [Use same array for input and output](#use-same-array-for-input-and-output)
- [All imports](#all-imports)
- [Implementations](#implementations)
- [Salsa20](#salsa)
- [ChaCha](#chacha)
- [AES](#aes)
- [Webcrypto AES](#webcrypto-aes)
- [Poly1305, GHash, Polyval](#poly1305-ghash-polyval)
- [FF1 format-preserving encryption](#ff1)
- [Managed nonces](#managed-nonces)
- [Guidance](#guidance)
- [Which cipher should I pick?](#which-cipher-should-i-pick)
- [How to encrypt properly](#how-to-encrypt-properly)
- [Nonces](#nonces)
- [Encryption limits](#encryption-limits)
- [AES internals and block modes](#aes-internals-and-block-modes)
- [Security](#security)
- [Speed](#speed)
- [Upgrading](#upgrading)
- [Contributing & testing](#contributing--testing)
- [Resources](#resources)
## Examples
#### Encrypt with XChaCha20-Poly1305
```js
import { xchacha20poly1305 } from '@noble/ciphers/chacha';
import { utf8ToBytes } from '@noble/ciphers/utils';
import { randomBytes } from '@noble/ciphers/webcrypto';
const key = randomBytes(32);
const nonce = randomBytes(24);
const chacha = xchacha20poly1305(key, nonce);
const data = utf8ToBytes('hello, noble');
const ciphertext = chacha.encrypt(data);
const data_ = chacha.decrypt(ciphertext); // utils.bytesToUtf8(data_) === data
```
#### Encrypt with AES-256-GCM
```js
import { gcm } from '@noble/ciphers/aes';
import { utf8ToBytes } from '@noble/ciphers/utils';
import { randomBytes } from '@noble/ciphers/webcrypto';
const key = randomBytes(32);
const nonce = randomBytes(24);
const aes = gcm(key, nonce);
const data = utf8ToBytes('hello, noble');
const ciphertext = aes.encrypt(data);
const data_ = aes.decrypt(ciphertext); // utils.bytesToUtf8(data_) === data
```
#### Use existing key instead of a new one
```js
const key = new Uint8Array([
169, 88, 160, 139, 168, 29, 147, 196, 14, 88, 237, 76, 243, 177, 109, 140, 195, 140, 80, 10, 216,
134, 215, 71, 191, 48, 20, 104, 189, 37, 38, 55,
]);
const nonce = new Uint8Array([
180, 90, 27, 63, 160, 191, 150, 33, 67, 212, 86, 71, 144, 6, 200, 102, 218, 32, 23, 147, 8, 41,
147, 11,
]);
// or, hex:
import { hexToBytes } from '@noble/ciphers/utils';
const key2 = hexToBytes('4b7f89bac90a1086fef73f5da2cbe93b2fae9dfbf7678ae1f3e75fd118ddf999');
const nonce2 = hexToBytes('9610467513de0bbd7c4cc2c3c64069f1802086fbd3232b13');
```
#### Encrypt without nonce
```js
import { xchacha20poly1305 } from '@noble/ciphers/chacha';
import { managedNonce } from '@noble/ciphers/webcrypto';
import { hexToBytes, utf8ToBytes } from '@noble/ciphers/utils';
const key = hexToBytes('fa686bfdffd3758f6377abbc23bf3d9bdc1a0dda4a6e7f8dbdd579fa1ff6d7e1');
const chacha = managedNonce(xchacha20poly1305)(key); // manages nonces for you
const data = utf8ToBytes('hello, noble');
const ciphertext = chacha.encrypt(data);
const data_ = chacha.decrypt(ciphertext);
```
#### Use same array for input and output
```js
import { chacha20poly1305 } from '@noble/ciphers/chacha';
import { utf8ToBytes } from '@noble/ciphers/utils';
import { randomBytes } from '@noble/ciphers/webcrypto';
const key = randomBytes(32);
const nonce = randomBytes(12);
const buf = new Uint8Array(12 + 16);
const _data = utf8ToBytes('hello, noble');
buf.set(_data, 0); // first 12 bytes
const _12b = buf.subarray(0, 12);
const chacha = chacha20poly1305(key, nonce);
chacha.encrypt(_12b, buf);
chacha.decrypt(buf, _12b); // _12b now same as _data
```
#### All imports
```js
import { gcm, siv } from '@noble/ciphers/aes';
import { xsalsa20poly1305 } from '@noble/ciphers/salsa';
import { chacha20poly1305, xchacha20poly1305 } from '@noble/ciphers/chacha';
// Unauthenticated encryption: make sure to use HMAC or similar
import { ctr, cfb, cbc, ecb } from '@noble/ciphers/aes';
import { salsa20, xsalsa20 } from '@noble/ciphers/salsa';
import { chacha20, xchacha20, chacha8, chacha12 } from '@noble/ciphers/chacha';
// Utilities
import { bytesToHex, hexToBytes, bytesToUtf8, utf8ToBytes } from '@noble/ciphers/utils';
import { managedNonce, randomBytes } from '@noble/ciphers/webcrypto';
```
## Implementations
### Salsa
```js
import { xsalsa20poly1305 } from '@noble/ciphers/salsa';
import { secretbox } from '@noble/ciphers/salsa'; // == xsalsa20poly1305
import { salsa20, xsalsa20 } from '@noble/ciphers/salsa';
```
[Salsa20](https://cr.yp.to/snuffle.html) stream cipher was released in 2005.
Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
which are hard to implement in a constant-time manner.
Salsa20 is usually faster than AES, a big deal on slow, budget mobile phones.
[XSalsa20](https://cr.yp.to/snuffle/xsalsa-20110204.pdf), extended-nonce
variant was released in 2008. It switched nonces from 96-bit to 192-bit,
and became safe to be picked at random.
Nacl / Libsodium popularized term "secretbox", a simple black-box
authenticated encryption. Secretbox is just xsalsa20-poly1305. We provide the
alias and corresponding seal / open methods. We don't provide "box" or "sealedbox".
Check out [PDF](https://cr.yp.to/snuffle/salsafamily-20071225.pdf) and
[wiki](https://en.wikipedia.org/wiki/Salsa20).
### ChaCha
```js
import { chacha20poly1305, xchacha20poly1305 } from '@noble/ciphers/chacha';
import { chacha20, xchacha20, chacha8, chacha12 } from '@noble/ciphers/chacha';
```
[ChaCha20](https://cr.yp.to/chacha.html) stream cipher was released
in 2008. ChaCha aims to increase the diffusion per round, but had slightly less
cryptanalysis. It was standardized in
[RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439) and is now used in TLS 1.3.
[XChaCha20](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha)
extended-nonce variant is also provided. Similar to XSalsa, it's safe to use with
randomly-generated nonces.
Check out [PDF](http://cr.yp.to/chacha/chacha-20080128.pdf) and [wiki](https://en.wikipedia.org/wiki/Salsa20).
### AES
```js
import { gcm, siv, ctr, cfb, cbc, ecb } from '@noble/ciphers/aes';
import { randomBytes } from '@noble/ciphers/webcrypto';
const plaintext = new Uint8Array(32).fill(16);
const key = randomBytes(32); // 24 for AES-192, 16 for AES-128
for (let cipher of [gcm, siv]) {
const stream = cipher(key, randomBytes(12));
const ciphertext_ = stream.encrypt(plaintext);
const plaintext_ = stream.decrypt(ciphertext_);
}
for (const cipher of [ctr, cbc, cbc]) {
const stream = cipher(key, randomBytes(16));
const ciphertext_ = stream.encrypt(plaintext);
const plaintext_ = stream.decrypt(ciphertext_);
}
for (const cipher of [ecb]) {
const stream = cipher(key);
const ciphertext_ = stream.encrypt(plaintext);
const plaintext_ = stream.decrypt(ciphertext_);
}
```
[AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)
is a variant of Rijndael block cipher, standardized by NIST in 2001.
We provide the fastest available pure JS implementation.
We support AES-128, AES-192 and AES-256: the mode is selected dynamically,
based on key length (16, 24, 32).
[AES-GCM-SIV](https://en.wikipedia.org/wiki/AES-GCM-SIV)
nonce-misuse-resistant mode is also provided. It's recommended to use it,
to prevent catastrophic consequences of nonce reuse. Our implementation of SIV
has the same speed as GCM: there is no performance hit.
Check out [AES internals and block modes](#aes-internals-and-block-modes).
### Webcrypto AES
```js
import { gcm, ctr, cbc, randomBytes } from '@noble/ciphers/webcrypto';
const plaintext = new Uint8Array(32).fill(16);
const key = randomBytes(32);
for (const cipher of [gcm]) {
const stream = cipher(key, randomBytes(12));
const ciphertext_ = await stream.encrypt(plaintext);
const plaintext_ = await stream.decrypt(ciphertext_);
}
for (const cipher of [ctr, cbc]) {
const stream = cipher(key, randomBytes(16));
const ciphertext_ = await stream.encrypt(plaintext);
const plaintext_ = await stream.decrypt(ciphertext_);
}
```
We also have a separate wrapper over WebCrypto built-in.
It's the same as using `crypto.subtle`, but with massively simplified API.
Unlike pure js version, it's asynchronous.
### Poly1305, GHash, Polyval
```js
import { poly1305 } from '@noble/ciphers/_poly1305';
import { ghash, polyval } from '@noble/ciphers/_polyval';
```
We expose polynomial-evaluation MACs: [Poly1305](https://cr.yp.to/mac.html),
AES-GCM's [GHash](https://en.wikipedia.org/wiki/Galois/Counter_Mode) and
AES-SIV's [Polyval](https://en.wikipedia.org/wiki/AES-GCM-SIV).
Poly1305 ([PDF](https://cr.yp.to/mac/poly1305-20050329.pdf),
[wiki](https://en.wikipedia.org/wiki/Poly1305))
is a fast and parallel secret-key message-authentication code suitable for
a wide variety of applications. It was standardized in
[RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439) and is now used in TLS 1.3.
Polynomial MACs are not perfect for every situation:
they lack Random Key Robustness: the MAC can be forged, and can't
be used in PAKE schemes. See
[invisible salamanders attack](https://keymaterial.net/2020/09/07/invisible-salamanders-in-aes-gcm-siv/).
To combat invisible salamanders, `hash(key)` can be included in ciphertext,
however, this would violate ciphertext indistinguishability:
an attacker would know which key was used - so `HKDF(key, i)`
could be used instead.
### FF1
Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
[See more info](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf).
### Managed nonces
```js
import { managedNonce } from '@noble/ciphers/webcrypto';
import { gcm, siv, ctr, cbc, cbc, ecb } from '@noble/ciphers/aes';
import { xsalsa20poly1305 } from '@noble/ciphers/salsa';
import { chacha20poly1305, xchacha20poly1305 } from '@noble/ciphers/chacha';
const wgcm = managedNonce(gcm);
const wsiv = managedNonce(siv);
const wcbc = managedNonce(cbc);
const wctr = managedNonce(ctr);
const wsalsapoly = managedNonce(xsalsa20poly1305);
const wchacha = managedNonce(chacha20poly1305);
const wxchacha = managedNonce(xchacha20poly1305);
// Now:
const encrypted = wgcm(key).encrypt(data); // no nonces
```
We provide API that manages nonce internally instead of exposing them to library's user.
For `encrypt`, a `nonceBytes`-length buffer is fetched from CSPRNG and prenended to encrypted ciphertext.
For `decrypt`, first `nonceBytes` of ciphertext are treated as nonce.
## Guidance
### Which cipher should I pick?
XChaCha20-Poly1305 is the safest bet these days.
AES-GCM-SIV is the second safest.
AES-GCM is the third.
### How to encrypt properly
- Use unpredictable key with enough entropy
- Random key must be using cryptographically secure random number generator (CSPRNG), not `Math.random` etc.
- Non-random key generated from KDF is fine
- Re-using key is fine, but be aware of rules for cryptographic key wear-out and [encryption limits](#encryption-limits)
- Use new nonce every time and [don't repeat it](#nonces)
- chacha and salsa20 are fine for sequential counters that _never_ repeat: `01, 02...`
- xchacha and xsalsa20 should be used for random nonces instead
- Prefer authenticated encryption (AEAD)
- HMAC+ChaCha / HMAC+AES / chacha20poly1305 / aes-gcm is good
- chacha20 without poly1305 or hmac / aes-ctr / aes-cbc is bad
- Flipping bits or ciphertext substitution won't be detected in unauthenticated ciphers
- Don't re-use keys between different protocols
- For example, using secp256k1 key in AES is bad
- Use hkdf or, at least, a hash function to create sub-key instead
### Nonces
Most ciphers need a key and a nonce (aka initialization vector / IV) to encrypt a data:
ciphertext = encrypt(plaintext, key, nonce)
Repeating (key, nonce) pair with different plaintexts would allow an attacker to decrypt it:
ciphertext_a = encrypt(plaintext_a, key, nonce)
ciphertext_b = encrypt(plaintext_b, key, nonce)
stream_diff = xor(ciphertext_a, ciphertext_b) # Break encryption
So, you can't repeat nonces. One way of doing so is using counters:
for i in 0..:
ciphertext[i] = encrypt(plaintexts[i], key, i)
Another is generating random nonce every time:
for i in 0..:
rand_nonces[i] = random()
ciphertext[i] = encrypt(plaintexts[i], key, rand_nonces[i])
Counters are OK, but it's not always possible to store current counter value:
e.g. in decentralized, unsyncable systems.
Randomness is OK, but there's a catch:
ChaCha20 and AES-GCM use 96-bit / 12-byte nonces, which implies
higher chance of collision. In the example above,
`random()` can collide and produce repeating nonce.
To safely use random nonces, utilize XSalsa20 or XChaCha:
they increased nonce length to 192-bit, minimizing a chance of collision.
AES-SIV is also fine. In situations where you can't use eXtended-nonce
algorithms, key rotation is advised. hkdf would work great for this case.
### Encryption limits
A "protected message" would mean a probability of `2**-50` that a passive attacker
successfully distinguishes the ciphertext outputs of the AEAD scheme from the outputs
of a random function. See [draft-irtf-cfrg-aead-limits](https://datatracker.ietf.org/doc/draft-irtf-cfrg-aead-limits/) for details.
- Max message size:
- AES-GCM: ~68GB, `2**36-256`
- Salsa, ChaCha, XSalsa, XChaCha: ~256GB, `2**38-64`
- Max amount of protected messages, under same key:
- AES-GCM: `2**32.5`
- Salsa, ChaCha: `2**46`, but only integrity is affected, not confidentiality
- XSalsa, XChaCha: `2**72`
- Max amount of protected messages, across all keys:
- AES-GCM: `2**69/B` where B is max blocks encrypted by a key. Meaning
`2**59` for 1KB, `2**49` for 1MB, `2**39` for 1GB
- Salsa, ChaCha, XSalsa, XChaCha: `2**100`
##### AES internals and block modes
`cipher = encrypt(block, key)`. Data is split into 128-bit blocks. Encrypted in 10/12/14 rounds (128/192/256bit). Every round does:
1. **S-box**, table substitution
2. **Shift rows**, cyclic shift left of all rows of data array
3. **Mix columns**, multiplying every column by fixed polynomial
4. **Add round key**, round_key xor i-th column of array
For non-deterministic (not ECB) schemes, initialization vector (IV) is mixed to block/key;
and each new round either depends on previous block's key, or on some counter.
- ECB — simple deterministic replacement. Dangerous: always map x to y. See [AES Penguin](https://words.filippo.io/the-ecb-penguin/)
- CBC — key is previous rounds block. Hard to use: need proper padding, also needs MAC
- CTR — counter, allows to create streaming cipher. Requires good IV. Parallelizable. OK, but no MAC
- GCM — modern CTR, parallel, with MAC
- SIV — synthetic initialization vector, nonce-misuse-resistant. Guarantees that, when a nonce is repeated,
the only security loss is that identical plaintexts will produce identical ciphertexts.
- XTS — used in hard drives. Similar to ECB (deterministic), but has `[i][j]`
tweak arguments corresponding to sector i and 16-byte block (part of sector) j. Not authenticated!
GCM / SIV are not ideal:
- Conservative key wear-out is `2**32` (4B) msgs
- MAC can be forged: see Poly1305 section above. Same for SIV
## Security
The library has not been independently audited yet.
It is tested against property-based, cross-library and Wycheproof vectors,
and has fuzzing by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz).
If you see anything unusual: investigate and report.
### Constant-timeness
_JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to
achieve [timing attack](https://en.wikipedia.org/wiki/Timing_attack) resistance
in a scripting language. Which means _any other JS library can't have
constant-timeness_. Even statically typed Rust, a language without GC,
[makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security)
for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones.
Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
AES uses T-tables, which means it can't be done in constant-time in JS.
### Supply chain security
- **Commits** are signed with PGP keys, to prevent forgery. Make sure to verify commit signatures.
- **Releases** are transparent and built on GitHub CI. Make sure to verify [provenance](https://docs.npmjs.com/generating-provenance-statements) logs
- **Rare releasing** is followed to ensure less re-audit need for end-users
- **Dependencies** are minimized and locked-down:
- If your app has 500 dependencies, any dep could get hacked and you'll be downloading
malware with every install. We make sure to use as few dependencies as possible
- We prevent automatic dependency updates by locking-down version ranges. Every update is checked with `npm-diff`
- **Dev Dependencies** are only used if you want to contribute to the repo. They are disabled for end-users:
- scure-base, micro-bmark and micro-should are developed by the same author and follow identical security practices
- prettier (linter), fast-check (property-based testing) and typescript are used for code quality, vector generation and ts compilation. The packages are big, which makes it hard to audit their source code thoroughly and fully
### Randomness
We're deferring to built-in
[crypto.getRandomValues](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues)
which is considered cryptographically secure (CSPRNG).
In the past, browsers had bugs that made it weak: it may happen again.
Implementing a userspace CSPRNG to get resilient to the weakness
is even worse: there is no reliable userspace source of quality entropy.
## Speed
To summarize, noble is the fastest JS implementation of Salsa, ChaCha and AES.
You can gain additional speed-up and
avoid memory allocations by passing `output`
uint8array into encrypt / decrypt methods.
Benchmark results on Apple M2 with node v20:
```
encrypt (64B)
├─xsalsa20poly1305 x 485,672 ops/sec @ 2μs/op
├─chacha20poly1305 x 466,200 ops/sec @ 2μs/op
├─xchacha20poly1305 x 312,500 ops/sec @ 3μs/op
├─aes-256-gcm x 151,057 ops/sec @ 6μs/op
└─aes-256-gcm-siv x 124,984 ops/sec @ 8μs/op
encrypt (1KB)
├─xsalsa20poly1305 x 146,477 ops/sec @ 6μs/op
├─chacha20poly1305 x 145,518 ops/sec @ 6μs/op
├─xchacha20poly1305 x 126,119 ops/sec @ 7μs/op
├─aes-256-gcm x 43,207 ops/sec @ 23μs/op
└─aes-256-gcm-siv x 39,363 ops/sec @ 25μs/op
encrypt (8KB)
├─xsalsa20poly1305 x 23,773 ops/sec @ 42μs/op
├─chacha20poly1305 x 24,134 ops/sec @ 41μs/op
├─xchacha20poly1305 x 23,520 ops/sec @ 42μs/op
├─aes-256-gcm x 8,420 ops/sec @ 118μs/op
└─aes-256-gcm-siv x 8,126 ops/sec @ 123μs/op
encrypt (1MB)
├─xsalsa20poly1305 x 195 ops/sec @ 5ms/op
├─chacha20poly1305 x 199 ops/sec @ 5ms/op
├─xchacha20poly1305 x 198 ops/sec @ 5ms/op
├─aes-256-gcm x 76 ops/sec @ 13ms/op
└─aes-256-gcm-siv x 78 ops/sec @ 12ms/op
```
Unauthenticated encryption:
```
encrypt (64B)
├─salsa x 1,287,001 ops/sec @ 777ns/op
├─chacha x 1,555,209 ops/sec @ 643ns/op
├─xsalsa x 938,086 ops/sec @ 1μs/op
└─xchacha x 920,810 ops/sec @ 1μs/op
encrypt (1KB)
├─salsa x 353,107 ops/sec @ 2μs/op
├─chacha x 377,216 ops/sec @ 2μs/op
├─xsalsa x 331,674 ops/sec @ 3μs/op
└─xchacha x 336,247 ops/sec @ 2μs/op
encrypt (8KB)
├─salsa x 57,084 ops/sec @ 17μs/op
├─chacha x 59,520 ops/sec @ 16μs/op
├─xsalsa x 57,097 ops/sec @ 17μs/op
└─xchacha x 58,278 ops/sec @ 17μs/op
encrypt (1MB)
├─salsa x 479 ops/sec @ 2ms/op
├─chacha x 491 ops/sec @ 2ms/op
├─xsalsa x 483 ops/sec @ 2ms/op
└─xchacha x 492 ops/sec @ 2ms/op
AES
encrypt (64B)
├─ctr-256 x 689,179 ops/sec @ 1μs/op
├─cbc-256 x 639,795 ops/sec @ 1μs/op
└─ecb-256 x 668,449 ops/sec @ 1μs/op
encrypt (1KB)
├─ctr-256 x 93,668 ops/sec @ 10μs/op
├─cbc-256 x 94,428 ops/sec @ 10μs/op
└─ecb-256 x 151,699 ops/sec @ 6μs/op
encrypt (8KB)
├─ctr-256 x 13,342 ops/sec @ 74μs/op
├─cbc-256 x 13,664 ops/sec @ 73μs/op
└─ecb-256 x 22,426 ops/sec @ 44μs/op
encrypt (1MB)
├─ctr-256 x 106 ops/sec @ 9ms/op
├─cbc-256 x 109 ops/sec @ 9ms/op
└─ecb-256 x 179 ops/sec @ 5ms/op
```
Compare to other implementations:
```
xsalsa20poly1305 (encrypt, 1MB)
├─tweetnacl x 108 ops/sec @ 9ms/op
└─noble x 190 ops/sec @ 5ms/op
chacha20poly1305 (encrypt, 1MB)
├─node x 1,360 ops/sec @ 735μs/op
├─stablelib x 117 ops/sec @ 8ms/op
└─noble x 193 ops/sec @ 5ms/op
chacha (encrypt, 1MB)
├─node x 2,035 ops/sec @ 491μs/op
├─stablelib x 206 ops/sec @ 4ms/op
└─noble x 474 ops/sec @ 2ms/op
ctr-256 (encrypt, 1MB)
├─node x 3,530 ops/sec @ 283μs/op
├─stablelib x 70 ops/sec @ 14ms/op
├─aesjs x 31 ops/sec @ 32ms/op
├─noble-webcrypto x 4,589 ops/sec @ 217μs/op
└─noble x 107 ops/sec @ 9ms/op
cbc-256 (encrypt, 1MB)
├─node x 993 ops/sec @ 1ms/op
├─stablelib x 63 ops/sec @ 15ms/op
├─aesjs x 29 ops/sec @ 34ms/op
├─noble-webcrypto x 1,087 ops/sec @ 919μs/op
└─noble x 110 ops/sec @ 9ms/op
gcm-256 (encrypt, 1MB)
├─node x 3,196 ops/sec @ 312μs/op
├─stablelib x 27 ops/sec @ 36ms/op
├─noble-webcrypto x 4,059 ops/sec @ 246μs/op
└─noble x 74 ops/sec @ 13ms/op
```
## Upgrading
Upgrade from `micro-aes-gcm` package is simple:
```js
// prepare
const key = Uint8Array.from([
64, 196, 127, 247, 172, 2, 34, 159, 6, 241, 30, 174, 183, 229, 41, 114, 253, 122, 119, 168, 177,
243, 155, 236, 164, 159, 98, 72, 162, 243, 224, 195,
]);
const message = 'Hello world';
// previous
import * as aes from 'micro-aes-gcm';
const ciphertext = await aes.encrypt(key, aes.utils.utf8ToBytes(message));
const plaintext = await aes.decrypt(key, ciphertext);
console.log(aes.utils.bytesToUtf8(plaintext) === message);
// became =>
import { gcm } from '@noble/ciphers/aes';
import { bytesToUtf8, utf8ToBytes } from '@noble/ciphers/utils';
import { managedNonce } from '@noble/ciphers/webcrypto';
const aes = managedNonce(gcm)(key);
const ciphertext = aes.encrypt(utf8ToBytes(message));
const plaintext = aes.decrypt(key, ciphertext);
console.log(bytesToUtf8(plaintext) === message);
```
## Contributing & testing
1. Clone the repository
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm run test` will execute all main tests
## Resources
Check out [paulmillr.com/noble](https://paulmillr.com/noble/)
for useful resources, articles, documentation and demos
related to the library.
## License
The MIT License (MIT)
Copyright (c) 2023 Paul Miller [(https://paulmillr.com)](https://paulmillr.com)
Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
See LICENSE file.

14
node_modules/@noble/ciphers/_arx.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import { XorStream } from './utils.js';
export declare const sigma: Uint32Array;
export declare function rotl(a: number, b: number): number;
export type CipherCoreFn = (sigma: Uint32Array, key: Uint32Array, nonce: Uint32Array, output: Uint32Array, counter: number, rounds?: number) => void;
export type ExtendNonceFn = (sigma: Uint32Array, key: Uint32Array, input: Uint32Array, output: Uint32Array) => void;
export type CipherOpts = {
allowShortKeys?: boolean;
extendNonceFn?: ExtendNonceFn;
counterLength?: number;
counterRight?: boolean;
rounds?: number;
};
export declare function createCipher(core: CipherCoreFn, opts: CipherOpts): XorStream;
//# sourceMappingURL=_arx.d.ts.map

1
node_modules/@noble/ciphers/_arx.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_arx.d.ts","sourceRoot":"","sources":["src/_arx.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AA4CvD,eAAO,MAAM,KAAK,aAAqB,CAAC;AAExC,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,MAAM,MAAM,YAAY,GAAG,CACzB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,KACZ,IAAI,CAAC;AAEV,MAAM,MAAM,aAAa,GAAG,CAC1B,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,WAAW,KAChB,IAAI,CAAC;AAEV,MAAM,MAAM,UAAU,GAAG;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAwDF,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAsF5E"}

175
node_modules/@noble/ciphers/_arx.js generated vendored Normal file
View File

@@ -0,0 +1,175 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCipher = exports.rotl = exports.sigma = void 0;
// Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.
const _assert_js_1 = require("./_assert.js");
const utils_js_1 = require("./utils.js");
/*
RFC8439 requires multi-step cipher stream, where
authKey starts with counter: 0, actual msg with counter: 1.
For this, we need a way to re-use nonce / counter:
const counter = new Uint8Array(4);
chacha(..., counter, ...); // counter is now 1
chacha(..., counter, ...); // counter is now 2
This is complicated:
- 32-bit counters are enough, no need for 64-bit: max ArrayBuffer size in JS is 4GB
- Original papers don't allow mutating counters
- Counter overflow is undefined [^1]
- Idea A: allow providing (nonce | counter) instead of just nonce, re-use it
- Caveat: Cannot be re-used through all cases:
- * chacha has (counter | nonce)
- * xchacha has (nonce16 | counter | nonce16)
- Idea B: separate nonce / counter and provide separate API for counter re-use
- Caveat: there are different counter sizes depending on an algorithm.
- salsa & chacha also differ in structures of key & sigma:
salsa20: s[0] | k(4) | s[1] | nonce(2) | ctr(2) | s[2] | k(4) | s[3]
chacha: s(4) | k(8) | ctr(1) | nonce(3)
chacha20orig: s(4) | k(8) | ctr(2) | nonce(2)
- Idea C: helper method such as `setSalsaState(key, nonce, sigma, data)`
- Caveat: we can't re-use counter array
xchacha [^2] uses the subkey and remaining 8 byte nonce with ChaCha20 as normal
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
[^1]: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
[^2]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
*/
// We can't make top-level var depend on utils.utf8ToBytes
// because it's not present in all envs. Creating a similar fn here
const _utf8ToBytes = (str) => Uint8Array.from(str.split('').map((c) => c.charCodeAt(0)));
const sigma16 = _utf8ToBytes('expand 16-byte k');
const sigma32 = _utf8ToBytes('expand 32-byte k');
const sigma16_32 = (0, utils_js_1.u32)(sigma16);
const sigma32_32 = (0, utils_js_1.u32)(sigma32);
exports.sigma = sigma32_32.slice();
function rotl(a, b) {
return (a << b) | (a >>> (32 - b));
}
exports.rotl = rotl;
// Is byte array aligned to 4 byte offset (u32)?
function isAligned32(b) {
return b.byteOffset % 4 === 0;
}
// Salsa and Chacha block length is always 512-bit
const BLOCK_LEN = 64;
const BLOCK_LEN32 = 16;
// new Uint32Array([2**32]) // => Uint32Array(1) [ 0 ]
// new Uint32Array([2**32-1]) // => Uint32Array(1) [ 4294967295 ]
const MAX_COUNTER = 2 ** 32 - 1;
const U32_EMPTY = new Uint32Array();
function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
const len = data.length;
const block = new Uint8Array(BLOCK_LEN);
const b32 = (0, utils_js_1.u32)(block);
// Make sure that buffers aligned to 4 bytes
const isAligned = isAligned32(data) && isAligned32(output);
const d32 = isAligned ? (0, utils_js_1.u32)(data) : U32_EMPTY;
const o32 = isAligned ? (0, utils_js_1.u32)(output) : U32_EMPTY;
for (let pos = 0; pos < len; counter++) {
core(sigma, key, nonce, b32, counter, rounds);
if (counter >= MAX_COUNTER)
throw new Error('arx: counter overflow');
const take = Math.min(BLOCK_LEN, len - pos);
// aligned to 4 bytes
if (isAligned && take === BLOCK_LEN) {
const pos32 = pos / 4;
if (pos % 4 !== 0)
throw new Error('arx: invalid block position');
for (let j = 0, posj; j < BLOCK_LEN32; j++) {
posj = pos32 + j;
o32[posj] = d32[posj] ^ b32[j];
}
pos += BLOCK_LEN;
continue;
}
for (let j = 0, posj; j < take; j++) {
posj = pos + j;
output[posj] = data[posj] ^ block[j];
}
pos += take;
}
}
function createCipher(core, opts) {
const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = (0, utils_js_1.checkOpts)({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
if (typeof core !== 'function')
throw new Error('core must be a function');
(0, _assert_js_1.number)(counterLength);
(0, _assert_js_1.number)(rounds);
(0, _assert_js_1.bool)(counterRight);
(0, _assert_js_1.bool)(allowShortKeys);
return (key, nonce, data, output, counter = 0) => {
(0, _assert_js_1.bytes)(key);
(0, _assert_js_1.bytes)(nonce);
(0, _assert_js_1.bytes)(data);
const len = data.length;
if (!output)
output = new Uint8Array(len);
(0, _assert_js_1.bytes)(output);
(0, _assert_js_1.number)(counter);
if (counter < 0 || counter >= MAX_COUNTER)
throw new Error('arx: counter overflow');
if (output.length < len)
throw new Error(`arx: output (${output.length}) is shorter than data (${len})`);
const toClean = [];
// Key & sigma
// key=16 -> sigma16, k=key|key
// key=32 -> sigma32, k=key
let l = key.length, k, sigma;
if (l === 32) {
k = key.slice();
toClean.push(k);
sigma = sigma32_32;
}
else if (l === 16 && allowShortKeys) {
k = new Uint8Array(32);
k.set(key);
k.set(key, 16);
sigma = sigma16_32;
toClean.push(k);
}
else {
throw new Error(`arx: invalid 32-byte key, got length=${l}`);
}
// Nonce
// salsa20: 8 (8-byte counter)
// chacha20orig: 8 (8-byte counter)
// chacha20: 12 (4-byte counter)
// xsalsa20: 24 (16 -> hsalsa, 8 -> old nonce)
// xchacha20: 24 (16 -> hchacha, 8 -> old nonce)
// Align nonce to 4 bytes
if (!isAligned32(nonce)) {
nonce = nonce.slice();
toClean.push(nonce);
}
const k32 = (0, utils_js_1.u32)(k);
// hsalsa & hchacha: handle extended nonce
if (extendNonceFn) {
if (nonce.length !== 24)
throw new Error(`arx: extended nonce must be 24 bytes`);
extendNonceFn(sigma, k32, (0, utils_js_1.u32)(nonce.subarray(0, 16)), k32);
nonce = nonce.subarray(16);
}
// Handle nonce counter
const nonceNcLen = 16 - counterLength;
if (nonceNcLen !== nonce.length)
throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);
// Pad counter when nonce is 64 bit
if (nonceNcLen !== 12) {
const nc = new Uint8Array(12);
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
nonce = nc;
toClean.push(nonce);
}
const n32 = (0, utils_js_1.u32)(nonce);
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
while (toClean.length > 0)
toClean.pop().fill(0);
return output;
};
}
exports.createCipher = createCipher;
//# sourceMappingURL=_arx.js.map

1
node_modules/@noble/ciphers/_arx.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

24
node_modules/@noble/ciphers/_assert.d.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
declare function number(n: number): void;
declare function bool(b: boolean): void;
export declare function isBytes(a: unknown): a is Uint8Array;
declare function bytes(b: Uint8Array | undefined, ...lengths: number[]): void;
export type Hash = {
(data: Uint8Array): Uint8Array;
blockLen: number;
outputLen: number;
create: any;
};
declare function hash(hash: Hash): void;
declare function exists(instance: any, checkFinished?: boolean): void;
declare function output(out: any, instance: any): void;
export { number, bool, bytes, hash, exists, output };
declare const assert: {
number: typeof number;
bool: typeof bool;
bytes: typeof bytes;
hash: typeof hash;
exists: typeof exists;
output: typeof output;
};
export default assert;
//# sourceMappingURL=_assert.d.ts.map

1
node_modules/@noble/ciphers/_assert.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_assert.d.ts","sourceRoot":"","sources":["src/_assert.ts"],"names":[],"mappings":"AAAA,iBAAS,MAAM,CAAC,CAAC,EAAE,MAAM,QAExB;AAED,iBAAS,IAAI,CAAC,CAAC,EAAE,OAAO,QAEvB;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,UAAU,CAKnD;AAED,iBAAS,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,QAI7D;AAED,MAAM,MAAM,IAAI,GAAG;IACjB,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;CACb,CAAC;AACF,iBAAS,IAAI,CAAC,IAAI,EAAE,IAAI,QAKvB;AAED,iBAAS,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,aAAa,UAAO,QAGlD;AAED,iBAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAMtC;AAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACrD,QAAA,MAAM,MAAM;;;;;;;CAAgD,CAAC;AAC7D,eAAe,MAAM,CAAC"}

50
node_modules/@noble/ciphers/_assert.js generated vendored Normal file
View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.output = exports.exists = exports.hash = exports.bytes = exports.bool = exports.number = exports.isBytes = void 0;
function number(n) {
if (!Number.isSafeInteger(n) || n < 0)
throw new Error(`positive integer expected, not ${n}`);
}
exports.number = number;
function bool(b) {
if (typeof b !== 'boolean')
throw new Error(`boolean expected, not ${b}`);
}
exports.bool = bool;
function isBytes(a) {
return (a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
}
exports.isBytes = isBytes;
function bytes(b, ...lengths) {
if (!isBytes(b))
throw new Error('Uint8Array expected');
if (lengths.length > 0 && !lengths.includes(b.length))
throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`);
}
exports.bytes = bytes;
function hash(hash) {
if (typeof hash !== 'function' || typeof hash.create !== 'function')
throw new Error('hash must be wrapped by utils.wrapConstructor');
number(hash.outputLen);
number(hash.blockLen);
}
exports.hash = hash;
function exists(instance, checkFinished = true) {
if (instance.destroyed)
throw new Error('Hash instance has been destroyed');
if (checkFinished && instance.finished)
throw new Error('Hash#digest() has already been called');
}
exports.exists = exists;
function output(out, instance) {
bytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
}
}
exports.output = output;
const assert = { number, bool, bytes, hash, exists, output };
exports.default = assert;
//# sourceMappingURL=_assert.js.map

1
node_modules/@noble/ciphers/_assert.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_assert.js","sourceRoot":"","sources":["src/_assert.ts"],"names":[],"mappings":";;;AAAA,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,EAAE,CAAC,CAAC;AAChG,CAAC;AA6CQ,wBAAM;AA3Cf,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC;AAyCgB,oBAAI;AAvCrB,SAAgB,OAAO,CAAC,CAAU;IAChC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,KAAK,YAAY,CAAC,CAC5E,CAAC;AACJ,CAAC;AALD,0BAKC;AAED,SAAS,KAAK,CAAC,CAAyB,EAAE,GAAG,OAAiB;IAC5D,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3F,CAAC;AA4BsB,sBAAK;AApB5B,SAAS,IAAI,CAAC,IAAU;IACtB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU;QACjE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAe6B,oBAAI;AAblC,SAAS,MAAM,CAAC,QAAa,EAAE,aAAa,GAAG,IAAI;IACjD,IAAI,QAAQ,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC5E,IAAI,aAAa,IAAI,QAAQ,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;AACnG,CAAC;AAUmC,wBAAM;AAR1C,SAAS,MAAM,CAAC,GAAQ,EAAE,QAAa;IACrC,KAAK,CAAC,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;IAC/B,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,yDAAyD,GAAG,EAAE,CAAC,CAAC;IAClF,CAAC;AACH,CAAC;AAE2C,wBAAM;AAClD,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC7D,kBAAe,MAAM,CAAC"}

70
node_modules/@noble/ciphers/_micro.d.ts generated vendored Normal file
View File

@@ -0,0 +1,70 @@
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
import { Cipher, XorStream } from './utils.js';
export declare function hsalsa(s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array): void;
export declare function hchacha(s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array): void;
/**
* salsa20, 12-byte nonce.
*/
export declare const salsa20: XorStream;
/**
* xsalsa20, 24-byte nonce.
*/
export declare const xsalsa20: XorStream;
/**
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
*/
export declare const chacha20orig: XorStream;
/**
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
*/
export declare const chacha20: XorStream;
/**
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export declare const xchacha20: XorStream;
/**
* 8-round chacha from the original paper.
*/
export declare const chacha8: XorStream;
/**
* 12-round chacha from the original paper.
*/
export declare const chacha12: XorStream;
export declare function poly1305(msg: Uint8Array, key: Uint8Array): Uint8Array;
/**
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
*/
export declare const xsalsa20poly1305: ((key: Uint8Array, nonce: Uint8Array) => {
encrypt: (plaintext: Uint8Array) => Uint8Array;
decrypt: (ciphertext: Uint8Array) => Uint8Array;
}) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
/**
* Alias to xsalsa20-poly1305
*/
export declare function secretbox(key: Uint8Array, nonce: Uint8Array): {
seal: (plaintext: Uint8Array) => Uint8Array;
open: (ciphertext: Uint8Array) => Uint8Array;
};
export declare const _poly1305_aead: (fn: XorStream) => (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher;
/**
* chacha20-poly1305 12-byte-nonce chacha.
*/
export declare const chacha20poly1305: ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
/**
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export declare const xchacha20poly1305: ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
//# sourceMappingURL=_micro.d.ts.map

1
node_modules/@noble/ciphers/_micro.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_micro.d.ts","sourceRoot":"","sources":["src/_micro.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,OAAO,EACL,MAAM,EAAE,SAAS,EAElB,MAAM,YAAY,CAAC;AA+EpB,wBAAgB,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,QAatF;AAuBD,wBAAgB,OAAO,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,QAavF;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,WAGlB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,WAGnB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY,WAIvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,WAGnB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,WAIpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO,WAIlB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,WAInB,CAAC;AAQH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,UAAU,CAcrE;AA4BD;;GAEG;AACH,eAAO,MAAM,gBAAgB,SAEI,UAAU,SAAS,UAAU;yBAInC,UAAU;0BAST,UAAU;;;;;CAWrC,CAAC;AAEF;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU;;;EAG3D;AAED,eAAO,MAAM,cAAc,OACpB,SAAS,WACR,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAuBvD,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,gBAAgB,SA5BrB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAAM;;;;CA+B/D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,SArCtB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAAM;;;;CAwC/D,CAAC"}

295
node_modules/@noble/ciphers/_micro.js generated vendored Normal file
View File

@@ -0,0 +1,295 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.xchacha20poly1305 = exports.chacha20poly1305 = exports._poly1305_aead = exports.secretbox = exports.xsalsa20poly1305 = exports.poly1305 = exports.chacha12 = exports.chacha8 = exports.xchacha20 = exports.chacha20 = exports.chacha20orig = exports.xsalsa20 = exports.salsa20 = exports.hchacha = exports.hsalsa = void 0;
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
// prettier-ignore
const utils_js_1 = require("./utils.js");
const _arx_js_1 = require("./_arx.js");
const _assert_js_1 = require("./_assert.js");
/*
noble-ciphers-micro: more auditable, but slower version of salsa20, chacha & poly1305.
Implements the same algorithms that are present in other files, but without
unrolled loops (https://en.wikipedia.org/wiki/Loop_unrolling).
*/
function bytesToNumberLE(bytes) {
return (0, utils_js_1.hexToNumber)((0, utils_js_1.bytesToHex)(Uint8Array.from(bytes).reverse()));
}
function numberToBytesLE(n, len) {
return (0, utils_js_1.numberToBytesBE)(n, len).reverse();
}
function salsaQR(x, a, b, c, d) {
x[b] ^= (0, _arx_js_1.rotl)((x[a] + x[d]) | 0, 7);
x[c] ^= (0, _arx_js_1.rotl)((x[b] + x[a]) | 0, 9);
x[d] ^= (0, _arx_js_1.rotl)((x[c] + x[b]) | 0, 13);
x[a] ^= (0, _arx_js_1.rotl)((x[d] + x[c]) | 0, 18);
}
// prettier-ignore
function chachaQR(x, a, b, c, d) {
x[a] = (x[a] + x[b]) | 0;
x[d] = (0, _arx_js_1.rotl)(x[d] ^ x[a], 16);
x[c] = (x[c] + x[d]) | 0;
x[b] = (0, _arx_js_1.rotl)(x[b] ^ x[c], 12);
x[a] = (x[a] + x[b]) | 0;
x[d] = (0, _arx_js_1.rotl)(x[d] ^ x[a], 8);
x[c] = (x[c] + x[d]) | 0;
x[b] = (0, _arx_js_1.rotl)(x[b] ^ x[c], 7);
}
function salsaRound(x, rounds = 20) {
for (let r = 0; r < rounds; r += 2) {
salsaQR(x, 0, 4, 8, 12);
salsaQR(x, 5, 9, 13, 1);
salsaQR(x, 10, 14, 2, 6);
salsaQR(x, 15, 3, 7, 11);
salsaQR(x, 0, 1, 2, 3);
salsaQR(x, 5, 6, 7, 4);
salsaQR(x, 10, 11, 8, 9);
salsaQR(x, 15, 12, 13, 14);
}
}
function chachaRound(x, rounds = 20) {
for (let r = 0; r < rounds; r += 2) {
chachaQR(x, 0, 4, 8, 12);
chachaQR(x, 1, 5, 9, 13);
chachaQR(x, 2, 6, 10, 14);
chachaQR(x, 3, 7, 11, 15);
chachaQR(x, 0, 5, 10, 15);
chachaQR(x, 1, 6, 11, 12);
chachaQR(x, 2, 7, 8, 13);
chachaQR(x, 3, 4, 9, 14);
}
}
function salsaCore(s, k, n, out, cnt, rounds = 20) {
// prettier-ignore
const y = new Uint32Array([
s[0], k[0], k[1], k[2], // "expa" Key Key Key
k[3], s[1], n[0], n[1], // Key "nd 3" Nonce Nonce
cnt, 0, s[2], k[4], // Pos. Pos. "2-by" Key
k[5], k[6], k[7], s[3], // Key Key Key "te k"
]);
const x = y.slice();
salsaRound(x, rounds);
for (let i = 0; i < 16; i++)
out[i] = (y[i] + x[i]) | 0;
}
// prettier-ignore
function hsalsa(s, k, i, o32) {
const x = new Uint32Array([
s[0], k[0], k[1], k[2],
k[3], s[1], i[0], i[1],
i[2], i[3], s[2], k[4],
k[5], k[6], k[7], s[3]
]);
salsaRound(x, 20);
let oi = 0;
o32[oi++] = x[0];
o32[oi++] = x[5];
o32[oi++] = x[10];
o32[oi++] = x[15];
o32[oi++] = x[6];
o32[oi++] = x[7];
o32[oi++] = x[8];
o32[oi++] = x[9];
}
exports.hsalsa = hsalsa;
function chachaCore(s, k, n, out, cnt, rounds = 20) {
// prettier-ignore
const y = new Uint32Array([
s[0], s[1], s[2], s[3], // "expa" "nd 3" "2-by" "te k"
k[0], k[1], k[2], k[3], // Key Key Key Key
k[4], k[5], k[6], k[7], // Key Key Key Key
cnt, n[0], n[1], n[2], // Counter Counter Nonce Nonce
]);
const x = y.slice();
chachaRound(x, rounds);
for (let i = 0; i < 16; i++)
out[i] = (y[i] + x[i]) | 0;
}
// prettier-ignore
function hchacha(s, k, i, o32) {
const x = new Uint32Array([
s[0], s[1], s[2], s[3],
k[0], k[1], k[2], k[3],
k[4], k[5], k[6], k[7],
i[0], i[1], i[2], i[3],
]);
chachaRound(x, 20);
let oi = 0;
o32[oi++] = x[0];
o32[oi++] = x[1];
o32[oi++] = x[2];
o32[oi++] = x[3];
o32[oi++] = x[12];
o32[oi++] = x[13];
o32[oi++] = x[14];
o32[oi++] = x[15];
}
exports.hchacha = hchacha;
/**
* salsa20, 12-byte nonce.
*/
exports.salsa20 = (0, _arx_js_1.createCipher)(salsaCore, {
allowShortKeys: true,
counterRight: true,
});
/**
* xsalsa20, 24-byte nonce.
*/
exports.xsalsa20 = (0, _arx_js_1.createCipher)(salsaCore, {
counterRight: true,
extendNonceFn: hsalsa,
});
/**
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
*/
exports.chacha20orig = (0, _arx_js_1.createCipher)(chachaCore, {
allowShortKeys: true,
counterRight: false,
counterLength: 8,
});
/**
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
*/
exports.chacha20 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 4,
});
/**
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
exports.xchacha20 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 8,
extendNonceFn: hchacha,
});
/**
* 8-round chacha from the original paper.
*/
exports.chacha8 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 8,
});
/**
* 12-round chacha from the original paper.
*/
exports.chacha12 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 12,
});
const POW_2_130_5 = BigInt(2) ** BigInt(130) - BigInt(5);
const POW_2_128_1 = BigInt(2) ** BigInt(16 * 8) - BigInt(1);
const CLAMP_R = BigInt('0x0ffffffc0ffffffc0ffffffc0fffffff');
const _0 = BigInt(0);
const _1 = BigInt(1);
// Can be speed-up using BigUint64Array, but would be more complicated
function poly1305(msg, key) {
(0, _assert_js_1.bytes)(msg);
(0, _assert_js_1.bytes)(key);
let acc = _0;
const r = bytesToNumberLE(key.subarray(0, 16)) & CLAMP_R;
const s = bytesToNumberLE(key.subarray(16));
// Process by 16 byte chunks
for (let i = 0; i < msg.length; i += 16) {
const m = msg.subarray(i, i + 16);
const n = bytesToNumberLE(m) | (_1 << BigInt(8 * m.length));
acc = ((acc + n) * r) % POW_2_130_5;
}
const res = (acc + s) & POW_2_128_1;
return numberToBytesLE(res, 16);
}
exports.poly1305 = poly1305;
function computeTag(fn, key, nonce, ciphertext, AAD) {
const res = [];
if (AAD) {
res.push(AAD);
const leftover = AAD.length % 16;
if (leftover > 0)
res.push(new Uint8Array(16 - leftover));
}
res.push(ciphertext);
const leftover = ciphertext.length % 16;
if (leftover > 0)
res.push(new Uint8Array(16 - leftover));
// Lengths
const num = new Uint8Array(16);
const view = (0, utils_js_1.createView)(num);
(0, utils_js_1.setBigUint64)(view, 0, BigInt(AAD ? AAD.length : 0), true);
(0, utils_js_1.setBigUint64)(view, 8, BigInt(ciphertext.length), true);
res.push(num);
const authKey = fn(key, nonce, new Uint8Array(32));
return poly1305((0, utils_js_1.concatBytes)(...res), authKey);
}
/**
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
*/
exports.xsalsa20poly1305 = (0, utils_js_1.wrapCipher)({ blockSize: 64, nonceLength: 24, tagLength: 16 }, function xsalsa20poly1305(key, nonce) {
(0, _assert_js_1.bytes)(key);
(0, _assert_js_1.bytes)(nonce);
return {
encrypt: (plaintext) => {
(0, _assert_js_1.bytes)(plaintext);
const m = (0, utils_js_1.concatBytes)(new Uint8Array(32), plaintext);
const c = (0, exports.xsalsa20)(key, nonce, m);
const authKey = c.subarray(0, 32);
const data = c.subarray(32);
const tag = poly1305(data, authKey);
return (0, utils_js_1.concatBytes)(tag, data);
},
decrypt: (ciphertext) => {
(0, _assert_js_1.bytes)(ciphertext);
if (ciphertext.length < 16)
throw new Error('encrypted data must be at least 16 bytes');
const c = (0, utils_js_1.concatBytes)(new Uint8Array(16), ciphertext);
const authKey = (0, exports.xsalsa20)(key, nonce, new Uint8Array(32));
const tag = poly1305(c.subarray(32), authKey);
if (!(0, utils_js_1.equalBytes)(c.subarray(16, 32), tag))
throw new Error('invalid poly1305 tag');
return (0, exports.xsalsa20)(key, nonce, c).subarray(32);
},
};
});
/**
* Alias to xsalsa20-poly1305
*/
function secretbox(key, nonce) {
const xs = (0, exports.xsalsa20poly1305)(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}
exports.secretbox = secretbox;
const _poly1305_aead = (fn) => (key, nonce, AAD) => {
const tagLength = 16;
const keyLength = 32;
(0, _assert_js_1.bytes)(key, keyLength);
(0, _assert_js_1.bytes)(nonce);
return {
encrypt: (plaintext) => {
(0, _assert_js_1.bytes)(plaintext);
const res = fn(key, nonce, plaintext, undefined, 1);
const tag = computeTag(fn, key, nonce, res, AAD);
return (0, utils_js_1.concatBytes)(res, tag);
},
decrypt: (ciphertext) => {
(0, _assert_js_1.bytes)(ciphertext);
if (ciphertext.length < tagLength)
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
const passedTag = ciphertext.subarray(-tagLength);
const data = ciphertext.subarray(0, -tagLength);
const tag = computeTag(fn, key, nonce, data, AAD);
if (!(0, utils_js_1.equalBytes)(passedTag, tag))
throw new Error('invalid poly1305 tag');
return fn(key, nonce, data, undefined, 1);
},
};
};
exports._poly1305_aead = _poly1305_aead;
/**
* chacha20-poly1305 12-byte-nonce chacha.
*/
exports.chacha20poly1305 = (0, utils_js_1.wrapCipher)({ blockSize: 64, nonceLength: 12, tagLength: 16 }, (0, exports._poly1305_aead)(exports.chacha20));
/**
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
exports.xchacha20poly1305 = (0, utils_js_1.wrapCipher)({ blockSize: 64, nonceLength: 24, tagLength: 16 }, (0, exports._poly1305_aead)(exports.xchacha20));
//# sourceMappingURL=_micro.js.map

1
node_modules/@noble/ciphers/_micro.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

15
node_modules/@noble/ciphers/_poly1305.d.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
import { Input, Hash } from './utils.js';
export type CHash = ReturnType<typeof wrapConstructorWithKey>;
export declare function wrapConstructorWithKey<H extends Hash<H>>(hashCons: (key: Input) => Hash<H>): {
(msg: Input, key: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(key: Input): Hash<H>;
};
export declare const poly1305: {
(msg: Input, key: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(key: Input): Hash<Hash<unknown>>;
};
//# sourceMappingURL=_poly1305.d.ts.map

1
node_modules/@noble/ciphers/_poly1305.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_poly1305.d.ts","sourceRoot":"","sources":["src/_poly1305.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAW,IAAI,EAAE,MAAM,YAAY,CAAC;AAkRlD,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC9D,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC;UACrE,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAI7B,KAAK;EAE3B;AAED,eAAO,MAAM,QAAQ;UARC,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAI7B,KAAK;CAI8C,CAAC"}

268
node_modules/@noble/ciphers/_poly1305.js generated vendored Normal file
View File

@@ -0,0 +1,268 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.poly1305 = exports.wrapConstructorWithKey = void 0;
const _assert_js_1 = require("./_assert.js");
const utils_js_1 = require("./utils.js");
// Poly1305 is a fast and parallel secret-key message-authentication code.
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
// https://datatracker.ietf.org/doc/html/rfc8439
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
const u8to16 = (a, i) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
class Poly1305 {
constructor(key) {
this.blockLen = 16;
this.outputLen = 16;
this.buffer = new Uint8Array(16);
this.r = new Uint16Array(10);
this.h = new Uint16Array(10);
this.pad = new Uint16Array(8);
this.pos = 0;
this.finished = false;
key = (0, utils_js_1.toBytes)(key);
(0, _assert_js_1.bytes)(key, 32);
const t0 = u8to16(key, 0);
const t1 = u8to16(key, 2);
const t2 = u8to16(key, 4);
const t3 = u8to16(key, 6);
const t4 = u8to16(key, 8);
const t5 = u8to16(key, 10);
const t6 = u8to16(key, 12);
const t7 = u8to16(key, 14);
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
this.r[0] = t0 & 0x1fff;
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
this.r[5] = (t4 >>> 1) & 0x1ffe;
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
this.r[9] = (t7 >>> 5) & 0x007f;
for (let i = 0; i < 8; i++)
this.pad[i] = u8to16(key, 16 + 2 * i);
}
process(data, offset, isLast = false) {
const hibit = isLast ? 0 : 1 << 11;
const { h, r } = this;
const r0 = r[0];
const r1 = r[1];
const r2 = r[2];
const r3 = r[3];
const r4 = r[4];
const r5 = r[5];
const r6 = r[6];
const r7 = r[7];
const r8 = r[8];
const r9 = r[9];
const t0 = u8to16(data, offset + 0);
const t1 = u8to16(data, offset + 2);
const t2 = u8to16(data, offset + 4);
const t3 = u8to16(data, offset + 6);
const t4 = u8to16(data, offset + 8);
const t5 = u8to16(data, offset + 10);
const t6 = u8to16(data, offset + 12);
const t7 = u8to16(data, offset + 14);
let h0 = h[0] + (t0 & 0x1fff);
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
let h9 = h[9] + ((t7 >>> 5) | hibit);
let c = 0;
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
c = d0 >>> 13;
d0 &= 0x1fff;
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
c += d0 >>> 13;
d0 &= 0x1fff;
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
c = d1 >>> 13;
d1 &= 0x1fff;
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
c += d1 >>> 13;
d1 &= 0x1fff;
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
c = d2 >>> 13;
d2 &= 0x1fff;
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
c += d2 >>> 13;
d2 &= 0x1fff;
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
c = d3 >>> 13;
d3 &= 0x1fff;
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
c += d3 >>> 13;
d3 &= 0x1fff;
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
c = d4 >>> 13;
d4 &= 0x1fff;
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
c += d4 >>> 13;
d4 &= 0x1fff;
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
c = d5 >>> 13;
d5 &= 0x1fff;
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
c += d5 >>> 13;
d5 &= 0x1fff;
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
c = d6 >>> 13;
d6 &= 0x1fff;
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
c += d6 >>> 13;
d6 &= 0x1fff;
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
c = d7 >>> 13;
d7 &= 0x1fff;
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
c += d7 >>> 13;
d7 &= 0x1fff;
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
c = d8 >>> 13;
d8 &= 0x1fff;
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
c += d8 >>> 13;
d8 &= 0x1fff;
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
c = d9 >>> 13;
d9 &= 0x1fff;
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
c += d9 >>> 13;
d9 &= 0x1fff;
c = ((c << 2) + c) | 0;
c = (c + d0) | 0;
d0 = c & 0x1fff;
c = c >>> 13;
d1 += c;
h[0] = d0;
h[1] = d1;
h[2] = d2;
h[3] = d3;
h[4] = d4;
h[5] = d5;
h[6] = d6;
h[7] = d7;
h[8] = d8;
h[9] = d9;
}
finalize() {
const { h, pad } = this;
const g = new Uint16Array(10);
let c = h[1] >>> 13;
h[1] &= 0x1fff;
for (let i = 2; i < 10; i++) {
h[i] += c;
c = h[i] >>> 13;
h[i] &= 0x1fff;
}
h[0] += c * 5;
c = h[0] >>> 13;
h[0] &= 0x1fff;
h[1] += c;
c = h[1] >>> 13;
h[1] &= 0x1fff;
h[2] += c;
g[0] = h[0] + 5;
c = g[0] >>> 13;
g[0] &= 0x1fff;
for (let i = 1; i < 10; i++) {
g[i] = h[i] + c;
c = g[i] >>> 13;
g[i] &= 0x1fff;
}
g[9] -= 1 << 13;
let mask = (c ^ 1) - 1;
for (let i = 0; i < 10; i++)
g[i] &= mask;
mask = ~mask;
for (let i = 0; i < 10; i++)
h[i] = (h[i] & mask) | g[i];
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
let f = h[0] + pad[0];
h[0] = f & 0xffff;
for (let i = 1; i < 8; i++) {
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
h[i] = f & 0xffff;
}
}
update(data) {
(0, _assert_js_1.exists)(this);
const { buffer, blockLen } = this;
data = (0, utils_js_1.toBytes)(data);
const len = data.length;
for (let pos = 0; pos < len;) {
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input
if (take === blockLen) {
for (; blockLen <= len - pos; pos += blockLen)
this.process(data, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(buffer, 0, false);
this.pos = 0;
}
}
return this;
}
destroy() {
this.h.fill(0);
this.r.fill(0);
this.buffer.fill(0);
this.pad.fill(0);
}
digestInto(out) {
(0, _assert_js_1.exists)(this);
(0, _assert_js_1.output)(out, this);
this.finished = true;
const { buffer, h } = this;
let { pos } = this;
if (pos) {
buffer[pos++] = 1;
// buffer.subarray(pos).fill(0);
for (; pos < 16; pos++)
buffer[pos] = 0;
this.process(buffer, 0, true);
}
this.finalize();
let opos = 0;
for (let i = 0; i < 8; i++) {
out[opos++] = h[i] >>> 0;
out[opos++] = h[i] >>> 8;
}
return out;
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
}
function wrapConstructorWithKey(hashCons) {
const hashC = (msg, key) => hashCons(key).update((0, utils_js_1.toBytes)(msg)).digest();
const tmp = hashCons(new Uint8Array(32));
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (key) => hashCons(key);
return hashC;
}
exports.wrapConstructorWithKey = wrapConstructorWithKey;
exports.poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
//# sourceMappingURL=_poly1305.js.map

1
node_modules/@noble/ciphers/_poly1305.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

27
node_modules/@noble/ciphers/_polyval.d.ts generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import { Input, Hash } from './utils.js';
/**
* `mulX_POLYVAL(ByteReverse(H))` from spec
* @param k mutated in place
*/
export declare function _toGHASHKey(k: Uint8Array): Uint8Array;
export type CHash = ReturnType<typeof wrapConstructorWithKey>;
declare function wrapConstructorWithKey<H extends Hash<H>>(hashCons: (key: Input, expectedLength?: number) => Hash<H>): {
(msg: Input, key: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(key: Input, expectedLength?: number): Hash<H>;
};
export declare const ghash: {
(msg: Input, key: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(key: Input, expectedLength?: number): Hash<Hash<unknown>>;
};
export declare const polyval: {
(msg: Input, key: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(key: Input, expectedLength?: number): Hash<Hash<unknown>>;
};
export {};
//# sourceMappingURL=_polyval.d.ts.map

1
node_modules/@noble/ciphers/_polyval.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_polyval.d.ts","sourceRoot":"","sources":["src/_polyval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,EAAE,IAAI,EAAO,MAAM,YAAY,CAAC;AAsCnE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,GAAG,UAAU,CAYrD;AA+KD,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC9D,iBAAS,sBAAsB,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAC/C,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,cAAc,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC;UAEtC,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAK7B,KAAK,mBAAmB,MAAM;EAEpD;AAED,eAAO,MAAM,KAAK;UATI,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAK7B,KAAK,mBAAmB,MAAM;CAMpD,CAAC;AACF,eAAO,MAAM,OAAO;UAZE,KAAK,OAAO,KAAK,GAAG,UAAU;;;gBAK7B,KAAK,mBAAmB,MAAM;CASpD,CAAC"}

221
node_modules/@noble/ciphers/_polyval.js generated vendored Normal file
View File

@@ -0,0 +1,221 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.polyval = exports.ghash = exports._toGHASHKey = void 0;
const utils_js_1 = require("./utils.js");
const _assert_js_1 = require("./_assert.js");
// GHash from AES-GCM and its little-endian "mirror image" Polyval from AES-SIV.
// Implemented in terms of GHash with conversion function for keys
// GCM GHASH from NIST SP800-38d, SIV from RFC 8452.
// https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
// GHASH modulo: x^128 + x^7 + x^2 + x + 1
// POLYVAL modulo: x^128 + x^127 + x^126 + x^121 + 1
const BLOCK_SIZE = 16;
// TODO: rewrite
// temporary padding buffer
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
const ZEROS32 = (0, utils_js_1.u32)(ZEROS16);
const POLY = 0xe1; // v = 2*v % POLY
// v = 2*v % POLY
// NOTE: because x + x = 0 (add/sub is same), mul2(x) != x+x
// We can multiply any number using montgomery ladder and this function (works as double, add is simple xor)
const mul2 = (s0, s1, s2, s3) => {
const hiBit = s3 & 1;
return {
s3: (s2 << 31) | (s3 >>> 1),
s2: (s1 << 31) | (s2 >>> 1),
s1: (s0 << 31) | (s1 >>> 1),
s0: (s0 >>> 1) ^ ((POLY << 24) & -(hiBit & 1)), // reduce % poly
};
};
const swapLE = (n) => (((n >>> 0) & 0xff) << 24) |
(((n >>> 8) & 0xff) << 16) |
(((n >>> 16) & 0xff) << 8) |
((n >>> 24) & 0xff) |
0;
/**
* `mulX_POLYVAL(ByteReverse(H))` from spec
* @param k mutated in place
*/
function _toGHASHKey(k) {
k.reverse();
const hiBit = k[15] & 1;
// k >>= 1
let carry = 0;
for (let i = 0; i < k.length; i++) {
const t = k[i];
k[i] = (t >>> 1) | carry;
carry = (t & 1) << 7;
}
k[0] ^= -hiBit & 0xe1; // if (hiBit) n ^= 0xe1000000000000000000000000000000;
return k;
}
exports._toGHASHKey = _toGHASHKey;
const estimateWindow = (bytes) => {
if (bytes > 64 * 1024)
return 8;
if (bytes > 1024)
return 4;
return 2;
};
class GHASH {
// We select bits per window adaptively based on expectedLength
constructor(key, expectedLength) {
this.blockLen = BLOCK_SIZE;
this.outputLen = BLOCK_SIZE;
this.s0 = 0;
this.s1 = 0;
this.s2 = 0;
this.s3 = 0;
this.finished = false;
key = (0, utils_js_1.toBytes)(key);
(0, _assert_js_1.bytes)(key, 16);
const kView = (0, utils_js_1.createView)(key);
let k0 = kView.getUint32(0, false);
let k1 = kView.getUint32(4, false);
let k2 = kView.getUint32(8, false);
let k3 = kView.getUint32(12, false);
// generate table of doubled keys (half of montgomery ladder)
const doubles = [];
for (let i = 0; i < 128; i++) {
doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) });
({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2(k0, k1, k2, k3));
}
const W = estimateWindow(expectedLength || 1024);
if (![1, 2, 4, 8].includes(W))
throw new Error(`ghash: wrong window size=${W}, should be 2, 4 or 8`);
this.W = W;
const bits = 128; // always 128 bits;
const windows = bits / W;
const windowSize = (this.windowSize = 2 ** W);
const items = [];
// Create precompute table for window of W bits
for (let w = 0; w < windows; w++) {
// truth table: 00, 01, 10, 11
for (let byte = 0; byte < windowSize; byte++) {
// prettier-ignore
let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
for (let j = 0; j < W; j++) {
const bit = (byte >>> (W - j - 1)) & 1;
if (!bit)
continue;
const { s0: d0, s1: d1, s2: d2, s3: d3 } = doubles[W * w + j];
(s0 ^= d0), (s1 ^= d1), (s2 ^= d2), (s3 ^= d3);
}
items.push({ s0, s1, s2, s3 });
}
}
this.t = items;
}
_updateBlock(s0, s1, s2, s3) {
(s0 ^= this.s0), (s1 ^= this.s1), (s2 ^= this.s2), (s3 ^= this.s3);
const { W, t, windowSize } = this;
// prettier-ignore
let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
const mask = (1 << W) - 1; // 2**W will kill performance.
let w = 0;
for (const num of [s0, s1, s2, s3]) {
for (let bytePos = 0; bytePos < 4; bytePos++) {
const byte = (num >>> (8 * bytePos)) & 0xff;
for (let bitPos = 8 / W - 1; bitPos >= 0; bitPos--) {
const bit = (byte >>> (W * bitPos)) & mask;
const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w * windowSize + bit];
(o0 ^= e0), (o1 ^= e1), (o2 ^= e2), (o3 ^= e3);
w += 1;
}
}
}
this.s0 = o0;
this.s1 = o1;
this.s2 = o2;
this.s3 = o3;
}
update(data) {
data = (0, utils_js_1.toBytes)(data);
(0, _assert_js_1.exists)(this);
const b32 = (0, utils_js_1.u32)(data);
const blocks = Math.floor(data.length / BLOCK_SIZE);
const left = data.length % BLOCK_SIZE;
for (let i = 0; i < blocks; i++) {
this._updateBlock(b32[i * 4 + 0], b32[i * 4 + 1], b32[i * 4 + 2], b32[i * 4 + 3]);
}
if (left) {
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
this._updateBlock(ZEROS32[0], ZEROS32[1], ZEROS32[2], ZEROS32[3]);
ZEROS32.fill(0); // clean tmp buffer
}
return this;
}
destroy() {
const { t } = this;
// clean precompute table
for (const elm of t) {
(elm.s0 = 0), (elm.s1 = 0), (elm.s2 = 0), (elm.s3 = 0);
}
}
digestInto(out) {
(0, _assert_js_1.exists)(this);
(0, _assert_js_1.output)(out, this);
this.finished = true;
const { s0, s1, s2, s3 } = this;
const o32 = (0, utils_js_1.u32)(out);
o32[0] = s0;
o32[1] = s1;
o32[2] = s2;
o32[3] = s3;
return out;
}
digest() {
const res = new Uint8Array(BLOCK_SIZE);
this.digestInto(res);
this.destroy();
return res;
}
}
class Polyval extends GHASH {
constructor(key, expectedLength) {
key = (0, utils_js_1.toBytes)(key);
const ghKey = _toGHASHKey(key.slice());
super(ghKey, expectedLength);
ghKey.fill(0);
}
update(data) {
data = (0, utils_js_1.toBytes)(data);
(0, _assert_js_1.exists)(this);
const b32 = (0, utils_js_1.u32)(data);
const left = data.length % BLOCK_SIZE;
const blocks = Math.floor(data.length / BLOCK_SIZE);
for (let i = 0; i < blocks; i++) {
this._updateBlock(swapLE(b32[i * 4 + 3]), swapLE(b32[i * 4 + 2]), swapLE(b32[i * 4 + 1]), swapLE(b32[i * 4 + 0]));
}
if (left) {
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
this._updateBlock(swapLE(ZEROS32[3]), swapLE(ZEROS32[2]), swapLE(ZEROS32[1]), swapLE(ZEROS32[0]));
ZEROS32.fill(0); // clean tmp buffer
}
return this;
}
digestInto(out) {
(0, _assert_js_1.exists)(this);
(0, _assert_js_1.output)(out, this);
this.finished = true;
// tmp ugly hack
const { s0, s1, s2, s3 } = this;
const o32 = (0, utils_js_1.u32)(out);
o32[0] = s0;
o32[1] = s1;
o32[2] = s2;
o32[3] = s3;
return out.reverse();
}
}
function wrapConstructorWithKey(hashCons) {
const hashC = (msg, key) => hashCons(key, msg.length).update((0, utils_js_1.toBytes)(msg)).digest();
const tmp = hashCons(new Uint8Array(16), 0);
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (key, expectedLength) => hashCons(key, expectedLength);
return hashC;
}
exports.ghash = wrapConstructorWithKey((key, expectedLength) => new GHASH(key, expectedLength));
exports.polyval = wrapConstructorWithKey((key, expectedLength) => new Polyval(key, expectedLength));
//# sourceMappingURL=_polyval.js.map

1
node_modules/@noble/ciphers/_polyval.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

86
node_modules/@noble/ciphers/aes.d.ts generated vendored Normal file
View File

@@ -0,0 +1,86 @@
import { Cipher, CipherWithOutput } from './utils.js';
export declare function expandKeyLE(key: Uint8Array): Uint32Array;
export declare function expandKeyDecLE(key: Uint8Array): Uint32Array;
declare function encrypt(xk: Uint32Array, s0: number, s1: number, s2: number, s3: number): {
s0: number;
s1: number;
s2: number;
s3: number;
};
declare function decrypt(xk: Uint32Array, s0: number, s1: number, s2: number, s3: number): {
s0: number;
s1: number;
s2: number;
s3: number;
};
declare function ctrCounter(xk: Uint32Array, nonce: Uint8Array, src: Uint8Array, dst?: Uint8Array): Uint8Array;
declare function ctr32(xk: Uint32Array, isLE: boolean, nonce: Uint8Array, src: Uint8Array, dst?: Uint8Array): Uint8Array;
/**
* CTR: counter mode. Creates stream cipher.
* Requires good IV. Parallelizable. OK, but no MAC.
*/
export declare const ctr: ((key: Uint8Array, nonce: Uint8Array) => CipherWithOutput) & {
blockSize: number;
nonceLength: number;
};
export type BlockOpts = {
disablePadding?: boolean;
};
/**
* ECB: Electronic CodeBook. Simple deterministic replacement.
* Dangerous: always map x to y. See [AES Penguin](https://words.filippo.io/the-ecb-penguin/).
*/
export declare const ecb: ((key: Uint8Array, opts?: BlockOpts) => CipherWithOutput) & {
blockSize: number;
};
/**
* CBC: Cipher-Block-Chaining. Key is previous rounds block.
* Fragile: needs proper padding. Unauthenticated: needs MAC.
*/
export declare const cbc: ((key: Uint8Array, iv: Uint8Array, opts?: BlockOpts) => CipherWithOutput) & {
blockSize: number;
nonceLength: number;
};
/**
* CFB: Cipher Feedback Mode. The input for the block cipher is the previous cipher output.
* Unauthenticated: needs MAC.
*/
export declare const cfb: ((key: Uint8Array, iv: Uint8Array) => CipherWithOutput) & {
blockSize: number;
nonceLength: number;
};
/**
* GCM: Galois/Counter Mode.
* Good, modern version of CTR, parallel, with MAC.
* Be careful: MACs can be forged.
*/
export declare const gcm: ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
/**
* AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
* Guarantees that, when a nonce is repeated, the only security loss is that identical
* plaintexts will produce identical ciphertexts.
* RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
*/
export declare const siv: ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => Cipher) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
declare function encryptBlock(xk: Uint32Array, block: Uint8Array): Uint8Array;
declare function decryptBlock(xk: Uint32Array, block: Uint8Array): Uint8Array;
export declare const unsafe: {
expandKeyLE: typeof expandKeyLE;
expandKeyDecLE: typeof expandKeyDecLE;
encrypt: typeof encrypt;
decrypt: typeof decrypt;
encryptBlock: typeof encryptBlock;
decryptBlock: typeof decryptBlock;
ctrCounter: typeof ctrCounter;
ctr32: typeof ctr32;
};
export {};
//# sourceMappingURL=aes.d.ts.map

1
node_modules/@noble/ciphers/aes.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"aes.d.ts","sourceRoot":"","sources":["src/aes.ts"],"names":[],"mappings":"AACA,OAAO,EACO,MAAM,EAAE,gBAAgB,EAErC,MAAM,YAAY,CAAC;AAmGpB,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,WAAW,CAmBxD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,WAAW,CAkB3D;AAwBD,iBAAS,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;;;;EAkB/E;AAED,iBAAS,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;;;;;EAkB/E;AAWD,iBAAS,UAAU,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,UAAU,cAmCxF;AAKD,iBAAS,KAAK,CACZ,EAAE,EAAE,WAAW,EACf,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,UAAU,EACf,GAAG,CAAC,EAAE,UAAU,cAiCjB;AAED;;;GAGG;AACH,eAAO,MAAM,GAAG,SAEI,UAAU,SAAS,UAAU,KAAG,gBAAgB;;;CAgBnE,CAAC;AAgDF,MAAM,MAAM,SAAS,GAAG;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,GAAG,SAEI,UAAU,SAAQ,SAAS,KAAQ,gBAAgB;;CAoCtE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,GAAG,SAEI,UAAU,MAAM,UAAU,SAAQ,SAAS,KAAQ,gBAAgB;;;CA+CtF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,GAAG,SAEI,UAAU,MAAM,UAAU,KAAG,gBAAgB;;;CAqChE,CAAC;AAqBF;;;;GAIG;AACH,eAAO,MAAM,GAAG,SAEI,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAAM;;;;CAyD3E,CAAC;AAOF;;;;;GAKG;AACH,eAAO,MAAM,GAAG,SAEI,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,MAAM;;;;CAqF3E,CAAC;AAUF,iBAAS,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,cAOvD;AAED,iBAAS,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,cAOvD;AAID,eAAO,MAAM,MAAM;;;;;;;;;CASlB,CAAC"}

675
node_modules/@noble/ciphers/aes.js generated vendored Normal file
View File

@@ -0,0 +1,675 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unsafe = exports.siv = exports.gcm = exports.cfb = exports.cbc = exports.ecb = exports.ctr = exports.expandKeyDecLE = exports.expandKeyLE = void 0;
// prettier-ignore
const utils_js_1 = require("./utils.js");
const _polyval_js_1 = require("./_polyval.js");
const _assert_js_1 = require("./_assert.js");
/*
AES (Advanced Encryption Standard) aka Rijndael block cipher.
Data is split into 128-bit blocks. Encrypted in 10/12/14 rounds (128/192/256 bits). In every round:
1. **S-box**, table substitution
2. **Shift rows**, cyclic shift left of all rows of data array
3. **Mix columns**, multiplying every column by fixed polynomial
4. **Add round key**, round_key xor i-th column of array
Resources:
- FIPS-197 https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf
- Original proposal: https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf
*/
const BLOCK_SIZE = 16;
const BLOCK_SIZE32 = 4;
const EMPTY_BLOCK = new Uint8Array(BLOCK_SIZE);
const POLY = 0x11b; // 1 + x + x**3 + x**4 + x**8
// TODO: remove multiplication, binary ops only
function mul2(n) {
return (n << 1) ^ (POLY & -(n >> 7));
}
function mul(a, b) {
let res = 0;
for (; b > 0; b >>= 1) {
// Montgomery ladder
res ^= a & -(b & 1); // if (b&1) res ^=a (but const-time).
a = mul2(a); // a = 2*a
}
return res;
}
// AES S-box is generated using finite field inversion,
// an affine transform, and xor of a constant 0x63.
const sbox = /* @__PURE__ */ (() => {
let t = new Uint8Array(256);
for (let i = 0, x = 1; i < 256; i++, x ^= mul2(x))
t[i] = x;
const box = new Uint8Array(256);
box[0] = 0x63; // first elm
for (let i = 0; i < 255; i++) {
let x = t[255 - i];
x |= x << 8;
box[t[i]] = (x ^ (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7) ^ 0x63) & 0xff;
}
return box;
})();
// Inverted S-box
const invSbox = /* @__PURE__ */ sbox.map((_, j) => sbox.indexOf(j));
// Rotate u32 by 8
const rotr32_8 = (n) => (n << 24) | (n >>> 8);
const rotl32_8 = (n) => (n << 8) | (n >>> 24);
// T-table is optimization suggested in 5.2 of original proposal (missed from FIPS-197). Changes:
// - LE instead of BE
// - bigger tables: T0 and T1 are merged into T01 table and T2 & T3 into T23;
// so index is u16, instead of u8. This speeds up things, unexpectedly
function genTtable(sbox, fn) {
if (sbox.length !== 256)
throw new Error('Wrong sbox length');
const T0 = new Uint32Array(256).map((_, j) => fn(sbox[j]));
const T1 = T0.map(rotl32_8);
const T2 = T1.map(rotl32_8);
const T3 = T2.map(rotl32_8);
const T01 = new Uint32Array(256 * 256);
const T23 = new Uint32Array(256 * 256);
const sbox2 = new Uint16Array(256 * 256);
for (let i = 0; i < 256; i++) {
for (let j = 0; j < 256; j++) {
const idx = i * 256 + j;
T01[idx] = T0[i] ^ T1[j];
T23[idx] = T2[i] ^ T3[j];
sbox2[idx] = (sbox[i] << 8) | sbox[j];
}
}
return { sbox, sbox2, T0, T1, T2, T3, T01, T23 };
}
const tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => (mul(s, 3) << 24) | (s << 16) | (s << 8) | mul(s, 2));
const tableDecoding = /* @__PURE__ */ genTtable(invSbox, (s) => (mul(s, 11) << 24) | (mul(s, 13) << 16) | (mul(s, 9) << 8) | mul(s, 14));
const xPowers = /* @__PURE__ */ (() => {
const p = new Uint8Array(16);
for (let i = 0, x = 1; i < 16; i++, x = mul2(x))
p[i] = x;
return p;
})();
function expandKeyLE(key) {
(0, _assert_js_1.bytes)(key);
const len = key.length;
if (![16, 24, 32].includes(len))
throw new Error(`aes: wrong key size: should be 16, 24 or 32, got: ${len}`);
const { sbox2 } = tableEncoding;
const k32 = (0, utils_js_1.u32)(key);
const Nk = k32.length;
const subByte = (n) => applySbox(sbox2, n, n, n, n);
const xk = new Uint32Array(len + 28); // expanded key
xk.set(k32);
// 4.3.1 Key expansion
for (let i = Nk; i < xk.length; i++) {
let t = xk[i - 1];
if (i % Nk === 0)
t = subByte(rotr32_8(t)) ^ xPowers[i / Nk - 1];
else if (Nk > 6 && i % Nk === 4)
t = subByte(t);
xk[i] = xk[i - Nk] ^ t;
}
return xk;
}
exports.expandKeyLE = expandKeyLE;
function expandKeyDecLE(key) {
const encKey = expandKeyLE(key);
const xk = encKey.slice();
const Nk = encKey.length;
const { sbox2 } = tableEncoding;
const { T0, T1, T2, T3 } = tableDecoding;
// Inverse key by chunks of 4 (rounds)
for (let i = 0; i < Nk; i += 4) {
for (let j = 0; j < 4; j++)
xk[i + j] = encKey[Nk - i - 4 + j];
}
encKey.fill(0);
// apply InvMixColumn except first & last round
for (let i = 4; i < Nk - 4; i++) {
const x = xk[i];
const w = applySbox(sbox2, x, x, x, x);
xk[i] = T0[w & 0xff] ^ T1[(w >>> 8) & 0xff] ^ T2[(w >>> 16) & 0xff] ^ T3[w >>> 24];
}
return xk;
}
exports.expandKeyDecLE = expandKeyDecLE;
// Apply tables
function apply0123(T01, T23, s0, s1, s2, s3) {
return (T01[((s0 << 8) & 0xff00) | ((s1 >>> 8) & 0xff)] ^
T23[((s2 >>> 8) & 0xff00) | ((s3 >>> 24) & 0xff)]);
}
function applySbox(sbox2, s0, s1, s2, s3) {
return (sbox2[(s0 & 0xff) | (s1 & 0xff00)] |
(sbox2[((s2 >>> 16) & 0xff) | ((s3 >>> 16) & 0xff00)] << 16));
}
function encrypt(xk, s0, s1, s2, s3) {
const { sbox2, T01, T23 } = tableEncoding;
let k = 0;
(s0 ^= xk[k++]), (s1 ^= xk[k++]), (s2 ^= xk[k++]), (s3 ^= xk[k++]);
const rounds = xk.length / 4 - 2;
for (let i = 0; i < rounds; i++) {
const t0 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
const t1 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
const t2 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
const t3 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
(s0 = t0), (s1 = t1), (s2 = t2), (s3 = t3);
}
// last round (without mixcolumns, so using SBOX2 table)
const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
return { s0: t0, s1: t1, s2: t2, s3: t3 };
}
function decrypt(xk, s0, s1, s2, s3) {
const { sbox2, T01, T23 } = tableDecoding;
let k = 0;
(s0 ^= xk[k++]), (s1 ^= xk[k++]), (s2 ^= xk[k++]), (s3 ^= xk[k++]);
const rounds = xk.length / 4 - 2;
for (let i = 0; i < rounds; i++) {
const t0 = xk[k++] ^ apply0123(T01, T23, s0, s3, s2, s1);
const t1 = xk[k++] ^ apply0123(T01, T23, s1, s0, s3, s2);
const t2 = xk[k++] ^ apply0123(T01, T23, s2, s1, s0, s3);
const t3 = xk[k++] ^ apply0123(T01, T23, s3, s2, s1, s0);
(s0 = t0), (s1 = t1), (s2 = t2), (s3 = t3);
}
// Last round
const t0 = xk[k++] ^ applySbox(sbox2, s0, s3, s2, s1);
const t1 = xk[k++] ^ applySbox(sbox2, s1, s0, s3, s2);
const t2 = xk[k++] ^ applySbox(sbox2, s2, s1, s0, s3);
const t3 = xk[k++] ^ applySbox(sbox2, s3, s2, s1, s0);
return { s0: t0, s1: t1, s2: t2, s3: t3 };
}
function getDst(len, dst) {
if (!dst)
return new Uint8Array(len);
(0, _assert_js_1.bytes)(dst);
if (dst.length < len)
throw new Error(`aes: wrong destination length, expected at least ${len}, got: ${dst.length}`);
return dst;
}
// TODO: investigate merging with ctr32
function ctrCounter(xk, nonce, src, dst) {
(0, _assert_js_1.bytes)(nonce, BLOCK_SIZE);
(0, _assert_js_1.bytes)(src);
const srcLen = src.length;
dst = getDst(srcLen, dst);
const ctr = nonce;
const c32 = (0, utils_js_1.u32)(ctr);
// Fill block (empty, ctr=0)
let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
const src32 = (0, utils_js_1.u32)(src);
const dst32 = (0, utils_js_1.u32)(dst);
// process blocks
for (let i = 0; i + 4 <= src32.length; i += 4) {
dst32[i + 0] = src32[i + 0] ^ s0;
dst32[i + 1] = src32[i + 1] ^ s1;
dst32[i + 2] = src32[i + 2] ^ s2;
dst32[i + 3] = src32[i + 3] ^ s3;
// Full 128 bit counter with wrap around
let carry = 1;
for (let i = ctr.length - 1; i >= 0; i--) {
carry = (carry + (ctr[i] & 0xff)) | 0;
ctr[i] = carry & 0xff;
carry >>>= 8;
}
({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
}
// leftovers (less than block)
// It's possible to handle > u32 fast, but is it worth it?
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
if (start < srcLen) {
const b32 = new Uint32Array([s0, s1, s2, s3]);
const buf = (0, utils_js_1.u8)(b32);
for (let i = start, pos = 0; i < srcLen; i++, pos++)
dst[i] = src[i] ^ buf[pos];
}
return dst;
}
// AES CTR with overflowing 32 bit counter
// It's possible to do 32le significantly simpler (and probably faster) by using u32.
// But, we need both, and perf bottleneck is in ghash anyway.
function ctr32(xk, isLE, nonce, src, dst) {
(0, _assert_js_1.bytes)(nonce, BLOCK_SIZE);
(0, _assert_js_1.bytes)(src);
dst = getDst(src.length, dst);
const ctr = nonce; // write new value to nonce, so it can be re-used
const c32 = (0, utils_js_1.u32)(ctr);
const view = (0, utils_js_1.createView)(ctr);
const src32 = (0, utils_js_1.u32)(src);
const dst32 = (0, utils_js_1.u32)(dst);
const ctrPos = isLE ? 0 : 12;
const srcLen = src.length;
// Fill block (empty, ctr=0)
let ctrNum = view.getUint32(ctrPos, isLE); // read current counter value
let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
// process blocks
for (let i = 0; i + 4 <= src32.length; i += 4) {
dst32[i + 0] = src32[i + 0] ^ s0;
dst32[i + 1] = src32[i + 1] ^ s1;
dst32[i + 2] = src32[i + 2] ^ s2;
dst32[i + 3] = src32[i + 3] ^ s3;
ctrNum = (ctrNum + 1) >>> 0; // u32 wrap
view.setUint32(ctrPos, ctrNum, isLE);
({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
}
// leftovers (less than a block)
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
if (start < srcLen) {
const b32 = new Uint32Array([s0, s1, s2, s3]);
const buf = (0, utils_js_1.u8)(b32);
for (let i = start, pos = 0; i < srcLen; i++, pos++)
dst[i] = src[i] ^ buf[pos];
}
return dst;
}
/**
* CTR: counter mode. Creates stream cipher.
* Requires good IV. Parallelizable. OK, but no MAC.
*/
exports.ctr = (0, utils_js_1.wrapCipher)({ blockSize: 16, nonceLength: 16 }, function ctr(key, nonce) {
(0, _assert_js_1.bytes)(key);
(0, _assert_js_1.bytes)(nonce, BLOCK_SIZE);
function processCtr(buf, dst) {
const xk = expandKeyLE(key);
const n = nonce.slice();
const out = ctrCounter(xk, n, buf, dst);
xk.fill(0);
n.fill(0);
return out;
}
return {
encrypt: (plaintext, dst) => processCtr(plaintext, dst),
decrypt: (ciphertext, dst) => processCtr(ciphertext, dst),
};
});
function validateBlockDecrypt(data) {
(0, _assert_js_1.bytes)(data);
if (data.length % BLOCK_SIZE !== 0) {
throw new Error(`aes/(cbc-ecb).decrypt ciphertext should consist of blocks with size ${BLOCK_SIZE}`);
}
}
function validateBlockEncrypt(plaintext, pcks5, dst) {
let outLen = plaintext.length;
const remaining = outLen % BLOCK_SIZE;
if (!pcks5 && remaining !== 0)
throw new Error('aec/(cbc-ecb): unpadded plaintext with disabled padding');
const b = (0, utils_js_1.u32)(plaintext);
if (pcks5) {
let left = BLOCK_SIZE - remaining;
if (!left)
left = BLOCK_SIZE; // if no bytes left, create empty padding block
outLen = outLen + left;
}
const out = getDst(outLen, dst);
const o = (0, utils_js_1.u32)(out);
return { b, o, out };
}
function validatePCKS(data, pcks5) {
if (!pcks5)
return data;
const len = data.length;
if (!len)
throw new Error(`aes/pcks5: empty ciphertext not allowed`);
const lastByte = data[len - 1];
if (lastByte <= 0 || lastByte > 16)
throw new Error(`aes/pcks5: wrong padding byte: ${lastByte}`);
const out = data.subarray(0, -lastByte);
for (let i = 0; i < lastByte; i++)
if (data[len - i - 1] !== lastByte)
throw new Error(`aes/pcks5: wrong padding`);
return out;
}
function padPCKS(left) {
const tmp = new Uint8Array(16);
const tmp32 = (0, utils_js_1.u32)(tmp);
tmp.set(left);
const paddingByte = BLOCK_SIZE - left.length;
for (let i = BLOCK_SIZE - paddingByte; i < BLOCK_SIZE; i++)
tmp[i] = paddingByte;
return tmp32;
}
/**
* ECB: Electronic CodeBook. Simple deterministic replacement.
* Dangerous: always map x to y. See [AES Penguin](https://words.filippo.io/the-ecb-penguin/).
*/
exports.ecb = (0, utils_js_1.wrapCipher)({ blockSize: 16 }, function ecb(key, opts = {}) {
(0, _assert_js_1.bytes)(key);
const pcks5 = !opts.disablePadding;
return {
encrypt: (plaintext, dst) => {
(0, _assert_js_1.bytes)(plaintext);
const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
const xk = expandKeyLE(key);
let i = 0;
for (; i + 4 <= b.length;) {
const { s0, s1, s2, s3 } = encrypt(xk, b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
if (pcks5) {
const tmp32 = padPCKS(plaintext.subarray(i * 4));
const { s0, s1, s2, s3 } = encrypt(xk, tmp32[0], tmp32[1], tmp32[2], tmp32[3]);
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
xk.fill(0);
return _out;
},
decrypt: (ciphertext, dst) => {
validateBlockDecrypt(ciphertext);
const xk = expandKeyDecLE(key);
const out = getDst(ciphertext.length, dst);
const b = (0, utils_js_1.u32)(ciphertext);
const o = (0, utils_js_1.u32)(out);
for (let i = 0; i + 4 <= b.length;) {
const { s0, s1, s2, s3 } = decrypt(xk, b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
xk.fill(0);
return validatePCKS(out, pcks5);
},
};
});
/**
* CBC: Cipher-Block-Chaining. Key is previous rounds block.
* Fragile: needs proper padding. Unauthenticated: needs MAC.
*/
exports.cbc = (0, utils_js_1.wrapCipher)({ blockSize: 16, nonceLength: 16 }, function cbc(key, iv, opts = {}) {
(0, _assert_js_1.bytes)(key);
(0, _assert_js_1.bytes)(iv, 16);
const pcks5 = !opts.disablePadding;
return {
encrypt: (plaintext, dst) => {
const xk = expandKeyLE(key);
const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
const n32 = (0, utils_js_1.u32)(iv);
// prettier-ignore
let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
let i = 0;
for (; i + 4 <= b.length;) {
(s0 ^= b[i + 0]), (s1 ^= b[i + 1]), (s2 ^= b[i + 2]), (s3 ^= b[i + 3]);
({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
if (pcks5) {
const tmp32 = padPCKS(plaintext.subarray(i * 4));
(s0 ^= tmp32[0]), (s1 ^= tmp32[1]), (s2 ^= tmp32[2]), (s3 ^= tmp32[3]);
({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
xk.fill(0);
return _out;
},
decrypt: (ciphertext, dst) => {
validateBlockDecrypt(ciphertext);
const xk = expandKeyDecLE(key);
const n32 = (0, utils_js_1.u32)(iv);
const out = getDst(ciphertext.length, dst);
const b = (0, utils_js_1.u32)(ciphertext);
const o = (0, utils_js_1.u32)(out);
// prettier-ignore
let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
for (let i = 0; i + 4 <= b.length;) {
// prettier-ignore
const ps0 = s0, ps1 = s1, ps2 = s2, ps3 = s3;
(s0 = b[i + 0]), (s1 = b[i + 1]), (s2 = b[i + 2]), (s3 = b[i + 3]);
const { s0: o0, s1: o1, s2: o2, s3: o3 } = decrypt(xk, s0, s1, s2, s3);
(o[i++] = o0 ^ ps0), (o[i++] = o1 ^ ps1), (o[i++] = o2 ^ ps2), (o[i++] = o3 ^ ps3);
}
xk.fill(0);
return validatePCKS(out, pcks5);
},
};
});
/**
* CFB: Cipher Feedback Mode. The input for the block cipher is the previous cipher output.
* Unauthenticated: needs MAC.
*/
exports.cfb = (0, utils_js_1.wrapCipher)({ blockSize: 16, nonceLength: 16 }, function cfb(key, iv) {
(0, _assert_js_1.bytes)(key);
(0, _assert_js_1.bytes)(iv, 16);
function processCfb(src, isEncrypt, dst) {
const xk = expandKeyLE(key);
const srcLen = src.length;
dst = getDst(srcLen, dst);
const src32 = (0, utils_js_1.u32)(src);
const dst32 = (0, utils_js_1.u32)(dst);
const next32 = isEncrypt ? dst32 : src32;
const n32 = (0, utils_js_1.u32)(iv);
// prettier-ignore
let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
for (let i = 0; i + 4 <= src32.length;) {
const { s0: e0, s1: e1, s2: e2, s3: e3 } = encrypt(xk, s0, s1, s2, s3);
dst32[i + 0] = src32[i + 0] ^ e0;
dst32[i + 1] = src32[i + 1] ^ e1;
dst32[i + 2] = src32[i + 2] ^ e2;
dst32[i + 3] = src32[i + 3] ^ e3;
(s0 = next32[i++]), (s1 = next32[i++]), (s2 = next32[i++]), (s3 = next32[i++]);
}
// leftovers (less than block)
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
if (start < srcLen) {
({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
const buf = (0, utils_js_1.u8)(new Uint32Array([s0, s1, s2, s3]));
for (let i = start, pos = 0; i < srcLen; i++, pos++)
dst[i] = src[i] ^ buf[pos];
buf.fill(0);
}
xk.fill(0);
return dst;
}
return {
encrypt: (plaintext, dst) => processCfb(plaintext, true, dst),
decrypt: (ciphertext, dst) => processCfb(ciphertext, false, dst),
};
});
// TODO: merge with chacha, however gcm has bitLen while chacha has byteLen
function computeTag(fn, isLE, key, data, AAD) {
const h = fn.create(key, data.length + (AAD?.length || 0));
if (AAD)
h.update(AAD);
h.update(data);
const num = new Uint8Array(16);
const view = (0, utils_js_1.createView)(num);
if (AAD)
(0, utils_js_1.setBigUint64)(view, 0, BigInt(AAD.length * 8), isLE);
(0, utils_js_1.setBigUint64)(view, 8, BigInt(data.length * 8), isLE);
h.update(num);
return h.digest();
}
/**
* GCM: Galois/Counter Mode.
* Good, modern version of CTR, parallel, with MAC.
* Be careful: MACs can be forged.
*/
exports.gcm = (0, utils_js_1.wrapCipher)({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function gcm(key, nonce, AAD) {
(0, _assert_js_1.bytes)(nonce);
// Nonce can be pretty much anything (even 1 byte). But smaller nonces less secure.
if (nonce.length === 0)
throw new Error('aes/gcm: empty nonce');
const tagLength = 16;
function _computeTag(authKey, tagMask, data) {
const tag = computeTag(_polyval_js_1.ghash, false, authKey, data, AAD);
for (let i = 0; i < tagMask.length; i++)
tag[i] ^= tagMask[i];
return tag;
}
function deriveKeys() {
const xk = expandKeyLE(key);
const authKey = EMPTY_BLOCK.slice();
const counter = EMPTY_BLOCK.slice();
ctr32(xk, false, counter, counter, authKey);
if (nonce.length === 12) {
counter.set(nonce);
}
else {
// Spec (NIST 800-38d) supports variable size nonce.
// Not supported for now, but can be useful.
const nonceLen = EMPTY_BLOCK.slice();
const view = (0, utils_js_1.createView)(nonceLen);
(0, utils_js_1.setBigUint64)(view, 8, BigInt(nonce.length * 8), false);
// ghash(nonce || u64be(0) || u64be(nonceLen*8))
_polyval_js_1.ghash.create(authKey).update(nonce).update(nonceLen).digestInto(counter);
}
const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK);
return { xk, authKey, counter, tagMask };
}
return {
encrypt: (plaintext) => {
(0, _assert_js_1.bytes)(plaintext);
const { xk, authKey, counter, tagMask } = deriveKeys();
const out = new Uint8Array(plaintext.length + tagLength);
ctr32(xk, false, counter, plaintext, out);
const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
out.set(tag, plaintext.length);
xk.fill(0);
return out;
},
decrypt: (ciphertext) => {
(0, _assert_js_1.bytes)(ciphertext);
if (ciphertext.length < tagLength)
throw new Error(`aes/gcm: ciphertext less than tagLen (${tagLength})`);
const { xk, authKey, counter, tagMask } = deriveKeys();
const data = ciphertext.subarray(0, -tagLength);
const passedTag = ciphertext.subarray(-tagLength);
const tag = _computeTag(authKey, tagMask, data);
if (!(0, utils_js_1.equalBytes)(tag, passedTag))
throw new Error('aes/gcm: invalid ghash tag');
const out = ctr32(xk, false, counter, data);
authKey.fill(0);
tagMask.fill(0);
xk.fill(0);
return out;
},
};
});
const limit = (name, min, max) => (value) => {
if (!Number.isSafeInteger(value) || min > value || value > max)
throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
};
/**
* AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
* Guarantees that, when a nonce is repeated, the only security loss is that identical
* plaintexts will produce identical ciphertexts.
* RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
*/
exports.siv = (0, utils_js_1.wrapCipher)({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function siv(key, nonce, AAD) {
const tagLength = 16;
// From RFC 8452: Section 6
const AAD_LIMIT = limit('AAD', 0, 2 ** 36);
const PLAIN_LIMIT = limit('plaintext', 0, 2 ** 36);
const NONCE_LIMIT = limit('nonce', 12, 12);
const CIPHER_LIMIT = limit('ciphertext', 16, 2 ** 36 + 16);
(0, _assert_js_1.bytes)(nonce);
NONCE_LIMIT(nonce.length);
if (AAD) {
(0, _assert_js_1.bytes)(AAD);
AAD_LIMIT(AAD.length);
}
function deriveKeys() {
const len = key.length;
if (len !== 16 && len !== 24 && len !== 32)
throw new Error(`key length must be 16, 24 or 32 bytes, got: ${len} bytes`);
const xk = expandKeyLE(key);
const encKey = new Uint8Array(len);
const authKey = new Uint8Array(16);
const n32 = (0, utils_js_1.u32)(nonce);
// prettier-ignore
let s0 = 0, s1 = n32[0], s2 = n32[1], s3 = n32[2];
let counter = 0;
for (const derivedKey of [authKey, encKey].map(utils_js_1.u32)) {
const d32 = (0, utils_js_1.u32)(derivedKey);
for (let i = 0; i < d32.length; i += 2) {
// aes(u32le(0) || nonce)[:8] || aes(u32le(1) || nonce)[:8] ...
const { s0: o0, s1: o1 } = encrypt(xk, s0, s1, s2, s3);
d32[i + 0] = o0;
d32[i + 1] = o1;
s0 = ++counter; // increment counter inside state
}
}
xk.fill(0);
return { authKey, encKey: expandKeyLE(encKey) };
}
function _computeTag(encKey, authKey, data) {
const tag = computeTag(_polyval_js_1.polyval, true, authKey, data, AAD);
// Compute the expected tag by XORing S_s and the nonce, clearing the
// most significant bit of the last byte and encrypting with the
// message-encryption key.
for (let i = 0; i < 12; i++)
tag[i] ^= nonce[i];
tag[15] &= 0x7f; // Clear the highest bit
// encrypt tag as block
const t32 = (0, utils_js_1.u32)(tag);
// prettier-ignore
let s0 = t32[0], s1 = t32[1], s2 = t32[2], s3 = t32[3];
({ s0, s1, s2, s3 } = encrypt(encKey, s0, s1, s2, s3));
(t32[0] = s0), (t32[1] = s1), (t32[2] = s2), (t32[3] = s3);
return tag;
}
// actual decrypt/encrypt of message.
function processSiv(encKey, tag, input) {
let block = tag.slice();
block[15] |= 0x80; // Force highest bit
return ctr32(encKey, true, block, input);
}
return {
encrypt: (plaintext) => {
(0, _assert_js_1.bytes)(plaintext);
PLAIN_LIMIT(plaintext.length);
const { encKey, authKey } = deriveKeys();
const tag = _computeTag(encKey, authKey, plaintext);
const out = new Uint8Array(plaintext.length + tagLength);
out.set(tag, plaintext.length);
out.set(processSiv(encKey, tag, plaintext));
encKey.fill(0);
authKey.fill(0);
return out;
},
decrypt: (ciphertext) => {
(0, _assert_js_1.bytes)(ciphertext);
CIPHER_LIMIT(ciphertext.length);
const tag = ciphertext.subarray(-tagLength);
const { encKey, authKey } = deriveKeys();
const plaintext = processSiv(encKey, tag, ciphertext.subarray(0, -tagLength));
const expectedTag = _computeTag(encKey, authKey, plaintext);
encKey.fill(0);
authKey.fill(0);
if (!(0, utils_js_1.equalBytes)(tag, expectedTag))
throw new Error('invalid polyval tag');
return plaintext;
},
};
});
function isBytes32(a) {
return (a != null &&
typeof a === 'object' &&
(a instanceof Uint32Array || a.constructor.name === 'Uint32Array'));
}
function encryptBlock(xk, block) {
(0, _assert_js_1.bytes)(block, 16);
if (!isBytes32(xk))
throw new Error('_encryptBlock accepts result of expandKeyLE');
const b32 = (0, utils_js_1.u32)(block);
let { s0, s1, s2, s3 } = encrypt(xk, b32[0], b32[1], b32[2], b32[3]);
(b32[0] = s0), (b32[1] = s1), (b32[2] = s2), (b32[3] = s3);
return block;
}
function decryptBlock(xk, block) {
(0, _assert_js_1.bytes)(block, 16);
if (!isBytes32(xk))
throw new Error('_decryptBlock accepts result of expandKeyLE');
const b32 = (0, utils_js_1.u32)(block);
let { s0, s1, s2, s3 } = decrypt(xk, b32[0], b32[1], b32[2], b32[3]);
(b32[0] = s0), (b32[1] = s1), (b32[2] = s2), (b32[3] = s3);
return block;
}
// Highly unsafe private functions for implementing new modes or ciphers based on AES
// Can change at any time, no API guarantees
exports.unsafe = {
expandKeyLE,
expandKeyDecLE,
encrypt,
decrypt,
encryptBlock,
decryptBlock,
ctrCounter,
ctr32,
};
//# sourceMappingURL=aes.js.map

1
node_modules/@noble/ciphers/aes.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

61
node_modules/@noble/ciphers/chacha.d.ts generated vendored Normal file
View File

@@ -0,0 +1,61 @@
import { CipherWithOutput, XorStream } from './utils.js';
/**
* hchacha helper method, used primarily in xchacha, to hash
* key and nonce into key' and nonce'.
* Same as chachaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
export declare function hchacha(s: Uint32Array, k: Uint32Array, i: Uint32Array, o32: Uint32Array): void;
/**
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
*/
export declare const chacha20orig: XorStream;
/**
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export declare const chacha20: XorStream;
/**
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export declare const xchacha20: XorStream;
/**
* Reduced 8-round chacha, described in original paper.
*/
export declare const chacha8: XorStream;
/**
* Reduced 12-round chacha, described in original paper.
*/
export declare const chacha12: XorStream;
/**
* AEAD algorithm from RFC 8439.
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
* We could have composed them similar to:
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
* But it's hard because of authKey:
* In salsa20, authKey changes position in salsa stream.
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
*/
export declare const _poly1305_aead: (xorStream: XorStream) => (key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => CipherWithOutput;
/**
* ChaCha20-Poly1305 from RFC 8439.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export declare const chacha20poly1305: ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => CipherWithOutput) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
/**
* XChaCha20-Poly1305 extended-nonce chacha.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export declare const xchacha20poly1305: ((key: Uint8Array, nonce: Uint8Array, AAD?: Uint8Array) => CipherWithOutput) & {
blockSize: number;
nonceLength: number;
tagLength: number;
};
//# sourceMappingURL=chacha.d.ts.map

1
node_modules/@noble/ciphers/chacha.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"chacha.d.ts","sourceRoot":"","sources":["src/chacha.ts"],"names":[],"mappings":"AACA,OAAO,EACO,gBAAgB,EAAE,SAAS,EACxC,MAAM,YAAY,CAAC;AA6EpB;;;;;GAKG;AAEH,wBAAgB,OAAO,CACrB,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,QAoDjE;AACD;;GAEG;AACH,eAAO,MAAM,YAAY,WAIvB,CAAC;AACH;;;GAGG;AACH,eAAO,MAAM,QAAQ,WAInB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,SAAS,WAKpB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,OAAO,WAIlB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,WAInB,CAAC;AAgCH;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,cACb,SAAS,WACf,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,gBAoCvD,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,gBAAgB,SA1CrB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,gBAAgB;;;;CA6CzE,CAAC;AACF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,SAnDtB,UAAU,SAAS,UAAU,QAAQ,UAAU,KAAG,gBAAgB;;;;CAsDzE,CAAC"}

323
node_modules/@noble/ciphers/chacha.js generated vendored Normal file
View File

@@ -0,0 +1,323 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.xchacha20poly1305 = exports.chacha20poly1305 = exports._poly1305_aead = exports.chacha12 = exports.chacha8 = exports.xchacha20 = exports.chacha20 = exports.chacha20orig = exports.hchacha = void 0;
// prettier-ignore
const utils_js_1 = require("./utils.js");
const _poly1305_js_1 = require("./_poly1305.js");
const _arx_js_1 = require("./_arx.js");
const _assert_js_1 = require("./_assert.js");
// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase
// the diffusion per round, but had slightly less cryptanalysis.
// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf
/**
* ChaCha core function.
*/
// prettier-ignore
function chachaCore(s, k, n, out, cnt, rounds = 20) {
let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], // "expa" "nd 3" "2-by" "te k"
y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], // Key Key Key Key
y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], // Key Key Key Key
y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Counter Nonce Nonce
// Save state to temporary variables
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
for (let r = 0; r < rounds; r += 2) {
x00 = (x00 + x04) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x00, 16);
x08 = (x08 + x12) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x08, 12);
x00 = (x00 + x04) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x00, 8);
x08 = (x08 + x12) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x08, 7);
x01 = (x01 + x05) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x01, 16);
x09 = (x09 + x13) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x09, 12);
x01 = (x01 + x05) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x01, 8);
x09 = (x09 + x13) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x09, 7);
x02 = (x02 + x06) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x02, 16);
x10 = (x10 + x14) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x10, 12);
x02 = (x02 + x06) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x02, 8);
x10 = (x10 + x14) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x10, 7);
x03 = (x03 + x07) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x03, 16);
x11 = (x11 + x15) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x11, 12);
x03 = (x03 + x07) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x03, 8);
x11 = (x11 + x15) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x11, 7);
x00 = (x00 + x05) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x00, 16);
x10 = (x10 + x15) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x10, 12);
x00 = (x00 + x05) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x00, 8);
x10 = (x10 + x15) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x10, 7);
x01 = (x01 + x06) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x01, 16);
x11 = (x11 + x12) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x11, 12);
x01 = (x01 + x06) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x01, 8);
x11 = (x11 + x12) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x11, 7);
x02 = (x02 + x07) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x02, 16);
x08 = (x08 + x13) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x08, 12);
x02 = (x02 + x07) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x02, 8);
x08 = (x08 + x13) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x08, 7);
x03 = (x03 + x04) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x03, 16);
x09 = (x09 + x14) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x09, 12);
x03 = (x03 + x04) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x03, 8);
x09 = (x09 + x14) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x09, 7);
}
// Write output
let oi = 0;
out[oi++] = (y00 + x00) | 0;
out[oi++] = (y01 + x01) | 0;
out[oi++] = (y02 + x02) | 0;
out[oi++] = (y03 + x03) | 0;
out[oi++] = (y04 + x04) | 0;
out[oi++] = (y05 + x05) | 0;
out[oi++] = (y06 + x06) | 0;
out[oi++] = (y07 + x07) | 0;
out[oi++] = (y08 + x08) | 0;
out[oi++] = (y09 + x09) | 0;
out[oi++] = (y10 + x10) | 0;
out[oi++] = (y11 + x11) | 0;
out[oi++] = (y12 + x12) | 0;
out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0;
out[oi++] = (y15 + x15) | 0;
}
/**
* hchacha helper method, used primarily in xchacha, to hash
* key and nonce into key' and nonce'.
* Same as chachaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
// prettier-ignore
function hchacha(s, k, i, o32) {
let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i[0], x13 = i[1], x14 = i[2], x15 = i[3];
for (let r = 0; r < 20; r += 2) {
x00 = (x00 + x04) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x00, 16);
x08 = (x08 + x12) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x08, 12);
x00 = (x00 + x04) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x00, 8);
x08 = (x08 + x12) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x08, 7);
x01 = (x01 + x05) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x01, 16);
x09 = (x09 + x13) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x09, 12);
x01 = (x01 + x05) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x01, 8);
x09 = (x09 + x13) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x09, 7);
x02 = (x02 + x06) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x02, 16);
x10 = (x10 + x14) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x10, 12);
x02 = (x02 + x06) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x02, 8);
x10 = (x10 + x14) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x10, 7);
x03 = (x03 + x07) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x03, 16);
x11 = (x11 + x15) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x11, 12);
x03 = (x03 + x07) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x03, 8);
x11 = (x11 + x15) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x11, 7);
x00 = (x00 + x05) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x00, 16);
x10 = (x10 + x15) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x10, 12);
x00 = (x00 + x05) | 0;
x15 = (0, _arx_js_1.rotl)(x15 ^ x00, 8);
x10 = (x10 + x15) | 0;
x05 = (0, _arx_js_1.rotl)(x05 ^ x10, 7);
x01 = (x01 + x06) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x01, 16);
x11 = (x11 + x12) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x11, 12);
x01 = (x01 + x06) | 0;
x12 = (0, _arx_js_1.rotl)(x12 ^ x01, 8);
x11 = (x11 + x12) | 0;
x06 = (0, _arx_js_1.rotl)(x06 ^ x11, 7);
x02 = (x02 + x07) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x02, 16);
x08 = (x08 + x13) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x08, 12);
x02 = (x02 + x07) | 0;
x13 = (0, _arx_js_1.rotl)(x13 ^ x02, 8);
x08 = (x08 + x13) | 0;
x07 = (0, _arx_js_1.rotl)(x07 ^ x08, 7);
x03 = (x03 + x04) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x03, 16);
x09 = (x09 + x14) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x09, 12);
x03 = (x03 + x04) | 0;
x14 = (0, _arx_js_1.rotl)(x14 ^ x03, 8);
x09 = (x09 + x14) | 0;
x04 = (0, _arx_js_1.rotl)(x04 ^ x09, 7);
}
let oi = 0;
o32[oi++] = x00;
o32[oi++] = x01;
o32[oi++] = x02;
o32[oi++] = x03;
o32[oi++] = x12;
o32[oi++] = x13;
o32[oi++] = x14;
o32[oi++] = x15;
}
exports.hchacha = hchacha;
/**
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
*/
exports.chacha20orig = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 8,
allowShortKeys: true,
});
/**
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
exports.chacha20 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 4,
allowShortKeys: false,
});
/**
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
exports.xchacha20 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 8,
extendNonceFn: hchacha,
allowShortKeys: false,
});
/**
* Reduced 8-round chacha, described in original paper.
*/
exports.chacha8 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 8,
});
/**
* Reduced 12-round chacha, described in original paper.
*/
exports.chacha12 = (0, _arx_js_1.createCipher)(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 12,
});
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
// Pad to digest size with zeros
const updatePadded = (h, msg) => {
h.update(msg);
const left = msg.length % 16;
if (left)
h.update(ZEROS16.subarray(left));
};
const ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
function computeTag(fn, key, nonce, data, AAD) {
const authKey = fn(key, nonce, ZEROS32);
const h = _poly1305_js_1.poly1305.create(authKey);
if (AAD)
updatePadded(h, AAD);
updatePadded(h, data);
const num = new Uint8Array(16);
const view = (0, utils_js_1.createView)(num);
(0, utils_js_1.setBigUint64)(view, 0, BigInt(AAD ? AAD.length : 0), true);
(0, utils_js_1.setBigUint64)(view, 8, BigInt(data.length), true);
h.update(num);
const res = h.digest();
authKey.fill(0);
return res;
}
/**
* AEAD algorithm from RFC 8439.
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
* We could have composed them similar to:
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
* But it's hard because of authKey:
* In salsa20, authKey changes position in salsa stream.
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
*/
const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
const tagLength = 16;
(0, _assert_js_1.bytes)(key, 32);
(0, _assert_js_1.bytes)(nonce);
return {
encrypt: (plaintext, output) => {
const plength = plaintext.length;
const clength = plength + tagLength;
if (output) {
(0, _assert_js_1.bytes)(output, clength);
}
else {
output = new Uint8Array(clength);
}
xorStream(key, nonce, plaintext, output, 1);
const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
output.set(tag, plength); // append tag
return output;
},
decrypt: (ciphertext, output) => {
const clength = ciphertext.length;
const plength = clength - tagLength;
if (clength < tagLength)
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
if (output) {
(0, _assert_js_1.bytes)(output, plength);
}
else {
output = new Uint8Array(plength);
}
const data = ciphertext.subarray(0, -tagLength);
const passedTag = ciphertext.subarray(-tagLength);
const tag = computeTag(xorStream, key, nonce, data, AAD);
if (!(0, utils_js_1.equalBytes)(passedTag, tag))
throw new Error('invalid tag');
xorStream(key, nonce, data, output, 1);
return output;
},
};
};
exports._poly1305_aead = _poly1305_aead;
/**
* ChaCha20-Poly1305 from RFC 8439.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
exports.chacha20poly1305 = (0, utils_js_1.wrapCipher)({ blockSize: 64, nonceLength: 12, tagLength: 16 }, (0, exports._poly1305_aead)(exports.chacha20));
/**
* XChaCha20-Poly1305 extended-nonce chacha.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
exports.xchacha20poly1305 = (0, utils_js_1.wrapCipher)({ blockSize: 64, nonceLength: 24, tagLength: 16 }, (0, exports._poly1305_aead)(exports.xchacha20));
//# sourceMappingURL=chacha.js.map

1
node_modules/@noble/ciphers/chacha.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

3
node_modules/@noble/ciphers/crypto.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export declare function randomBytes(bytesLength?: number): Uint8Array;
export declare function getWebcryptoSubtle(): any;
//# sourceMappingURL=crypto.d.ts.map

1
node_modules/@noble/ciphers/crypto.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["src/crypto.ts"],"names":[],"mappings":"AAKA,wBAAgB,WAAW,CAAC,WAAW,SAAK,GAAG,UAAU,CAIxD;AAED,wBAAgB,kBAAkB,QAGjC"}

17
node_modules/@noble/ciphers/crypto.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWebcryptoSubtle = exports.randomBytes = void 0;
const cr = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
function randomBytes(bytesLength = 32) {
if (cr && typeof cr.getRandomValues === 'function')
return cr.getRandomValues(new Uint8Array(bytesLength));
throw new Error('crypto.getRandomValues must be defined');
}
exports.randomBytes = randomBytes;
function getWebcryptoSubtle() {
if (cr && typeof cr.subtle === 'object' && cr.subtle != null)
return cr.subtle;
throw new Error('crypto.subtle must be defined');
}
exports.getWebcryptoSubtle = getWebcryptoSubtle;
//# sourceMappingURL=crypto.js.map

1
node_modules/@noble/ciphers/crypto.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["src/crypto.ts"],"names":[],"mappings":";;;AAGA,MAAM,EAAE,GAAG,OAAO,UAAU,KAAK,QAAQ,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAEpG,SAAgB,WAAW,CAAC,WAAW,GAAG,EAAE;IAC1C,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,eAAe,KAAK,UAAU;QAChD,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAJD,kCAIC;AAED,SAAgB,kBAAkB;IAChC,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC,MAAM,CAAC;IAC/E,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACnD,CAAC;AAHD,gDAGC"}

3
node_modules/@noble/ciphers/cryptoNode.d.ts generated vendored Normal file
View File

@@ -0,0 +1,3 @@
export declare function randomBytes(bytesLength?: number): Uint8Array;
export declare function getWebcryptoSubtle(): any;
//# sourceMappingURL=cryptoNode.d.ts.map

1
node_modules/@noble/ciphers/cryptoNode.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cryptoNode.d.ts","sourceRoot":"","sources":["src/cryptoNode.ts"],"names":[],"mappings":"AAOA,wBAAgB,WAAW,CAAC,WAAW,SAAK,GAAG,UAAU,CAIxD;AAED,wBAAgB,kBAAkB,QAGjC"}

22
node_modules/@noble/ciphers/cryptoNode.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWebcryptoSubtle = exports.randomBytes = void 0;
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// See utils.ts for details.
// The file will throw on node.js 14 and earlier.
// @ts-ignore
const nc = require("node:crypto");
const cr = nc && typeof nc === 'object' && 'webcrypto' in nc ? nc.webcrypto : undefined;
function randomBytes(bytesLength = 32) {
if (cr && typeof cr.getRandomValues === 'function')
return cr.getRandomValues(new Uint8Array(bytesLength));
throw new Error('crypto.getRandomValues must be defined');
}
exports.randomBytes = randomBytes;
function getWebcryptoSubtle() {
if (cr && typeof cr.subtle === 'object' && cr.subtle != null)
return cr.subtle;
throw new Error('crypto.subtle must be defined');
}
exports.getWebcryptoSubtle = getWebcryptoSubtle;
//# sourceMappingURL=cryptoNode.js.map

1
node_modules/@noble/ciphers/cryptoNode.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cryptoNode.js","sourceRoot":"","sources":["src/cryptoNode.ts"],"names":[],"mappings":";;;AAAA,oFAAoF;AACpF,4BAA4B;AAC5B,iDAAiD;AACjD,aAAa;AACb,kCAAkC;AAClC,MAAM,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,WAAW,IAAI,EAAE,CAAC,CAAC,CAAE,EAAE,CAAC,SAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;AAEjG,SAAgB,WAAW,CAAC,WAAW,GAAG,EAAE;IAC1C,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,eAAe,KAAK,UAAU;QAChD,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAJD,kCAIC;AAED,SAAgB,kBAAkB;IAChC,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC,MAAM,CAAC;IAC/E,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACnD,CAAC;AAHD,gDAGC"}

170
node_modules/@noble/ciphers/esm/_arx.js generated vendored Normal file
View File

@@ -0,0 +1,170 @@
// Basic utils for ARX (add-rotate-xor) salsa and chacha ciphers.
import { number as anumber, bytes as abytes, bool as abool } from './_assert.js';
import { checkOpts, u32 } from './utils.js';
/*
RFC8439 requires multi-step cipher stream, where
authKey starts with counter: 0, actual msg with counter: 1.
For this, we need a way to re-use nonce / counter:
const counter = new Uint8Array(4);
chacha(..., counter, ...); // counter is now 1
chacha(..., counter, ...); // counter is now 2
This is complicated:
- 32-bit counters are enough, no need for 64-bit: max ArrayBuffer size in JS is 4GB
- Original papers don't allow mutating counters
- Counter overflow is undefined [^1]
- Idea A: allow providing (nonce | counter) instead of just nonce, re-use it
- Caveat: Cannot be re-used through all cases:
- * chacha has (counter | nonce)
- * xchacha has (nonce16 | counter | nonce16)
- Idea B: separate nonce / counter and provide separate API for counter re-use
- Caveat: there are different counter sizes depending on an algorithm.
- salsa & chacha also differ in structures of key & sigma:
salsa20: s[0] | k(4) | s[1] | nonce(2) | ctr(2) | s[2] | k(4) | s[3]
chacha: s(4) | k(8) | ctr(1) | nonce(3)
chacha20orig: s(4) | k(8) | ctr(2) | nonce(2)
- Idea C: helper method such as `setSalsaState(key, nonce, sigma, data)`
- Caveat: we can't re-use counter array
xchacha [^2] uses the subkey and remaining 8 byte nonce with ChaCha20 as normal
(prefixed by 4 NUL bytes, since [RFC8439] specifies a 12-byte nonce).
[^1]: https://mailarchive.ietf.org/arch/msg/cfrg/gsOnTJzcbgG6OqD8Sc0GO5aR_tU/
[^2]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#appendix-A.2
*/
// We can't make top-level var depend on utils.utf8ToBytes
// because it's not present in all envs. Creating a similar fn here
const _utf8ToBytes = (str) => Uint8Array.from(str.split('').map((c) => c.charCodeAt(0)));
const sigma16 = _utf8ToBytes('expand 16-byte k');
const sigma32 = _utf8ToBytes('expand 32-byte k');
const sigma16_32 = u32(sigma16);
const sigma32_32 = u32(sigma32);
export const sigma = sigma32_32.slice();
export function rotl(a, b) {
return (a << b) | (a >>> (32 - b));
}
// Is byte array aligned to 4 byte offset (u32)?
function isAligned32(b) {
return b.byteOffset % 4 === 0;
}
// Salsa and Chacha block length is always 512-bit
const BLOCK_LEN = 64;
const BLOCK_LEN32 = 16;
// new Uint32Array([2**32]) // => Uint32Array(1) [ 0 ]
// new Uint32Array([2**32-1]) // => Uint32Array(1) [ 4294967295 ]
const MAX_COUNTER = 2 ** 32 - 1;
const U32_EMPTY = new Uint32Array();
function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
const len = data.length;
const block = new Uint8Array(BLOCK_LEN);
const b32 = u32(block);
// Make sure that buffers aligned to 4 bytes
const isAligned = isAligned32(data) && isAligned32(output);
const d32 = isAligned ? u32(data) : U32_EMPTY;
const o32 = isAligned ? u32(output) : U32_EMPTY;
for (let pos = 0; pos < len; counter++) {
core(sigma, key, nonce, b32, counter, rounds);
if (counter >= MAX_COUNTER)
throw new Error('arx: counter overflow');
const take = Math.min(BLOCK_LEN, len - pos);
// aligned to 4 bytes
if (isAligned && take === BLOCK_LEN) {
const pos32 = pos / 4;
if (pos % 4 !== 0)
throw new Error('arx: invalid block position');
for (let j = 0, posj; j < BLOCK_LEN32; j++) {
posj = pos32 + j;
o32[posj] = d32[posj] ^ b32[j];
}
pos += BLOCK_LEN;
continue;
}
for (let j = 0, posj; j < take; j++) {
posj = pos + j;
output[posj] = data[posj] ^ block[j];
}
pos += take;
}
}
export function createCipher(core, opts) {
const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
if (typeof core !== 'function')
throw new Error('core must be a function');
anumber(counterLength);
anumber(rounds);
abool(counterRight);
abool(allowShortKeys);
return (key, nonce, data, output, counter = 0) => {
abytes(key);
abytes(nonce);
abytes(data);
const len = data.length;
if (!output)
output = new Uint8Array(len);
abytes(output);
anumber(counter);
if (counter < 0 || counter >= MAX_COUNTER)
throw new Error('arx: counter overflow');
if (output.length < len)
throw new Error(`arx: output (${output.length}) is shorter than data (${len})`);
const toClean = [];
// Key & sigma
// key=16 -> sigma16, k=key|key
// key=32 -> sigma32, k=key
let l = key.length, k, sigma;
if (l === 32) {
k = key.slice();
toClean.push(k);
sigma = sigma32_32;
}
else if (l === 16 && allowShortKeys) {
k = new Uint8Array(32);
k.set(key);
k.set(key, 16);
sigma = sigma16_32;
toClean.push(k);
}
else {
throw new Error(`arx: invalid 32-byte key, got length=${l}`);
}
// Nonce
// salsa20: 8 (8-byte counter)
// chacha20orig: 8 (8-byte counter)
// chacha20: 12 (4-byte counter)
// xsalsa20: 24 (16 -> hsalsa, 8 -> old nonce)
// xchacha20: 24 (16 -> hchacha, 8 -> old nonce)
// Align nonce to 4 bytes
if (!isAligned32(nonce)) {
nonce = nonce.slice();
toClean.push(nonce);
}
const k32 = u32(k);
// hsalsa & hchacha: handle extended nonce
if (extendNonceFn) {
if (nonce.length !== 24)
throw new Error(`arx: extended nonce must be 24 bytes`);
extendNonceFn(sigma, k32, u32(nonce.subarray(0, 16)), k32);
nonce = nonce.subarray(16);
}
// Handle nonce counter
const nonceNcLen = 16 - counterLength;
if (nonceNcLen !== nonce.length)
throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);
// Pad counter when nonce is 64 bit
if (nonceNcLen !== 12) {
const nc = new Uint8Array(12);
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
nonce = nc;
toClean.push(nonce);
}
const n32 = u32(nonce);
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
while (toClean.length > 0)
toClean.pop().fill(0);
return output;
};
}
//# sourceMappingURL=_arx.js.map

1
node_modules/@noble/ciphers/esm/_arx.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

41
node_modules/@noble/ciphers/esm/_assert.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
function number(n) {
if (!Number.isSafeInteger(n) || n < 0)
throw new Error(`positive integer expected, not ${n}`);
}
function bool(b) {
if (typeof b !== 'boolean')
throw new Error(`boolean expected, not ${b}`);
}
export function isBytes(a) {
return (a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
}
function bytes(b, ...lengths) {
if (!isBytes(b))
throw new Error('Uint8Array expected');
if (lengths.length > 0 && !lengths.includes(b.length))
throw new Error(`Uint8Array expected of length ${lengths}, not of length=${b.length}`);
}
function hash(hash) {
if (typeof hash !== 'function' || typeof hash.create !== 'function')
throw new Error('hash must be wrapped by utils.wrapConstructor');
number(hash.outputLen);
number(hash.blockLen);
}
function exists(instance, checkFinished = true) {
if (instance.destroyed)
throw new Error('Hash instance has been destroyed');
if (checkFinished && instance.finished)
throw new Error('Hash#digest() has already been called');
}
function output(out, instance) {
bytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error(`digestInto() expects output buffer of length at least ${min}`);
}
}
export { number, bool, bytes, hash, exists, output };
const assert = { number, bool, bytes, hash, exists, output };
export default assert;
//# sourceMappingURL=_assert.js.map

1
node_modules/@noble/ciphers/esm/_assert.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"_assert.js","sourceRoot":"","sources":["../src/_assert.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,CAAS;IACvB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,EAAE,CAAC,CAAC;AAChG,CAAC;AAED,SAAS,IAAI,CAAC,CAAU;IACtB,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,CAAU;IAChC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,KAAK,YAAY,CAAC,CAC5E,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,CAAyB,EAAE,GAAG,OAAiB;IAC5D,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3F,CAAC;AAQD,SAAS,IAAI,CAAC,IAAU;IACtB,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU;QACjE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,MAAM,CAAC,QAAa,EAAE,aAAa,GAAG,IAAI;IACjD,IAAI,QAAQ,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAC5E,IAAI,aAAa,IAAI,QAAQ,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,MAAM,CAAC,GAAQ,EAAE,QAAa;IACrC,KAAK,CAAC,GAAG,CAAC,CAAC;IACX,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC;IAC/B,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,yDAAyD,GAAG,EAAE,CAAC,CAAC;IAClF,CAAC;AACH,CAAC;AAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACrD,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC7D,eAAe,MAAM,CAAC"}

287
node_modules/@noble/ciphers/esm/_micro.js generated vendored Normal file
View File

@@ -0,0 +1,287 @@
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
// prettier-ignore
import { createView, setBigUint64, wrapCipher, bytesToHex, concatBytes, equalBytes, hexToNumber, numberToBytesBE, } from './utils.js';
import { createCipher, rotl } from './_arx.js';
import { bytes as abytes } from './_assert.js';
/*
noble-ciphers-micro: more auditable, but slower version of salsa20, chacha & poly1305.
Implements the same algorithms that are present in other files, but without
unrolled loops (https://en.wikipedia.org/wiki/Loop_unrolling).
*/
function bytesToNumberLE(bytes) {
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
}
function numberToBytesLE(n, len) {
return numberToBytesBE(n, len).reverse();
}
function salsaQR(x, a, b, c, d) {
x[b] ^= rotl((x[a] + x[d]) | 0, 7);
x[c] ^= rotl((x[b] + x[a]) | 0, 9);
x[d] ^= rotl((x[c] + x[b]) | 0, 13);
x[a] ^= rotl((x[d] + x[c]) | 0, 18);
}
// prettier-ignore
function chachaQR(x, a, b, c, d) {
x[a] = (x[a] + x[b]) | 0;
x[d] = rotl(x[d] ^ x[a], 16);
x[c] = (x[c] + x[d]) | 0;
x[b] = rotl(x[b] ^ x[c], 12);
x[a] = (x[a] + x[b]) | 0;
x[d] = rotl(x[d] ^ x[a], 8);
x[c] = (x[c] + x[d]) | 0;
x[b] = rotl(x[b] ^ x[c], 7);
}
function salsaRound(x, rounds = 20) {
for (let r = 0; r < rounds; r += 2) {
salsaQR(x, 0, 4, 8, 12);
salsaQR(x, 5, 9, 13, 1);
salsaQR(x, 10, 14, 2, 6);
salsaQR(x, 15, 3, 7, 11);
salsaQR(x, 0, 1, 2, 3);
salsaQR(x, 5, 6, 7, 4);
salsaQR(x, 10, 11, 8, 9);
salsaQR(x, 15, 12, 13, 14);
}
}
function chachaRound(x, rounds = 20) {
for (let r = 0; r < rounds; r += 2) {
chachaQR(x, 0, 4, 8, 12);
chachaQR(x, 1, 5, 9, 13);
chachaQR(x, 2, 6, 10, 14);
chachaQR(x, 3, 7, 11, 15);
chachaQR(x, 0, 5, 10, 15);
chachaQR(x, 1, 6, 11, 12);
chachaQR(x, 2, 7, 8, 13);
chachaQR(x, 3, 4, 9, 14);
}
}
function salsaCore(s, k, n, out, cnt, rounds = 20) {
// prettier-ignore
const y = new Uint32Array([
s[0], k[0], k[1], k[2], // "expa" Key Key Key
k[3], s[1], n[0], n[1], // Key "nd 3" Nonce Nonce
cnt, 0, s[2], k[4], // Pos. Pos. "2-by" Key
k[5], k[6], k[7], s[3], // Key Key Key "te k"
]);
const x = y.slice();
salsaRound(x, rounds);
for (let i = 0; i < 16; i++)
out[i] = (y[i] + x[i]) | 0;
}
// prettier-ignore
export function hsalsa(s, k, i, o32) {
const x = new Uint32Array([
s[0], k[0], k[1], k[2],
k[3], s[1], i[0], i[1],
i[2], i[3], s[2], k[4],
k[5], k[6], k[7], s[3]
]);
salsaRound(x, 20);
let oi = 0;
o32[oi++] = x[0];
o32[oi++] = x[5];
o32[oi++] = x[10];
o32[oi++] = x[15];
o32[oi++] = x[6];
o32[oi++] = x[7];
o32[oi++] = x[8];
o32[oi++] = x[9];
}
function chachaCore(s, k, n, out, cnt, rounds = 20) {
// prettier-ignore
const y = new Uint32Array([
s[0], s[1], s[2], s[3], // "expa" "nd 3" "2-by" "te k"
k[0], k[1], k[2], k[3], // Key Key Key Key
k[4], k[5], k[6], k[7], // Key Key Key Key
cnt, n[0], n[1], n[2], // Counter Counter Nonce Nonce
]);
const x = y.slice();
chachaRound(x, rounds);
for (let i = 0; i < 16; i++)
out[i] = (y[i] + x[i]) | 0;
}
// prettier-ignore
export function hchacha(s, k, i, o32) {
const x = new Uint32Array([
s[0], s[1], s[2], s[3],
k[0], k[1], k[2], k[3],
k[4], k[5], k[6], k[7],
i[0], i[1], i[2], i[3],
]);
chachaRound(x, 20);
let oi = 0;
o32[oi++] = x[0];
o32[oi++] = x[1];
o32[oi++] = x[2];
o32[oi++] = x[3];
o32[oi++] = x[12];
o32[oi++] = x[13];
o32[oi++] = x[14];
o32[oi++] = x[15];
}
/**
* salsa20, 12-byte nonce.
*/
export const salsa20 = /* @__PURE__ */ createCipher(salsaCore, {
allowShortKeys: true,
counterRight: true,
});
/**
* xsalsa20, 24-byte nonce.
*/
export const xsalsa20 = /* @__PURE__ */ createCipher(salsaCore, {
counterRight: true,
extendNonceFn: hsalsa,
});
/**
* chacha20 non-RFC, original version by djb. 8-byte nonce, 8-byte counter.
*/
export const chacha20orig = /* @__PURE__ */ createCipher(chachaCore, {
allowShortKeys: true,
counterRight: false,
counterLength: 8,
});
/**
* chacha20 RFC 8439 (IETF / TLS). 12-byte nonce, 4-byte counter.
*/
export const chacha20 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
});
/**
* xchacha20 eXtended-nonce. https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export const xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 8,
extendNonceFn: hchacha,
});
/**
* 8-round chacha from the original paper.
*/
export const chacha8 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 8,
});
/**
* 12-round chacha from the original paper.
*/
export const chacha12 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 12,
});
const POW_2_130_5 = BigInt(2) ** BigInt(130) - BigInt(5);
const POW_2_128_1 = BigInt(2) ** BigInt(16 * 8) - BigInt(1);
const CLAMP_R = BigInt('0x0ffffffc0ffffffc0ffffffc0fffffff');
const _0 = BigInt(0);
const _1 = BigInt(1);
// Can be speed-up using BigUint64Array, but would be more complicated
export function poly1305(msg, key) {
abytes(msg);
abytes(key);
let acc = _0;
const r = bytesToNumberLE(key.subarray(0, 16)) & CLAMP_R;
const s = bytesToNumberLE(key.subarray(16));
// Process by 16 byte chunks
for (let i = 0; i < msg.length; i += 16) {
const m = msg.subarray(i, i + 16);
const n = bytesToNumberLE(m) | (_1 << BigInt(8 * m.length));
acc = ((acc + n) * r) % POW_2_130_5;
}
const res = (acc + s) & POW_2_128_1;
return numberToBytesLE(res, 16);
}
function computeTag(fn, key, nonce, ciphertext, AAD) {
const res = [];
if (AAD) {
res.push(AAD);
const leftover = AAD.length % 16;
if (leftover > 0)
res.push(new Uint8Array(16 - leftover));
}
res.push(ciphertext);
const leftover = ciphertext.length % 16;
if (leftover > 0)
res.push(new Uint8Array(16 - leftover));
// Lengths
const num = new Uint8Array(16);
const view = createView(num);
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
setBigUint64(view, 8, BigInt(ciphertext.length), true);
res.push(num);
const authKey = fn(key, nonce, new Uint8Array(32));
return poly1305(concatBytes(...res), authKey);
}
/**
* xsalsa20-poly1305 eXtended-nonce (24 bytes) salsa.
*/
export const xsalsa20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, function xsalsa20poly1305(key, nonce) {
abytes(key);
abytes(nonce);
return {
encrypt: (plaintext) => {
abytes(plaintext);
const m = concatBytes(new Uint8Array(32), plaintext);
const c = xsalsa20(key, nonce, m);
const authKey = c.subarray(0, 32);
const data = c.subarray(32);
const tag = poly1305(data, authKey);
return concatBytes(tag, data);
},
decrypt: (ciphertext) => {
abytes(ciphertext);
if (ciphertext.length < 16)
throw new Error('encrypted data must be at least 16 bytes');
const c = concatBytes(new Uint8Array(16), ciphertext);
const authKey = xsalsa20(key, nonce, new Uint8Array(32));
const tag = poly1305(c.subarray(32), authKey);
if (!equalBytes(c.subarray(16, 32), tag))
throw new Error('invalid poly1305 tag');
return xsalsa20(key, nonce, c).subarray(32);
},
};
});
/**
* Alias to xsalsa20-poly1305
*/
export function secretbox(key, nonce) {
const xs = xsalsa20poly1305(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}
export const _poly1305_aead = (fn) => (key, nonce, AAD) => {
const tagLength = 16;
const keyLength = 32;
abytes(key, keyLength);
abytes(nonce);
return {
encrypt: (plaintext) => {
abytes(plaintext);
const res = fn(key, nonce, plaintext, undefined, 1);
const tag = computeTag(fn, key, nonce, res, AAD);
return concatBytes(res, tag);
},
decrypt: (ciphertext) => {
abytes(ciphertext);
if (ciphertext.length < tagLength)
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
const passedTag = ciphertext.subarray(-tagLength);
const data = ciphertext.subarray(0, -tagLength);
const tag = computeTag(fn, key, nonce, data, AAD);
if (!equalBytes(passedTag, tag))
throw new Error('invalid poly1305 tag');
return fn(key, nonce, data, undefined, 1);
},
};
};
/**
* chacha20-poly1305 12-byte-nonce chacha.
*/
export const chacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 12, tagLength: 16 }, _poly1305_aead(chacha20));
/**
* xchacha20-poly1305 eXtended-nonce (24 bytes) chacha.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
//# sourceMappingURL=_micro.js.map

1
node_modules/@noble/ciphers/esm/_micro.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

264
node_modules/@noble/ciphers/esm/_poly1305.js generated vendored Normal file
View File

@@ -0,0 +1,264 @@
import { exists as aexists, bytes as abytes, output as aoutput } from './_assert.js';
import { toBytes } from './utils.js';
// Poly1305 is a fast and parallel secret-key message-authentication code.
// https://cr.yp.to/mac.html, https://cr.yp.to/mac/poly1305-20050329.pdf
// https://datatracker.ietf.org/doc/html/rfc8439
// Based on Public Domain poly1305-donna https://github.com/floodyberry/poly1305-donna
const u8to16 = (a, i) => (a[i++] & 0xff) | ((a[i++] & 0xff) << 8);
class Poly1305 {
constructor(key) {
this.blockLen = 16;
this.outputLen = 16;
this.buffer = new Uint8Array(16);
this.r = new Uint16Array(10);
this.h = new Uint16Array(10);
this.pad = new Uint16Array(8);
this.pos = 0;
this.finished = false;
key = toBytes(key);
abytes(key, 32);
const t0 = u8to16(key, 0);
const t1 = u8to16(key, 2);
const t2 = u8to16(key, 4);
const t3 = u8to16(key, 6);
const t4 = u8to16(key, 8);
const t5 = u8to16(key, 10);
const t6 = u8to16(key, 12);
const t7 = u8to16(key, 14);
// https://github.com/floodyberry/poly1305-donna/blob/e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781/poly1305-donna-16.h#L47
this.r[0] = t0 & 0x1fff;
this.r[1] = ((t0 >>> 13) | (t1 << 3)) & 0x1fff;
this.r[2] = ((t1 >>> 10) | (t2 << 6)) & 0x1f03;
this.r[3] = ((t2 >>> 7) | (t3 << 9)) & 0x1fff;
this.r[4] = ((t3 >>> 4) | (t4 << 12)) & 0x00ff;
this.r[5] = (t4 >>> 1) & 0x1ffe;
this.r[6] = ((t4 >>> 14) | (t5 << 2)) & 0x1fff;
this.r[7] = ((t5 >>> 11) | (t6 << 5)) & 0x1f81;
this.r[8] = ((t6 >>> 8) | (t7 << 8)) & 0x1fff;
this.r[9] = (t7 >>> 5) & 0x007f;
for (let i = 0; i < 8; i++)
this.pad[i] = u8to16(key, 16 + 2 * i);
}
process(data, offset, isLast = false) {
const hibit = isLast ? 0 : 1 << 11;
const { h, r } = this;
const r0 = r[0];
const r1 = r[1];
const r2 = r[2];
const r3 = r[3];
const r4 = r[4];
const r5 = r[5];
const r6 = r[6];
const r7 = r[7];
const r8 = r[8];
const r9 = r[9];
const t0 = u8to16(data, offset + 0);
const t1 = u8to16(data, offset + 2);
const t2 = u8to16(data, offset + 4);
const t3 = u8to16(data, offset + 6);
const t4 = u8to16(data, offset + 8);
const t5 = u8to16(data, offset + 10);
const t6 = u8to16(data, offset + 12);
const t7 = u8to16(data, offset + 14);
let h0 = h[0] + (t0 & 0x1fff);
let h1 = h[1] + (((t0 >>> 13) | (t1 << 3)) & 0x1fff);
let h2 = h[2] + (((t1 >>> 10) | (t2 << 6)) & 0x1fff);
let h3 = h[3] + (((t2 >>> 7) | (t3 << 9)) & 0x1fff);
let h4 = h[4] + (((t3 >>> 4) | (t4 << 12)) & 0x1fff);
let h5 = h[5] + ((t4 >>> 1) & 0x1fff);
let h6 = h[6] + (((t4 >>> 14) | (t5 << 2)) & 0x1fff);
let h7 = h[7] + (((t5 >>> 11) | (t6 << 5)) & 0x1fff);
let h8 = h[8] + (((t6 >>> 8) | (t7 << 8)) & 0x1fff);
let h9 = h[9] + ((t7 >>> 5) | hibit);
let c = 0;
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
c = d0 >>> 13;
d0 &= 0x1fff;
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
c += d0 >>> 13;
d0 &= 0x1fff;
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
c = d1 >>> 13;
d1 &= 0x1fff;
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
c += d1 >>> 13;
d1 &= 0x1fff;
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
c = d2 >>> 13;
d2 &= 0x1fff;
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
c += d2 >>> 13;
d2 &= 0x1fff;
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
c = d3 >>> 13;
d3 &= 0x1fff;
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
c += d3 >>> 13;
d3 &= 0x1fff;
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
c = d4 >>> 13;
d4 &= 0x1fff;
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
c += d4 >>> 13;
d4 &= 0x1fff;
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
c = d5 >>> 13;
d5 &= 0x1fff;
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
c += d5 >>> 13;
d5 &= 0x1fff;
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
c = d6 >>> 13;
d6 &= 0x1fff;
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
c += d6 >>> 13;
d6 &= 0x1fff;
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
c = d7 >>> 13;
d7 &= 0x1fff;
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
c += d7 >>> 13;
d7 &= 0x1fff;
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
c = d8 >>> 13;
d8 &= 0x1fff;
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
c += d8 >>> 13;
d8 &= 0x1fff;
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
c = d9 >>> 13;
d9 &= 0x1fff;
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
c += d9 >>> 13;
d9 &= 0x1fff;
c = ((c << 2) + c) | 0;
c = (c + d0) | 0;
d0 = c & 0x1fff;
c = c >>> 13;
d1 += c;
h[0] = d0;
h[1] = d1;
h[2] = d2;
h[3] = d3;
h[4] = d4;
h[5] = d5;
h[6] = d6;
h[7] = d7;
h[8] = d8;
h[9] = d9;
}
finalize() {
const { h, pad } = this;
const g = new Uint16Array(10);
let c = h[1] >>> 13;
h[1] &= 0x1fff;
for (let i = 2; i < 10; i++) {
h[i] += c;
c = h[i] >>> 13;
h[i] &= 0x1fff;
}
h[0] += c * 5;
c = h[0] >>> 13;
h[0] &= 0x1fff;
h[1] += c;
c = h[1] >>> 13;
h[1] &= 0x1fff;
h[2] += c;
g[0] = h[0] + 5;
c = g[0] >>> 13;
g[0] &= 0x1fff;
for (let i = 1; i < 10; i++) {
g[i] = h[i] + c;
c = g[i] >>> 13;
g[i] &= 0x1fff;
}
g[9] -= 1 << 13;
let mask = (c ^ 1) - 1;
for (let i = 0; i < 10; i++)
g[i] &= mask;
mask = ~mask;
for (let i = 0; i < 10; i++)
h[i] = (h[i] & mask) | g[i];
h[0] = (h[0] | (h[1] << 13)) & 0xffff;
h[1] = ((h[1] >>> 3) | (h[2] << 10)) & 0xffff;
h[2] = ((h[2] >>> 6) | (h[3] << 7)) & 0xffff;
h[3] = ((h[3] >>> 9) | (h[4] << 4)) & 0xffff;
h[4] = ((h[4] >>> 12) | (h[5] << 1) | (h[6] << 14)) & 0xffff;
h[5] = ((h[6] >>> 2) | (h[7] << 11)) & 0xffff;
h[6] = ((h[7] >>> 5) | (h[8] << 8)) & 0xffff;
h[7] = ((h[8] >>> 8) | (h[9] << 5)) & 0xffff;
let f = h[0] + pad[0];
h[0] = f & 0xffff;
for (let i = 1; i < 8; i++) {
f = (((h[i] + pad[i]) | 0) + (f >>> 16)) | 0;
h[i] = f & 0xffff;
}
}
update(data) {
aexists(this);
const { buffer, blockLen } = this;
data = toBytes(data);
const len = data.length;
for (let pos = 0; pos < len;) {
const take = Math.min(blockLen - this.pos, len - pos);
// Fast path: we have at least one block in input
if (take === blockLen) {
for (; blockLen <= len - pos; pos += blockLen)
this.process(data, pos);
continue;
}
buffer.set(data.subarray(pos, pos + take), this.pos);
this.pos += take;
pos += take;
if (this.pos === blockLen) {
this.process(buffer, 0, false);
this.pos = 0;
}
}
return this;
}
destroy() {
this.h.fill(0);
this.r.fill(0);
this.buffer.fill(0);
this.pad.fill(0);
}
digestInto(out) {
aexists(this);
aoutput(out, this);
this.finished = true;
const { buffer, h } = this;
let { pos } = this;
if (pos) {
buffer[pos++] = 1;
// buffer.subarray(pos).fill(0);
for (; pos < 16; pos++)
buffer[pos] = 0;
this.process(buffer, 0, true);
}
this.finalize();
let opos = 0;
for (let i = 0; i < 8; i++) {
out[opos++] = h[i] >>> 0;
out[opos++] = h[i] >>> 8;
}
return out;
}
digest() {
const { buffer, outputLen } = this;
this.digestInto(buffer);
const res = buffer.slice(0, outputLen);
this.destroy();
return res;
}
}
export function wrapConstructorWithKey(hashCons) {
const hashC = (msg, key) => hashCons(key).update(toBytes(msg)).digest();
const tmp = hashCons(new Uint8Array(32));
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (key) => hashCons(key);
return hashC;
}
export const poly1305 = wrapConstructorWithKey((key) => new Poly1305(key));
//# sourceMappingURL=_poly1305.js.map

1
node_modules/@noble/ciphers/esm/_poly1305.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

217
node_modules/@noble/ciphers/esm/_polyval.js generated vendored Normal file
View File

@@ -0,0 +1,217 @@
import { createView, toBytes, u32 } from './utils.js';
import { bytes as abytes, exists as aexists, output as aoutput } from './_assert.js';
// GHash from AES-GCM and its little-endian "mirror image" Polyval from AES-SIV.
// Implemented in terms of GHash with conversion function for keys
// GCM GHASH from NIST SP800-38d, SIV from RFC 8452.
// https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
// GHASH modulo: x^128 + x^7 + x^2 + x + 1
// POLYVAL modulo: x^128 + x^127 + x^126 + x^121 + 1
const BLOCK_SIZE = 16;
// TODO: rewrite
// temporary padding buffer
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
const ZEROS32 = u32(ZEROS16);
const POLY = 0xe1; // v = 2*v % POLY
// v = 2*v % POLY
// NOTE: because x + x = 0 (add/sub is same), mul2(x) != x+x
// We can multiply any number using montgomery ladder and this function (works as double, add is simple xor)
const mul2 = (s0, s1, s2, s3) => {
const hiBit = s3 & 1;
return {
s3: (s2 << 31) | (s3 >>> 1),
s2: (s1 << 31) | (s2 >>> 1),
s1: (s0 << 31) | (s1 >>> 1),
s0: (s0 >>> 1) ^ ((POLY << 24) & -(hiBit & 1)), // reduce % poly
};
};
const swapLE = (n) => (((n >>> 0) & 0xff) << 24) |
(((n >>> 8) & 0xff) << 16) |
(((n >>> 16) & 0xff) << 8) |
((n >>> 24) & 0xff) |
0;
/**
* `mulX_POLYVAL(ByteReverse(H))` from spec
* @param k mutated in place
*/
export function _toGHASHKey(k) {
k.reverse();
const hiBit = k[15] & 1;
// k >>= 1
let carry = 0;
for (let i = 0; i < k.length; i++) {
const t = k[i];
k[i] = (t >>> 1) | carry;
carry = (t & 1) << 7;
}
k[0] ^= -hiBit & 0xe1; // if (hiBit) n ^= 0xe1000000000000000000000000000000;
return k;
}
const estimateWindow = (bytes) => {
if (bytes > 64 * 1024)
return 8;
if (bytes > 1024)
return 4;
return 2;
};
class GHASH {
// We select bits per window adaptively based on expectedLength
constructor(key, expectedLength) {
this.blockLen = BLOCK_SIZE;
this.outputLen = BLOCK_SIZE;
this.s0 = 0;
this.s1 = 0;
this.s2 = 0;
this.s3 = 0;
this.finished = false;
key = toBytes(key);
abytes(key, 16);
const kView = createView(key);
let k0 = kView.getUint32(0, false);
let k1 = kView.getUint32(4, false);
let k2 = kView.getUint32(8, false);
let k3 = kView.getUint32(12, false);
// generate table of doubled keys (half of montgomery ladder)
const doubles = [];
for (let i = 0; i < 128; i++) {
doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) });
({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2(k0, k1, k2, k3));
}
const W = estimateWindow(expectedLength || 1024);
if (![1, 2, 4, 8].includes(W))
throw new Error(`ghash: wrong window size=${W}, should be 2, 4 or 8`);
this.W = W;
const bits = 128; // always 128 bits;
const windows = bits / W;
const windowSize = (this.windowSize = 2 ** W);
const items = [];
// Create precompute table for window of W bits
for (let w = 0; w < windows; w++) {
// truth table: 00, 01, 10, 11
for (let byte = 0; byte < windowSize; byte++) {
// prettier-ignore
let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
for (let j = 0; j < W; j++) {
const bit = (byte >>> (W - j - 1)) & 1;
if (!bit)
continue;
const { s0: d0, s1: d1, s2: d2, s3: d3 } = doubles[W * w + j];
(s0 ^= d0), (s1 ^= d1), (s2 ^= d2), (s3 ^= d3);
}
items.push({ s0, s1, s2, s3 });
}
}
this.t = items;
}
_updateBlock(s0, s1, s2, s3) {
(s0 ^= this.s0), (s1 ^= this.s1), (s2 ^= this.s2), (s3 ^= this.s3);
const { W, t, windowSize } = this;
// prettier-ignore
let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
const mask = (1 << W) - 1; // 2**W will kill performance.
let w = 0;
for (const num of [s0, s1, s2, s3]) {
for (let bytePos = 0; bytePos < 4; bytePos++) {
const byte = (num >>> (8 * bytePos)) & 0xff;
for (let bitPos = 8 / W - 1; bitPos >= 0; bitPos--) {
const bit = (byte >>> (W * bitPos)) & mask;
const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w * windowSize + bit];
(o0 ^= e0), (o1 ^= e1), (o2 ^= e2), (o3 ^= e3);
w += 1;
}
}
}
this.s0 = o0;
this.s1 = o1;
this.s2 = o2;
this.s3 = o3;
}
update(data) {
data = toBytes(data);
aexists(this);
const b32 = u32(data);
const blocks = Math.floor(data.length / BLOCK_SIZE);
const left = data.length % BLOCK_SIZE;
for (let i = 0; i < blocks; i++) {
this._updateBlock(b32[i * 4 + 0], b32[i * 4 + 1], b32[i * 4 + 2], b32[i * 4 + 3]);
}
if (left) {
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
this._updateBlock(ZEROS32[0], ZEROS32[1], ZEROS32[2], ZEROS32[3]);
ZEROS32.fill(0); // clean tmp buffer
}
return this;
}
destroy() {
const { t } = this;
// clean precompute table
for (const elm of t) {
(elm.s0 = 0), (elm.s1 = 0), (elm.s2 = 0), (elm.s3 = 0);
}
}
digestInto(out) {
aexists(this);
aoutput(out, this);
this.finished = true;
const { s0, s1, s2, s3 } = this;
const o32 = u32(out);
o32[0] = s0;
o32[1] = s1;
o32[2] = s2;
o32[3] = s3;
return out;
}
digest() {
const res = new Uint8Array(BLOCK_SIZE);
this.digestInto(res);
this.destroy();
return res;
}
}
class Polyval extends GHASH {
constructor(key, expectedLength) {
key = toBytes(key);
const ghKey = _toGHASHKey(key.slice());
super(ghKey, expectedLength);
ghKey.fill(0);
}
update(data) {
data = toBytes(data);
aexists(this);
const b32 = u32(data);
const left = data.length % BLOCK_SIZE;
const blocks = Math.floor(data.length / BLOCK_SIZE);
for (let i = 0; i < blocks; i++) {
this._updateBlock(swapLE(b32[i * 4 + 3]), swapLE(b32[i * 4 + 2]), swapLE(b32[i * 4 + 1]), swapLE(b32[i * 4 + 0]));
}
if (left) {
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
this._updateBlock(swapLE(ZEROS32[3]), swapLE(ZEROS32[2]), swapLE(ZEROS32[1]), swapLE(ZEROS32[0]));
ZEROS32.fill(0); // clean tmp buffer
}
return this;
}
digestInto(out) {
aexists(this);
aoutput(out, this);
this.finished = true;
// tmp ugly hack
const { s0, s1, s2, s3 } = this;
const o32 = u32(out);
o32[0] = s0;
o32[1] = s1;
o32[2] = s2;
o32[3] = s3;
return out.reverse();
}
}
function wrapConstructorWithKey(hashCons) {
const hashC = (msg, key) => hashCons(key, msg.length).update(toBytes(msg)).digest();
const tmp = hashCons(new Uint8Array(16), 0);
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (key, expectedLength) => hashCons(key, expectedLength);
return hashC;
}
export const ghash = wrapConstructorWithKey((key, expectedLength) => new GHASH(key, expectedLength));
export const polyval = wrapConstructorWithKey((key, expectedLength) => new Polyval(key, expectedLength));
//# sourceMappingURL=_polyval.js.map

1
node_modules/@noble/ciphers/esm/_polyval.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

670
node_modules/@noble/ciphers/esm/aes.js generated vendored Normal file
View File

@@ -0,0 +1,670 @@
// prettier-ignore
import { wrapCipher, createView, setBigUint64, equalBytes, u32, u8, } from './utils.js';
import { ghash, polyval } from './_polyval.js';
import { bytes as abytes } from './_assert.js';
/*
AES (Advanced Encryption Standard) aka Rijndael block cipher.
Data is split into 128-bit blocks. Encrypted in 10/12/14 rounds (128/192/256 bits). In every round:
1. **S-box**, table substitution
2. **Shift rows**, cyclic shift left of all rows of data array
3. **Mix columns**, multiplying every column by fixed polynomial
4. **Add round key**, round_key xor i-th column of array
Resources:
- FIPS-197 https://csrc.nist.gov/files/pubs/fips/197/final/docs/fips-197.pdf
- Original proposal: https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/aes-development/rijndael-ammended.pdf
*/
const BLOCK_SIZE = 16;
const BLOCK_SIZE32 = 4;
const EMPTY_BLOCK = new Uint8Array(BLOCK_SIZE);
const POLY = 0x11b; // 1 + x + x**3 + x**4 + x**8
// TODO: remove multiplication, binary ops only
function mul2(n) {
return (n << 1) ^ (POLY & -(n >> 7));
}
function mul(a, b) {
let res = 0;
for (; b > 0; b >>= 1) {
// Montgomery ladder
res ^= a & -(b & 1); // if (b&1) res ^=a (but const-time).
a = mul2(a); // a = 2*a
}
return res;
}
// AES S-box is generated using finite field inversion,
// an affine transform, and xor of a constant 0x63.
const sbox = /* @__PURE__ */ (() => {
let t = new Uint8Array(256);
for (let i = 0, x = 1; i < 256; i++, x ^= mul2(x))
t[i] = x;
const box = new Uint8Array(256);
box[0] = 0x63; // first elm
for (let i = 0; i < 255; i++) {
let x = t[255 - i];
x |= x << 8;
box[t[i]] = (x ^ (x >> 4) ^ (x >> 5) ^ (x >> 6) ^ (x >> 7) ^ 0x63) & 0xff;
}
return box;
})();
// Inverted S-box
const invSbox = /* @__PURE__ */ sbox.map((_, j) => sbox.indexOf(j));
// Rotate u32 by 8
const rotr32_8 = (n) => (n << 24) | (n >>> 8);
const rotl32_8 = (n) => (n << 8) | (n >>> 24);
// T-table is optimization suggested in 5.2 of original proposal (missed from FIPS-197). Changes:
// - LE instead of BE
// - bigger tables: T0 and T1 are merged into T01 table and T2 & T3 into T23;
// so index is u16, instead of u8. This speeds up things, unexpectedly
function genTtable(sbox, fn) {
if (sbox.length !== 256)
throw new Error('Wrong sbox length');
const T0 = new Uint32Array(256).map((_, j) => fn(sbox[j]));
const T1 = T0.map(rotl32_8);
const T2 = T1.map(rotl32_8);
const T3 = T2.map(rotl32_8);
const T01 = new Uint32Array(256 * 256);
const T23 = new Uint32Array(256 * 256);
const sbox2 = new Uint16Array(256 * 256);
for (let i = 0; i < 256; i++) {
for (let j = 0; j < 256; j++) {
const idx = i * 256 + j;
T01[idx] = T0[i] ^ T1[j];
T23[idx] = T2[i] ^ T3[j];
sbox2[idx] = (sbox[i] << 8) | sbox[j];
}
}
return { sbox, sbox2, T0, T1, T2, T3, T01, T23 };
}
const tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => (mul(s, 3) << 24) | (s << 16) | (s << 8) | mul(s, 2));
const tableDecoding = /* @__PURE__ */ genTtable(invSbox, (s) => (mul(s, 11) << 24) | (mul(s, 13) << 16) | (mul(s, 9) << 8) | mul(s, 14));
const xPowers = /* @__PURE__ */ (() => {
const p = new Uint8Array(16);
for (let i = 0, x = 1; i < 16; i++, x = mul2(x))
p[i] = x;
return p;
})();
export function expandKeyLE(key) {
abytes(key);
const len = key.length;
if (![16, 24, 32].includes(len))
throw new Error(`aes: wrong key size: should be 16, 24 or 32, got: ${len}`);
const { sbox2 } = tableEncoding;
const k32 = u32(key);
const Nk = k32.length;
const subByte = (n) => applySbox(sbox2, n, n, n, n);
const xk = new Uint32Array(len + 28); // expanded key
xk.set(k32);
// 4.3.1 Key expansion
for (let i = Nk; i < xk.length; i++) {
let t = xk[i - 1];
if (i % Nk === 0)
t = subByte(rotr32_8(t)) ^ xPowers[i / Nk - 1];
else if (Nk > 6 && i % Nk === 4)
t = subByte(t);
xk[i] = xk[i - Nk] ^ t;
}
return xk;
}
export function expandKeyDecLE(key) {
const encKey = expandKeyLE(key);
const xk = encKey.slice();
const Nk = encKey.length;
const { sbox2 } = tableEncoding;
const { T0, T1, T2, T3 } = tableDecoding;
// Inverse key by chunks of 4 (rounds)
for (let i = 0; i < Nk; i += 4) {
for (let j = 0; j < 4; j++)
xk[i + j] = encKey[Nk - i - 4 + j];
}
encKey.fill(0);
// apply InvMixColumn except first & last round
for (let i = 4; i < Nk - 4; i++) {
const x = xk[i];
const w = applySbox(sbox2, x, x, x, x);
xk[i] = T0[w & 0xff] ^ T1[(w >>> 8) & 0xff] ^ T2[(w >>> 16) & 0xff] ^ T3[w >>> 24];
}
return xk;
}
// Apply tables
function apply0123(T01, T23, s0, s1, s2, s3) {
return (T01[((s0 << 8) & 0xff00) | ((s1 >>> 8) & 0xff)] ^
T23[((s2 >>> 8) & 0xff00) | ((s3 >>> 24) & 0xff)]);
}
function applySbox(sbox2, s0, s1, s2, s3) {
return (sbox2[(s0 & 0xff) | (s1 & 0xff00)] |
(sbox2[((s2 >>> 16) & 0xff) | ((s3 >>> 16) & 0xff00)] << 16));
}
function encrypt(xk, s0, s1, s2, s3) {
const { sbox2, T01, T23 } = tableEncoding;
let k = 0;
(s0 ^= xk[k++]), (s1 ^= xk[k++]), (s2 ^= xk[k++]), (s3 ^= xk[k++]);
const rounds = xk.length / 4 - 2;
for (let i = 0; i < rounds; i++) {
const t0 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
const t1 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
const t2 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
const t3 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
(s0 = t0), (s1 = t1), (s2 = t2), (s3 = t3);
}
// last round (without mixcolumns, so using SBOX2 table)
const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
return { s0: t0, s1: t1, s2: t2, s3: t3 };
}
function decrypt(xk, s0, s1, s2, s3) {
const { sbox2, T01, T23 } = tableDecoding;
let k = 0;
(s0 ^= xk[k++]), (s1 ^= xk[k++]), (s2 ^= xk[k++]), (s3 ^= xk[k++]);
const rounds = xk.length / 4 - 2;
for (let i = 0; i < rounds; i++) {
const t0 = xk[k++] ^ apply0123(T01, T23, s0, s3, s2, s1);
const t1 = xk[k++] ^ apply0123(T01, T23, s1, s0, s3, s2);
const t2 = xk[k++] ^ apply0123(T01, T23, s2, s1, s0, s3);
const t3 = xk[k++] ^ apply0123(T01, T23, s3, s2, s1, s0);
(s0 = t0), (s1 = t1), (s2 = t2), (s3 = t3);
}
// Last round
const t0 = xk[k++] ^ applySbox(sbox2, s0, s3, s2, s1);
const t1 = xk[k++] ^ applySbox(sbox2, s1, s0, s3, s2);
const t2 = xk[k++] ^ applySbox(sbox2, s2, s1, s0, s3);
const t3 = xk[k++] ^ applySbox(sbox2, s3, s2, s1, s0);
return { s0: t0, s1: t1, s2: t2, s3: t3 };
}
function getDst(len, dst) {
if (!dst)
return new Uint8Array(len);
abytes(dst);
if (dst.length < len)
throw new Error(`aes: wrong destination length, expected at least ${len}, got: ${dst.length}`);
return dst;
}
// TODO: investigate merging with ctr32
function ctrCounter(xk, nonce, src, dst) {
abytes(nonce, BLOCK_SIZE);
abytes(src);
const srcLen = src.length;
dst = getDst(srcLen, dst);
const ctr = nonce;
const c32 = u32(ctr);
// Fill block (empty, ctr=0)
let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
const src32 = u32(src);
const dst32 = u32(dst);
// process blocks
for (let i = 0; i + 4 <= src32.length; i += 4) {
dst32[i + 0] = src32[i + 0] ^ s0;
dst32[i + 1] = src32[i + 1] ^ s1;
dst32[i + 2] = src32[i + 2] ^ s2;
dst32[i + 3] = src32[i + 3] ^ s3;
// Full 128 bit counter with wrap around
let carry = 1;
for (let i = ctr.length - 1; i >= 0; i--) {
carry = (carry + (ctr[i] & 0xff)) | 0;
ctr[i] = carry & 0xff;
carry >>>= 8;
}
({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
}
// leftovers (less than block)
// It's possible to handle > u32 fast, but is it worth it?
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
if (start < srcLen) {
const b32 = new Uint32Array([s0, s1, s2, s3]);
const buf = u8(b32);
for (let i = start, pos = 0; i < srcLen; i++, pos++)
dst[i] = src[i] ^ buf[pos];
}
return dst;
}
// AES CTR with overflowing 32 bit counter
// It's possible to do 32le significantly simpler (and probably faster) by using u32.
// But, we need both, and perf bottleneck is in ghash anyway.
function ctr32(xk, isLE, nonce, src, dst) {
abytes(nonce, BLOCK_SIZE);
abytes(src);
dst = getDst(src.length, dst);
const ctr = nonce; // write new value to nonce, so it can be re-used
const c32 = u32(ctr);
const view = createView(ctr);
const src32 = u32(src);
const dst32 = u32(dst);
const ctrPos = isLE ? 0 : 12;
const srcLen = src.length;
// Fill block (empty, ctr=0)
let ctrNum = view.getUint32(ctrPos, isLE); // read current counter value
let { s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]);
// process blocks
for (let i = 0; i + 4 <= src32.length; i += 4) {
dst32[i + 0] = src32[i + 0] ^ s0;
dst32[i + 1] = src32[i + 1] ^ s1;
dst32[i + 2] = src32[i + 2] ^ s2;
dst32[i + 3] = src32[i + 3] ^ s3;
ctrNum = (ctrNum + 1) >>> 0; // u32 wrap
view.setUint32(ctrPos, ctrNum, isLE);
({ s0, s1, s2, s3 } = encrypt(xk, c32[0], c32[1], c32[2], c32[3]));
}
// leftovers (less than a block)
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
if (start < srcLen) {
const b32 = new Uint32Array([s0, s1, s2, s3]);
const buf = u8(b32);
for (let i = start, pos = 0; i < srcLen; i++, pos++)
dst[i] = src[i] ^ buf[pos];
}
return dst;
}
/**
* CTR: counter mode. Creates stream cipher.
* Requires good IV. Parallelizable. OK, but no MAC.
*/
export const ctr = wrapCipher({ blockSize: 16, nonceLength: 16 }, function ctr(key, nonce) {
abytes(key);
abytes(nonce, BLOCK_SIZE);
function processCtr(buf, dst) {
const xk = expandKeyLE(key);
const n = nonce.slice();
const out = ctrCounter(xk, n, buf, dst);
xk.fill(0);
n.fill(0);
return out;
}
return {
encrypt: (plaintext, dst) => processCtr(plaintext, dst),
decrypt: (ciphertext, dst) => processCtr(ciphertext, dst),
};
});
function validateBlockDecrypt(data) {
abytes(data);
if (data.length % BLOCK_SIZE !== 0) {
throw new Error(`aes/(cbc-ecb).decrypt ciphertext should consist of blocks with size ${BLOCK_SIZE}`);
}
}
function validateBlockEncrypt(plaintext, pcks5, dst) {
let outLen = plaintext.length;
const remaining = outLen % BLOCK_SIZE;
if (!pcks5 && remaining !== 0)
throw new Error('aec/(cbc-ecb): unpadded plaintext with disabled padding');
const b = u32(plaintext);
if (pcks5) {
let left = BLOCK_SIZE - remaining;
if (!left)
left = BLOCK_SIZE; // if no bytes left, create empty padding block
outLen = outLen + left;
}
const out = getDst(outLen, dst);
const o = u32(out);
return { b, o, out };
}
function validatePCKS(data, pcks5) {
if (!pcks5)
return data;
const len = data.length;
if (!len)
throw new Error(`aes/pcks5: empty ciphertext not allowed`);
const lastByte = data[len - 1];
if (lastByte <= 0 || lastByte > 16)
throw new Error(`aes/pcks5: wrong padding byte: ${lastByte}`);
const out = data.subarray(0, -lastByte);
for (let i = 0; i < lastByte; i++)
if (data[len - i - 1] !== lastByte)
throw new Error(`aes/pcks5: wrong padding`);
return out;
}
function padPCKS(left) {
const tmp = new Uint8Array(16);
const tmp32 = u32(tmp);
tmp.set(left);
const paddingByte = BLOCK_SIZE - left.length;
for (let i = BLOCK_SIZE - paddingByte; i < BLOCK_SIZE; i++)
tmp[i] = paddingByte;
return tmp32;
}
/**
* ECB: Electronic CodeBook. Simple deterministic replacement.
* Dangerous: always map x to y. See [AES Penguin](https://words.filippo.io/the-ecb-penguin/).
*/
export const ecb = wrapCipher({ blockSize: 16 }, function ecb(key, opts = {}) {
abytes(key);
const pcks5 = !opts.disablePadding;
return {
encrypt: (plaintext, dst) => {
abytes(plaintext);
const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
const xk = expandKeyLE(key);
let i = 0;
for (; i + 4 <= b.length;) {
const { s0, s1, s2, s3 } = encrypt(xk, b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
if (pcks5) {
const tmp32 = padPCKS(plaintext.subarray(i * 4));
const { s0, s1, s2, s3 } = encrypt(xk, tmp32[0], tmp32[1], tmp32[2], tmp32[3]);
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
xk.fill(0);
return _out;
},
decrypt: (ciphertext, dst) => {
validateBlockDecrypt(ciphertext);
const xk = expandKeyDecLE(key);
const out = getDst(ciphertext.length, dst);
const b = u32(ciphertext);
const o = u32(out);
for (let i = 0; i + 4 <= b.length;) {
const { s0, s1, s2, s3 } = decrypt(xk, b[i + 0], b[i + 1], b[i + 2], b[i + 3]);
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
xk.fill(0);
return validatePCKS(out, pcks5);
},
};
});
/**
* CBC: Cipher-Block-Chaining. Key is previous rounds block.
* Fragile: needs proper padding. Unauthenticated: needs MAC.
*/
export const cbc = wrapCipher({ blockSize: 16, nonceLength: 16 }, function cbc(key, iv, opts = {}) {
abytes(key);
abytes(iv, 16);
const pcks5 = !opts.disablePadding;
return {
encrypt: (plaintext, dst) => {
const xk = expandKeyLE(key);
const { b, o, out: _out } = validateBlockEncrypt(plaintext, pcks5, dst);
const n32 = u32(iv);
// prettier-ignore
let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
let i = 0;
for (; i + 4 <= b.length;) {
(s0 ^= b[i + 0]), (s1 ^= b[i + 1]), (s2 ^= b[i + 2]), (s3 ^= b[i + 3]);
({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
if (pcks5) {
const tmp32 = padPCKS(plaintext.subarray(i * 4));
(s0 ^= tmp32[0]), (s1 ^= tmp32[1]), (s2 ^= tmp32[2]), (s3 ^= tmp32[3]);
({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
(o[i++] = s0), (o[i++] = s1), (o[i++] = s2), (o[i++] = s3);
}
xk.fill(0);
return _out;
},
decrypt: (ciphertext, dst) => {
validateBlockDecrypt(ciphertext);
const xk = expandKeyDecLE(key);
const n32 = u32(iv);
const out = getDst(ciphertext.length, dst);
const b = u32(ciphertext);
const o = u32(out);
// prettier-ignore
let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
for (let i = 0; i + 4 <= b.length;) {
// prettier-ignore
const ps0 = s0, ps1 = s1, ps2 = s2, ps3 = s3;
(s0 = b[i + 0]), (s1 = b[i + 1]), (s2 = b[i + 2]), (s3 = b[i + 3]);
const { s0: o0, s1: o1, s2: o2, s3: o3 } = decrypt(xk, s0, s1, s2, s3);
(o[i++] = o0 ^ ps0), (o[i++] = o1 ^ ps1), (o[i++] = o2 ^ ps2), (o[i++] = o3 ^ ps3);
}
xk.fill(0);
return validatePCKS(out, pcks5);
},
};
});
/**
* CFB: Cipher Feedback Mode. The input for the block cipher is the previous cipher output.
* Unauthenticated: needs MAC.
*/
export const cfb = wrapCipher({ blockSize: 16, nonceLength: 16 }, function cfb(key, iv) {
abytes(key);
abytes(iv, 16);
function processCfb(src, isEncrypt, dst) {
const xk = expandKeyLE(key);
const srcLen = src.length;
dst = getDst(srcLen, dst);
const src32 = u32(src);
const dst32 = u32(dst);
const next32 = isEncrypt ? dst32 : src32;
const n32 = u32(iv);
// prettier-ignore
let s0 = n32[0], s1 = n32[1], s2 = n32[2], s3 = n32[3];
for (let i = 0; i + 4 <= src32.length;) {
const { s0: e0, s1: e1, s2: e2, s3: e3 } = encrypt(xk, s0, s1, s2, s3);
dst32[i + 0] = src32[i + 0] ^ e0;
dst32[i + 1] = src32[i + 1] ^ e1;
dst32[i + 2] = src32[i + 2] ^ e2;
dst32[i + 3] = src32[i + 3] ^ e3;
(s0 = next32[i++]), (s1 = next32[i++]), (s2 = next32[i++]), (s3 = next32[i++]);
}
// leftovers (less than block)
const start = BLOCK_SIZE * Math.floor(src32.length / BLOCK_SIZE32);
if (start < srcLen) {
({ s0, s1, s2, s3 } = encrypt(xk, s0, s1, s2, s3));
const buf = u8(new Uint32Array([s0, s1, s2, s3]));
for (let i = start, pos = 0; i < srcLen; i++, pos++)
dst[i] = src[i] ^ buf[pos];
buf.fill(0);
}
xk.fill(0);
return dst;
}
return {
encrypt: (plaintext, dst) => processCfb(plaintext, true, dst),
decrypt: (ciphertext, dst) => processCfb(ciphertext, false, dst),
};
});
// TODO: merge with chacha, however gcm has bitLen while chacha has byteLen
function computeTag(fn, isLE, key, data, AAD) {
const h = fn.create(key, data.length + (AAD?.length || 0));
if (AAD)
h.update(AAD);
h.update(data);
const num = new Uint8Array(16);
const view = createView(num);
if (AAD)
setBigUint64(view, 0, BigInt(AAD.length * 8), isLE);
setBigUint64(view, 8, BigInt(data.length * 8), isLE);
h.update(num);
return h.digest();
}
/**
* GCM: Galois/Counter Mode.
* Good, modern version of CTR, parallel, with MAC.
* Be careful: MACs can be forged.
*/
export const gcm = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function gcm(key, nonce, AAD) {
abytes(nonce);
// Nonce can be pretty much anything (even 1 byte). But smaller nonces less secure.
if (nonce.length === 0)
throw new Error('aes/gcm: empty nonce');
const tagLength = 16;
function _computeTag(authKey, tagMask, data) {
const tag = computeTag(ghash, false, authKey, data, AAD);
for (let i = 0; i < tagMask.length; i++)
tag[i] ^= tagMask[i];
return tag;
}
function deriveKeys() {
const xk = expandKeyLE(key);
const authKey = EMPTY_BLOCK.slice();
const counter = EMPTY_BLOCK.slice();
ctr32(xk, false, counter, counter, authKey);
if (nonce.length === 12) {
counter.set(nonce);
}
else {
// Spec (NIST 800-38d) supports variable size nonce.
// Not supported for now, but can be useful.
const nonceLen = EMPTY_BLOCK.slice();
const view = createView(nonceLen);
setBigUint64(view, 8, BigInt(nonce.length * 8), false);
// ghash(nonce || u64be(0) || u64be(nonceLen*8))
ghash.create(authKey).update(nonce).update(nonceLen).digestInto(counter);
}
const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK);
return { xk, authKey, counter, tagMask };
}
return {
encrypt: (plaintext) => {
abytes(plaintext);
const { xk, authKey, counter, tagMask } = deriveKeys();
const out = new Uint8Array(plaintext.length + tagLength);
ctr32(xk, false, counter, plaintext, out);
const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
out.set(tag, plaintext.length);
xk.fill(0);
return out;
},
decrypt: (ciphertext) => {
abytes(ciphertext);
if (ciphertext.length < tagLength)
throw new Error(`aes/gcm: ciphertext less than tagLen (${tagLength})`);
const { xk, authKey, counter, tagMask } = deriveKeys();
const data = ciphertext.subarray(0, -tagLength);
const passedTag = ciphertext.subarray(-tagLength);
const tag = _computeTag(authKey, tagMask, data);
if (!equalBytes(tag, passedTag))
throw new Error('aes/gcm: invalid ghash tag');
const out = ctr32(xk, false, counter, data);
authKey.fill(0);
tagMask.fill(0);
xk.fill(0);
return out;
},
};
});
const limit = (name, min, max) => (value) => {
if (!Number.isSafeInteger(value) || min > value || value > max)
throw new Error(`${name}: invalid value=${value}, must be [${min}..${max}]`);
};
/**
* AES-GCM-SIV: classic AES-GCM with nonce-misuse resistance.
* Guarantees that, when a nonce is repeated, the only security loss is that identical
* plaintexts will produce identical ciphertexts.
* RFC 8452, https://datatracker.ietf.org/doc/html/rfc8452
*/
export const siv = wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16 }, function siv(key, nonce, AAD) {
const tagLength = 16;
// From RFC 8452: Section 6
const AAD_LIMIT = limit('AAD', 0, 2 ** 36);
const PLAIN_LIMIT = limit('plaintext', 0, 2 ** 36);
const NONCE_LIMIT = limit('nonce', 12, 12);
const CIPHER_LIMIT = limit('ciphertext', 16, 2 ** 36 + 16);
abytes(nonce);
NONCE_LIMIT(nonce.length);
if (AAD) {
abytes(AAD);
AAD_LIMIT(AAD.length);
}
function deriveKeys() {
const len = key.length;
if (len !== 16 && len !== 24 && len !== 32)
throw new Error(`key length must be 16, 24 or 32 bytes, got: ${len} bytes`);
const xk = expandKeyLE(key);
const encKey = new Uint8Array(len);
const authKey = new Uint8Array(16);
const n32 = u32(nonce);
// prettier-ignore
let s0 = 0, s1 = n32[0], s2 = n32[1], s3 = n32[2];
let counter = 0;
for (const derivedKey of [authKey, encKey].map(u32)) {
const d32 = u32(derivedKey);
for (let i = 0; i < d32.length; i += 2) {
// aes(u32le(0) || nonce)[:8] || aes(u32le(1) || nonce)[:8] ...
const { s0: o0, s1: o1 } = encrypt(xk, s0, s1, s2, s3);
d32[i + 0] = o0;
d32[i + 1] = o1;
s0 = ++counter; // increment counter inside state
}
}
xk.fill(0);
return { authKey, encKey: expandKeyLE(encKey) };
}
function _computeTag(encKey, authKey, data) {
const tag = computeTag(polyval, true, authKey, data, AAD);
// Compute the expected tag by XORing S_s and the nonce, clearing the
// most significant bit of the last byte and encrypting with the
// message-encryption key.
for (let i = 0; i < 12; i++)
tag[i] ^= nonce[i];
tag[15] &= 0x7f; // Clear the highest bit
// encrypt tag as block
const t32 = u32(tag);
// prettier-ignore
let s0 = t32[0], s1 = t32[1], s2 = t32[2], s3 = t32[3];
({ s0, s1, s2, s3 } = encrypt(encKey, s0, s1, s2, s3));
(t32[0] = s0), (t32[1] = s1), (t32[2] = s2), (t32[3] = s3);
return tag;
}
// actual decrypt/encrypt of message.
function processSiv(encKey, tag, input) {
let block = tag.slice();
block[15] |= 0x80; // Force highest bit
return ctr32(encKey, true, block, input);
}
return {
encrypt: (plaintext) => {
abytes(plaintext);
PLAIN_LIMIT(plaintext.length);
const { encKey, authKey } = deriveKeys();
const tag = _computeTag(encKey, authKey, plaintext);
const out = new Uint8Array(plaintext.length + tagLength);
out.set(tag, plaintext.length);
out.set(processSiv(encKey, tag, plaintext));
encKey.fill(0);
authKey.fill(0);
return out;
},
decrypt: (ciphertext) => {
abytes(ciphertext);
CIPHER_LIMIT(ciphertext.length);
const tag = ciphertext.subarray(-tagLength);
const { encKey, authKey } = deriveKeys();
const plaintext = processSiv(encKey, tag, ciphertext.subarray(0, -tagLength));
const expectedTag = _computeTag(encKey, authKey, plaintext);
encKey.fill(0);
authKey.fill(0);
if (!equalBytes(tag, expectedTag))
throw new Error('invalid polyval tag');
return plaintext;
},
};
});
function isBytes32(a) {
return (a != null &&
typeof a === 'object' &&
(a instanceof Uint32Array || a.constructor.name === 'Uint32Array'));
}
function encryptBlock(xk, block) {
abytes(block, 16);
if (!isBytes32(xk))
throw new Error('_encryptBlock accepts result of expandKeyLE');
const b32 = u32(block);
let { s0, s1, s2, s3 } = encrypt(xk, b32[0], b32[1], b32[2], b32[3]);
(b32[0] = s0), (b32[1] = s1), (b32[2] = s2), (b32[3] = s3);
return block;
}
function decryptBlock(xk, block) {
abytes(block, 16);
if (!isBytes32(xk))
throw new Error('_decryptBlock accepts result of expandKeyLE');
const b32 = u32(block);
let { s0, s1, s2, s3 } = decrypt(xk, b32[0], b32[1], b32[2], b32[3]);
(b32[0] = s0), (b32[1] = s1), (b32[2] = s2), (b32[3] = s3);
return block;
}
// Highly unsafe private functions for implementing new modes or ciphers based on AES
// Can change at any time, no API guarantees
export const unsafe = {
expandKeyLE,
expandKeyDecLE,
encrypt,
decrypt,
encryptBlock,
decryptBlock,
ctrCounter,
ctr32,
};
//# sourceMappingURL=aes.js.map

1
node_modules/@noble/ciphers/esm/aes.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

318
node_modules/@noble/ciphers/esm/chacha.js generated vendored Normal file
View File

@@ -0,0 +1,318 @@
// prettier-ignore
import { wrapCipher, createView, equalBytes, setBigUint64, } from './utils.js';
import { poly1305 } from './_poly1305.js';
import { createCipher, rotl } from './_arx.js';
import { bytes as abytes } from './_assert.js';
// ChaCha20 stream cipher was released in 2008. ChaCha aims to increase
// the diffusion per round, but had slightly less cryptanalysis.
// https://cr.yp.to/chacha.html, http://cr.yp.to/chacha/chacha-20080128.pdf
/**
* ChaCha core function.
*/
// prettier-ignore
function chachaCore(s, k, n, out, cnt, rounds = 20) {
let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], // "expa" "nd 3" "2-by" "te k"
y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], // Key Key Key Key
y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], // Key Key Key Key
y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2]; // Counter Counter Nonce Nonce
// Save state to temporary variables
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
for (let r = 0; r < rounds; r += 2) {
x00 = (x00 + x04) | 0;
x12 = rotl(x12 ^ x00, 16);
x08 = (x08 + x12) | 0;
x04 = rotl(x04 ^ x08, 12);
x00 = (x00 + x04) | 0;
x12 = rotl(x12 ^ x00, 8);
x08 = (x08 + x12) | 0;
x04 = rotl(x04 ^ x08, 7);
x01 = (x01 + x05) | 0;
x13 = rotl(x13 ^ x01, 16);
x09 = (x09 + x13) | 0;
x05 = rotl(x05 ^ x09, 12);
x01 = (x01 + x05) | 0;
x13 = rotl(x13 ^ x01, 8);
x09 = (x09 + x13) | 0;
x05 = rotl(x05 ^ x09, 7);
x02 = (x02 + x06) | 0;
x14 = rotl(x14 ^ x02, 16);
x10 = (x10 + x14) | 0;
x06 = rotl(x06 ^ x10, 12);
x02 = (x02 + x06) | 0;
x14 = rotl(x14 ^ x02, 8);
x10 = (x10 + x14) | 0;
x06 = rotl(x06 ^ x10, 7);
x03 = (x03 + x07) | 0;
x15 = rotl(x15 ^ x03, 16);
x11 = (x11 + x15) | 0;
x07 = rotl(x07 ^ x11, 12);
x03 = (x03 + x07) | 0;
x15 = rotl(x15 ^ x03, 8);
x11 = (x11 + x15) | 0;
x07 = rotl(x07 ^ x11, 7);
x00 = (x00 + x05) | 0;
x15 = rotl(x15 ^ x00, 16);
x10 = (x10 + x15) | 0;
x05 = rotl(x05 ^ x10, 12);
x00 = (x00 + x05) | 0;
x15 = rotl(x15 ^ x00, 8);
x10 = (x10 + x15) | 0;
x05 = rotl(x05 ^ x10, 7);
x01 = (x01 + x06) | 0;
x12 = rotl(x12 ^ x01, 16);
x11 = (x11 + x12) | 0;
x06 = rotl(x06 ^ x11, 12);
x01 = (x01 + x06) | 0;
x12 = rotl(x12 ^ x01, 8);
x11 = (x11 + x12) | 0;
x06 = rotl(x06 ^ x11, 7);
x02 = (x02 + x07) | 0;
x13 = rotl(x13 ^ x02, 16);
x08 = (x08 + x13) | 0;
x07 = rotl(x07 ^ x08, 12);
x02 = (x02 + x07) | 0;
x13 = rotl(x13 ^ x02, 8);
x08 = (x08 + x13) | 0;
x07 = rotl(x07 ^ x08, 7);
x03 = (x03 + x04) | 0;
x14 = rotl(x14 ^ x03, 16);
x09 = (x09 + x14) | 0;
x04 = rotl(x04 ^ x09, 12);
x03 = (x03 + x04) | 0;
x14 = rotl(x14 ^ x03, 8);
x09 = (x09 + x14) | 0;
x04 = rotl(x04 ^ x09, 7);
}
// Write output
let oi = 0;
out[oi++] = (y00 + x00) | 0;
out[oi++] = (y01 + x01) | 0;
out[oi++] = (y02 + x02) | 0;
out[oi++] = (y03 + x03) | 0;
out[oi++] = (y04 + x04) | 0;
out[oi++] = (y05 + x05) | 0;
out[oi++] = (y06 + x06) | 0;
out[oi++] = (y07 + x07) | 0;
out[oi++] = (y08 + x08) | 0;
out[oi++] = (y09 + x09) | 0;
out[oi++] = (y10 + x10) | 0;
out[oi++] = (y11 + x11) | 0;
out[oi++] = (y12 + x12) | 0;
out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0;
out[oi++] = (y15 + x15) | 0;
}
/**
* hchacha helper method, used primarily in xchacha, to hash
* key and nonce into key' and nonce'.
* Same as chachaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
// prettier-ignore
export function hchacha(s, k, i, o32) {
let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i[0], x13 = i[1], x14 = i[2], x15 = i[3];
for (let r = 0; r < 20; r += 2) {
x00 = (x00 + x04) | 0;
x12 = rotl(x12 ^ x00, 16);
x08 = (x08 + x12) | 0;
x04 = rotl(x04 ^ x08, 12);
x00 = (x00 + x04) | 0;
x12 = rotl(x12 ^ x00, 8);
x08 = (x08 + x12) | 0;
x04 = rotl(x04 ^ x08, 7);
x01 = (x01 + x05) | 0;
x13 = rotl(x13 ^ x01, 16);
x09 = (x09 + x13) | 0;
x05 = rotl(x05 ^ x09, 12);
x01 = (x01 + x05) | 0;
x13 = rotl(x13 ^ x01, 8);
x09 = (x09 + x13) | 0;
x05 = rotl(x05 ^ x09, 7);
x02 = (x02 + x06) | 0;
x14 = rotl(x14 ^ x02, 16);
x10 = (x10 + x14) | 0;
x06 = rotl(x06 ^ x10, 12);
x02 = (x02 + x06) | 0;
x14 = rotl(x14 ^ x02, 8);
x10 = (x10 + x14) | 0;
x06 = rotl(x06 ^ x10, 7);
x03 = (x03 + x07) | 0;
x15 = rotl(x15 ^ x03, 16);
x11 = (x11 + x15) | 0;
x07 = rotl(x07 ^ x11, 12);
x03 = (x03 + x07) | 0;
x15 = rotl(x15 ^ x03, 8);
x11 = (x11 + x15) | 0;
x07 = rotl(x07 ^ x11, 7);
x00 = (x00 + x05) | 0;
x15 = rotl(x15 ^ x00, 16);
x10 = (x10 + x15) | 0;
x05 = rotl(x05 ^ x10, 12);
x00 = (x00 + x05) | 0;
x15 = rotl(x15 ^ x00, 8);
x10 = (x10 + x15) | 0;
x05 = rotl(x05 ^ x10, 7);
x01 = (x01 + x06) | 0;
x12 = rotl(x12 ^ x01, 16);
x11 = (x11 + x12) | 0;
x06 = rotl(x06 ^ x11, 12);
x01 = (x01 + x06) | 0;
x12 = rotl(x12 ^ x01, 8);
x11 = (x11 + x12) | 0;
x06 = rotl(x06 ^ x11, 7);
x02 = (x02 + x07) | 0;
x13 = rotl(x13 ^ x02, 16);
x08 = (x08 + x13) | 0;
x07 = rotl(x07 ^ x08, 12);
x02 = (x02 + x07) | 0;
x13 = rotl(x13 ^ x02, 8);
x08 = (x08 + x13) | 0;
x07 = rotl(x07 ^ x08, 7);
x03 = (x03 + x04) | 0;
x14 = rotl(x14 ^ x03, 16);
x09 = (x09 + x14) | 0;
x04 = rotl(x04 ^ x09, 12);
x03 = (x03 + x04) | 0;
x14 = rotl(x14 ^ x03, 8);
x09 = (x09 + x14) | 0;
x04 = rotl(x04 ^ x09, 7);
}
let oi = 0;
o32[oi++] = x00;
o32[oi++] = x01;
o32[oi++] = x02;
o32[oi++] = x03;
o32[oi++] = x12;
o32[oi++] = x13;
o32[oi++] = x14;
o32[oi++] = x15;
}
/**
* Original, non-RFC chacha20 from DJB. 8-byte nonce, 8-byte counter.
*/
export const chacha20orig = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 8,
allowShortKeys: true,
});
/**
* ChaCha stream cipher. Conforms to RFC 8439 (IETF, TLS). 12-byte nonce, 4-byte counter.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const chacha20 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
allowShortKeys: false,
});
/**
* XChaCha eXtended-nonce ChaCha. 24-byte nonce.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
*/
export const xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 8,
extendNonceFn: hchacha,
allowShortKeys: false,
});
/**
* Reduced 8-round chacha, described in original paper.
*/
export const chacha8 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 8,
});
/**
* Reduced 12-round chacha, described in original paper.
*/
export const chacha12 = /* @__PURE__ */ createCipher(chachaCore, {
counterRight: false,
counterLength: 4,
rounds: 12,
});
const ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
// Pad to digest size with zeros
const updatePadded = (h, msg) => {
h.update(msg);
const left = msg.length % 16;
if (left)
h.update(ZEROS16.subarray(left));
};
const ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
function computeTag(fn, key, nonce, data, AAD) {
const authKey = fn(key, nonce, ZEROS32);
const h = poly1305.create(authKey);
if (AAD)
updatePadded(h, AAD);
updatePadded(h, data);
const num = new Uint8Array(16);
const view = createView(num);
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
setBigUint64(view, 8, BigInt(data.length), true);
h.update(num);
const res = h.digest();
authKey.fill(0);
return res;
}
/**
* AEAD algorithm from RFC 8439.
* Salsa20 and chacha (RFC 8439) use poly1305 differently.
* We could have composed them similar to:
* https://github.com/paulmillr/scure-base/blob/b266c73dde977b1dd7ef40ef7a23cc15aab526b3/index.ts#L250
* But it's hard because of authKey:
* In salsa20, authKey changes position in salsa stream.
* In chacha, authKey can't be computed inside computeTag, it modifies the counter.
*/
export const _poly1305_aead = (xorStream) => (key, nonce, AAD) => {
const tagLength = 16;
abytes(key, 32);
abytes(nonce);
return {
encrypt: (plaintext, output) => {
const plength = plaintext.length;
const clength = plength + tagLength;
if (output) {
abytes(output, clength);
}
else {
output = new Uint8Array(clength);
}
xorStream(key, nonce, plaintext, output, 1);
const tag = computeTag(xorStream, key, nonce, output.subarray(0, -tagLength), AAD);
output.set(tag, plength); // append tag
return output;
},
decrypt: (ciphertext, output) => {
const clength = ciphertext.length;
const plength = clength - tagLength;
if (clength < tagLength)
throw new Error(`encrypted data must be at least ${tagLength} bytes`);
if (output) {
abytes(output, plength);
}
else {
output = new Uint8Array(plength);
}
const data = ciphertext.subarray(0, -tagLength);
const passedTag = ciphertext.subarray(-tagLength);
const tag = computeTag(xorStream, key, nonce, data, AAD);
if (!equalBytes(passedTag, tag))
throw new Error('invalid tag');
xorStream(key, nonce, data, output, 1);
return output;
},
};
};
/**
* ChaCha20-Poly1305 from RFC 8439.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const chacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 12, tagLength: 16 }, _poly1305_aead(chacha20));
/**
* XChaCha20-Poly1305 extended-nonce chacha.
* https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
//# sourceMappingURL=chacha.js.map

1
node_modules/@noble/ciphers/esm/chacha.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

12
node_modules/@noble/ciphers/esm/crypto.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
const cr = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
export function randomBytes(bytesLength = 32) {
if (cr && typeof cr.getRandomValues === 'function')
return cr.getRandomValues(new Uint8Array(bytesLength));
throw new Error('crypto.getRandomValues must be defined');
}
export function getWebcryptoSubtle() {
if (cr && typeof cr.subtle === 'object' && cr.subtle != null)
return cr.subtle;
throw new Error('crypto.subtle must be defined');
}
//# sourceMappingURL=crypto.js.map

1
node_modules/@noble/ciphers/esm/crypto.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAGA,MAAM,EAAE,GAAG,OAAO,UAAU,KAAK,QAAQ,IAAI,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAEpG,MAAM,UAAU,WAAW,CAAC,WAAW,GAAG,EAAE;IAC1C,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,eAAe,KAAK,UAAU;QAChD,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC,MAAM,CAAC;IAC/E,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACnD,CAAC"}

17
node_modules/@noble/ciphers/esm/cryptoNode.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// See utils.ts for details.
// The file will throw on node.js 14 and earlier.
// @ts-ignore
import * as nc from 'node:crypto';
const cr = nc && typeof nc === 'object' && 'webcrypto' in nc ? nc.webcrypto : undefined;
export function randomBytes(bytesLength = 32) {
if (cr && typeof cr.getRandomValues === 'function')
return cr.getRandomValues(new Uint8Array(bytesLength));
throw new Error('crypto.getRandomValues must be defined');
}
export function getWebcryptoSubtle() {
if (cr && typeof cr.subtle === 'object' && cr.subtle != null)
return cr.subtle;
throw new Error('crypto.subtle must be defined');
}
//# sourceMappingURL=cryptoNode.js.map

1
node_modules/@noble/ciphers/esm/cryptoNode.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cryptoNode.js","sourceRoot":"","sources":["../src/cryptoNode.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,4BAA4B;AAC5B,iDAAiD;AACjD,aAAa;AACb,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,MAAM,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,WAAW,IAAI,EAAE,CAAC,CAAC,CAAE,EAAE,CAAC,SAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;AAEjG,MAAM,UAAU,WAAW,CAAC,WAAW,GAAG,EAAE;IAC1C,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,eAAe,KAAK,UAAU;QAChD,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC,MAAM,CAAC;IAC/E,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACnD,CAAC"}

149
node_modules/@noble/ciphers/esm/ff1.js generated vendored Normal file
View File

@@ -0,0 +1,149 @@
import { bytesToNumberBE, numberToBytesBE } from './utils.js';
import { unsafe } from './aes.js';
// NOTE: no point in inlining encrypt instead of encryptBlock, since BigInt stuff will be slow
const { expandKeyLE, encryptBlock } = unsafe;
// Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
const BLOCK_LEN = 16;
function mod(a, b) {
const result = a % b;
return result >= 0 ? result : b + result;
}
function NUMradix(radix, data) {
let res = BigInt(0);
for (let i of data)
res = res * BigInt(radix) + BigInt(i);
return res;
}
function getRound(radix, key, tweak, x) {
if (radix > 2 ** 16 - 1)
throw new Error(`Invalid radix: ${radix}`);
// radix**minlen ≥ 100
const minLen = Math.ceil(Math.log(100) / Math.log(radix));
const maxLen = 2 ** 32 - 1;
// 2 ≤ minlen ≤ maxlen < 2**32
if (2 > minLen || minLen > maxLen || maxLen >= 2 ** 32)
throw new Error('Invalid radix: 2 ≤ minlen ≤ maxlen < 2**32');
if (x.length < minLen || x.length > maxLen)
throw new Error('X is outside minLen..maxLen bounds');
const u = Math.floor(x.length / 2);
const v = x.length - u;
const b = Math.ceil(Math.ceil(v * Math.log2(radix)) / 8);
const d = 4 * Math.ceil(b / 4) + 4;
const padding = mod(-tweak.length - b - 1, 16);
// P = [1]1 || [2]1 || [1]1 || [radix]3 || [10]1 || [u mod 256]1 || [n]4 || [t]4.
const P = new Uint8Array([1, 2, 1, 0, 0, 0, 10, u, 0, 0, 0, 0, 0, 0, 0, 0]);
const view = new DataView(P.buffer);
view.setUint16(4, radix, false);
view.setUint32(8, x.length, false);
view.setUint32(12, tweak.length, false);
// Q = T || [0](tb1) mod 16 || [i]1 || [NUMradix(B)]b.
const PQ = new Uint8Array(P.length + tweak.length + padding + 1 + b);
PQ.set(P);
P.fill(0);
PQ.set(tweak, P.length);
const xk = expandKeyLE(key);
const round = (A, B, i, decrypt = false) => {
// Q = ... || [i]1 || [NUMradix(B)]b.
PQ[PQ.length - b - 1] = i;
if (b)
PQ.set(numberToBytesBE(NUMradix(radix, B), b), PQ.length - b);
// PRF
let r = new Uint8Array(16);
for (let j = 0; j < PQ.length / BLOCK_LEN; j++) {
for (let i = 0; i < BLOCK_LEN; i++)
r[i] ^= PQ[j * BLOCK_LEN + i];
encryptBlock(xk, r);
}
// Let S be the first d bytes of the following string of ⎡d/16⎤ blocks:
// R || CIPHK(R ⊕[1]16) || CIPHK(R ⊕[2]16) ...CIPHK(R ⊕[⎡d / 16⎤ 1]16).
let s = Array.from(r);
for (let j = 1; s.length < d; j++) {
const block = numberToBytesBE(BigInt(j), 16);
for (let k = 0; k < BLOCK_LEN; k++)
block[k] ^= r[k];
s.push(...Array.from(encryptBlock(xk, block)));
}
let y = bytesToNumberBE(Uint8Array.from(s.slice(0, d)));
s.fill(0);
if (decrypt)
y = -y;
const m = i % 2 === 0 ? u : v;
let c = mod(NUMradix(radix, A) + y, BigInt(radix) ** BigInt(m));
// STR(radix, m, c)
const C = Array(m).fill(0);
for (let i = 0; i < m; i++, c /= BigInt(radix))
C[m - 1 - i] = Number(c % BigInt(radix));
A.fill(0);
A = B;
B = C;
return [A, B];
};
const destroy = () => {
xk.fill(0);
PQ.fill(0);
};
return { u, round, destroy };
}
const EMPTY_BUF = new Uint8Array([]);
export function FF1(radix, key, tweak = EMPTY_BUF) {
const PQ = getRound.bind(null, radix, key, tweak);
return {
encrypt(x) {
const { u, round, destroy } = PQ(x);
let [A, B] = [x.slice(0, u), x.slice(u)];
for (let i = 0; i < 10; i++)
[A, B] = round(A, B, i);
destroy();
const res = A.concat(B);
A.fill(0);
B.fill(0);
return res;
},
decrypt(x) {
const { u, round, destroy } = PQ(x);
// The FF1.Decrypt algorithm is similar to the FF1.Encrypt algorithm;
// the differences are in Step 6, where:
// 1) the order of the indices is reversed,
// 2) the roles of A and B are swapped
// 3) modular addition is replaced by modular subtraction, in Step 6vi.
let [B, A] = [x.slice(0, u), x.slice(u)];
for (let i = 9; i >= 0; i--)
[A, B] = round(A, B, i, true);
destroy();
const res = B.concat(A);
A.fill(0);
B.fill(0);
return res;
},
};
}
// Binary string which encodes each byte in little-endian byte order
const binLE = {
encode(bytes) {
const x = [];
for (let i = 0; i < bytes.length; i++) {
for (let j = 0, tmp = bytes[i]; j < 8; j++, tmp >>= 1)
x.push(tmp & 1);
}
return x;
},
decode(b) {
if (b.length % 8)
throw new Error('Invalid binary string');
const res = new Uint8Array(b.length / 8);
for (let i = 0, j = 0; i < res.length; i++) {
res[i] = b[j++] | (b[j++] << 1) | (b[j++] << 2) | (b[j++] << 3);
res[i] |= (b[j++] << 4) | (b[j++] << 5) | (b[j++] << 6) | (b[j++] << 7);
}
return res;
},
};
export function BinaryFF1(key, tweak = EMPTY_BUF) {
const ff1 = FF1(2, key, tweak);
return {
encrypt: (x) => binLE.decode(ff1.encrypt(binLE.encode(x))),
decrypt: (x) => binLE.decode(ff1.decrypt(binLE.encode(x))),
};
}
//# sourceMappingURL=ff1.js.map

1
node_modules/@noble/ciphers/esm/ff1.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

3
node_modules/@noble/ciphers/esm/index.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
throw new Error('noble-ciphers have no entry-point: consult README for usage');
export {};
//# sourceMappingURL=index.js.map

1
node_modules/@noble/ciphers/esm/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC"}

11
node_modules/@noble/ciphers/esm/package.json generated vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"type": "module",
"sideEffects": false,
"browser": {
"node:crypto": false
},
"node": {
"./crypto.js": "./esm/cryptoNode.js",
"./crypto": "./esm/cryptoNode.js"
}
}

205
node_modules/@noble/ciphers/esm/salsa.js generated vendored Normal file
View File

@@ -0,0 +1,205 @@
import { bytes as abytes } from './_assert.js';
import { createCipher, rotl } from './_arx.js';
import { poly1305 } from './_poly1305.js';
import { wrapCipher, equalBytes } from './utils.js';
// Salsa20 stream cipher was released in 2005.
// Salsa's goal was to implement AES replacement that does not rely on S-Boxes,
// which are hard to implement in a constant-time manner.
// https://cr.yp.to/snuffle.html, https://cr.yp.to/snuffle/salsafamily-20071225.pdf
/**
* Salsa20 core function.
*/
// prettier-ignore
function salsaCore(s, k, n, out, cnt, rounds = 20) {
// Based on https://cr.yp.to/salsa20.html
let y00 = s[0], y01 = k[0], y02 = k[1], y03 = k[2], // "expa" Key Key Key
y04 = k[3], y05 = s[1], y06 = n[0], y07 = n[1], // Key "nd 3" Nonce Nonce
y08 = cnt, y09 = 0, y10 = s[2], y11 = k[4], // Pos. Pos. "2-by" Key
y12 = k[5], y13 = k[6], y14 = k[7], y15 = s[3]; // Key Key Key "te k"
// Save state to temporary variables
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
for (let r = 0; r < rounds; r += 2) {
x04 ^= rotl(x00 + x12 | 0, 7);
x08 ^= rotl(x04 + x00 | 0, 9);
x12 ^= rotl(x08 + x04 | 0, 13);
x00 ^= rotl(x12 + x08 | 0, 18);
x09 ^= rotl(x05 + x01 | 0, 7);
x13 ^= rotl(x09 + x05 | 0, 9);
x01 ^= rotl(x13 + x09 | 0, 13);
x05 ^= rotl(x01 + x13 | 0, 18);
x14 ^= rotl(x10 + x06 | 0, 7);
x02 ^= rotl(x14 + x10 | 0, 9);
x06 ^= rotl(x02 + x14 | 0, 13);
x10 ^= rotl(x06 + x02 | 0, 18);
x03 ^= rotl(x15 + x11 | 0, 7);
x07 ^= rotl(x03 + x15 | 0, 9);
x11 ^= rotl(x07 + x03 | 0, 13);
x15 ^= rotl(x11 + x07 | 0, 18);
x01 ^= rotl(x00 + x03 | 0, 7);
x02 ^= rotl(x01 + x00 | 0, 9);
x03 ^= rotl(x02 + x01 | 0, 13);
x00 ^= rotl(x03 + x02 | 0, 18);
x06 ^= rotl(x05 + x04 | 0, 7);
x07 ^= rotl(x06 + x05 | 0, 9);
x04 ^= rotl(x07 + x06 | 0, 13);
x05 ^= rotl(x04 + x07 | 0, 18);
x11 ^= rotl(x10 + x09 | 0, 7);
x08 ^= rotl(x11 + x10 | 0, 9);
x09 ^= rotl(x08 + x11 | 0, 13);
x10 ^= rotl(x09 + x08 | 0, 18);
x12 ^= rotl(x15 + x14 | 0, 7);
x13 ^= rotl(x12 + x15 | 0, 9);
x14 ^= rotl(x13 + x12 | 0, 13);
x15 ^= rotl(x14 + x13 | 0, 18);
}
// Write output
let oi = 0;
out[oi++] = (y00 + x00) | 0;
out[oi++] = (y01 + x01) | 0;
out[oi++] = (y02 + x02) | 0;
out[oi++] = (y03 + x03) | 0;
out[oi++] = (y04 + x04) | 0;
out[oi++] = (y05 + x05) | 0;
out[oi++] = (y06 + x06) | 0;
out[oi++] = (y07 + x07) | 0;
out[oi++] = (y08 + x08) | 0;
out[oi++] = (y09 + x09) | 0;
out[oi++] = (y10 + x10) | 0;
out[oi++] = (y11 + x11) | 0;
out[oi++] = (y12 + x12) | 0;
out[oi++] = (y13 + x13) | 0;
out[oi++] = (y14 + x14) | 0;
out[oi++] = (y15 + x15) | 0;
}
/**
* hsalsa hashing function, used primarily in xsalsa, to hash
* key and nonce into key' and nonce'.
* Same as salsaCore, but there doesn't seem to be a way to move the block
* out without 25% performance hit.
*/
// prettier-ignore
export function hsalsa(s, k, i, o32) {
let x00 = s[0], x01 = k[0], x02 = k[1], x03 = k[2], x04 = k[3], x05 = s[1], x06 = i[0], x07 = i[1], x08 = i[2], x09 = i[3], x10 = s[2], x11 = k[4], x12 = k[5], x13 = k[6], x14 = k[7], x15 = s[3];
for (let r = 0; r < 20; r += 2) {
x04 ^= rotl(x00 + x12 | 0, 7);
x08 ^= rotl(x04 + x00 | 0, 9);
x12 ^= rotl(x08 + x04 | 0, 13);
x00 ^= rotl(x12 + x08 | 0, 18);
x09 ^= rotl(x05 + x01 | 0, 7);
x13 ^= rotl(x09 + x05 | 0, 9);
x01 ^= rotl(x13 + x09 | 0, 13);
x05 ^= rotl(x01 + x13 | 0, 18);
x14 ^= rotl(x10 + x06 | 0, 7);
x02 ^= rotl(x14 + x10 | 0, 9);
x06 ^= rotl(x02 + x14 | 0, 13);
x10 ^= rotl(x06 + x02 | 0, 18);
x03 ^= rotl(x15 + x11 | 0, 7);
x07 ^= rotl(x03 + x15 | 0, 9);
x11 ^= rotl(x07 + x03 | 0, 13);
x15 ^= rotl(x11 + x07 | 0, 18);
x01 ^= rotl(x00 + x03 | 0, 7);
x02 ^= rotl(x01 + x00 | 0, 9);
x03 ^= rotl(x02 + x01 | 0, 13);
x00 ^= rotl(x03 + x02 | 0, 18);
x06 ^= rotl(x05 + x04 | 0, 7);
x07 ^= rotl(x06 + x05 | 0, 9);
x04 ^= rotl(x07 + x06 | 0, 13);
x05 ^= rotl(x04 + x07 | 0, 18);
x11 ^= rotl(x10 + x09 | 0, 7);
x08 ^= rotl(x11 + x10 | 0, 9);
x09 ^= rotl(x08 + x11 | 0, 13);
x10 ^= rotl(x09 + x08 | 0, 18);
x12 ^= rotl(x15 + x14 | 0, 7);
x13 ^= rotl(x12 + x15 | 0, 9);
x14 ^= rotl(x13 + x12 | 0, 13);
x15 ^= rotl(x14 + x13 | 0, 18);
}
let oi = 0;
o32[oi++] = x00;
o32[oi++] = x05;
o32[oi++] = x10;
o32[oi++] = x15;
o32[oi++] = x06;
o32[oi++] = x07;
o32[oi++] = x08;
o32[oi++] = x09;
}
/**
* Salsa20 from original paper.
* With 12-byte nonce, it's not safe to use fill it with random (CSPRNG), due to collision chance.
*/
export const salsa20 = /* @__PURE__ */ createCipher(salsaCore, {
allowShortKeys: true,
counterRight: true,
});
/**
* xsalsa20 eXtended-nonce salsa.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
*/
export const xsalsa20 = /* @__PURE__ */ createCipher(salsaCore, {
counterRight: true,
extendNonceFn: hsalsa,
});
/**
* xsalsa20-poly1305 eXtended-nonce salsa.
* With 24-byte nonce, it's safe to use fill it with random (CSPRNG).
* Also known as secretbox from libsodium / nacl.
*/
export const xsalsa20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, (key, nonce) => {
const tagLength = 16;
abytes(key, 32);
abytes(nonce, 24);
return {
encrypt: (plaintext, output) => {
abytes(plaintext);
// This is small optimization (calculate auth key with same call as encryption itself) makes it hard
// to separate tag calculation and encryption itself, since 32 byte is half-block of salsa (64 byte)
const clength = plaintext.length + 32;
if (output) {
abytes(output, clength);
}
else {
output = new Uint8Array(clength);
}
output.set(plaintext, 32);
xsalsa20(key, nonce, output, output);
const authKey = output.subarray(0, 32);
const tag = poly1305(output.subarray(32), authKey);
// Clean auth key, even though JS provides no guarantees about memory cleaning
output.set(tag, tagLength);
output.subarray(0, tagLength).fill(0);
return output.subarray(tagLength);
},
decrypt: (ciphertext) => {
abytes(ciphertext);
const clength = ciphertext.length;
if (clength < tagLength)
throw new Error('encrypted data should be at least 16 bytes');
// Create new ciphertext array:
// auth tag auth tag from ciphertext ciphertext
// [bytes 0..16] [bytes 16..32] [bytes 32..]
// 16 instead of 32, because we already have 16 byte tag
const ciphertext_ = new Uint8Array(clength + tagLength); // alloc
ciphertext_.set(ciphertext, tagLength);
// Each xsalsa20 calls to hsalsa to calculate key, but seems not much perf difference
// Separate call to calculate authkey, since first bytes contains tag
const authKey = xsalsa20(key, nonce, new Uint8Array(32)); // alloc(32)
const tag = poly1305(ciphertext_.subarray(32), authKey);
if (!equalBytes(ciphertext_.subarray(16, 32), tag))
throw new Error('invalid tag');
const plaintext = xsalsa20(key, nonce, ciphertext_); // alloc
// Clean auth key, even though JS provides no guarantees about memory cleaning
plaintext.subarray(0, 32).fill(0);
authKey.fill(0);
return plaintext.subarray(32);
},
};
});
/**
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
*/
export function secretbox(key, nonce) {
const xs = xsalsa20poly1305(key, nonce);
return { seal: xs.encrypt, open: xs.decrypt };
}
//# sourceMappingURL=salsa.js.map

1
node_modules/@noble/ciphers/esm/salsa.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

182
node_modules/@noble/ciphers/esm/utils.js generated vendored Normal file
View File

@@ -0,0 +1,182 @@
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
import { bytes as abytes, isBytes } from './_assert.js';
// Cast array to different type
export const u8 = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
export const u16 = (arr) => new Uint16Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 2));
export const u32 = (arr) => new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
// Cast array to view
export const createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
// big-endian hardware is rare. Just in case someone still decides to run ciphers:
// early-throw an error because we don't support BE yet.
export const isLE = new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44;
if (!isLE)
throw new Error('Non little-endian hardware is not supported');
// Array where index 0xf0 (240) is mapped to string 'f0'
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
/**
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
export function bytesToHex(bytes) {
abytes(bytes);
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
// We use optimized technique to convert hex string to byte array
const asciis = { _0: 48, _9: 57, _A: 65, _F: 70, _a: 97, _f: 102 };
function asciiToBase16(char) {
if (char >= asciis._0 && char <= asciis._9)
return char - asciis._0;
if (char >= asciis._A && char <= asciis._F)
return char - (asciis._A - 10);
if (char >= asciis._a && char <= asciis._f)
return char - (asciis._a - 10);
return;
}
/**
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
*/
export function hexToBytes(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
const hl = hex.length;
const al = hl / 2;
if (hl % 2)
throw new Error('padded hex string expected, got unpadded hex of length ' + hl);
const array = new Uint8Array(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
const n1 = asciiToBase16(hex.charCodeAt(hi));
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
if (n1 === undefined || n2 === undefined) {
const char = hex[hi] + hex[hi + 1];
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
}
array[ai] = n1 * 16 + n2;
}
return array;
}
export function hexToNumber(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
// Big Endian
return BigInt(hex === '' ? '0' : `0x${hex}`);
}
// BE: Big Endian, LE: Little Endian
export function bytesToNumberBE(bytes) {
return hexToNumber(bytesToHex(bytes));
}
export function numberToBytesBE(n, len) {
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
}
// There is no setImmediate in browser and setTimeout is slow.
// call of async fn will return Promise, which will be fullfiled only on
// next scheduler queue processing step and this is exactly what we need.
export const nextTick = async () => { };
// Returns control to thread each 'tick' ms to avoid blocking
export async function asyncLoop(iters, tick, cb) {
let ts = Date.now();
for (let i = 0; i < iters; i++) {
cb(i);
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
const diff = Date.now() - ts;
if (diff >= 0 && diff < tick)
continue;
await nextTick();
ts += diff;
}
}
/**
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
*/
export function utf8ToBytes(str) {
if (typeof str !== 'string')
throw new Error(`string expected, got ${typeof str}`);
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
}
/**
* @example bytesToUtf8(new Uint8Array([97, 98, 99])) // 'abc'
*/
export function bytesToUtf8(bytes) {
return new TextDecoder().decode(bytes);
}
/**
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
* Warning: when Uint8Array is passed, it would NOT get copied.
* Keep in mind for future mutable operations.
*/
export function toBytes(data) {
if (typeof data === 'string')
data = utf8ToBytes(data);
else if (isBytes(data))
data = data.slice();
else
throw new Error(`Uint8Array expected, got ${typeof data}`);
return data;
}
/**
* Copies several Uint8Arrays into one.
*/
export function concatBytes(...arrays) {
let sum = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
abytes(a);
sum += a.length;
}
const res = new Uint8Array(sum);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const a = arrays[i];
res.set(a, pad);
pad += a.length;
}
return res;
}
export function checkOpts(defaults, opts) {
if (opts == null || typeof opts !== 'object')
throw new Error('options must be defined');
const merged = Object.assign(defaults, opts);
return merged;
}
// Compares 2 u8a-s in kinda constant time
export function equalBytes(a, b) {
if (a.length !== b.length)
return false;
let diff = 0;
for (let i = 0; i < a.length; i++)
diff |= a[i] ^ b[i];
return diff === 0;
}
// For runtime check if class implements interface
export class Hash {
}
/**
* @__NO_SIDE_EFFECTS__
*/
export const wrapCipher = (params, c) => {
Object.assign(c, params);
return c;
};
// Polyfill for Safari 14
export function setBigUint64(view, byteOffset, value, isLE) {
if (typeof view.setBigUint64 === 'function')
return view.setBigUint64(byteOffset, value, isLE);
const _32n = BigInt(32);
const _u32_max = BigInt(0xffffffff);
const wh = Number((value >> _32n) & _u32_max);
const wl = Number(value & _u32_max);
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
export function u64Lengths(ciphertext, AAD) {
const num = new Uint8Array(16);
const view = createView(num);
setBigUint64(view, 0, BigInt(AAD ? AAD.length : 0), true);
setBigUint64(view, 8, BigInt(ciphertext.length), true);
return num;
}
//# sourceMappingURL=utils.js.map

1
node_modules/@noble/ciphers/esm/utils.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

105
node_modules/@noble/ciphers/esm/webcrypto.js generated vendored Normal file
View File

@@ -0,0 +1,105 @@
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// node.js versions earlier than v19 don't declare it in global scope.
// For node.js, package.js on#exports field mapping rewrites import
// from `crypto` to `cryptoNode`, which imports native module.
// Makes the utils un-importable in browsers without a bundler.
// Once node.js 18 is deprecated, we can just drop the import.
//
// Use full path so that Node.js can rewrite it to `cryptoNode.js`.
import { randomBytes, getWebcryptoSubtle } from '@noble/ciphers/crypto';
import { concatBytes } from './utils.js';
import { number, bytes as abytes } from './_assert.js';
/**
* Secure PRNG. Uses `crypto.getRandomValues`, which defers to OS.
*/
export { randomBytes, getWebcryptoSubtle };
// Uses CSPRG for nonce, nonce injected in ciphertext
export function managedNonce(fn) {
number(fn.nonceLength);
return ((key, ...args) => ({
encrypt: (plaintext, ...argsEnc) => {
const { nonceLength } = fn;
const nonce = randomBytes(nonceLength);
const ciphertext = fn(key, nonce, ...args).encrypt(plaintext, ...argsEnc);
const out = concatBytes(nonce, ciphertext);
ciphertext.fill(0);
return out;
},
decrypt: (ciphertext, ...argsDec) => {
const { nonceLength } = fn;
const nonce = ciphertext.subarray(0, nonceLength);
const data = ciphertext.subarray(nonceLength);
return fn(key, nonce, ...args).decrypt(data, ...argsDec);
},
}));
}
// Overridable
export const utils = {
async encrypt(key, keyParams, cryptParams, plaintext) {
const cr = getWebcryptoSubtle();
const iKey = await cr.importKey('raw', key, keyParams, true, ['encrypt']);
const ciphertext = await cr.encrypt(cryptParams, iKey, plaintext);
return new Uint8Array(ciphertext);
},
async decrypt(key, keyParams, cryptParams, ciphertext) {
const cr = getWebcryptoSubtle();
const iKey = await cr.importKey('raw', key, keyParams, true, ['decrypt']);
const plaintext = await cr.decrypt(cryptParams, iKey, ciphertext);
return new Uint8Array(plaintext);
},
};
const mode = {
CBC: 'AES-CBC',
CTR: 'AES-CTR',
GCM: 'AES-GCM',
};
function getCryptParams(algo, nonce, AAD) {
if (algo === mode.CBC)
return { name: mode.CBC, iv: nonce };
if (algo === mode.CTR)
return { name: mode.CTR, counter: nonce, length: 64 };
if (algo === mode.GCM) {
if (AAD)
return { name: mode.GCM, iv: nonce, additionalData: AAD };
else
return { name: mode.GCM, iv: nonce };
}
throw new Error('unknown aes block mode');
}
function generate(algo) {
return (key, nonce, AAD) => {
abytes(key);
abytes(nonce);
const keyParams = { name: algo, length: key.length * 8 };
const cryptParams = getCryptParams(algo, nonce, AAD);
return {
// keyLength,
encrypt(plaintext) {
abytes(plaintext);
return utils.encrypt(key, keyParams, cryptParams, plaintext);
},
decrypt(ciphertext) {
abytes(ciphertext);
return utils.decrypt(key, keyParams, cryptParams, ciphertext);
},
};
};
}
export const cbc = generate(mode.CBC);
export const ctr = generate(mode.CTR);
export const gcm = generate(mode.GCM);
// // Type tests
// import { siv, gcm, ctr, ecb, cbc } from '../aes.js';
// import { xsalsa20poly1305 } from '../salsa.js';
// import { chacha20poly1305, xchacha20poly1305 } from '../chacha.js';
// const wsiv = managedNonce(siv);
// const wgcm = managedNonce(gcm);
// const wctr = managedNonce(ctr);
// const wcbc = managedNonce(cbc);
// const wsalsapoly = managedNonce(xsalsa20poly1305);
// const wchacha = managedNonce(chacha20poly1305);
// const wxchacha = managedNonce(xchacha20poly1305);
// // should fail
// const wcbc2 = managedNonce(managedNonce(cbc));
// const wecb = managedNonce(ecb);
//# sourceMappingURL=webcrypto.js.map

1
node_modules/@noble/ciphers/esm/webcrypto.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"webcrypto.js","sourceRoot":"","sources":["../src/webcrypto.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,sEAAsE;AACtE,mEAAmE;AACnE,8DAA8D;AAC9D,+DAA+D;AAC/D,8DAA8D;AAC9D,EAAE;AACF,mEAAmE;AACnE,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAuB,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,MAAM,EAAE,MAAM,cAAc,CAAC;AAEvD;;GAEG;AACH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;AAe3C,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAA4B,EAAK;IAC3D,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC;IACvB,OAAO,CAAC,CAAC,GAAe,EAAE,GAAG,IAAW,EAAO,EAAE,CAAC,CAAC;QACjD,OAAO,EAAE,CAAC,SAAqB,EAAE,GAAG,OAAc,EAAE,EAAE;YACpD,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;YACvC,MAAM,UAAU,GAAI,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,OAAe,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC;YACnF,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;YAC3C,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,EAAE,CAAC,UAAsB,EAAE,GAAG,OAAc,EAAE,EAAE;YACrD,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAC9C,OAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,OAAe,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,CAAC;QACpE,CAAC;KACF,CAAC,CAAmB,CAAC;AACxB,CAAC;AAED,cAAc;AACd,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,KAAK,CAAC,OAAO,CAAC,GAAe,EAAE,SAAc,EAAE,WAAgB,EAAE,SAAqB;QACpF,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1E,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAClE,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,GAAe,EAAE,SAAc,EAAE,WAAgB,EAAE,UAAsB;QACrF,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1E,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAClE,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;CACF,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,SAAS;CACN,CAAC;AAGX,SAAS,cAAc,CAAC,IAAe,EAAE,KAAiB,EAAE,GAAgB;IAC1E,IAAI,IAAI,KAAK,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC5D,IAAI,IAAI,KAAK,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC7E,IAAI,IAAI,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,IAAI,GAAG;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC;;YAC9D,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,QAAQ,CAAC,IAAe;IAC/B,OAAO,CAAC,GAAe,EAAE,KAAiB,EAAE,GAAgB,EAAe,EAAE;QAC3E,MAAM,CAAC,GAAG,CAAC,CAAC;QACZ,MAAM,CAAC,KAAK,CAAC,CAAC;QACd,MAAM,SAAS,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,MAAM,WAAW,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QACrD,OAAO;YACL,aAAa;YACb,OAAO,CAAC,SAAqB;gBAC3B,MAAM,CAAC,SAAS,CAAC,CAAC;gBAClB,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO,CAAC,UAAsB;gBAC5B,MAAM,CAAC,UAAU,CAAC,CAAC;gBACnB,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;YAChE,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEtC,gBAAgB;AAChB,uDAAuD;AACvD,kDAAkD;AAClD,sEAAsE;AAEtE,kCAAkC;AAClC,kCAAkC;AAClC,kCAAkC;AAClC,kCAAkC;AAClC,qDAAqD;AACrD,kDAAkD;AAClD,oDAAoD;AAEpD,iBAAiB;AACjB,iDAAiD;AACjD,kCAAkC"}

7
node_modules/@noble/ciphers/ff1.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
import { Cipher } from './utils.js';
export declare function FF1(radix: number, key: Uint8Array, tweak?: Uint8Array): {
encrypt(x: number[]): number[];
decrypt(x: number[]): number[];
};
export declare function BinaryFF1(key: Uint8Array, tweak?: Uint8Array): Cipher;
//# sourceMappingURL=ff1.d.ts.map

1
node_modules/@noble/ciphers/ff1.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"ff1.d.ts","sourceRoot":"","sources":["src/ff1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoC,MAAM,YAAY,CAAC;AA0FtE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,GAAE,UAAsB;eAGlE,MAAM,EAAE;eAUR,MAAM,EAAE;EAgBtB;AAqBD,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,GAAE,UAAsB,GAAG,MAAM,CAMhF"}

154
node_modules/@noble/ciphers/ff1.js generated vendored Normal file
View File

@@ -0,0 +1,154 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BinaryFF1 = exports.FF1 = void 0;
const utils_js_1 = require("./utils.js");
const aes_js_1 = require("./aes.js");
// NOTE: no point in inlining encrypt instead of encryptBlock, since BigInt stuff will be slow
const { expandKeyLE, encryptBlock } = aes_js_1.unsafe;
// Format-preserving encryption algorithm (FPE-FF1) specified in NIST Special Publication 800-38G.
// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf
const BLOCK_LEN = 16;
function mod(a, b) {
const result = a % b;
return result >= 0 ? result : b + result;
}
function NUMradix(radix, data) {
let res = BigInt(0);
for (let i of data)
res = res * BigInt(radix) + BigInt(i);
return res;
}
function getRound(radix, key, tweak, x) {
if (radix > 2 ** 16 - 1)
throw new Error(`Invalid radix: ${radix}`);
// radix**minlen ≥ 100
const minLen = Math.ceil(Math.log(100) / Math.log(radix));
const maxLen = 2 ** 32 - 1;
// 2 ≤ minlen ≤ maxlen < 2**32
if (2 > minLen || minLen > maxLen || maxLen >= 2 ** 32)
throw new Error('Invalid radix: 2 ≤ minlen ≤ maxlen < 2**32');
if (x.length < minLen || x.length > maxLen)
throw new Error('X is outside minLen..maxLen bounds');
const u = Math.floor(x.length / 2);
const v = x.length - u;
const b = Math.ceil(Math.ceil(v * Math.log2(radix)) / 8);
const d = 4 * Math.ceil(b / 4) + 4;
const padding = mod(-tweak.length - b - 1, 16);
// P = [1]1 || [2]1 || [1]1 || [radix]3 || [10]1 || [u mod 256]1 || [n]4 || [t]4.
const P = new Uint8Array([1, 2, 1, 0, 0, 0, 10, u, 0, 0, 0, 0, 0, 0, 0, 0]);
const view = new DataView(P.buffer);
view.setUint16(4, radix, false);
view.setUint32(8, x.length, false);
view.setUint32(12, tweak.length, false);
// Q = T || [0](tb1) mod 16 || [i]1 || [NUMradix(B)]b.
const PQ = new Uint8Array(P.length + tweak.length + padding + 1 + b);
PQ.set(P);
P.fill(0);
PQ.set(tweak, P.length);
const xk = expandKeyLE(key);
const round = (A, B, i, decrypt = false) => {
// Q = ... || [i]1 || [NUMradix(B)]b.
PQ[PQ.length - b - 1] = i;
if (b)
PQ.set((0, utils_js_1.numberToBytesBE)(NUMradix(radix, B), b), PQ.length - b);
// PRF
let r = new Uint8Array(16);
for (let j = 0; j < PQ.length / BLOCK_LEN; j++) {
for (let i = 0; i < BLOCK_LEN; i++)
r[i] ^= PQ[j * BLOCK_LEN + i];
encryptBlock(xk, r);
}
// Let S be the first d bytes of the following string of ⎡d/16⎤ blocks:
// R || CIPHK(R ⊕[1]16) || CIPHK(R ⊕[2]16) ...CIPHK(R ⊕[⎡d / 16⎤ 1]16).
let s = Array.from(r);
for (let j = 1; s.length < d; j++) {
const block = (0, utils_js_1.numberToBytesBE)(BigInt(j), 16);
for (let k = 0; k < BLOCK_LEN; k++)
block[k] ^= r[k];
s.push(...Array.from(encryptBlock(xk, block)));
}
let y = (0, utils_js_1.bytesToNumberBE)(Uint8Array.from(s.slice(0, d)));
s.fill(0);
if (decrypt)
y = -y;
const m = i % 2 === 0 ? u : v;
let c = mod(NUMradix(radix, A) + y, BigInt(radix) ** BigInt(m));
// STR(radix, m, c)
const C = Array(m).fill(0);
for (let i = 0; i < m; i++, c /= BigInt(radix))
C[m - 1 - i] = Number(c % BigInt(radix));
A.fill(0);
A = B;
B = C;
return [A, B];
};
const destroy = () => {
xk.fill(0);
PQ.fill(0);
};
return { u, round, destroy };
}
const EMPTY_BUF = new Uint8Array([]);
function FF1(radix, key, tweak = EMPTY_BUF) {
const PQ = getRound.bind(null, radix, key, tweak);
return {
encrypt(x) {
const { u, round, destroy } = PQ(x);
let [A, B] = [x.slice(0, u), x.slice(u)];
for (let i = 0; i < 10; i++)
[A, B] = round(A, B, i);
destroy();
const res = A.concat(B);
A.fill(0);
B.fill(0);
return res;
},
decrypt(x) {
const { u, round, destroy } = PQ(x);
// The FF1.Decrypt algorithm is similar to the FF1.Encrypt algorithm;
// the differences are in Step 6, where:
// 1) the order of the indices is reversed,
// 2) the roles of A and B are swapped
// 3) modular addition is replaced by modular subtraction, in Step 6vi.
let [B, A] = [x.slice(0, u), x.slice(u)];
for (let i = 9; i >= 0; i--)
[A, B] = round(A, B, i, true);
destroy();
const res = B.concat(A);
A.fill(0);
B.fill(0);
return res;
},
};
}
exports.FF1 = FF1;
// Binary string which encodes each byte in little-endian byte order
const binLE = {
encode(bytes) {
const x = [];
for (let i = 0; i < bytes.length; i++) {
for (let j = 0, tmp = bytes[i]; j < 8; j++, tmp >>= 1)
x.push(tmp & 1);
}
return x;
},
decode(b) {
if (b.length % 8)
throw new Error('Invalid binary string');
const res = new Uint8Array(b.length / 8);
for (let i = 0, j = 0; i < res.length; i++) {
res[i] = b[j++] | (b[j++] << 1) | (b[j++] << 2) | (b[j++] << 3);
res[i] |= (b[j++] << 4) | (b[j++] << 5) | (b[j++] << 6) | (b[j++] << 7);
}
return res;
},
};
function BinaryFF1(key, tweak = EMPTY_BUF) {
const ff1 = FF1(2, key, tweak);
return {
encrypt: (x) => binLE.decode(ff1.encrypt(binLE.encode(x))),
decrypt: (x) => binLE.decode(ff1.decrypt(binLE.encode(x))),
};
}
exports.BinaryFF1 = BinaryFF1;
//# sourceMappingURL=ff1.js.map

1
node_modules/@noble/ciphers/ff1.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

1
node_modules/@noble/ciphers/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
//# sourceMappingURL=index.d.ts.map

1
node_modules/@noble/ciphers/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":""}

3
node_modules/@noble/ciphers/index.js generated vendored Normal file
View File

@@ -0,0 +1,3 @@
"use strict";
throw new Error('noble-ciphers have no entry-point: consult README for usage');
//# sourceMappingURL=index.js.map

1
node_modules/@noble/ciphers/index.js.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":";AAAA,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC"}

126
node_modules/@noble/ciphers/package.json generated vendored Normal file
View File

@@ -0,0 +1,126 @@
{
"name": "@noble/ciphers",
"version": "0.5.3",
"description": "Auditable & minimal JS implementation of Salsa20, ChaCha and AES",
"files": ["esm", "src", "*.js", "*.js.map", "*.d.ts", "*.d.ts.map"],
"scripts": {
"bench": "node benchmark/aead.js noble && node benchmark/ciphers.js noble",
"bench:all": "node benchmark/{aead,ciphers,poly}.js",
"bench:install": "cd benchmark && npm install && cd ../../",
"build": "npm run build:clean; tsc && tsc -p tsconfig.esm.json",
"build:release": "cd build && npm i && npm run build",
"build:clean": "rm *.{js,d.ts,js.map,d.ts.map} esm/*.{js,d.ts,js.map,d.ts.map} 2> /dev/null",
"lint": "prettier --check 'src/**/*.{js,ts}' 'test/**/*.{js,ts,mjs}'",
"format": "prettier --write 'src/**/*.{js,ts}' 'test/**/*.{js,ts,mjs}'",
"test": "node test/index.js"
},
"author": "Paul Miller (https://paulmillr.com)",
"homepage": "https://paulmillr.com/noble/",
"repository": {
"type": "git",
"url": "git+https://github.com/paulmillr/noble-ciphers.git"
},
"license": "MIT",
"sideEffects": false,
"devDependencies": {
"@paulmillr/jsbt": "0.1.0",
"@scure/base": "1.1.3",
"fast-check": "3.0.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
"prettier": "3.1.1",
"typescript": "5.3.2"
},
"main": "index.js",
"exports": {
".": {
"types": "./index.d.ts",
"import": "./esm/index.js",
"default": "./index.js"
},
"./_micro": {
"types": "./_micro.d.ts",
"import": "./esm/_micro.js",
"default": "./_micro.js"
},
"./_poly1305": {
"types": "./_poly1305.d.ts",
"import": "./esm/_poly1305.js",
"default": "./_poly1305.js"
},
"./_polyval": {
"types": "./_polyval.d.ts",
"import": "./esm/_polyval.js",
"default": "./_polyval.js"
},
"./crypto": {
"types": "./crypto.d.ts",
"node": {
"import": "./esm/cryptoNode.js",
"default": "./cryptoNode.js"
},
"import": "./esm/crypto.js",
"default": "./crypto.js"
},
"./aes": {
"types": "./aes.d.ts",
"import": "./esm/aes.js",
"default": "./aes.js"
},
"./chacha": {
"types": "./chacha.d.ts",
"import": "./esm/chacha.js",
"default": "./chacha.js"
},
"./salsa": {
"types": "./salsa.d.ts",
"import": "./esm/salsa.js",
"default": "./salsa.js"
},
"./ff1": {
"types": "./ff1.d.ts",
"import": "./esm/ff1.js",
"default": "./ff1.js"
},
"./utils": {
"types": "./utils.d.ts",
"import": "./esm/utils.js",
"default": "./utils.js"
},
"./index": {
"types": "./index.d.ts",
"import": "./esm/index.js",
"default": "./index.js"
},
"./webcrypto": {
"types": "./webcrypto.d.ts",
"import": "./esm/webcrypto.js",
"default": "./webcrypto.js"
}
},
"browser": {
"node:crypto": false,
"./crypto": "./crypto.js"
},
"keywords": [
"salsa20",
"chacha",
"aes",
"cryptography",
"crypto",
"noble",
"cipher",
"ciphers",
"xsalsa20",
"xchacha20",
"poly1305",
"xsalsa20poly1305",
"chacha20poly1305",
"xchacha20poly1305",
"secretbox",
"rijndael",
"siv",
"ff1"
],
"funding": "https://paulmillr.com/funding/"
}

Some files were not shown because too many files have changed in this diff Show More