diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 75112b2..8779db0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -103,25 +103,6 @@ jobs: GOEXPERIMENT=greenteagc,jsonv2 GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \ go build -ldflags "-s -w" -o release-binaries/orly-${VERSION}-linux-amd64 . - # # Build for Linux ARM64 (pure Go with purego) - # echo "Building Linux ARM64 (pure Go + purego)..." - # GOEXPERIMENT=greenteagc,jsonv2 GOOS=linux GOARCH=arm64 CGO_ENABLED=0 \ - # go build -ldflags "-s -w" -o release-binaries/orly-${VERSION}-linux-arm64 . - - # # Build for macOS (pure Go with purego) - # echo "Building macOS AMD64 (pure Go + purego)..." - # GOEXPERIMENT=greenteagc,jsonv2 GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 \ - # go build -ldflags "-s -w" -o release-binaries/orly-${VERSION}-darwin-amd64 . - - # echo "Building macOS ARM64 (pure Go + purego)..." - # GOEXPERIMENT=greenteagc,jsonv2 GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 \ - # go build -ldflags "-s -w" -o release-binaries/orly-${VERSION}-darwin-arm64 . - - # # Build for Windows (pure Go with purego) - # echo "Building Windows AMD64 (pure Go + purego)..." - # GOEXPERIMENT=greenteagc,jsonv2 GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \ - # go build -ldflags "-s -w" -o release-binaries/orly-${VERSION}-windows-amd64.exe . - # Note: Only building orly binary as requested # Other cmd utilities (aggregator, benchmark, convert, policytest, stresstest) are development tools diff --git a/pkg/crypto/encryption/LICENSE b/pkg/crypto/encryption/LICENSE new file mode 100644 index 0000000..b171fe2 --- /dev/null +++ b/pkg/crypto/encryption/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 ekzyis + +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. \ No newline at end of file diff --git a/pkg/crypto/encryption/PERFORMANCE_REPORT.md b/pkg/crypto/encryption/PERFORMANCE_REPORT.md deleted file mode 100644 index 7580747..0000000 --- a/pkg/crypto/encryption/PERFORMANCE_REPORT.md +++ /dev/null @@ -1,240 +0,0 @@ -# Encryption Performance Optimization Report - -## Executive Summary - -This report documents the profiling and optimization of encryption functions in the `next.orly.dev/pkg/crypto/encryption` package. The optimization focused on reducing memory allocations and CPU processing time for NIP-44 and NIP-4 encryption/decryption operations. - -## Methodology - -### Profiling Setup - -1. Created comprehensive benchmark tests covering: - - NIP-44 encryption/decryption (small, medium, large messages) - - NIP-4 encryption/decryption - - Conversation key generation - - Round-trip operations - - Internal helper functions (HMAC, padding, key derivation) - -2. Used Go's built-in profiling tools: - - CPU profiling (`-cpuprofile`) - - Memory profiling (`-memprofile`) - - Allocation tracking (`-benchmem`) - -### Initial Findings - -The profiling data revealed several key bottlenecks: - -1. **NIP-44 Encrypt**: 27 allocations per operation, 1936 bytes allocated -2. **NIP-44 Decrypt**: 24 allocations per operation, 1776 bytes allocated -3. **Memory Allocations**: Primary hotspots identified: - - `crypto/hmac.New`: 1.80GB total allocations (29.64% of all allocations) - - `encrypt` function: 0.78GB allocations (12.86% of all allocations) - - `hkdf.Expand`: 1.15GB allocations (19.01% of all allocations) - - Base64 encoding/decoding allocations - -4. **CPU Processing**: Primary hotspots: - - `getKeys`: 2.86s (27.26% of CPU time) - - `encrypt`: 1.74s (16.59% of CPU time) - - `sha256Hmac`: 1.67s (15.92% of CPU time) - - `sha256.block`: 1.71s (16.30% of CPU time) - -## Optimizations Implemented - -### 1. NIP-44 Encrypt Optimization - -**Problem**: Multiple allocations from `append` operations and buffer growth. - -**Solution**: -- Pre-allocate ciphertext buffer with exact size instead of using `append` -- Use `copy` instead of `append` for better performance and fewer allocations - -**Code Changes** (`nip44.go`): -```go -// Pre-allocate with exact size to avoid reallocation -ctLen := 1 + 32 + len(cipher) + 32 -ct := make([]byte, ctLen) -ct[0] = version -copy(ct[1:], o.nonce) -copy(ct[33:], cipher) -copy(ct[33+len(cipher):], mac) -cipherString = make([]byte, base64.StdEncoding.EncodedLen(ctLen)) -base64.StdEncoding.Encode(cipherString, ct) -``` - -**Results**: -- **Before**: 3217 ns/op, 1936 B/op, 27 allocs/op -- **After**: 3147 ns/op, 1936 B/op, 27 allocs/op -- **Improvement**: 2% faster, allocation count unchanged (minor improvement) - -### 2. NIP-44 Decrypt Optimization - -**Problem**: String conversion overhead from `base64.StdEncoding.DecodeString(string(b64ciphertextWrapped))` and inefficient buffer allocation. - -**Solution**: -- Use `base64.StdEncoding.Decode` directly with byte slices to avoid string conversion -- Pre-allocate decoded buffer and slice to actual decoded length -- This eliminates the string allocation and copy overhead - -**Code Changes** (`nip44.go`): -```go -// Pre-allocate decoded buffer to avoid string conversion overhead -decodedLen := base64.StdEncoding.DecodedLen(len(b64ciphertextWrapped)) -decoded := make([]byte, decodedLen) -var n int -if n, err = base64.StdEncoding.Decode(decoded, b64ciphertextWrapped); chk.E(err) { - return -} -decoded = decoded[:n] -``` - -**Results**: -- **Before**: 2530 ns/op, 1776 B/op, 24 allocs/op -- **After**: 2446 ns/op, 1600 B/op, 23 allocs/op -- **Improvement**: 3% faster, 10% less memory, 4% fewer allocations -- **Large messages**: 19028 ns/op → 17109 ns/op (10% faster), 17248 B → 11104 B (36% less memory) - -### 3. NIP-4 Decrypt Optimization - -**Problem**: IV buffer allocation issue where decoded buffer was larger than needed, causing CBC decrypter to fail. - -**Solution**: -- Properly slice decoded buffers to actual decoded length -- Add validation for IV length (must be 16 bytes) -- Use `base64.StdEncoding.Decode` directly instead of `DecodeString` - -**Code Changes** (`nip4.go`): -```go -ciphertextBuf := make([]byte, base64.StdEncoding.EncodedLen(len(parts[0]))) -var ciphertextLen int -if ciphertextLen, err = base64.StdEncoding.Decode(ciphertextBuf, parts[0]); chk.E(err) { - err = errorf.E("error decoding ciphertext from base64: %w", err) - return -} -ciphertext := ciphertextBuf[:ciphertextLen] - -ivBuf := make([]byte, base64.StdEncoding.EncodedLen(len(parts[1]))) -var ivLen int -if ivLen, err = base64.StdEncoding.Decode(ivBuf, parts[1]); chk.E(err) { - err = errorf.E("error decoding iv from base64: %w", err) - return -} -iv := ivBuf[:ivLen] -if len(iv) != 16 { - err = errorf.E("invalid IV length: %d, expected 16", len(iv)) - return -} -``` - -**Results**: -- Fixed critical bug where IV buffer was incorrect size -- Reduced allocations by properly sizing buffers -- Added validation for IV length - -## Performance Comparison - -### NIP-44 Encryption/Decryption - -| Operation | Metric | Before | After | Improvement | -|-----------|--------|--------|-------|-------------| -| Encrypt | Time | 3217 ns/op | 3147 ns/op | **2% faster** | -| Encrypt | Memory | 1936 B/op | 1936 B/op | No change | -| Encrypt | Allocations | 27 allocs/op | 27 allocs/op | No change | -| Decrypt | Time | 2530 ns/op | 2446 ns/op | **3% faster** | -| Decrypt | Memory | 1776 B/op | 1600 B/op | **10% less** | -| Decrypt | Allocations | 24 allocs/op | 23 allocs/op | **4% fewer** | -| Decrypt Large | Time | 19028 ns/op | 17109 ns/op | **10% faster** | -| Decrypt Large | Memory | 17248 B/op | 11104 B/op | **36% less** | -| RoundTrip | Time | 5842 ns/op | 5763 ns/op | **1% faster** | -| RoundTrip | Memory | 3712 B/op | 3536 B/op | **5% less** | -| RoundTrip | Allocations | 51 allocs/op | 50 allocs/op | **2% fewer** | - -### NIP-4 Encryption/Decryption - -| Operation | Metric | Before | After | Notes | -|-----------|--------|--------|-------|-------| -| Encrypt | Time | 866.8 ns/op | 832.8 ns/op | **4% faster** | -| Decrypt | Time | - | 697.2 ns/op | Fixed bug, now working | -| RoundTrip | Time | - | 1568 ns/op | Fixed bug, now working | - -## Key Insights - -### Allocation Reduction - -The most significant improvement came from optimizing base64 decoding: -- **Decrypt**: Reduced from 24 to 23 allocations (4% reduction) -- **Decrypt Large**: Reduced from 17248 to 11104 bytes (36% reduction) -- Eliminated string conversion overhead in `Decrypt` function - -### String Conversion Elimination - -Replacing `base64.StdEncoding.DecodeString(string(b64ciphertextWrapped))` with direct `Decode` on byte slices: -- Eliminates string allocation and copy -- Reduces memory pressure -- Improves cache locality - -### Buffer Pre-allocation - -Pre-allocating buffers with exact sizes: -- Prevents multiple slice growth operations -- Reduces memory fragmentation -- Improves cache locality - -### Remaining Optimization Opportunities - -1. **HMAC Creation**: `crypto/hmac.New` creates a new hash.Hash each time (1.80GB allocations). This is necessary for thread safety, but could potentially be optimized with: - - A sync.Pool for HMAC instances (requires careful reset handling) - - Or pre-allocating HMAC hash state - -2. **HKDF Operations**: `hkdf.Expand` allocations (1.15GB) come from the underlying crypto library. These are harder to optimize without changing the library. - -3. **ChaCha20 Cipher Creation**: Each encryption creates a new cipher instance. This is necessary for thread safety but could potentially be pooled. - -4. **Base64 Encoding**: While we optimized decoding, encoding still allocates. However, encoding is already quite efficient. - -## Recommendations - -1. **Use Direct Base64 Decode**: Always use `base64.StdEncoding.Decode` with byte slices instead of `DecodeString` when possible. - -2. **Pre-allocate Buffers**: When possible, pre-allocate buffers with exact sizes using `make([]byte, size)` instead of `append`. - -3. **Consider HMAC Pooling**: For high-throughput scenarios, consider implementing a sync.Pool for HMAC instances, being careful to properly reset them. - -4. **Monitor Large Messages**: Large message decryption benefits most from these optimizations (36% memory reduction). - -## Conclusion - -The optimizations implemented improved decryption performance: -- **3-10% faster** decryption depending on message size -- **10-36% reduction** in memory allocations -- **4% reduction** in allocation count -- **Fixed critical bug** in NIP-4 decryption - -These improvements will reduce GC pressure and improve overall system throughput, especially under high load conditions with many encryption/decryption operations. The optimizations maintain backward compatibility and require no changes to calling code. - -## Benchmark Results - -Full benchmark output: - -``` -BenchmarkNIP44Encrypt-12 347715 3215 ns/op 1936 B/op 27 allocs/op -BenchmarkNIP44EncryptSmall-12 379057 2957 ns/op 1808 B/op 27 allocs/op -BenchmarkNIP44EncryptLarge-12 62637 19518 ns/op 22192 B/op 27 allocs/op -BenchmarkNIP44Decrypt-12 465872 2494 ns/op 1600 B/op 23 allocs/op -BenchmarkNIP44DecryptSmall-12 486536 2281 ns/op 1536 B/op 23 allocs/op -BenchmarkNIP44DecryptLarge-12 68013 17593 ns/op 11104 B/op 23 allocs/op -BenchmarkNIP44RoundTrip-12 205341 5839 ns/op 3536 B/op 50 allocs/op -BenchmarkNIP4Encrypt-12 1430288 853.4 ns/op 1569 B/op 10 allocs/op -BenchmarkNIP4Decrypt-12 1629267 743.9 ns/op 1296 B/op 6 allocs/op -BenchmarkNIP4RoundTrip-12 686995 1670 ns/op 2867 B/op 16 allocs/op -BenchmarkGenerateConversationKey-12 10000 104030 ns/op 769 B/op 14 allocs/op -BenchmarkCalcPadding-12 48890450 25.49 ns/op 0 B/op 0 allocs/op -BenchmarkGetKeys-12 856620 1279 ns/op 896 B/op 15 allocs/op -BenchmarkEncryptInternal-12 2283678 517.8 ns/op 256 B/op 1 allocs/op -BenchmarkSHA256Hmac-12 1852015 659.4 ns/op 480 B/op 6 allocs/op -``` - -## Date - -Report generated: 2025-11-02 - - diff --git a/pkg/crypto/encryption/README.md b/pkg/crypto/encryption/README.md index 2ca8f06..0cbb670 100644 --- a/pkg/crypto/encryption/README.md +++ b/pkg/crypto/encryption/README.md @@ -1 +1,7 @@ -Code copied from https://github.com/paulmillr/nip44/tree/e7aed61aaf77240ac10c325683eed14b22e7950f/go. +**NIP-44 implementation in Go** + +NIP-44 specification: https://github.com/nostr-protocol/nips/blob/master/44.md + +To use as library: `go get -u github.com/ekzyis/nip44` + +To run tests, clone repository and then run `go test`. diff --git a/pkg/crypto/encryption/benchmark_test.go b/pkg/crypto/encryption/benchmark_test.go index 574c6c8..e06b93d 100644 --- a/pkg/crypto/encryption/benchmark_test.go +++ b/pkg/crypto/encryption/benchmark_test.go @@ -3,8 +3,8 @@ package encryption import ( "testing" - "next.orly.dev/pkg/interfaces/signer/p8k" "lukechampine.com/frand" + "next.orly.dev/pkg/interfaces/signer/p8k" ) // createTestConversationKey creates a test conversation key @@ -25,12 +25,12 @@ func createTestKeyPair() (*p8k.Signer, []byte) { func BenchmarkNIP44Encrypt(b *testing.B) { conversationKey := createTestConversationKey() plaintext := []byte("This is a test message for encryption benchmarking") - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := Encrypt(plaintext, conversationKey) + _, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } @@ -41,12 +41,12 @@ func BenchmarkNIP44Encrypt(b *testing.B) { func BenchmarkNIP44EncryptSmall(b *testing.B) { conversationKey := createTestConversationKey() plaintext := []byte("a") - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := Encrypt(plaintext, conversationKey) + _, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } @@ -60,12 +60,12 @@ func BenchmarkNIP44EncryptLarge(b *testing.B) { for i := range plaintext { plaintext[i] = byte(i % 256) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := Encrypt(plaintext, conversationKey) + _, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } @@ -76,16 +76,16 @@ func BenchmarkNIP44EncryptLarge(b *testing.B) { func BenchmarkNIP44Decrypt(b *testing.B) { conversationKey := createTestConversationKey() plaintext := []byte("This is a test message for encryption benchmarking") - ciphertext, err := Encrypt(plaintext, conversationKey) + ciphertext, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := Decrypt(ciphertext, conversationKey) + _, err := Decrypt(conversationKey, ciphertext) if err != nil { b.Fatal(err) } @@ -96,16 +96,16 @@ func BenchmarkNIP44Decrypt(b *testing.B) { func BenchmarkNIP44DecryptSmall(b *testing.B) { conversationKey := createTestConversationKey() plaintext := []byte("a") - ciphertext, err := Encrypt(plaintext, conversationKey) + ciphertext, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := Decrypt(ciphertext, conversationKey) + _, err := Decrypt(conversationKey, ciphertext) if err != nil { b.Fatal(err) } @@ -119,16 +119,16 @@ func BenchmarkNIP44DecryptLarge(b *testing.B) { for i := range plaintext { plaintext[i] = byte(i % 256) } - ciphertext, err := Encrypt(plaintext, conversationKey) + ciphertext, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := Decrypt(ciphertext, conversationKey) + _, err := Decrypt(conversationKey, ciphertext) if err != nil { b.Fatal(err) } @@ -139,16 +139,16 @@ func BenchmarkNIP44DecryptLarge(b *testing.B) { func BenchmarkNIP44RoundTrip(b *testing.B) { conversationKey := createTestConversationKey() plaintext := []byte("This is a test message for encryption benchmarking") - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - ciphertext, err := Encrypt(plaintext, conversationKey) + ciphertext, err := Encrypt(conversationKey, plaintext, nil) if err != nil { b.Fatal(err) } - _, err = Decrypt(ciphertext, conversationKey) + _, err = Decrypt(conversationKey, ciphertext) if err != nil { b.Fatal(err) } @@ -159,10 +159,10 @@ func BenchmarkNIP44RoundTrip(b *testing.B) { func BenchmarkNIP4Encrypt(b *testing.B) { key := createTestConversationKey() msg := []byte("This is a test message for NIP-4 encryption benchmarking") - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { _, err := EncryptNip4(msg, key) if err != nil { @@ -179,10 +179,10 @@ func BenchmarkNIP4Decrypt(b *testing.B) { if err != nil { b.Fatal(err) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { decrypted, err := DecryptNip4(ciphertext, key) if err != nil { @@ -198,10 +198,10 @@ func BenchmarkNIP4Decrypt(b *testing.B) { func BenchmarkNIP4RoundTrip(b *testing.B) { key := createTestConversationKey() msg := []byte("This is a test message for NIP-4 encryption benchmarking") - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { ciphertext, err := EncryptNip4(msg, key) if err != nil { @@ -216,32 +216,42 @@ func BenchmarkNIP4RoundTrip(b *testing.B) { // BenchmarkGenerateConversationKey benchmarks conversation key generation func BenchmarkGenerateConversationKey(b *testing.B) { - signer1, pub1 := createTestKeyPair() + signer1, _ := createTestKeyPair() signer2, _ := createTestKeyPair() - + + // Get compressed public keys + pub1, err := signer1.PubCompressed() + if err != nil { + b.Fatal(err) + } + pub2, err := signer2.PubCompressed() + if err != nil { + b.Fatal(err) + } + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := GenerateConversationKeyWithSigner(signer1, pub1) + _, err := GenerateConversationKey(signer1.Sec(), pub1) if err != nil { b.Fatal(err) } // Use signer2's pubkey for next iteration to vary inputs - pub1 = signer2.Pub() + pub1 = pub2 } } // BenchmarkCalcPadding benchmarks padding calculation func BenchmarkCalcPadding(b *testing.B) { sizes := []int{1, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768} - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { size := sizes[i%len(sizes)] - _ = CalcPadding(size) + _ = calcPadding(size) } } @@ -249,12 +259,12 @@ func BenchmarkCalcPadding(b *testing.B) { func BenchmarkGetKeys(b *testing.B) { conversationKey := createTestConversationKey() nonce := frand.Bytes(32) - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, _, _, err := getKeys(conversationKey, nonce) + _, _, _, err := MessageKeys(conversationKey, nonce) if err != nil { b.Fatal(err) } @@ -269,12 +279,12 @@ func BenchmarkEncryptInternal(b *testing.B) { for i := range message { message[i] = byte(i % 256) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { - _, err := encrypt(key, nonce, message) + _, err := chacha20_(key, nonce, message) if err != nil { b.Fatal(err) } @@ -289,10 +299,10 @@ func BenchmarkSHA256Hmac(b *testing.B) { for i := range ciphertext { ciphertext[i] = byte(i % 256) } - + b.ResetTimer() b.ReportAllocs() - + for i := 0; i < b.N; i++ { _, err := sha256Hmac(key, ciphertext, nonce) if err != nil { @@ -300,4 +310,3 @@ func BenchmarkSHA256Hmac(b *testing.B) { } } } - diff --git a/pkg/crypto/encryption/nip44.go b/pkg/crypto/encryption/nip44.go index ef91579..9b17166 100644 --- a/pkg/crypto/encryption/nip44.go +++ b/pkg/crypto/encryption/nip44.go @@ -1,283 +1,280 @@ package encryption import ( + "bytes" "crypto/hmac" "crypto/rand" + "crypto/sha256" "encoding/base64" "encoding/binary" + "errors" "io" "math" - "github.com/minio/sha256-simd" "golang.org/x/crypto/chacha20" "golang.org/x/crypto/hkdf" - "lol.mleku.dev/chk" "lol.mleku.dev/errorf" - "next.orly.dev/pkg/encoders/hex" - "next.orly.dev/pkg/interfaces/signer" - "next.orly.dev/pkg/interfaces/signer/p8k" - "next.orly.dev/pkg/utils" + "next.orly.dev/pkg/crypto/ec/secp256k1" ) -const ( - version byte = 2 - MinPlaintextSize int = 0x0001 // 1b msg => padded to 32b - MaxPlaintextSize int = 0xffff // 65535 (64kb-1) => padded to 64kb +var ( + MinPlaintextSize = 0x0001 // 1b msg => padded to 32b + MaxPlaintextSize = 0xffff // 65535 (64kb-1) => padded to 64kb ) -type Opts struct { - err error - nonce []byte +type EncryptOptions struct { + Salt []byte + Version int } -// Deprecated: use WithCustomNonce instead of WithCustomSalt, so the naming is less confusing -var WithCustomSalt = WithCustomNonce - -// WithCustomNonce enables using a custom nonce (salt) instead of using the -// system crypto/rand entropy source. -func WithCustomNonce(salt []byte) func(opts *Opts) { - return func(opts *Opts) { - if len(salt) != 32 { - opts.err = errorf.E("salt must be 32 bytes, got %d", len(salt)) - } - opts.nonce = salt +func Encrypt(conversationKey []byte, plaintext []byte, options *EncryptOptions) (ciphertext string, err error) { + var ( + version int = 2 + salt []byte + enc []byte + nonce []byte + auth []byte + padded []byte + encrypted []byte + hmac_ []byte + concat []byte + ) + if options != nil && options.Version != 0 { + version = options.Version } -} - -// Encrypt data using a provided symmetric conversation key using NIP-44 -// encryption (chacha20 cipher stream and sha256 HMAC). -func Encrypt( - plaintext, conversationKey []byte, applyOptions ...func(opts *Opts), -) ( - cipherString []byte, err error, -) { - - var o Opts - for _, apply := range applyOptions { - apply(&o) - } - if chk.E(o.err) { - err = o.err - return - } - if o.nonce == nil { - o.nonce = make([]byte, 32) - if _, err = rand.Read(o.nonce); chk.E(err) { + if options != nil && options.Salt != nil { + salt = options.Salt + } else { + if salt, err = randomBytes(32); err != nil { return } } - var enc, cc20nonce, auth []byte - if enc, cc20nonce, auth, err = getKeys( - conversationKey, o.nonce, - ); chk.E(err) { + if version != 2 { + err = errorf.E("unknown version %d", version) return } - plain := plaintext - size := len(plain) - if size < MinPlaintextSize || size > MaxPlaintextSize { - err = errorf.E("plaintext should be between 1b and 64kB") + if len(salt) != 32 { + err = errorf.E("salt must be 32 bytes") return } - padding := CalcPadding(size) - padded := make([]byte, 2+padding) - binary.BigEndian.PutUint16(padded, uint16(size)) - copy(padded[2:], plain) - var cipher []byte - if cipher, err = encrypt(enc, cc20nonce, padded); chk.E(err) { + if enc, nonce, auth, err = MessageKeys(conversationKey, salt); err != nil { return } - var mac []byte - if mac, err = sha256Hmac(auth, cipher, o.nonce); chk.E(err) { + if padded, err = pad(plaintext); err != nil { return } - // Pre-allocate with exact size to avoid reallocation - ctLen := 1 + 32 + len(cipher) + 32 - ct := make([]byte, ctLen) - ct[0] = version - copy(ct[1:], o.nonce) - copy(ct[33:], cipher) - copy(ct[33+len(cipher):], mac) - cipherString = make([]byte, base64.StdEncoding.EncodedLen(ctLen)) - base64.StdEncoding.Encode(cipherString, ct) + if encrypted, err = chacha20_(enc, nonce, padded); err != nil { + return + } + if hmac_, err = sha256Hmac(auth, encrypted, salt); err != nil { + return + } + concat = append(concat, []byte{byte(version)}...) + concat = append(concat, salt...) + concat = append(concat, encrypted...) + concat = append(concat, hmac_...) + ciphertext = base64.StdEncoding.EncodeToString(concat) return } -// Decrypt data that has been encoded using a provided symmetric conversation -// key using NIP-44 encryption (chacha20 cipher stream and sha256 HMAC). -func Decrypt(b64ciphertextWrapped, conversationKey []byte) ( - plaintext []byte, - err error, -) { - cLen := len(b64ciphertextWrapped) +func Decrypt(conversationKey []byte, ciphertext string) (plaintext string, err error) { + var ( + version int = 2 + decoded []byte + cLen int + dLen int + salt []byte + ciphertext_ []byte + hmac []byte + hmac_ []byte + enc []byte + nonce []byte + auth []byte + padded []byte + unpaddedLen uint16 + unpadded []byte + ) + cLen = len(ciphertext) if cLen < 132 || cLen > 87472 { err = errorf.E("invalid payload length: %d", cLen) return } - if len(b64ciphertextWrapped) > 0 && b64ciphertextWrapped[0] == '#' { + if ciphertext[0:1] == "#" { err = errorf.E("unknown version") return } - // Pre-allocate decoded buffer to avoid string conversion overhead - decodedLen := base64.StdEncoding.DecodedLen(len(b64ciphertextWrapped)) - decoded := make([]byte, decodedLen) - var n int - if n, err = base64.StdEncoding.Decode(decoded, b64ciphertextWrapped); chk.E(err) { + if decoded, err = base64.StdEncoding.DecodeString(ciphertext); err != nil { + err = errorf.E("invalid base64") return } - decoded = decoded[:n] - if decoded[0] != version { - err = errorf.E("unknown version %d", decoded[0]) + if version = int(decoded[0]); version != 2 { + err = errorf.E("unknown version %d", version) return } - dLen := len(decoded) + dLen = len(decoded) if dLen < 99 || dLen > 65603 { err = errorf.E("invalid data length: %d", dLen) return } - nonce, ciphertext, givenMac := decoded[1:33], decoded[33:dLen-32], decoded[dLen-32:] - var enc, cc20nonce, auth []byte - if enc, cc20nonce, auth, err = getKeys(conversationKey, nonce); chk.E(err) { + salt, ciphertext_, hmac_ = decoded[1:33], decoded[33:dLen-32], decoded[dLen-32:] + if enc, nonce, auth, err = MessageKeys(conversationKey, salt); err != nil { return } - var expectedMac []byte - if expectedMac, err = sha256Hmac(auth, ciphertext, nonce); chk.E(err) { + if hmac, err = sha256Hmac(auth, ciphertext_, salt); err != nil { return } - if !utils.FastEqual(givenMac, expectedMac) { + if !bytes.Equal(hmac_, hmac) { err = errorf.E("invalid hmac") return } - var padded []byte - if padded, err = encrypt(enc, cc20nonce, ciphertext); chk.E(err) { + if padded, err = chacha20_(enc, nonce, ciphertext_); err != nil { return } - unpaddedLen := binary.BigEndian.Uint16(padded[0:2]) - if unpaddedLen < uint16(MinPlaintextSize) || unpaddedLen > uint16(MaxPlaintextSize) || - len(padded) != 2+CalcPadding(int(unpaddedLen)) { + unpaddedLen = binary.BigEndian.Uint16(padded[0:2]) + if unpaddedLen < uint16(MinPlaintextSize) || unpaddedLen > uint16(MaxPlaintextSize) || len(padded) != 2+calcPadding(int(unpaddedLen)) { err = errorf.E("invalid padding") return } - unpadded := padded[2:][:unpaddedLen] + unpadded = padded[2 : unpaddedLen+2] if len(unpadded) == 0 || len(unpadded) != int(unpaddedLen) { err = errorf.E("invalid padding") return } - plaintext = unpadded + plaintext = string(unpadded) return } -// GenerateConversationKeyFromHex performs an ECDH key generation hashed with the nip-44-v2 using hkdf. -// Parameters match NIP-44 spec: sender's private key first, then recipient's public key. -// The public key can be either: -// - 32 bytes (x-coordinate only, 64 hex characters) -// - 33 bytes (compressed format with 0x02/0x03 prefix, 66 hex characters) -func GenerateConversationKeyFromHex(skh, pkh string) (ck []byte, err error) { - if skh >= "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" || - skh == "0000000000000000000000000000000000000000000000000000000000000000" { - err = errorf.E( - "invalid private key: x coordinate %s is not on the secp256k1 curve", - skh, - ) +func GenerateConversationKey(sendPrivkey []byte, recvPubkey []byte) (conversationKey []byte, err error) { + // Parse the private key + var privKey secp256k1.SecretKey + if overflow := privKey.Key.SetByteSlice(sendPrivkey); overflow { + err = errorf.E("invalid private key: x coordinate %x is not on the secp256k1 curve", sendPrivkey) return } - var sign *p8k.Signer - if sign, err = p8k.New(); chk.E(err) { + + // Check if private key is zero + if privKey.Key.IsZero() { + err = errorf.E("invalid private key: x coordinate %x is not on the secp256k1 curve", sendPrivkey) return } - var sk []byte - if sk, err = hex.Dec(skh); chk.E(err) { + + // Parse the public key + // If it's 32 bytes, prepend format byte for compressed format (0x02 for even y) + // If it's already 33 bytes, use as-is + var pubKeyBytes []byte + if len(recvPubkey) == 32 { + // Nostr-style 32-byte public key - prepend compressed format byte + pubKeyBytes = make([]byte, 33) + pubKeyBytes[0] = secp256k1.PubKeyFormatCompressedEven + copy(pubKeyBytes[1:], recvPubkey) + } else if len(recvPubkey) == 33 { + // Already in compressed format + pubKeyBytes = recvPubkey + } else { + err = errorf.E("invalid public key length: %d (expected 32 or 33 bytes)", len(recvPubkey)) return } - if err = sign.InitSec(sk); chk.E(err) { + + pubKey, err := secp256k1.ParsePubKey(pubKeyBytes) + if err != nil { return } - var pk []byte - if pk, err = hex.Dec(pkh); chk.E(err) { - return - } - // pk can be 32 bytes (x-coordinate) or 33 bytes (compressed) - if len(pk) != 32 && len(pk) != 33 { - err = errorf.E("public key must be 32 bytes (x-coordinate) or 33 bytes (compressed format), got %d bytes", len(pk)) - return - } - var shared []byte - if shared, err = sign.ECDHRaw(pk); chk.E(err) { - return - } - ck = hkdf.Extract(sha256.New, shared, []byte("nip44-v2")) + + // Compute ECDH shared secret (returns only x-coordinate, 32 bytes) + shared := secp256k1.GenerateSharedSecret(&privKey, pubKey) + + // Apply HKDF-Extract with salt "nip44-v2" + conversationKey = hkdf.Extract(sha256.New, shared, []byte("nip44-v2")) return } -func GenerateConversationKeyWithSigner(sign signer.I, pk []byte) ( - ck []byte, err error, -) { - var shared []byte - if shared, err = sign.ECDHRaw(pk); chk.E(err) { - return +func chacha20_(key []byte, nonce []byte, message []byte) ([]byte, error) { + var ( + cipher *chacha20.Cipher + dst = make([]byte, len(message)) + err error + ) + if cipher, err = chacha20.NewUnauthenticatedCipher(key, nonce); err != nil { + return nil, err } - ck = hkdf.Extract(sha256.New, shared, []byte("nip44-v2")) - return -} - -func encrypt(key, nonce, message []byte) (dst []byte, err error) { - var cipher *chacha20.Cipher - if cipher, err = chacha20.NewUnauthenticatedCipher(key, nonce); chk.E(err) { - return - } - dst = make([]byte, len(message)) cipher.XORKeyStream(dst, message) - return + return dst, nil } -func sha256Hmac(key, ciphertext, nonce []byte) (h []byte, err error) { - if len(nonce) != sha256.Size { - err = errorf.E("nonce aad must be 32 bytes") - return +func randomBytes(n int) ([]byte, error) { + buf := make([]byte, n) + if _, err := rand.Read(buf); err != nil { + return nil, err } - hm := hmac.New(sha256.New, key) - hm.Write(nonce) - hm.Write(ciphertext) - h = hm.Sum(nil) - return + return buf, nil } -func getKeys(conversationKey, nonce []byte) ( - enc, cc20nonce, auth []byte, err error, -) { +func sha256Hmac(key []byte, ciphertext []byte, aad []byte) ([]byte, error) { + if len(aad) != 32 { + return nil, errors.New("aad data must be 32 bytes") + } + h := hmac.New(sha256.New, key) + h.Write(aad) + h.Write(ciphertext) + return h.Sum(nil), nil +} + +func MessageKeys(conversationKey []byte, salt []byte) ([]byte, []byte, []byte, error) { + var ( + r io.Reader + enc []byte = make([]byte, 32) + nonce []byte = make([]byte, 12) + auth []byte = make([]byte, 32) + err error + ) if len(conversationKey) != 32 { - err = errorf.E("conversation key must be 32 bytes") - return + return nil, nil, nil, errors.New("conversation key must be 32 bytes") } - if len(nonce) != 32 { - err = errorf.E("nonce must be 32 bytes") - return + if len(salt) != 32 { + return nil, nil, nil, errors.New("salt must be 32 bytes") } - r := hkdf.Expand(sha256.New, conversationKey, nonce) - enc = make([]byte, 32) - if _, err = io.ReadFull(r, enc); chk.E(err) { - return + r = hkdf.Expand(sha256.New, conversationKey, salt) + if _, err = io.ReadFull(r, enc); err != nil { + return nil, nil, nil, err } - cc20nonce = make([]byte, 12) - if _, err = io.ReadFull(r, cc20nonce); chk.E(err) { - return + if _, err = io.ReadFull(r, nonce); err != nil { + return nil, nil, nil, err } - auth = make([]byte, 32) - if _, err = io.ReadFull(r, auth); chk.E(err) { - return + if _, err = io.ReadFull(r, auth); err != nil { + return nil, nil, nil, err } - return + return enc, nonce, auth, nil } -// CalcPadding creates padding for the message payload that is precisely a power -// of two in order to reduce the chances of plaintext attack. This is plainly -// retarded because it could blow out the message size a lot when just a random few -// dozen bytes and a length prefix would achieve the same result. -func CalcPadding(sLen int) (l int) { +func pad(s []byte) ([]byte, error) { + var ( + sb []byte + sbLen int + padding int + result []byte + ) + sb = s + sbLen = len(sb) + if sbLen < 1 || sbLen > MaxPlaintextSize { + return nil, errors.New("plaintext should be between 1b and 64kB") + } + padding = calcPadding(sbLen) + result = make([]byte, 2) + binary.BigEndian.PutUint16(result, uint16(sbLen)) + result = append(result, sb...) + result = append(result, make([]byte, padding-sbLen)...) + return result, nil +} + +func calcPadding(sLen int) int { + var ( + nextPower int + chunk int + ) if sLen <= 32 { return 32 } - nextPower := 1 << int(math.Floor(math.Log2(float64(sLen-1)))+1) - chunk := int(math.Max(32, float64(nextPower/8))) - l = chunk * int(math.Floor(float64((sLen-1)/chunk))+1) - return + nextPower = 1 << int(math.Floor(math.Log2(float64(sLen-1)))+1) + chunk = int(math.Max(32, float64(nextPower/8))) + return chunk * int(math.Floor(float64((sLen-1)/chunk))+1) } diff --git a/pkg/crypto/encryption/nip44_test.go b/pkg/crypto/encryption/nip44_test.go index 7927a23..79f956f 100644 --- a/pkg/crypto/encryption/nip44_test.go +++ b/pkg/crypto/encryption/nip44_test.go @@ -1,288 +1,274 @@ -package encryption +package encryption_test import ( - "crypto/rand" - "fmt" + "encoding/hex" "hash" - "strings" "testing" "github.com/minio/sha256-simd" + "github.com/stretchr/testify/assert" - "lol.mleku.dev/chk" - "next.orly.dev/pkg/crypto/keys" - "next.orly.dev/pkg/encoders/hex" - "next.orly.dev/pkg/interfaces/signer/p8k" + "next.orly.dev/pkg/crypto/ec/secp256k1" + nip44 "next.orly.dev/pkg/crypto/encryption" ) -func assertCryptPriv( - t *testing.T, - sk1, sk2, conversationKey, salt, plaintext, expected string, -) { +func assertCryptPriv(t *testing.T, sk1 string, sk2 string, conversationKey string, salt string, plaintext string, expected string) { var ( - k1, s, plaintextBytes, actualBytes, - expectedBytes, decrypted []byte - ok bool - err error + k1 []byte + s []byte + actual string + decrypted string + ok bool + err error ) - k1, err = hex.Dec(conversationKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for conversation key: %v", err, - ); !ok { + k1, err = hex.DecodeString(conversationKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok { return } - if ok = assertConversationKeyGenerationSec( - t, sk1, sk2, conversationKey, - ); !ok { + if ok = assertConversationKeyGenerationSec(t, sk1, sk2, conversationKey); !ok { return } - s, err = hex.Dec(salt) - if ok = assert.NoErrorf( - t, err, "hex decode failed for salt: %v", err, - ); !ok { + s, err = hex.DecodeString(salt) + if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok { return } - plaintextBytes = []byte(plaintext) - actualBytes, err = Encrypt(plaintextBytes, k1, WithCustomNonce(s)) + actual, err = nip44.Encrypt(k1, []byte(plaintext), &nip44.EncryptOptions{Salt: s}) if ok = assert.NoError(t, err, "encryption failed: %v", err); !ok { return } - expectedBytes = []byte(expected) - if ok = assert.Equalf( - t, string(expectedBytes), string(actualBytes), "wrong encryption", - ); !ok { + if ok = assert.Equalf(t, expected, actual, "wrong encryption"); !ok { return } - decrypted, err = Decrypt(expectedBytes, k1) + decrypted, err = nip44.Decrypt(k1, expected) if ok = assert.NoErrorf(t, err, "decryption failed: %v", err); !ok { return } - assert.Equal(t, decrypted, plaintextBytes, "wrong decryption") + assert.Equal(t, decrypted, plaintext, "wrong decryption") } -func assertDecryptFail( - t *testing.T, conversationKey, plaintext, ciphertext, msg string, -) { +func assertCryptPub(t *testing.T, sk1 string, pub2 string, conversationKey string, salt string, plaintext string, expected string) { var ( - k1, ciphertextBytes []byte + k1 []byte + s []byte + actual string + decrypted string + ok bool + err error + ) + k1, err = hex.DecodeString(conversationKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok { + return + } + if ok = assertConversationKeyGenerationPub(t, sk1, pub2, conversationKey); !ok { + return + } + s, err = hex.DecodeString(salt) + if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok { + return + } + actual, err = nip44.Encrypt(k1, []byte(plaintext), &nip44.EncryptOptions{Salt: s}) + if ok = assert.NoError(t, err, "encryption failed: %v", err); !ok { + return + } + if ok = assert.Equalf(t, expected, actual, "wrong encryption"); !ok { + return + } + decrypted, err = nip44.Decrypt(k1, expected) + if ok = assert.NoErrorf(t, err, "decryption failed: %v", err); !ok { + return + } + assert.Equal(t, decrypted, plaintext, "wrong decryption") +} + +func assertDecryptFail(t *testing.T, conversationKey string, plaintext string, ciphertext string, msg string) { + var ( + k1 []byte + ok bool + err error + ) + k1, err = hex.DecodeString(conversationKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok { + return + } + _, err = nip44.Decrypt(k1, ciphertext) + assert.ErrorContains(t, err, msg) +} + +func assertConversationKeyFail(t *testing.T, sk1 string, pub2 string, msg string) { + var ( + sk1Decoded []byte + pub2Decoded []byte + ok bool + err error + ) + sk1Decoded, err = hex.DecodeString(sk1) + if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok { + return + } + pub2Decoded, err = hex.DecodeString("02" + pub2) + if ok = assert.NoErrorf(t, err, "hex decode failed for pub2: %v", err); !ok { + return + } + _, err = nip44.GenerateConversationKey(sk1Decoded, pub2Decoded) + assert.ErrorContains(t, err, msg) +} + +func assertConversationKeyGeneration(t *testing.T, sendPrivkey []byte, recvPubkey []byte, conversationKey string) bool { + var ( + actualConversationKey []byte + expectedConversationKey []byte + ok bool + err error + ) + expectedConversationKey, err = hex.DecodeString(conversationKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for conversation key: %v", err); !ok { + return false + } + actualConversationKey, err = nip44.GenerateConversationKey(sendPrivkey, recvPubkey) + if ok = assert.NoErrorf(t, err, "conversation key generation failed: %v", err); !ok { + return false + } + if ok = assert.Equalf(t, expectedConversationKey, actualConversationKey, "wrong conversation key"); !ok { + return false + } + return true +} + +func assertConversationKeyGenerationSec(t *testing.T, sk1 string, sk2 string, conversationKey string) bool { + var ( + sk1Decoded []byte + pub2Decoded []byte + ok bool + err error + ) + sk1Decoded, err = hex.DecodeString(sk1) + if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok { + return false + } + if decoded, err := hex.DecodeString(sk2); err == nil { + var privKey secp256k1.SecretKey + if !privKey.Key.SetByteSlice(decoded) { + pubKey := privKey.PubKey() + pub2Decoded = pubKey.SerializeCompressed() + } + } + if ok = assert.NoErrorf(t, err, "hex decode failed for sk2: %v", err); !ok { + return false + } + return assertConversationKeyGeneration(t, sk1Decoded, pub2Decoded, conversationKey) +} + +func assertConversationKeyGenerationPub(t *testing.T, sk1 string, pub2 string, conversationKey string) bool { + var ( + sk1Decoded []byte + pub2Decoded []byte + ok bool + err error + ) + sk1Decoded, err = hex.DecodeString(sk1) + if ok = assert.NoErrorf(t, err, "hex decode failed for sk1: %v", err); !ok { + return false + } + pub2Decoded, err = hex.DecodeString("02" + pub2) + if ok = assert.NoErrorf(t, err, "hex decode failed for pub2: %v", err); !ok { + return false + } + return assertConversationKeyGeneration(t, sk1Decoded, pub2Decoded, conversationKey) +} + +func assertMessageKeyGeneration(t *testing.T, conversationKey string, salt string, chachaKey string, chachaSalt string, hmacKey string) bool { + var ( + convKey []byte + convSalt []byte + actualChaChaKey []byte + expectedChaChaKey []byte + actualChaChaNonce []byte + expectedChaChaNonce []byte + actualHmacKey []byte + expectedHmacKey []byte ok bool err error ) - k1, err = hex.Dec(conversationKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for conversation key: %v", err, - ); !ok { - return - } - ciphertextBytes = []byte(ciphertext) - _, err = Decrypt(ciphertextBytes, k1) - assert.ErrorContains(t, err, msg) -} - -func assertConversationKeyFail( - t *testing.T, priv string, pub string, msg string, -) { - _, err := GenerateConversationKeyFromHex(priv, pub) - assert.ErrorContains(t, err, msg) -} - -func assertConversationKeyGeneration( - t *testing.T, priv, pub, conversationKey string, -) bool { - var ( - actualConversationKey, - expectedConversationKey []byte - ok bool - err error - ) - expectedConversationKey, err = hex.Dec(conversationKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for conversation key: %v", err, - ); !ok { + convKey, err = hex.DecodeString(conversationKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for convKey: %v", err); !ok { return false } - actualConversationKey, err = GenerateConversationKeyFromHex(priv, pub) - if ok = assert.NoErrorf( - t, err, "conversation key generation failed: %v", err, - ); !ok { + convSalt, err = hex.DecodeString(salt) + if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok { return false } - if ok = assert.Equalf( - t, expectedConversationKey, actualConversationKey, - "wrong conversation key", - ); !ok { + expectedChaChaKey, err = hex.DecodeString(chachaKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for chacha key: %v", err); !ok { + return false + } + expectedChaChaNonce, err = hex.DecodeString(chachaSalt) + if ok = assert.NoErrorf(t, err, "hex decode failed for chacha nonce: %v", err); !ok { + return false + } + expectedHmacKey, err = hex.DecodeString(hmacKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for hmac key: %v", err); !ok { + return false + } + actualChaChaKey, actualChaChaNonce, actualHmacKey, err = nip44.MessageKeys(convKey, convSalt) + if ok = assert.NoErrorf(t, err, "message key generation failed: %v", err); !ok { + return false + } + if ok = assert.Equalf(t, expectedChaChaKey, actualChaChaKey, "wrong chacha key"); !ok { + return false + } + if ok = assert.Equalf(t, expectedChaChaNonce, actualChaChaNonce, "wrong chacha nonce"); !ok { + return false + } + if ok = assert.Equalf(t, expectedHmacKey, actualHmacKey, "wrong hmac key"); !ok { return false } return true } -func assertConversationKeyGenerationSec( - t *testing.T, sk1, sk2, conversationKey string, -) bool { - // Like ekzyis reference: derive compressed public key from sk2 - var signer2 *p8k.Signer - var err error - var sk2Bytes []byte - - if sk2Bytes, err = hex.Dec(sk2); !assert.NoErrorf( - t, err, "hex decode failed for sk2: %v", err, - ) { - return false - } - - if signer2, err = p8k.New(); !assert.NoErrorf( - t, err, "failed to create signer: %v", err, - ) { - return false - } - - if err = signer2.InitSec(sk2Bytes); !assert.NoErrorf( - t, err, "failed to init secret: %v", err, - ) { - return false - } - - // Get compressed public key (33 bytes with 0x02/0x03 prefix) - var pub2Compressed []byte - if pub2Compressed, err = signer2.PubCompressed(); !assert.NoErrorf( - t, err, "failed to get compressed pubkey: %v", err, - ) { - return false - } - - pub2 := hex.Enc(pub2Compressed) - return assertConversationKeyGeneration(t, sk1, pub2, conversationKey) -} - -func assertConversationKeyGenerationPub( - t *testing.T, sk, pub, conversationKey string, -) bool { - return assertConversationKeyGeneration(t, sk, pub, conversationKey) -} - -func assertMessageKeyGeneration( - t *testing.T, - conversationKey, salt, chachaKey, chachaSalt, hmacKey string, -) bool { +func assertCryptLong(t *testing.T, conversationKey string, salt string, pattern string, repeat int, plaintextSha256 string, payloadSha256 string) { var ( - convKey, convSalt, actualChaChaKey, expectedChaChaKey, actualChaChaNonce, - expectedChaChaNonce, actualHmacKey, expectedHmacKey []byte - ok bool - err error + convKey []byte + convSalt []byte + plaintext string + actualPlaintextSha256 string + actualPayload string + actualPayloadSha256 string + h hash.Hash + ok bool + err error ) - convKey, err = hex.Dec(conversationKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for convKey: %v", err, - ); !ok { - return false - } - convSalt, err = hex.Dec(salt) - if ok = assert.NoErrorf( - t, err, "hex decode failed for salt: %v", err, - ); !ok { - return false - } - expectedChaChaKey, err = hex.Dec(chachaKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for encrypt key: %v", err, - ); !ok { - return false - } - expectedChaChaNonce, err = hex.Dec(chachaSalt) - if ok = assert.NoErrorf( - t, err, "hex decode failed for encrypt nonce: %v", err, - ); !ok { - return false - } - expectedHmacKey, err = hex.Dec(hmacKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for hmac key: %v", err, - ); !ok { - return false - } - actualChaChaKey, actualChaChaNonce, actualHmacKey, err = getKeys( - convKey, convSalt, - ) - if ok = assert.NoErrorf( - t, err, "message key generation failed: %v", err, - ); !ok { - return false - } - if ok = assert.Equalf( - t, expectedChaChaKey, actualChaChaKey, "wrong encrypt key", - ); !ok { - return false - } - if ok = assert.Equalf( - t, expectedChaChaNonce, actualChaChaNonce, - "wrong encrypt nonce", - ); !ok { - return false - } - if ok = assert.Equalf( - t, expectedHmacKey, actualHmacKey, "wrong hmac key", - ); !ok { - return false - } - return true -} - -func assertCryptLong( - t *testing.T, conversationKey, salt string, pattern []byte, repeat int, - plaintextSha256, payloadSha256 string, -) { - var ( - convKey, convSalt, plaintext, payloadBytes []byte - actualPlaintextSha256, actualPayloadSha256 string - h hash.Hash - ok bool - err error - ) - convKey, err = hex.Dec(conversationKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for convKey: %v", err, - ); !ok { + convKey, err = hex.DecodeString(conversationKey) + if ok = assert.NoErrorf(t, err, "hex decode failed for convKey: %v", err); !ok { return } - convSalt, err = hex.Dec(salt) - if ok = assert.NoErrorf( - t, err, "hex decode failed for salt: %v", err, - ); !ok { + convSalt, err = hex.DecodeString(salt) + if ok = assert.NoErrorf(t, err, "hex decode failed for salt: %v", err); !ok { return } - plaintext = make([]byte, 0, len(pattern)*repeat) + plaintext = "" for i := 0; i < repeat; i++ { - plaintext = append(plaintext, pattern...) + plaintext += pattern } h = sha256.New() - h.Write(plaintext) - actualPlaintextSha256 = hex.Enc(h.Sum(nil)) - if ok = assert.Equalf( - t, plaintextSha256, actualPlaintextSha256, - "invalid plaintext sha256 hash: %v", err, - ); !ok { + h.Write([]byte(plaintext)) + actualPlaintextSha256 = hex.EncodeToString(h.Sum(nil)) + if ok = assert.Equalf(t, plaintextSha256, actualPlaintextSha256, "invalid plaintext sha256 hash: %v", err); !ok { return } - payloadBytes, err = Encrypt( - plaintext, convKey, WithCustomNonce(convSalt), - ) + actualPayload, err = nip44.Encrypt(convKey, []byte(plaintext), &nip44.EncryptOptions{Salt: convSalt}) if ok = assert.NoErrorf(t, err, "encryption failed: %v", err); !ok { return } h.Reset() - h.Write(payloadBytes) - actualPayloadSha256 = hex.Enc(h.Sum(nil)) - if ok = assert.Equalf( - t, payloadSha256, actualPayloadSha256, - "invalid payload sha256 hash: %v", err, - ); !ok { + h.Write([]byte(actualPayload)) + actualPayloadSha256 = hex.EncodeToString(h.Sum(nil)) + if ok = assert.Equalf(t, payloadSha256, actualPayloadSha256, "invalid payload sha256 hash: %v", err); !ok { return } } func TestCryptPriv001(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000002", "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d", @@ -293,8 +279,7 @@ func TestCryptPriv001(t *testing.T) { } func TestCryptPriv002(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "0000000000000000000000000000000000000000000000000000000000000002", "0000000000000000000000000000000000000000000000000000000000000001", "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d", @@ -305,8 +290,7 @@ func TestCryptPriv002(t *testing.T) { } func TestCryptPriv003(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a", "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d", "3e2b52a63be47d34fe0a80e34e73d436d6963bc8f39827f327057a9986c20a45", @@ -317,8 +301,7 @@ func TestCryptPriv003(t *testing.T) { } func TestCryptPriv004(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "8f40e50a84a7462e2b8d24c28898ef1f23359fff50d8c509e6fb7ce06e142f9c", "b9b0a1e9cc20100c5faa3bbe2777303d25950616c4c6a3fa2e3e046f936ec2ba", "d5a2f879123145a4b291d767428870f5a8d9e5007193321795b40183d4ab8c2b", @@ -329,8 +312,7 @@ func TestCryptPriv004(t *testing.T) { } func TestCryptPriv005(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "875adb475056aec0b4809bd2db9aa00cff53a649e7b59d8edcbf4e6330b0995c", "9c05781112d5b0a2a7148a222e50e0bd891d6b60c5483f03456e982185944aae", "3b15c977e20bfe4b8482991274635edd94f366595b1a3d2993515705ca3cedb8", @@ -341,8 +323,7 @@ func TestCryptPriv005(t *testing.T) { } func TestCryptPriv006(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "eba1687cab6a3101bfc68fd70f214aa4cc059e9ec1b79fdb9ad0a0a4e259829f", "dff20d262bef9dfd94666548f556393085e6ea421c8af86e9d333fa8747e94b3", "4f1538411098cf11c8af216836444787c462d47f97287f46cf7edb2c4915b8a5", @@ -353,8 +334,7 @@ func TestCryptPriv006(t *testing.T) { } func TestCryptPriv007(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", @@ -365,8 +345,7 @@ func TestCryptPriv007(t *testing.T) { } func TestCryptPriv008(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", @@ -377,8 +356,7 @@ func TestCryptPriv008(t *testing.T) { } func TestCryptPriv009X(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", @@ -389,8 +367,7 @@ func TestCryptPriv009X(t *testing.T) { } func TestCryptPriv010(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", @@ -401,8 +378,7 @@ func TestCryptPriv010(t *testing.T) { } func TestCryptPriv011(t *testing.T) { - assertCryptPriv( - t, + assertCryptPriv(t, "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", @@ -413,11 +389,10 @@ func TestCryptPriv011(t *testing.T) { } func TestCryptLong001(t *testing.T) { - assertCryptLong( - t, + assertCryptLong(t, "8fc262099ce0d0bb9b89bac05bb9e04f9bc0090acc181fef6840ccee470371ed", "326bcb2c943cd6bb717588c9e5a7e738edf6ed14ec5f5344caa6ef56f0b9cff7", - []byte("x"), + "x", 65535, "09ab7495d3e61a76f0deb12cb0306f0696cbb17ffc12131368c7a939f12f56d3", "90714492225faba06310bff2f249ebdc2a5e609d65a629f1c87f2d4ffc55330a", @@ -425,11 +400,10 @@ func TestCryptLong001(t *testing.T) { } func TestCryptLong002(t *testing.T) { - assertCryptLong( - t, + assertCryptLong(t, "56adbe3720339363ab9c3b8526ffce9fd77600927488bfc4b59f7a68ffe5eae0", "ad68da81833c2a8ff609c3d2c0335fd44fe5954f85bb580c6a8d467aa9fc5dd0", - []byte("!"), + "!", 65535, "6af297793b72ae092c422e552c3bb3cbc310da274bd1cf9e31023a7fe4a2d75e", "8013e45a109fad3362133132b460a2d5bce235fe71c8b8f4014793fb52a49844", @@ -437,11 +411,10 @@ func TestCryptLong002(t *testing.T) { } func TestCryptLong003(t *testing.T) { - assertCryptLong( - t, + assertCryptLong(t, "7fc540779979e472bb8d12480b443d1e5eb1098eae546ef2390bee499bbf46be", "34905e82105c20de9a2f6cd385a0d541e6bcc10601d12481ff3a7575dc622033", - []byte("🦄"), + "🦄", 16383, "a249558d161b77297bc0cb311dde7d77190f6571b25c7e4429cd19044634a61f", "b3348422471da1f3c59d79acfe2fe103f3cd24488109e5b18734cdb5953afd15", @@ -450,8 +423,7 @@ func TestCryptLong003(t *testing.T) { func TestConversationKeyFail001(t *testing.T) { // sec1 higher than curve.n - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "invalid private key: x coordinate ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff is not on the secp256k1 curve", @@ -460,8 +432,7 @@ func TestConversationKeyFail001(t *testing.T) { func TestConversationKeyFail002(t *testing.T) { // sec1 is 0 - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "0000000000000000000000000000000000000000000000000000000000000000", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "invalid private key: x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve", @@ -470,19 +441,16 @@ func TestConversationKeyFail002(t *testing.T) { func TestConversationKeyFail003(t *testing.T) { // pub2 is invalid, no sqrt, all-ff - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "failed to parse public key", - // "invalid public key: x >= field prime", + "invalid public key: x >= field prime", ) } func TestConversationKeyFail004(t *testing.T) { // sec1 == curve.n - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", "invalid private key: x coordinate fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 is not on the secp256k1 curve", @@ -491,51 +459,42 @@ func TestConversationKeyFail004(t *testing.T) { func TestConversationKeyFail005(t *testing.T) { // pub2 is invalid, no sqrt - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "0000000000000000000000000000000000000000000000000000000000000002", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - "failed to parse public key", - // "invalid public key: x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve", + "invalid public key: x coordinate 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef is not on the secp256k1 curve", ) } func TestConversationKeyFail006(t *testing.T) { // pub2 is point of order 3 on twist - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", "0000000000000000000000000000000000000000000000000000000000000000", - "failed to parse public key", - // "invalid public key: x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve", + "invalid public key: x coordinate 0000000000000000000000000000000000000000000000000000000000000000 is not on the secp256k1 curve", ) } func TestConversationKeyFail007(t *testing.T) { // pub2 is point of order 13 on twist - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", "eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d", - "failed to parse public key", - // "invalid public key: x coordinate eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d is not on the secp256k1 curve", + "invalid public key: x coordinate eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d is not on the secp256k1 curve", ) } func TestConversationKeyFail008(t *testing.T) { // pub2 is point of order 3319 on twist - assertConversationKeyFail( - t, + assertConversationKeyFail(t, "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", "709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f", - "failed to parse public key", - // "invalid public key: x coordinate 709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f is not on the secp256k1 curve", + "invalid public key: x coordinate 709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f is not on the secp256k1 curve", ) } func TestDecryptFail001(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "ca2527a037347b91bea0c8a30fc8d9600ffd81ec00038671e3a0f0cb0fc9f642", // "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db", "n o b l e", @@ -545,8 +504,7 @@ func TestDecryptFail001(t *testing.T) { } func TestDecryptFail002(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "36f04e558af246352dcf73b692fbd3646a2207bd8abd4b1cd26b234db84d9481", // "ad408d4be8616dc84bb0bf046454a2a102edac937c35209c43cd7964c5feb781", "⚠️", @@ -556,19 +514,17 @@ func TestDecryptFail002(t *testing.T) { } func TestDecryptFail003(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "ca2527a037347b91bea0c8a30fc8d9600ffd81ec00038671e3a0f0cb0fc9f642", // "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db", "n o s t r", "Atфupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbrhdG8VmJZE0UICD06CUvEvdnr1cp1fiMtlM/GrE92xAc1EwsVCQEgWEu2gsHUVf4JAa3TpgkmFc3TWsax0v6n/Wq", - "illegal base64 data at input byte 2", + "invalid base64", ) } func TestDecryptFail004(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "cff7bd6a3e29a450fd27f6c125d5edeb0987c475fd1e8d97591e0d4d8a89763c", // "09ff97750b084012e15ecb84614ce88180d7b8ec0d468508a86b6d70c0361a25", "¯\\_(ツ)_/¯", @@ -578,8 +534,7 @@ func TestDecryptFail004(t *testing.T) { } func TestDecryptFail005(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "cfcc9cf682dfb00b11357f65bdc45e29156b69db424d20b3596919074f5bf957", // "65b14b0b949aaa7d52c417eb753b390e8ad6d84b23af4bec6d9bfa3e03a08af4", "🥎", @@ -589,8 +544,7 @@ func TestDecryptFail005(t *testing.T) { } func TestDecryptFail006(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "5254827d29177622d40a7b67cad014fe7137700c3c523903ebbe3e1b74d40214", // "7ab65dbb8bbc2b8e35cafb5745314e1f050325a864d11d0475ef75b3660d91c1", "elliptic-curve cryptography", @@ -600,19 +554,18 @@ func TestDecryptFail006(t *testing.T) { } func TestDecryptFail007(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "fea39aca9aa8340c3a78ae1f0902aa7e726946e4efcd7783379df8096029c496", // "7d4283e3b54c885d6afee881f48e62f0a3f5d7a9e1cb71ccab594a7882c39330", "noble", "An1Cg+O1TIhdav7ogfSOYvCj9dep4ctxzKtZSniCw5MwRrrPJFyAQYZh5VpjC2QYzny5LIQ9v9lhqmZR4WBYRNJ0ognHVNMwiFV1SHpvUFT8HHZN/m/QarflbvDHAtO6pY16", "invalid padding", ) + } func TestDecryptFail008(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "0c4cffb7a6f7e706ec94b2e879f1fc54ff8de38d8db87e11787694d5392d5b3f", // "6f9fd72667c273acd23ca6653711a708434474dd9eb15c3edb01ce9a95743e9b", "censorship-resistant and global social network", @@ -622,8 +575,7 @@ func TestDecryptFail008(t *testing.T) { } func TestDecryptFail009(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "5cd2d13b9e355aeb2452afbd3786870dbeecb9d355b12cb0a3b6e9da5744cd35", // "b60036976a1ada277b948fd4caa065304b96964742b89d26f26a25263a5060bd", "0", @@ -633,8 +585,7 @@ func TestDecryptFail009(t *testing.T) { } func TestDecryptFail010(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "d61d3f09c7dfe1c0be91af7109b60a7d9d498920c90cbba1e137320fdd938853", // "1a29d02c8b4527745a2ccb38bfa45655deb37bc338ab9289d756354cea1fd07c", "1", @@ -644,8 +595,7 @@ func TestDecryptFail010(t *testing.T) { } func TestDecryptFail011(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "873bb0fc665eb950a8e7d5971965539f6ebd645c83c08cd6a85aafbad0f0bc47", // "c826d3c38e765ab8cc42060116cd1464b2a6ce01d33deba5dedfb48615306d4a", "2", @@ -653,10 +603,8 @@ func TestDecryptFail011(t *testing.T) { "invalid payload length: 48", ) } - func TestDecryptFail012(t *testing.T) { - assertDecryptFail( - t, + assertDecryptFail(t, "9f2fef8f5401ac33f74641b568a7a30bb19409c76ffdc5eae2db6b39d2617fbe", // "9ff6484642545221624eaac7b9ea27133a4cc2356682a6033aceeef043549861", "3", @@ -666,188 +614,167 @@ func TestDecryptFail012(t *testing.T) { } func TestConversationKey001(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "315e59ff51cb9209768cf7da80791ddcaae56ac9775eb25b6dee1234bc5d2268", "c2f9d9948dc8c7c38321e4b85c8558872eafa0641cd269db76848a6073e69133", - "9d4ce2dced70fd54894bd4e1e3509136bee1c5573b08ffd86c3dcc04f2cc99ca", + "3dfef0ce2a4d80a25e7a328accf73448ef67096f65f79588e358d9a0eb9013f1", ) } func TestConversationKey002(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "a1e37752c9fdc1273be53f68c5f74be7c8905728e8de75800b94262f9497c86e", "03bb7947065dde12ba991ea045132581d0954f042c84e06d8c00066e23c1a800", - "217cdcc158edaa9ebac91af882353ffc0372b450c135315c245e48ffa23efdf7", + "4d14f36e81b8452128da64fe6f1eae873baae2f444b02c950b90e43553f2178b", ) } func TestConversationKey003(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "98a5902fd67518a0c900f0fb62158f278f94a21d6f9d33d30cd3091195500311", "aae65c15f98e5e677b5050de82e3aba47a6fe49b3dab7863cf35d9478ba9f7d1", - "17540957c96b901bd4d665ad7b33ac6144793c024f050ba460f975f1bf952b6e", + "9c00b769d5f54d02bf175b7284a1cbd28b6911b06cda6666b2243561ac96bad7", ) } func TestConversationKey004(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "86ae5ac8034eb2542ce23ec2f84375655dab7f836836bbd3c54cefe9fdc9c19f", "59f90272378089d73f1339710c02e2be6db584e9cdbe86eed3578f0c67c23585", - "7c4af2456b151d0966b64e9e462bee907b92a3f6d253882556c254fc11c9140f", + "19f934aafd3324e8415299b64df42049afaa051c71c98d0aa10e1081f2e3e2ba", ) } func TestConversationKey005(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "2528c287fe822421bc0dc4c3615878eb98e8a8c31657616d08b29c00ce209e34", "f66ea16104c01a1c532e03f166c5370a22a5505753005a566366097150c6df60", - "652493c2472a24794907b8bdfb7dc8e56ea2022e607918ca6f9e170e9f1886bc", + "c833bbb292956c43366145326d53b955ffb5da4e4998a2d853611841903f5442", ) } func TestConversationKey006(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "49808637b2d21129478041813aceb6f2c9d4929cd1303cdaf4fbdbd690905ff2", "74d2aab13e97827ea21baf253ad7e39b974bb2498cc747cdb168582a11847b65", - "7f186c96ebdcb32e6ad374d33303f2d618aad43a8f965a3392ac3cb1d0e85110", + "4bf304d3c8c4608864c0fe03890b90279328cd24a018ffa9eb8f8ccec06b505d", ) } func TestConversationKey007(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "af67c382106242c5baabf856efdc0629cc1c5b4061f85b8ceaba52aa7e4b4082", "bdaf0001d63e7ec994fad736eab178ee3c2d7cfc925ae29f37d19224486db57b", - "8d4f18de53fdae5aa404547764429674f5075e589790947e248a1dcf4b867697", + "a3a575dd66d45e9379904047ebfb9a7873c471687d0535db00ef2daa24b391db", ) } func TestConversationKey008(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "0e44e2d1db3c1717b05ffa0f08d102a09c554a1cbbf678ab158b259a44e682f1", "1ffa76c5cc7a836af6914b840483726207cb750889753d7499fb8b76aa8fe0de", - "2d90b6069def88c4fce31c28d3d9ec8328bc6893d1c5dd02235f403af7ea5540", + "a39970a667b7f861f100e3827f4adbf6f464e2697686fe1a81aeda817d6b8bdf", ) } func TestConversationKey009(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "5fc0070dbd0666dbddc21d788db04050b86ed8b456b080794c2a0c8e33287bb6", "31990752f296dd22e146c9e6f152a269d84b241cc95bb3ff8ec341628a54caf0", - "8d02fe35ec3ff734de79a0da26fe38223232d2fa909e7a9438451d633f8395a1", + "72c21075f4b2349ce01a3e604e02a9ab9f07e35dd07eff746de348b4f3c6365e", ) } func TestConversationKey010(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "1b7de0d64d9b12ddbb52ef217a3a7c47c4362ce7ea837d760dad58ab313cba64", "24383541dd8083b93d144b431679d70ef4eec10c98fceef1eff08b1d81d4b065", - "e3efc88ea3b67f27602c5a0033bf57e1174eaed468d685ab6835629319a1f9f9", + "dd152a76b44e63d1afd4dfff0785fa07b3e494a9e8401aba31ff925caeb8f5b1", ) } func TestConversationKey011(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "df2f560e213ca5fb33b9ecde771c7c0cbd30f1cf43c2c24de54480069d9ab0af", "eeea26e552fc8b5e377acaa03e47daa2d7b0c787fac1e0774c9504d9094c430e", - "77efc793bdaf6b7ea889353b68707530e615fa106d454001fd9013880576ab3f", + "770519e803b80f411c34aef59c3ca018608842ebf53909c48d35250bd9323af6", ) } func TestConversationKey012(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "cffff919fcc07b8003fdc63bc8a00c0f5dc81022c1c927c62c597352190d95b9", "eb5c3cca1a968e26684e5b0eb733aecfc844f95a09ac4e126a9e58a4e4902f92", - "248d4c8b660266a25b3e595fb51afc3f22e83db85b9ebcb8f56c4587a272701f", + "46a14ee7e80e439ec75c66f04ad824b53a632b8409a29bbb7c192e43c00bb795", ) } func TestConversationKey013(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "64ba5a685e443e881e9094647ddd32db14444bb21aa7986beeba3d1c4673ba0a", "50e6a4339fac1f3bf86f2401dd797af43ad45bbf58e0801a7877a3984c77c3c4", - "4fdb2226074f4cfa308fcd1a2fdf3c40e61d97b15d52d4306ae65c86cd21f25d", + "968b9dbbfcede1664a4ca35a5d3379c064736e87aafbf0b5d114dff710b8a946", ) } func TestConversationKey014(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "dd0c31ccce4ec8083f9b75dbf23cc2878e6d1b6baa17713841a2428f69dee91a", "b483e84c1339812bed25be55cff959778dfc6edde97ccd9e3649f442472c091b", - "9f865913b556656341ac1222d949d2471973f0c52af50034255489582a4421c1", + "09024503c7bde07eb7865505891c1ea672bf2d9e25e18dd7a7cea6c69bf44b5d", ) } func TestConversationKey015(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "af71313b0d95c41e968a172b33ba5ebd19d06cdf8a7a98df80ecf7af4f6f0358", "2a5c25266695b461ee2af927a6c44a3c598b8095b0557e9bd7f787067435bc7c", - "0a4be1d6c43298e93a7ca27b9f3e20b8a2a2ea9be31c8a542cf525cf85e10372", + "fe5155b27c1c4b4e92a933edae23726a04802a7cc354a77ac273c85aa3c97a92", ) } func TestConversationKey016(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "6636e8a389f75fe068a03b3edb3ea4a785e2768e3f73f48ffb1fc5e7cb7289dc", "514eb2064224b6a5829ea21b6e8f7d3ea15ff8e70e8555010f649eb6e09aec70", - "49d2c0088e89856b56566d5a4b492ac9e7c219c1019018bca65cb465c24d3631", + "ff7afacd4d1a6856d37ca5b546890e46e922b508639214991cf8048ddbe9745c", ) } func TestConversationKey017(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "94b212f02a3cfb8ad147d52941d3f1dbe1753804458e6645af92c7b2ea791caa", "f0cac333231367a04b652a77ab4f8d658b94e86b5a8a0c472c5c7b0d4c6a40cc", - "98cd935572ff535b68990f558638ba3399c19acaea4a783a167a349bad9c4872", + "e292eaf873addfed0a457c6bd16c8effde33d6664265697f69f420ab16f6669b", ) } func TestConversationKey018(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "aa61f9734e69ae88e5d4ced5aae881c96f0d7f16cca603d3bed9eec391136da6", "4303e5360a884c360221de8606b72dd316da49a37fe51e17ada4f35f671620a6", - "49d2c0088e89856b56566d5a4b492ac9e7c219c1019018bca65cb465c24d3631", + "8e7d44fd4767456df1fb61f134092a52fcd6836ebab3b00766e16732683ed848", ) } func TestConversationKey019(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "5e914bdac54f3f8e2cba94ee898b33240019297b69e96e70c8a495943a72fc98", "5bd097924f606695c59f18ff8fd53c174adbafaaa71b3c0b4144a3e0a474b198", - "d9aee5a1c3491352e9cba0b8d3887c9aeb6f4a6caae19811d507bb3ef47210b2d", + "f5a0aecf2984bf923c8cd5e7bb8be262d1a8353cb93959434b943a07cf5644bc", ) } func TestConversationKey020(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "8b275067add6312ddee064bcdbeb9d17e88aa1df36f430b2cea5cc0413d8278a", "65bbbfca819c90c7579f7a82b750a18c858db1afbec8f35b3c1e0e7b5588e9b8", - "469f0da3a3b53edbb0af1db5d3d595f39e42edb3d9c916618a50927d272bff71", + "2c565e7027eb46038c2263563d7af681697107e975e9914b799d425effd248d6", ) } func TestConversationKey021(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "1ac848de312285f85e0f7ec208aac20142a1f453402af9b34ec2ec7a1f9c96fc", "45f7318fe96034d23ee3ddc25b77f275cc1dd329664dd51b89f89c4963868e41", "b56e970e5057a8fd929f8aad9248176b9af87819a708d9ddd56e41d1aec74088", @@ -855,8 +782,7 @@ func TestConversationKey021(t *testing.T) { } func TestConversationKey022(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "295a1cf621de401783d29d0e89036aa1c62d13d9ad307161b4ceb535ba1b40e6", "840115ddc7f1034d3b21d8e2103f6cb5ab0b63cf613f4ea6e61ae3d016715cdd", "b4ee9c0b9b9fef88975773394f0a6f981ca016076143a1bb575b9ff46e804753", @@ -864,8 +790,7 @@ func TestConversationKey022(t *testing.T) { } func TestConversationKey023(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "a28eed0fe977893856ab9667e06ace39f03abbcdb845c329a1981be438ba565d", "b0f38b950a5013eba5ab4237f9ed29204a59f3625c71b7e210fec565edfa288c", "9d3a802b45bc5aeeb3b303e8e18a92ddd353375710a31600d7f5fff8f3a7285b", @@ -873,8 +798,7 @@ func TestConversationKey023(t *testing.T) { } func TestConversationKey024(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "7ab65af72a478c05f5c651bdc4876c74b63d20d04cdbf71741e46978797cd5a4", "f1112159161b568a9cb8c9dd6430b526c4204bcc8ce07464b0845b04c041beda", "943884cddaca5a3fef355e9e7f08a3019b0b66aa63ec90278b0f9fdb64821e79", @@ -882,8 +806,7 @@ func TestConversationKey024(t *testing.T) { } func TestConversationKey025(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "95c79a7b75ba40f2229e85756884c138916f9d103fc8f18acc0877a7cceac9fe", "cad76bcbd31ca7bbda184d20cc42f725ed0bb105b13580c41330e03023f0ffb3", "81c0832a669eea13b4247c40be51ccfd15bb63fcd1bba5b4530ce0e2632f301b", @@ -891,8 +814,7 @@ func TestConversationKey025(t *testing.T) { } func TestConversationKey026(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "baf55cc2febd4d980b4b393972dfc1acf49541e336b56d33d429bce44fa12ec9", "0c31cf87fe565766089b64b39460ebbfdedd4a2bc8379be73ad3c0718c912e18", "37e2344da9ecdf60ae2205d81e89d34b280b0a3f111171af7e4391ded93b8ea6", @@ -900,8 +822,7 @@ func TestConversationKey026(t *testing.T) { } func TestConversationKey027(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "6eeec45acd2ed31693c5256026abf9f072f01c4abb61f51cf64e6956b6dc8907", "e501b34ed11f13d816748c0369b0c728e540df3755bab59ed3327339e16ff828", "afaa141b522ddb27bb880d768903a7f618bb8b6357728cae7fb03af639b946e6", @@ -909,17 +830,15 @@ func TestConversationKey027(t *testing.T) { } func TestConversationKey028(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "261a076a9702af1647fb343c55b3f9a4f1096273002287df0015ba81ce5294df", "b2777c863878893ae100fb740c8fab4bebd2bf7be78c761a75593670380a6112", - "1f70de97fd7f605973b35b5ca64b2939ce5a039e70cab88c2a088bdeccc81bf8", + "76f8d2853de0734e51189ced523c09427c3e46338b9522cd6f74ef5e5b475c74", ) } func TestConversationKey029(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "ed3ec71ca406552ea41faec53e19f44b8f90575eda4b7e96380f9cc73c26d6f3", "86425951e61f94b62e20cae24184b42e8e17afcf55bafa58645efd0172624fae", "f7ffc520a3a0e9e9b3c0967325c9bf12707f8e7a03f28b6cd69ae92cf33f7036", @@ -927,8 +846,7 @@ func TestConversationKey029(t *testing.T) { } func TestConversationKey030(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "5a788fc43378d1303ac78639c59a58cb88b08b3859df33193e63a5a3801c722e", "a8cba2f87657d229db69bee07850fd6f7a2ed070171a06d006ec3a8ac562cf70", "7d705a27feeedf78b5c07283362f8e361760d3e9f78adab83e3ae5ce7aeb6409", @@ -936,27 +854,24 @@ func TestConversationKey030(t *testing.T) { } func TestConversationKey031(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "63bffa986e382b0ac8ccc1aa93d18a7aa445116478be6f2453bad1f2d3af2344", "b895c70a83e782c1cf84af558d1038e6b211c6f84ede60408f519a293201031d", - "3445872a13f45a46ecd362c0e347cd32b3532b1b4cd35ec567ad4d4afe7a1665", + "3a3b8f00d4987fc6711d9be64d9c59cf9a709c6c6481c2cde404bcc7a28f174e", ) } func TestConversationKey032(t *testing.T) { - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "e4a8bcacbf445fd3721792b939ff58e691cdcba6a8ba67ac3467b45567a03e5c", "b54053189e8c9252c6950059c783edb10675d06d20c7b342f73ec9fa6ed39c9d", - "d9aee5a1c3491352e9cba0b8d3887c9aeb6f4a6caae19811d507bb3ef47210b2d", + "7b3933b4ef8189d347169c7955589fc1cfc01da5239591a08a183ff6694c44ad", ) } func TestConversationKey033(t *testing.T) { // sec1 = n-2, pub2: random, 0x02 - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139", "0000000000000000000000000000000000000000000000000000000000000002", "8b6392dbf2ec6a2b2d5b1477fc2be84d63ef254b667cadd31bd3f444c44ae6ba", @@ -965,8 +880,7 @@ func TestConversationKey033(t *testing.T) { func TestConversationKey034(t *testing.T) { // sec1 = 2, pub2: rand - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "0000000000000000000000000000000000000000000000000000000000000002", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeb", "be234f46f60a250bef52a5ee34c758800c4ca8e5030bf4cc1a31d37ba2104d43", @@ -975,17 +889,15 @@ func TestConversationKey034(t *testing.T) { func TestConversationKey035(t *testing.T) { // sec1 == pub2 - assertConversationKeyGenerationPub( - t, + assertConversationKeyGenerationPub(t, "0000000000000000000000000000000000000000000000000000000000000001", "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "7b88c5403f9b6598e1dcad39aa052aadfd50f357c7dc498b93d928e518685737", + "3b4610cb7189beb9cc29eb3716ecc6102f1247e8f3101a03a1787d8908aeb54e", ) } func TestMessageKeyGeneration001(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "e1e6f880560d6d149ed83dcc7e5861ee62a5ee051f7fde9975fe5d25d2a02d72", "f145f3bed47cb70dbeaac07f3a3fe683e822b3715edb7c4fe310829014ce7d76", @@ -995,8 +907,7 @@ func TestMessageKeyGeneration001(t *testing.T) { } func TestMessageKeyGeneration002(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "e1d6d28c46de60168b43d79dacc519698512ec35e8ccb12640fc8e9f26121101", "e35b88f8d4a8f1606c5082f7a64b100e5d85fcdb2e62aeafbec03fb9e860ad92", @@ -1006,8 +917,7 @@ func TestMessageKeyGeneration002(t *testing.T) { } func TestMessageKeyGeneration003(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "cfc13bef512ac9c15951ab00030dfaf2626fdca638dedb35f2993a9eeb85d650", "020783eb35fdf5b80ef8c75377f4e937efb26bcbad0e61b4190e39939860c4bf", @@ -1017,8 +927,7 @@ func TestMessageKeyGeneration003(t *testing.T) { } func TestMessageKeyGeneration004(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "ea6eb84cac23c5c1607c334e8bdf66f7977a7e374052327ec28c6906cbe25967", "ff68db24b34fa62c78ac5ffeeaf19533afaedf651fb6a08384e46787f6ce94be", @@ -1028,8 +937,7 @@ func TestMessageKeyGeneration004(t *testing.T) { } func TestMessageKeyGeneration005(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "8c2e1dd3792802f1f9f7842e0323e5d52ad7472daf360f26e15f97290173605d", "2f9daeda8683fdeede81adac247c63cc7671fa817a1fd47352e95d9487989d8b", @@ -1039,8 +947,7 @@ func TestMessageKeyGeneration005(t *testing.T) { } func TestMessageKeyGeneration006(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "05c28bf3d834fa4af8143bf5201a856fa5fac1a3aee58f4c93a764fc2f722367", "1e3d45777025a035be566d80fd580def73ed6f7c043faec2c8c1c690ad31c110", @@ -1050,8 +957,7 @@ func TestMessageKeyGeneration006(t *testing.T) { } func TestMessageKeyGeneration007(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "5e043fb153227866e75a06d60185851bc90273bfb93342f6632a728e18a07a17", "1ea72c9293841e7737c71567d8120145a58991aaa1c436ef77bf7adb83f882f1", @@ -1061,8 +967,7 @@ func TestMessageKeyGeneration007(t *testing.T) { } func TestMessageKeyGeneration009(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "7be7338eaf06a87e274244847fe7a97f5c6a91f44adc18fcc3e411ad6f786dbf", "881e7968a1f0c2c80742ee03cd49ea587e13f22699730f1075ade01931582bf6", @@ -1072,8 +977,7 @@ func TestMessageKeyGeneration009(t *testing.T) { } func TestMessageKeyGeneration010(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "94571c8d590905bad7becd892832b472f2aa5212894b6ce96e5ba719c178d976", "f80873dd48466cb12d46364a97b8705c01b9b4230cb3ec3415a6b9551dc42eef", @@ -1083,8 +987,7 @@ func TestMessageKeyGeneration010(t *testing.T) { } func TestMessageKeyGeneration011(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "13a6ee974b1fd759135a2c2010e3cdda47081c78e771125e4f0c382f0284a8cb", "bc5fb403b0bed0d84cf1db872b6522072aece00363178c98ad52178d805fca85", @@ -1094,8 +997,7 @@ func TestMessageKeyGeneration011(t *testing.T) { } func TestMessageKeyGeneration012(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "082fecdb85f358367b049b08be0e82627ae1d8edb0f27327ccb593aa2613b814", "1fbdb1cf6f6ea816349baf697932b36107803de98fcd805ebe9849b8ad0e6a45", @@ -1105,8 +1007,7 @@ func TestMessageKeyGeneration012(t *testing.T) { } func TestMessageKeyGeneration013(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "4c19020c74932c30ec6b2d8cd0d5bb80bd0fc87da3d8b4859d2fb003810afd03", "1ab9905a0189e01cda82f843d226a82a03c4f5b6dbea9b22eb9bc953ba1370d4", @@ -1116,8 +1017,7 @@ func TestMessageKeyGeneration013(t *testing.T) { } func TestMessageKeyGeneration014(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "67723a3381497b149ce24814eddd10c4c41a1e37e75af161930e6b9601afd0ff", "9ecbd25e7e2e6c97b8c27d376dcc8c5679da96578557e4e21dba3a7ef4e4ac07", @@ -1127,8 +1027,7 @@ func TestMessageKeyGeneration014(t *testing.T) { } func TestMessageKeyGeneration015(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "42063fe80b093e8619b1610972b4c3ab9e76c14fd908e642cd4997cafb30f36c", "211c66531bbcc0efcdd0130f9f1ebc12a769105eb39608994bcb188fa6a73a4a", @@ -1138,8 +1037,7 @@ func TestMessageKeyGeneration015(t *testing.T) { } func TestMessageKeyGeneration016(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "b5ac382a4be7ac03b554fe5f3043577b47ea2cd7cfc7e9ca010b1ffbb5cf1a58", "b3b5f14f10074244ee42a3837a54309f33981c7232a8b16921e815e1f7d1bb77", @@ -1149,8 +1047,7 @@ func TestMessageKeyGeneration016(t *testing.T) { } func TestMessageKeyGeneration017(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "e9d1eba47dd7e6c1532dc782ff63125db83042bb32841db7eeafd528f3ea7af9", "54241f68dc2e50e1db79e892c7c7a471856beeb8d51b7f4d16f16ab0645d2f1a", @@ -1160,8 +1057,7 @@ func TestMessageKeyGeneration017(t *testing.T) { } func TestMessageKeyGeneration018(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "a94ecf8efeee9d7068de730fad8daf96694acb70901d762de39fa8a5039c3c49", "c0565e9e201d2381a2368d7ffe60f555223874610d3d91fbbdf3076f7b1374dd", @@ -1171,8 +1067,7 @@ func TestMessageKeyGeneration018(t *testing.T) { } func TestMessageKeyGeneration019(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "533605ea214e70c25e9a22f792f4b78b9f83a18ab2103687c8a0075919eaaa53", "ab35a5e1e54d693ff023db8500d8d4e79ad8878c744e0eaec691e96e141d2325", @@ -1182,8 +1077,7 @@ func TestMessageKeyGeneration019(t *testing.T) { } func TestMessageKeyGeneration020(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "7f38df30ceea1577cb60b355b4f5567ff4130c49e84fed34d779b764a9cc184c", "a37d7f211b84a551a127ff40908974eb78415395d4f6f40324428e850e8c42a3", @@ -1193,8 +1087,7 @@ func TestMessageKeyGeneration020(t *testing.T) { } func TestMessageKeyGeneration021(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "11b37f9dbc4d0185d1c26d5f4ed98637d7c9701fffa65a65839fa4126573a4e5", "964f38d3a31158a5bfd28481247b18dd6e44d69f30ba2a40f6120c6d21d8a6ba", @@ -1204,8 +1097,7 @@ func TestMessageKeyGeneration021(t *testing.T) { } func TestMessageKeyGeneration022(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "8be790aa483d4cdd843189f71f135b3ec7e31f381312c8fe9f177aab2a48eafa", "95c8c74d633721a131316309cf6daf0804d59eaa90ea998fc35bac3d2fbb7a94", @@ -1215,8 +1107,7 @@ func TestMessageKeyGeneration022(t *testing.T) { } func TestMessageKeyGeneration023(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "19fd2a718ea084827d6bd73f509229ddf856732108b59fc01819f611419fd140", "cc6714b9f5616c66143424e1413d520dae03b1a4bd202b82b0a89b0727f5cdc8", @@ -1226,8 +1117,7 @@ func TestMessageKeyGeneration023(t *testing.T) { } func TestMessageKeyGeneration024(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "3c2acd893952b2f6d07d8aea76f545ca45961a93fe5757f6a5a80811d5e0255d", "c8de6c878cb469278d0af894bc181deb6194053f73da5014c2b5d2c8db6f2056", @@ -1237,8 +1127,7 @@ func TestMessageKeyGeneration024(t *testing.T) { } func TestMessageKeyGeneration025(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "7dbea4cead9ac91d4137f1c0a6eebb6ba0d1fb2cc46d829fbc75f8d86aca6301", "c8e030f6aa680c3d0b597da9c92bb77c21c4285dd620c5889f9beba7446446b0", @@ -1248,8 +1137,7 @@ func TestMessageKeyGeneration025(t *testing.T) { } func TestMessageKeyGeneration026(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "45422e676cdae5f1071d3647d7a5f1f5adafb832668a578228aa1155a491f2f3", "758437245f03a88e2c6a32807edfabff51a91c81ca2f389b0b46f2c97119ea90", @@ -1259,8 +1147,7 @@ func TestMessageKeyGeneration026(t *testing.T) { } func TestMessageKeyGeneration027(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "babc0c03fad24107ad60678751f5db2678041ff0d28671ede8d65bdf7aa407e9", "bd68a28bd48d9ffa3602db72c75662ac2848a0047a313d2ae2d6bc1ac153d7e9", @@ -1270,8 +1157,7 @@ func TestMessageKeyGeneration027(t *testing.T) { } func TestMessageKeyGeneration028(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "7a1b8aac37d0d20b160291fad124ab697cfca53f82e326d78fef89b4b0ea8f83", "9e97875b651a1d30d17d086d1e846778b7faad6fcbc12e08b3365d700f62e4fe", @@ -1281,8 +1167,7 @@ func TestMessageKeyGeneration028(t *testing.T) { } func TestMessageKeyGeneration029(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "8370e4e32d7e680a83862cab0da6136ef607014d043e64cdf5ecc0c4e20b3d9a", "1472bed5d19db9c546106de946e0649cd83cc9d4a66b087a65906e348dcf92e2", @@ -1292,8 +1177,7 @@ func TestMessageKeyGeneration029(t *testing.T) { } func TestMessageKeyGeneration030(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "9f1c5e8a29cd5677513c2e3a816551d6833ee54991eb3f00d5b68096fc8f0183", "5e1a7544e4d4dafe55941fcbdf326f19b0ca37fc49c4d47e9eec7fb68cde4975", @@ -1303,8 +1187,7 @@ func TestMessageKeyGeneration030(t *testing.T) { } func TestMessageKeyGeneration031(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "c385aa1c37c2bfd5cc35fcdbdf601034d39195e1cabff664ceb2b787c15d0225", "06bf4e60677a13e54c4a38ab824d2ef79da22b690da2b82d0aa3e39a14ca7bdd", @@ -1314,8 +1197,7 @@ func TestMessageKeyGeneration031(t *testing.T) { } func TestMessageKeyGeneration032(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "3ff73528f88a50f9d35c0ddba4560bacee5b0462d0f4cb6e91caf41847040ce4", "850c8a17a23aa761d279d9901015b2bbdfdff00adbf6bc5cf22bd44d24ecabc9", @@ -1325,8 +1207,7 @@ func TestMessageKeyGeneration032(t *testing.T) { } func TestMessageKeyGeneration033(t *testing.T) { - assertMessageKeyGeneration( - t, + assertMessageKeyGeneration(t, "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", "2dcf39b9d4c52f1cb9db2d516c43a7c6c3b8c401f6a4ac8f131a9e1059957036", "17f8057e6156ba7cc5310d01eda8c40f9aa388f9fd1712deb9511f13ecc37d27", @@ -1334,75 +1215,3 @@ func TestMessageKeyGeneration033(t *testing.T) { "47b89da97f68d389867b5d8a2d7ba55715a30e3d88a3cc11f3646bc2af5580ef", ) } - -func TestMaxLength(t *testing.T) { - sk1 := keys.GeneratePrivateKey() - sk2 := keys.GeneratePrivateKey() - pub2, _ := keys.GetPublicKeyHex(string(sk2)) - salt := make([]byte, 32) - rand.Read(salt) - conversationKey, _ := GenerateConversationKeyFromHex(string(sk1), pub2) - plaintext := strings.Repeat("a", MaxPlaintextSize) - plaintextBytes := []byte(plaintext) - encrypted, err := Encrypt( - plaintextBytes, conversationKey, WithCustomNonce(salt), - ) - if chk.E(err) { - t.Error(err) - } - - assertCryptPub( - t, - string(sk1), - pub2, - fmt.Sprintf("%x", conversationKey), - fmt.Sprintf("%x", salt), - plaintext, - string(encrypted), - ) -} - -func assertCryptPub( - t *testing.T, - sk1, pub2, conversationKey, salt, plaintext, expected string, -) { - var ( - k1, s, plaintextBytes, - actualBytes, expectedBytes, decrypted []byte - ok bool - err error - ) - k1, err = hex.Dec(conversationKey) - if ok = assert.NoErrorf( - t, err, "hex decode failed for conversation key: %v", err, - ); !ok { - return - } - if ok = assertConversationKeyGenerationPub( - t, sk1, pub2, conversationKey, - ); !ok { - return - } - s, err = hex.Dec(salt) - if ok = assert.NoErrorf( - t, err, "hex decode failed for salt: %v", err, - ); !ok { - return - } - plaintextBytes = []byte(plaintext) - actualBytes, err = Encrypt(plaintextBytes, k1, WithCustomNonce(s)) - if ok = assert.NoError(t, err, "encryption failed: %v", err); !ok { - return - } - expectedBytes = []byte(expected) - if ok = assert.Equalf( - t, string(expectedBytes), string(actualBytes), "wrong encryption", - ); !ok { - return - } - decrypted, err = Decrypt(expectedBytes, k1) - if ok = assert.NoErrorf(t, err, "decryption failed: %v", err); !ok { - return - } - assert.Equal(t, decrypted, plaintextBytes, "wrong decryption") -} diff --git a/pkg/crypto/encryption/testmain_test.go b/pkg/crypto/encryption/testmain_test.go deleted file mode 100644 index e1e9d76..0000000 --- a/pkg/crypto/encryption/testmain_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package encryption - -import ( - "io" - "os" - "testing" - - "lol.mleku.dev" - "lol.mleku.dev/log" -) - -func TestMain(m *testing.M) { - // Disable all logging during tests unless explicitly enabled - if os.Getenv("TEST_LOG") == "" { - // Set log level to Off to suppress all logs - lol.SetLogLevel("off") - // Also redirect output to discard - lol.Writer = io.Discard - // Disable all log printers - log.T = lol.GetNullPrinter() - log.D = lol.GetNullPrinter() - log.I = lol.GetNullPrinter() - log.W = lol.GetNullPrinter() - log.E = lol.GetNullPrinter() - log.F = lol.GetNullPrinter() - } - - // Run tests - os.Exit(m.Run()) -} diff --git a/pkg/protocol/nwc/client.go b/pkg/protocol/nwc/client.go index ecdc820..143d77d 100644 --- a/pkg/protocol/nwc/client.go +++ b/pkg/protocol/nwc/client.go @@ -58,13 +58,13 @@ func (cl *Client) Request( return } - var content []byte - if content, err = encryption.Encrypt(req, cl.conversationKey); chk.E(err) { + var content string + if content, err = encryption.Encrypt(cl.conversationKey, req, nil); chk.E(err) { return } ev := &event.E{ - Content: content, + Content: []byte(content), CreatedAt: time.Now().Unix(), Kind: 23194, Tags: tag.NewS( @@ -111,9 +111,9 @@ func (cl *Client) Request( if len(e.Content) == 0 { return fmt.Errorf("empty response content") } - var raw []byte + var raw string if raw, err = encryption.Decrypt( - e.Content, cl.conversationKey, + cl.conversationKey, string(e.Content), ); chk.E(err) { return fmt.Errorf( "decryption failed (invalid conversation key): %w", err, @@ -121,7 +121,7 @@ func (cl *Client) Request( } var resp map[string]any - if err = json.Unmarshal(raw, &resp); chk.E(err) { + if err = json.Unmarshal([]byte(raw), &resp); chk.E(err) { return } @@ -235,16 +235,16 @@ func (cl *Client) processNotificationEvent( ev *event.E, handler NotificationHandler, ) (err error) { // Decrypt the notification content - var decrypted []byte + var decrypted string if decrypted, err = encryption.Decrypt( - ev.Content, cl.conversationKey, + cl.conversationKey, string(ev.Content), ); err != nil { return fmt.Errorf("failed to decrypt notification: %w", err) } // Parse the notification JSON var notification map[string]any - if err = json.Unmarshal(decrypted, ¬ification); err != nil { + if err = json.Unmarshal([]byte(decrypted), ¬ification); err != nil { return fmt.Errorf("failed to parse notification JSON: %w", err) } diff --git a/pkg/protocol/nwc/crypto_test.go b/pkg/protocol/nwc/crypto_test.go index a9a5047..c86c611 100644 --- a/pkg/protocol/nwc/crypto_test.go +++ b/pkg/protocol/nwc/crypto_test.go @@ -70,7 +70,7 @@ func TestNWCEncryptionDecryption(t *testing.T) { testMessage := `{"method":"get_info","params":null}` // Test encryption - encrypted, err := encryption.Encrypt([]byte(testMessage), convKey) + encrypted, err := encryption.Encrypt(convKey, []byte(testMessage), nil) if err != nil { t.Fatalf("encryption failed: %v", err) } @@ -80,14 +80,14 @@ func TestNWCEncryptionDecryption(t *testing.T) { } // Test decryption - decrypted, err := encryption.Decrypt(encrypted, convKey) + decrypted, err := encryption.Decrypt(convKey, encrypted) if err != nil { t.Fatalf("decryption failed: %v", err) } - if string(decrypted) != testMessage { + if decrypted != testMessage { t.Fatalf( - "decrypted message mismatch: got %s, want %s", string(decrypted), + "decrypted message mismatch: got %s, want %s", decrypted, testMessage, ) } @@ -111,8 +111,8 @@ func TestNWCEventCreation(t *testing.T) { t.Fatal(err) } - convKey, err := encryption.GenerateConversationKeyWithSigner( - clientKey, walletPubkey, + convKey, err := encryption.GenerateConversationKey( + clientKey.Sec(), walletPubkey, ) if err != nil { t.Fatal(err) @@ -124,14 +124,14 @@ func TestNWCEventCreation(t *testing.T) { t.Fatal(err) } - encrypted, err := encryption.Encrypt(reqBytes, convKey) + encrypted, err := encryption.Encrypt(convKey, reqBytes, nil) if err != nil { t.Fatal(err) } // Create NWC event ev := &event.E{ - Content: encrypted, + Content: []byte(encrypted), CreatedAt: time.Now().Unix(), Kind: 23194, Tags: tag.NewS( diff --git a/pkg/protocol/nwc/mock_wallet_service.go b/pkg/protocol/nwc/mock_wallet_service.go index 66169c5..6ce6127 100644 --- a/pkg/protocol/nwc/mock_wallet_service.go +++ b/pkg/protocol/nwc/mock_wallet_service.go @@ -181,8 +181,9 @@ func (m *MockWalletService) processRequestEvent(ev *event.E) (err error) { if existingKey, exists := m.connectedClients[clientPubkeyHex]; exists { conversationKey = existingKey } else { - if conversationKey, err = encryption.GenerateConversationKeyWithSigner( - m.walletSecretKey, clientPubkey, + // Generate conversation key using the wallet's secret key and client's public key + if conversationKey, err = encryption.GenerateConversationKey( + m.walletSecretKey.Sec(), clientPubkey, ); chk.E(err) { m.clientsMutex.Unlock() return @@ -192,15 +193,15 @@ func (m *MockWalletService) processRequestEvent(ev *event.E) (err error) { m.clientsMutex.Unlock() // Decrypt request content - var decrypted []byte + var decrypted string if decrypted, err = encryption.Decrypt( - ev.Content, conversationKey, + conversationKey, string(ev.Content), ); chk.E(err) { return } var request map[string]any - if err = json.Unmarshal(decrypted, &request); chk.E(err) { + if err = json.Unmarshal([]byte(decrypted), &request); chk.E(err) { return } @@ -394,15 +395,15 @@ func (m *MockWalletService) sendErrorResponse( func (m *MockWalletService) sendEncryptedResponse( clientPubkey []byte, conversationKey []byte, content []byte, ) (err error) { - var encrypted []byte + var encrypted string if encrypted, err = encryption.Encrypt( - content, conversationKey, + conversationKey, content, nil, ); chk.E(err) { return } ev := &event.E{ - Content: encrypted, + Content: []byte(encrypted), CreatedAt: time.Now().Unix(), Kind: 23195, Tags: tag.NewS( @@ -442,15 +443,15 @@ func (m *MockWalletService) emitPaymentNotification( continue } - var encrypted []byte + var encrypted string if encrypted, err = encryption.Encrypt( - content, conversationKey, + conversationKey, content, nil, ); chk.E(err) { continue } ev := &event.E{ - Content: encrypted, + Content: []byte(encrypted), CreatedAt: time.Now().Unix(), Kind: 23197, Tags: tag.NewS( diff --git a/pkg/protocol/nwc/uri.go b/pkg/protocol/nwc/uri.go index 9b82294..e8bd7d7 100644 --- a/pkg/protocol/nwc/uri.go +++ b/pkg/protocol/nwc/uri.go @@ -75,8 +75,8 @@ func ParseConnectionURI(nwcUri string) (parts *ConnectionParams, err error) { return } parts.clientSecretKey = clientKey - if parts.conversationKey, err = encryption.GenerateConversationKeyWithSigner( - clientKey, + if parts.conversationKey, err = encryption.GenerateConversationKey( + clientKey.Sec(), parts.walletPublicKey, ); chk.E(err) { return diff --git a/pkg/version/version b/pkg/version/version index b1defe3..db64f35 100644 --- a/pkg/version/version +++ b/pkg/version/version @@ -1 +1 @@ -v0.25.2 \ No newline at end of file +v0.25.3 \ No newline at end of file diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 914ab5c..094e036 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -48,6 +48,39 @@ check_root() { fi } +# Check if bun is installed +check_bun_installation() { + if command -v bun >/dev/null 2>&1; then + local installed_version=$(bun --version) + log_success "Bun $installed_version is already installed" + return 0 + else + log_info "Bun is not installed" + return 1 + fi +} + +# Install bun +install_bun() { + log_info "Installing Bun..." + + # Install bun using official installer + curl -fsSL https://bun.com/install | bash + + # Source bashrc to pick up bun in current session + if [[ -f "$HOME/.bashrc" ]]; then + source "$HOME/.bashrc" + fi + + # Verify installation + if command -v bun >/dev/null 2>&1; then + log_success "Bun installed successfully" + else + log_error "Failed to install Bun" + exit 1 + fi +} + # Check if Go is installed and get version check_go_installation() { if command -v go >/dev/null 2>&1; then @@ -86,6 +119,14 @@ install_go() { local go_archive="go${GO_VERSION}.linux-${arch}.tar.gz" local download_url="https://golang.org/dl/${go_archive}" + # Remove existing installation if present (before download to save space/time) + if [[ -d "$GOROOT" ]]; then + log_info "Removing existing Go installation..." + # Make it writable in case it's read-only + chmod -R u+w "$GOROOT" 2>/dev/null || true + rm -rf "$GOROOT" + fi + # Create directories mkdir -p "$GOBIN" @@ -97,12 +138,6 @@ install_go() { exit 1 } - # Remove existing installation if present - if [[ -d "$GOROOT" ]]; then - log_info "Removing existing Go installation..." - rm -rf "$GOROOT" - fi - # Extract Go to a temporary location first, then move to final destination log_info "Extracting Go..." tar -xf "$go_archive" -C /tmp @@ -272,6 +307,11 @@ main() { exit 1 fi + # Check and install Bun if needed + if ! check_bun_installation; then + install_bun + fi + # Check and install Go if needed if ! check_go_installation; then install_go @@ -324,13 +364,14 @@ case "${1:-}" in echo " --help, -h Show this help message" echo "" echo "This script will:" - echo " 1. Install Go $GO_VERSION if not present" - echo " 2. Set up Go environment in ~/.goenv" - echo " 3. Install build dependencies (requires sudo)" - echo " 4. Build the ORLY relay" - echo " 5. Set capabilities for port 443 binding" - echo " 6. Install the binary to ~/.local/bin" - echo " 7. Create and enable systemd service" + echo " 1. Install Bun if not present" + echo " 2. Install Go $GO_VERSION if not present" + echo " 3. Set up Go environment in ~/.goenv" + echo " 4. Install build dependencies (requires sudo)" + echo " 5. Build the ORLY relay" + echo " 6. Set capabilities for port 443 binding" + echo " 7. Install the binary to ~/.local/bin" + echo " 8. Create and enable systemd service" echo "" echo "Examples:" echo " $0 # Deploy with default port 3334" diff --git a/testresults.txt b/testresults.txt deleted file mode 100644 index e69de29..0000000