905 lines
25 KiB
Go
905 lines
25 KiB
Go
package xcipher
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/rand"
|
|
"errors"
|
|
"fmt"
|
|
"golang.org/x/crypto/chacha20poly1305"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Generate a random key
|
|
func generateRandomKey() ([]byte, error) {
|
|
key := make([]byte, chacha20poly1305.KeySize)
|
|
_, err := rand.Read(key)
|
|
return key, err
|
|
}
|
|
|
|
// Generate random data of specified size
|
|
func generateRandomData(size int) ([]byte, error) {
|
|
data := make([]byte, size)
|
|
_, err := rand.Read(data)
|
|
return data, err
|
|
}
|
|
|
|
// Create temporary file and write data to it
|
|
func createTempFile(t *testing.T, data []byte) string {
|
|
tempDir := t.TempDir()
|
|
tempFile := filepath.Join(tempDir, "test_data")
|
|
if err := os.WriteFile(tempFile, data, 0644); err != nil {
|
|
t.Fatalf("Failed to create temporary file: %v", err)
|
|
}
|
|
return tempFile
|
|
}
|
|
|
|
// TestStreamEncryptDecrypt tests basic stream encryption/decryption functionality
|
|
func TestStreamEncryptDecrypt(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
// Generate random test data (1MB)
|
|
testSize := 1 * 1024 * 1024
|
|
testData, err := generateRandomData(testSize)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate test data: %v", err)
|
|
}
|
|
|
|
// Additional data
|
|
additionalData := []byte("Test additional data")
|
|
|
|
// Create input and output buffers
|
|
var encryptedBuf bytes.Buffer
|
|
encryptedReader := bytes.NewReader(testData)
|
|
|
|
// Perform stream encryption
|
|
err = xcipher.EncryptStream(encryptedReader, &encryptedBuf, additionalData)
|
|
if err != nil {
|
|
t.Fatalf("Stream encryption failed: %v", err)
|
|
}
|
|
|
|
// Create decryption buffer
|
|
var decryptedBuf bytes.Buffer
|
|
decryptReader := bytes.NewReader(encryptedBuf.Bytes())
|
|
|
|
// Perform stream decryption
|
|
err = xcipher.DecryptStream(decryptReader, &decryptedBuf, additionalData)
|
|
if err != nil {
|
|
t.Fatalf("Stream decryption failed: %v", err)
|
|
}
|
|
|
|
// Verify decrypted data matches original data
|
|
if !bytes.Equal(testData, decryptedBuf.Bytes()) {
|
|
t.Fatal("Stream encrypted/decrypted data does not match")
|
|
}
|
|
|
|
t.Logf("Successfully stream processed %d bytes of data", testSize)
|
|
}
|
|
|
|
// TestStreamEncryptDecryptWithOptions tests stream encryption/decryption with options
|
|
func TestStreamEncryptDecryptWithOptions(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
// Generate random test data (2MB)
|
|
testSize := 2 * 1024 * 1024
|
|
testData, err := generateRandomData(testSize)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate test data: %v", err)
|
|
}
|
|
|
|
// Create temporary file for testing large data
|
|
inputFile := createTempFile(t, testData)
|
|
defer os.Remove(inputFile)
|
|
|
|
// Additional data
|
|
additionalData := []byte("Test additional data")
|
|
|
|
// Test different buffer size options
|
|
bufferSizes := []int{8 * 1024, 32 * 1024, 128 * 1024}
|
|
for _, bufSize := range bufferSizes {
|
|
t.Run(fmt.Sprintf("BufferSize=%dKB", bufSize/1024), func(t *testing.T) {
|
|
// Create input and output files
|
|
encryptedFile := inputFile + ".enc"
|
|
decryptedFile := inputFile + ".dec"
|
|
defer os.Remove(encryptedFile)
|
|
defer os.Remove(decryptedFile)
|
|
|
|
// Open input file
|
|
inFile, err := os.Open(inputFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to open input file: %v", err)
|
|
}
|
|
defer inFile.Close()
|
|
|
|
// Create encrypted output file
|
|
outFile, err := os.Create(encryptedFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create encrypted output file: %v", err)
|
|
}
|
|
defer outFile.Close()
|
|
|
|
// 使用简单的EncryptStream方法
|
|
err = xcipher.EncryptStream(inFile, outFile, additionalData)
|
|
if err != nil {
|
|
t.Fatalf("Stream encryption failed: %v", err)
|
|
}
|
|
|
|
// 确保文件已写入并关闭
|
|
outFile.Close()
|
|
|
|
// 打开加密文件进行解密
|
|
encFile, err := os.Open(encryptedFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to open encrypted file: %v", err)
|
|
}
|
|
defer encFile.Close()
|
|
|
|
// 创建解密输出文件
|
|
decFile, err := os.Create(decryptedFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create decrypted output file: %v", err)
|
|
}
|
|
defer decFile.Close()
|
|
|
|
// 使用简单的DecryptStream方法
|
|
err = xcipher.DecryptStream(encFile, decFile, additionalData)
|
|
if err != nil {
|
|
t.Fatalf("Stream decryption failed: %v", err)
|
|
}
|
|
|
|
// 确保文件已写入并关闭
|
|
decFile.Close()
|
|
|
|
// 读取解密后的数据进行验证
|
|
decryptedData, err := ioutil.ReadFile(decryptedFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read decrypted file: %v", err)
|
|
}
|
|
|
|
// 验证数据
|
|
if !bytes.Equal(testData, decryptedData) {
|
|
t.Fatal("Stream encrypted/decrypted data does not match")
|
|
}
|
|
|
|
t.Logf("Successfully stream processed %d bytes of data (buffer=%dKB)", testSize, bufSize/1024)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestStreamParallelProcessing tests the parallel stream encryption/decryption
|
|
func TestStreamParallelProcessing(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
// Generate smaller test data
|
|
testSize := 1 * 1024 * 1024 // 1MB
|
|
testData, err := generateRandomData(testSize)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate test data: %v", err)
|
|
}
|
|
|
|
// Use memory buffer for testing
|
|
t.Log("Starting encryption")
|
|
var encryptedBuffer bytes.Buffer
|
|
|
|
// Perform stream encryption
|
|
err = xcipher.EncryptStream(bytes.NewReader(testData), &encryptedBuffer, nil)
|
|
if err != nil {
|
|
t.Fatalf("Stream encryption failed: %v", err)
|
|
}
|
|
|
|
// Get encrypted data
|
|
encryptedData := encryptedBuffer.Bytes()
|
|
t.Logf("Encrypted data size: %d bytes", len(encryptedData))
|
|
|
|
// Check if encrypted data is valid
|
|
if len(encryptedData) <= nonceSize {
|
|
t.Fatalf("Invalid encrypted data, length too short: %d bytes", len(encryptedData))
|
|
}
|
|
|
|
// Start decryption
|
|
t.Log("Starting decryption")
|
|
var decryptedBuffer bytes.Buffer
|
|
|
|
// Perform stream decryption
|
|
err = xcipher.DecryptStream(bytes.NewReader(encryptedData), &decryptedBuffer, nil)
|
|
if err != nil {
|
|
t.Fatalf("Stream decryption failed: %v (encrypted data size: %d bytes)", err, len(encryptedData))
|
|
}
|
|
|
|
// Get decrypted data
|
|
decryptedData := decryptedBuffer.Bytes()
|
|
|
|
// Verify data
|
|
if !bytes.Equal(testData, decryptedData) {
|
|
t.Fatal("Stream encrypted/decrypted data does not match")
|
|
}
|
|
|
|
t.Logf("Successfully completed stream processing of %d bytes", testSize)
|
|
}
|
|
|
|
// TestStreamCancellation tests cancellation of stream encryption/decryption operations
|
|
func TestStreamCancellation(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
// Generate large test data (50MB, enough time to cancel)
|
|
testSize := 50 * 1024 * 1024
|
|
testData, err := generateRandomData(testSize)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate test data: %v", err)
|
|
}
|
|
|
|
// Create an unlimited data source to simulate large file
|
|
infiniteReader := &infiniteDataReader{data: testData}
|
|
|
|
// Create output buffer
|
|
var outputBuf bytes.Buffer
|
|
|
|
// Create context with cancellation
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
// Create options with cancel channel
|
|
options := DefaultStreamOptions()
|
|
options.CancelChan = ctx.Done()
|
|
|
|
// Cancel operation after a short time
|
|
go func() {
|
|
time.Sleep(100 * time.Millisecond) // Let encryption run for a short time
|
|
cancel()
|
|
}()
|
|
|
|
// Perform stream encryption, should be cancelled
|
|
_, err = xcipher.EncryptStreamWithOptions(infiniteReader, &outputBuf, options)
|
|
|
|
// Verify error is cancellation error
|
|
if !errors.Is(err, ErrOperationCancelled) {
|
|
t.Fatalf("Expected cancellation error, but got: %v", err)
|
|
}
|
|
|
|
t.Log("Successfully tested stream encryption cancellation")
|
|
}
|
|
|
|
// TestStreamErrors tests error handling in stream encryption/decryption
|
|
func TestStreamErrors(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
// Test authentication failure
|
|
t.Run("AuthenticationFailure", func(t *testing.T) {
|
|
// First encrypt some data
|
|
plaintext := []byte("Test authentication failure")
|
|
var encBuf bytes.Buffer
|
|
err := xcipher.EncryptStream(bytes.NewReader(plaintext), &encBuf, nil)
|
|
if err != nil {
|
|
t.Fatalf("Encryption failed: %v", err)
|
|
}
|
|
|
|
// Tamper with encrypted data
|
|
encryptedData := encBuf.Bytes()
|
|
if len(encryptedData) > nonceSize+10 {
|
|
// Modify one byte in ciphertext part
|
|
encryptedData[nonceSize+10]++
|
|
}
|
|
|
|
// Try to decrypt tampered data
|
|
var decBuf bytes.Buffer
|
|
err = xcipher.DecryptStream(bytes.NewReader(encryptedData), &decBuf, nil)
|
|
if err == nil || !errors.Is(err, ErrAuthenticationFailed) {
|
|
t.Fatalf("Expected authentication failure error, but got: %v", err)
|
|
}
|
|
})
|
|
|
|
// Test read error
|
|
t.Run("ReadError", func(t *testing.T) {
|
|
reader := &errorReader{err: fmt.Errorf("simulated read error")}
|
|
var buf bytes.Buffer
|
|
|
|
err := xcipher.EncryptStream(reader, &buf, nil)
|
|
if err == nil || !errors.Is(err, ErrReadFailed) {
|
|
t.Fatalf("Expected read failure error, but got: %v", err)
|
|
}
|
|
})
|
|
|
|
// Test write error
|
|
t.Run("WriteError", func(t *testing.T) {
|
|
writer := &errorWriter{err: fmt.Errorf("simulated write error")}
|
|
|
|
err := xcipher.EncryptStream(bytes.NewReader([]byte("test")), writer, nil)
|
|
if err == nil || !errors.Is(err, ErrWriteFailed) {
|
|
t.Fatalf("Expected write failure error, but got: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Infinite data reader for testing cancellation
|
|
type infiniteDataReader struct {
|
|
data []byte
|
|
pos int
|
|
}
|
|
|
|
func (r *infiniteDataReader) Read(p []byte) (n int, err error) {
|
|
if r.pos >= len(r.data) {
|
|
r.pos = 0 // Cycle through data
|
|
}
|
|
|
|
n = copy(p, r.data[r.pos:])
|
|
r.pos += n
|
|
return n, nil
|
|
}
|
|
|
|
// Reader that simulates read errors
|
|
type errorReader struct {
|
|
err error
|
|
}
|
|
|
|
func (r *errorReader) Read(p []byte) (n int, err error) {
|
|
return 0, r.err
|
|
}
|
|
|
|
// Writer that simulates write errors
|
|
type errorWriter struct {
|
|
err error
|
|
}
|
|
|
|
func (w *errorWriter) Write(p []byte) (n int, err error) {
|
|
return 0, w.err
|
|
}
|
|
|
|
// TestCPUFeatureDetection tests CPU feature detection functionality
|
|
func TestCPUFeatureDetection(t *testing.T) {
|
|
// Get system optimization info
|
|
info := GetSystemOptimizationInfo()
|
|
|
|
// Output detected CPU features
|
|
t.Logf("CPU architecture: %s", info.Architecture)
|
|
t.Logf("CPU core count: %d", info.NumCPUs)
|
|
t.Logf("AVX support: %v", info.HasAVX)
|
|
t.Logf("AVX2 support: %v", info.HasAVX2)
|
|
t.Logf("SSE4.1 support: %v", info.HasSSE41)
|
|
t.Logf("NEON support: %v", info.HasNEON)
|
|
t.Logf("Estimated L1 cache size: %d KB", info.EstimatedL1Cache/1024)
|
|
t.Logf("Estimated L2 cache size: %d KB", info.EstimatedL2Cache/1024)
|
|
t.Logf("Estimated L3 cache size: %d MB", info.EstimatedL3Cache/1024/1024)
|
|
|
|
// Check recommended parameters
|
|
t.Logf("Recommended buffer size: %d KB", info.RecommendedBufferSize/1024)
|
|
t.Logf("Recommended worker count: %d", info.RecommendedWorkers)
|
|
|
|
// Simple validation of recommended parameters
|
|
if info.RecommendedBufferSize < minBufferSize || info.RecommendedBufferSize > maxBufferSize {
|
|
t.Errorf("Recommended buffer size %d outside valid range [%d, %d]",
|
|
info.RecommendedBufferSize, minBufferSize, maxBufferSize)
|
|
}
|
|
|
|
if info.RecommendedWorkers < minWorkers || info.RecommendedWorkers > maxWorkers {
|
|
t.Errorf("Recommended worker count %d outside valid range [%d, %d]",
|
|
info.RecommendedWorkers, minWorkers, maxWorkers)
|
|
}
|
|
}
|
|
|
|
// TestDynamicParameterAdjustment tests dynamic parameter adjustment system
|
|
func TestDynamicParameterAdjustment(t *testing.T) {
|
|
// Test different buffer size requests
|
|
testCases := []struct {
|
|
requestedSize int
|
|
description string
|
|
}{
|
|
{0, "Zero request (use auto-optimization)"},
|
|
{4 * 1024, "Below minimum"},
|
|
{16 * 1024, "Normal small value"},
|
|
{64 * 1024, "Medium value"},
|
|
{256 * 1024, "Larger value"},
|
|
{2 * 1024 * 1024, "Above maximum"},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
// Get adjusted buffer size
|
|
adjustedSize := adaptiveBufferSize(tc.requestedSize)
|
|
|
|
t.Logf("Requested size: %d, adjusted size: %d", tc.requestedSize, adjustedSize)
|
|
|
|
// Validate adjusted size is within valid range
|
|
if adjustedSize < minBufferSize {
|
|
t.Errorf("Adjusted buffer size %d less than minimum %d", adjustedSize, minBufferSize)
|
|
}
|
|
|
|
if adjustedSize > maxBufferSize {
|
|
t.Errorf("Adjusted buffer size %d greater than maximum %d", adjustedSize, maxBufferSize)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test different worker thread count requests
|
|
workerTestCases := []struct {
|
|
requestedWorkers int
|
|
bufferSize int
|
|
description string
|
|
}{
|
|
{0, 16 * 1024, "Auto-select (small buffer)"},
|
|
{0, 512 * 1024, "Auto-select (large buffer)"},
|
|
{1, 64 * 1024, "Single thread request"},
|
|
{12, 64 * 1024, "Multi-thread request"},
|
|
}
|
|
|
|
for _, tc := range workerTestCases {
|
|
t.Run(tc.description, func(t *testing.T) {
|
|
// Get adjusted worker count
|
|
adjustedWorkers := adaptiveWorkerCount(tc.requestedWorkers, tc.bufferSize)
|
|
|
|
t.Logf("Requested workers: %d, buffer size: %d, adjusted workers: %d",
|
|
tc.requestedWorkers, tc.bufferSize, adjustedWorkers)
|
|
|
|
// Validate adjusted worker count is within valid range
|
|
if adjustedWorkers < minWorkers {
|
|
t.Errorf("Adjusted worker count %d less than minimum %d", adjustedWorkers, minWorkers)
|
|
}
|
|
|
|
if adjustedWorkers > maxWorkers {
|
|
t.Errorf("Adjusted worker count %d greater than maximum %d", adjustedWorkers, maxWorkers)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestOptimizedStreamOptions tests optimized stream options
|
|
func TestOptimizedStreamOptions(t *testing.T) {
|
|
// Get optimized stream options
|
|
options := GetOptimizedStreamOptions()
|
|
|
|
t.Logf("Optimized stream options:")
|
|
t.Logf("- Buffer size: %d KB", options.BufferSize/1024)
|
|
t.Logf("- Use parallel: %v", options.UseParallel)
|
|
t.Logf("- Max workers: %d", options.MaxWorkers)
|
|
|
|
// Validate options are within valid ranges
|
|
if options.BufferSize < minBufferSize || options.BufferSize > maxBufferSize {
|
|
t.Errorf("Buffer size %d outside valid range [%d, %d]",
|
|
options.BufferSize, minBufferSize, maxBufferSize)
|
|
}
|
|
|
|
if options.MaxWorkers < minWorkers || options.MaxWorkers > maxWorkers {
|
|
t.Errorf("Max worker count %d outside valid range [%d, %d]",
|
|
options.MaxWorkers, minWorkers, maxWorkers)
|
|
}
|
|
}
|
|
|
|
// TestZeroCopyMechanism tests zero-copy mechanism
|
|
func TestZeroCopyMechanism(t *testing.T) {
|
|
// Test zero-copy string conversion between string and byte slice
|
|
original := "测试零拷贝字符串转换"
|
|
byteData := stringToBytes(original)
|
|
restored := bytesToString(byteData)
|
|
|
|
if original != restored {
|
|
t.Errorf("Zero-copy string conversion failed: %s != %s", original, restored)
|
|
}
|
|
|
|
// Test buffer reuse
|
|
data := []byte("测试缓冲区重用")
|
|
|
|
// Request a buffer larger than original data
|
|
largerCap := len(data) * 2
|
|
newBuf := reuseBuffer(data, largerCap)
|
|
|
|
// Verify data was copied correctly
|
|
if !bytes.Equal(data, newBuf[:len(data)]) {
|
|
t.Error("Data mismatch after buffer reuse")
|
|
}
|
|
|
|
// Verify capacity was increased
|
|
if cap(newBuf) < largerCap {
|
|
t.Errorf("Buffer capacity not properly increased: %d < %d", cap(newBuf), largerCap)
|
|
}
|
|
|
|
// Test reuse when original buffer is large enough
|
|
largeBuf := make([]byte, 100)
|
|
copy(largeBuf, data)
|
|
|
|
// Request capacity smaller than original buffer
|
|
smallerCap := 50
|
|
reusedBuf := reuseBuffer(largeBuf, smallerCap)
|
|
|
|
// Verify it's the same underlying array (by comparing length)
|
|
if len(reusedBuf) != smallerCap {
|
|
t.Errorf("Reused buffer length incorrect: %d != %d", len(reusedBuf), smallerCap)
|
|
}
|
|
|
|
// Verify data integrity
|
|
if !bytes.Equal(largeBuf[:len(data)], data) {
|
|
t.Error("Original data corrupted after reuse")
|
|
}
|
|
}
|
|
|
|
// TestAutoParallelDecision tests automatic parallel processing decision
|
|
func TestAutoParallelDecision(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
dataSize int // Data size in bytes
|
|
forceParallel bool // Whether to force parallel mode
|
|
}{
|
|
{"Small data", 10 * 1024, false}, // 10KB
|
|
{"Medium data", 500 * 1024, false}, // 500KB
|
|
{"Large data", 2 * 1024 * 1024, true}, // 2MB - force parallel mode
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Generate test data
|
|
testData, err := generateRandomData(tc.dataSize)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate test data: %v", err)
|
|
}
|
|
|
|
// Create default options and enable stats collection
|
|
options := DefaultStreamOptions()
|
|
options.CollectStats = true
|
|
options.UseParallel = tc.forceParallel // For large data, force parallel mode
|
|
|
|
// Create temporary file for testing
|
|
var encBuffer bytes.Buffer
|
|
var stats *StreamStats
|
|
|
|
// For large data, use file IO instead of memory buffer to ensure parallel mode is triggered
|
|
if tc.dataSize >= parallelThreshold {
|
|
// Create temporary file
|
|
tempFile := createTempFile(t, testData)
|
|
defer os.Remove(tempFile)
|
|
|
|
// Create temporary output file
|
|
tempOutFile, err := os.CreateTemp("", "xcipher-test-*")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temporary output file: %v", err)
|
|
}
|
|
tempOutPath := tempOutFile.Name()
|
|
tempOutFile.Close()
|
|
defer os.Remove(tempOutPath)
|
|
|
|
// Open file for encryption
|
|
inFile, err := os.Open(tempFile)
|
|
if err != nil {
|
|
t.Fatalf("Failed to open temporary file: %v", err)
|
|
}
|
|
defer inFile.Close()
|
|
|
|
outFile, err := os.Create(tempOutPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to open output file: %v", err)
|
|
}
|
|
defer outFile.Close()
|
|
|
|
// Perform encryption
|
|
stats, err = xcipher.EncryptStreamWithOptions(inFile, outFile, options)
|
|
if err != nil {
|
|
t.Fatalf("Encryption failed: %v", err)
|
|
}
|
|
} else {
|
|
// Use memory buffer for small data
|
|
stats, err = xcipher.EncryptStreamWithOptions(
|
|
bytes.NewReader(testData), &encBuffer, options)
|
|
if err != nil {
|
|
t.Fatalf("Encryption failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// Output decision results
|
|
t.Logf("Data size: %d bytes", tc.dataSize)
|
|
t.Logf("Auto decision: Use parallel=%v, workers=%d, buffer size=%d",
|
|
stats.ParallelProcessing, stats.WorkerCount, stats.BufferSize)
|
|
t.Logf("Performance: Time=%v, throughput=%.2f MB/s",
|
|
stats.Duration(), stats.Throughput)
|
|
|
|
// Verify parallel processing state matches expectation
|
|
if tc.forceParallel && !stats.ParallelProcessing {
|
|
t.Errorf("Forced parallel processing was set, but system did not use parallel mode")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestNetworkImageStreamProcessing tests encrypting and decrypting an image from network stream
|
|
func TestNetworkImageStreamProcessing(t *testing.T) {
|
|
// Generate random key
|
|
key, err := generateRandomKey()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate key: %v", err)
|
|
}
|
|
|
|
// Initialize cipher
|
|
xcipher := NewXCipher(key)
|
|
|
|
// Create output directory if not exists
|
|
outputDir := "testdata"
|
|
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
|
t.Fatalf("Failed to create output directory: %v", err)
|
|
}
|
|
|
|
// Define file paths in local directory
|
|
originalPath := filepath.Join(outputDir, "original.jpg")
|
|
encryptedPath := filepath.Join(outputDir, "encrypted.bin")
|
|
decryptedPath := filepath.Join(outputDir, "decrypted.jpg")
|
|
|
|
// Download image from URL
|
|
imageURL := "https://cdn.picui.cn/vip/2025/03/20/67dbc6154b20f.jpg"
|
|
resp, err := http.Get(imageURL)
|
|
if err != nil {
|
|
t.Fatalf("Failed to download image: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Save original image
|
|
originalFile, err := os.Create(originalPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create original file: %v", err)
|
|
}
|
|
|
|
// Create a TeeReader to save original image while reading
|
|
imageReader := io.TeeReader(resp.Body, originalFile)
|
|
|
|
// Create encrypted output file
|
|
encryptedFile, err := os.Create(encryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create encrypted file: %v", err)
|
|
}
|
|
|
|
// 使用简单的 EncryptStream 方法加密
|
|
err = xcipher.EncryptStream(imageReader, encryptedFile, []byte("123456"))
|
|
if err != nil {
|
|
t.Fatalf("Failed to encrypt image stream: %v", err)
|
|
}
|
|
|
|
// Close files
|
|
originalFile.Close()
|
|
encryptedFile.Close()
|
|
|
|
t.Logf("Original image saved to: %s", originalPath)
|
|
t.Logf("Encrypted file saved to: %s", encryptedPath)
|
|
|
|
// Open encrypted file for reading
|
|
encryptedFile, err = os.Open(encryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to open encrypted file: %v", err)
|
|
}
|
|
defer encryptedFile.Close()
|
|
|
|
// Create decrypted output file
|
|
decryptedFile, err := os.Create(decryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create decrypted file: %v", err)
|
|
}
|
|
defer decryptedFile.Close()
|
|
|
|
err = xcipher.DecryptStream(encryptedFile, decryptedFile, []byte("123456"))
|
|
if err != nil {
|
|
t.Fatalf("Failed to decrypt image stream: %v", err)
|
|
}
|
|
|
|
t.Logf("Decrypted file saved to: %s", decryptedPath)
|
|
|
|
// Get file sizes
|
|
originalInfo, err := os.Stat(originalPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to stat original file: %v", err)
|
|
}
|
|
|
|
encryptedInfo, err := os.Stat(encryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to stat encrypted file: %v", err)
|
|
}
|
|
|
|
decryptedInfo, err := os.Stat(decryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to stat decrypted file: %v", err)
|
|
}
|
|
|
|
// Print file sizes
|
|
t.Logf("File sizes:")
|
|
t.Logf("- Original: %d bytes", originalInfo.Size())
|
|
t.Logf("- Encrypted: %d bytes", encryptedInfo.Size())
|
|
t.Logf("- Decrypted: %d bytes", decryptedInfo.Size())
|
|
|
|
// Verify the decrypted file matches the original
|
|
originalData, err := os.ReadFile(originalPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read original file: %v", err)
|
|
}
|
|
|
|
decryptedData, err := os.ReadFile(decryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read decrypted file: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(originalData, decryptedData) {
|
|
t.Fatal("Decrypted file does not match original file")
|
|
}
|
|
|
|
// Check if it's a valid JPEG file by checking signature
|
|
if len(decryptedData) < 2 || decryptedData[0] != 0xFF || decryptedData[1] != 0xD8 {
|
|
t.Fatal("Decrypted file is not a valid JPEG image")
|
|
}
|
|
|
|
t.Log("Successfully verified: decrypted file matches original and is a valid JPEG image")
|
|
|
|
encryptedData, err := os.ReadFile(encryptedPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read encrypted file: %v", err)
|
|
}
|
|
|
|
var decryptedBuffer bytes.Buffer
|
|
|
|
encryptedReader := bytes.NewReader(encryptedData)
|
|
|
|
err = xcipher.DecryptStream(encryptedReader, &decryptedBuffer, []byte("123456"))
|
|
if err != nil {
|
|
t.Fatalf("Failed to decrypt image stream: %v", err)
|
|
}
|
|
|
|
decryptedBytes := decryptedBuffer.Bytes()
|
|
|
|
previewLen := 100
|
|
if len(decryptedBytes) < previewLen {
|
|
previewLen = len(decryptedBytes)
|
|
}
|
|
t.Logf("Decrypted data preview (first %d bytes): %v", previewLen, decryptedBytes[:previewLen])
|
|
t.Logf("Total decrypted data length: %d bytes", len(decryptedBytes))
|
|
|
|
if len(decryptedBytes) < 2 || decryptedBytes[0] != 0xFF || decryptedBytes[1] != 0xD8 {
|
|
t.Fatal("Decrypted data is not a valid JPEG image")
|
|
}
|
|
|
|
originalData, err = os.ReadFile(originalPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read original file: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(originalData, decryptedBytes) {
|
|
t.Fatal("Decrypted data does not match original file")
|
|
}
|
|
|
|
t.Log("Successfully verified: decrypted data matches original and is a valid JPEG image")
|
|
}
|
|
|
|
func TestEncryptAndDecryptToBytes(t *testing.T) {
|
|
|
|
key := make([]byte, chacha20poly1305.KeySize)
|
|
copy(key, "this-is-32-byte-testing-key-data!")
|
|
|
|
cipher := NewXCipher(key)
|
|
|
|
testData := []byte("Hello, this is test data for encryption!")
|
|
|
|
t.Run("First Encryption Test", func(t *testing.T) {
|
|
reader := bytes.NewReader(testData)
|
|
encrypted, err := cipher.EncryptToBytes(reader, nil)
|
|
if err != nil {
|
|
t.Fatalf("First encryption failed: %v", err)
|
|
}
|
|
if len(encrypted) <= headerSize {
|
|
t.Fatal("First encrypted data too short")
|
|
}
|
|
|
|
// 解密并验证
|
|
decReader := bytes.NewReader(encrypted)
|
|
decrypted, err := cipher.DecryptToBytes(decReader, nil)
|
|
if err != nil {
|
|
t.Fatalf("First decryption failed: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(decrypted, testData) {
|
|
t.Fatal("First decrypted data doesn't match original")
|
|
}
|
|
})
|
|
|
|
t.Run("Second Encryption Test", func(t *testing.T) {
|
|
reader := bytes.NewReader(testData)
|
|
encrypted2, err := cipher.EncryptToBytes(reader, nil)
|
|
if err != nil {
|
|
t.Fatalf("Second encryption failed: %v", err)
|
|
}
|
|
if len(encrypted2) <= headerSize {
|
|
t.Fatal("Second encrypted data too short")
|
|
}
|
|
|
|
// 解密并验证
|
|
decReader := bytes.NewReader(encrypted2)
|
|
decrypted2, err := cipher.DecryptToBytes(decReader, nil)
|
|
if err != nil {
|
|
t.Fatalf("Second decryption failed: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(decrypted2, testData) {
|
|
t.Fatal("Second decrypted data doesn't match original")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestEncryptToBytesWithDifferentSizes(t *testing.T) {
|
|
|
|
key := make([]byte, chacha20poly1305.KeySize)
|
|
copy(key, "this-is-32-byte-testing-key-data!")
|
|
|
|
cipher := NewXCipher(key)
|
|
|
|
testSizes := []int{
|
|
10, // 10 bytes
|
|
1024, // 1KB
|
|
64 * 1024, // 64KB
|
|
1024 * 1024, // 1MB
|
|
}
|
|
|
|
for _, size := range testSizes {
|
|
t.Run(fmt.Sprintf("Size_%d", size), func(t *testing.T) {
|
|
|
|
testData := make([]byte, size)
|
|
_, err := rand.Read(testData)
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate test data: %v", err)
|
|
}
|
|
|
|
reader := bytes.NewReader(testData)
|
|
encrypted, err := cipher.EncryptToBytes(reader, nil)
|
|
if err != nil {
|
|
t.Fatalf("Encryption failed for size %d: %v", size, err)
|
|
}
|
|
|
|
decReader := bytes.NewReader(encrypted)
|
|
decrypted, err := cipher.DecryptToBytes(decReader, nil)
|
|
if err != nil {
|
|
t.Fatalf("Decryption failed for size %d: %v", size, err)
|
|
}
|
|
|
|
if !bytes.Equal(decrypted, testData) {
|
|
t.Fatalf("Data mismatch for size %d", size)
|
|
}
|
|
})
|
|
}
|
|
}
|