Files
StarFleetCPTN 31871bd16e feat: Implement storage provider management functionality
- Added new routes and handlers for managing storage providers, including creation, editing, and deletion.
- Introduced a new StorageProvider form component for user input.
- Enhanced the database schema to support storage provider references in transfer configurations.
- Implemented encryption for sensitive fields in storage provider data.
- Added tests for storage provider API endpoints and integration with the database.
- Updated frontend components to support storage provider selection and testing.
2025-04-16 17:18:53 -07:00

275 lines
8.1 KiB
Go

package encryption
import (
"bytes"
"encoding/base64"
"os"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func setupTestKeyManager(t *testing.T) KeyManager {
// Setup test environment
testEnvVar := "TEST_ENCRYPTION_KEY"
validKey := make([]byte, AES256KeySize)
for i := range validKey {
validKey[i] = byte(i % 256)
}
validKeyBase64 := base64.StdEncoding.EncodeToString(validKey)
// Set a valid key in environment
os.Setenv(testEnvVar, validKeyBase64)
t.Cleanup(func() {
os.Unsetenv(testEnvVar)
})
km := NewKeyManager(testEnvVar)
err := km.(KeyManager).Initialize()
require.NoError(t, err)
return km
}
func setupEncryptionService(t *testing.T) *EncryptionService {
km := setupTestKeyManager(t)
service, err := NewEncryptionService(km)
require.NoError(t, err)
return service
}
func TestNewEncryptionService(t *testing.T) {
t.Run("Valid key manager", func(t *testing.T) {
km := setupTestKeyManager(t)
service, err := NewEncryptionService(km)
require.NoError(t, err)
assert.NotNil(t, service)
})
t.Run("Nil key manager", func(t *testing.T) {
service, err := NewEncryptionService(nil)
require.Error(t, err)
assert.Nil(t, service)
})
}
func TestEncryptionService_Encrypt(t *testing.T) {
service := setupEncryptionService(t)
t.Run("Encrypt valid data", func(t *testing.T) {
plaintext := []byte("This is a test message that needs to be encrypted")
encrypted, err := service.Encrypt(plaintext)
require.NoError(t, err)
assert.NotEmpty(t, encrypted)
// Encrypted data should be base64 encoded
_, err = base64.StdEncoding.DecodeString(encrypted)
require.NoError(t, err)
})
t.Run("Encrypt empty data", func(t *testing.T) {
encrypted, err := service.Encrypt([]byte{})
require.Error(t, err)
assert.Equal(t, ErrEmptyPlaintext, err)
assert.Empty(t, encrypted)
})
t.Run("Same plaintext produces different ciphertexts", func(t *testing.T) {
plaintext := []byte("This should encrypt to different ciphertexts each time")
encrypted1, err := service.Encrypt(plaintext)
require.NoError(t, err)
encrypted2, err := service.Encrypt(plaintext)
require.NoError(t, err)
assert.NotEqual(t, encrypted1, encrypted2, "Same plaintext should encrypt to different ciphertexts due to random IV")
})
}
func TestEncryptionService_Decrypt(t *testing.T) {
service := setupEncryptionService(t)
t.Run("Decrypt valid data", func(t *testing.T) {
plaintext := []byte("This is a test message that needs to be encrypted and decrypted")
encrypted, err := service.Encrypt(plaintext)
require.NoError(t, err)
decrypted, err := service.Decrypt(encrypted)
require.NoError(t, err)
assert.Equal(t, plaintext, decrypted)
})
t.Run("Decrypt empty data", func(t *testing.T) {
decrypted, err := service.Decrypt("")
require.Error(t, err)
assert.Equal(t, ErrEmptyCiphertext, err)
assert.Nil(t, decrypted)
})
t.Run("Decrypt invalid base64", func(t *testing.T) {
decrypted, err := service.Decrypt("this-is-not-valid-base64!@#$%^")
require.Error(t, err)
assert.Nil(t, decrypted)
})
t.Run("Decrypt corrupted data - last byte modified", func(t *testing.T) {
plaintext := []byte("This is a test message with proper length for padding")
encrypted, err := service.Encrypt(plaintext)
require.NoError(t, err)
// Modify the last byte to corrupt the padding
decoded, err := base64.StdEncoding.DecodeString(encrypted)
require.NoError(t, err)
decoded[len(decoded)-1] ^= 0x01 // Flip one bit in the last byte
corrupted := base64.StdEncoding.EncodeToString(decoded)
decrypted, err := service.Decrypt(corrupted)
require.Error(t, err, "Decryption should fail with corrupted data")
assert.Nil(t, decrypted)
})
t.Run("Decrypt with short data", func(t *testing.T) {
// Create a short invalid encrypted string (not enough bytes for IV)
shortData := base64.StdEncoding.EncodeToString([]byte("tooshort"))
decrypted, err := service.Decrypt(shortData)
require.Error(t, err)
assert.Nil(t, decrypted)
})
}
func TestEncryptionService_EncryptString(t *testing.T) {
service := setupEncryptionService(t)
t.Run("Encrypt valid string", func(t *testing.T) {
plaintext := "This is a test string that needs to be encrypted"
encrypted, err := service.EncryptString(plaintext)
require.NoError(t, err)
assert.NotEmpty(t, encrypted)
// Encrypted data should be base64 encoded
_, err = base64.StdEncoding.DecodeString(encrypted)
require.NoError(t, err)
})
t.Run("Encrypt empty string", func(t *testing.T) {
encrypted, err := service.EncryptString("")
require.Error(t, err)
assert.Equal(t, ErrEmptyPlaintext, err)
assert.Empty(t, encrypted)
})
}
func TestEncryptionService_DecryptString(t *testing.T) {
service := setupEncryptionService(t)
t.Run("Decrypt valid string", func(t *testing.T) {
plaintext := "This is a test string that needs to be encrypted and decrypted"
encrypted, err := service.EncryptString(plaintext)
require.NoError(t, err)
decrypted, err := service.DecryptString(encrypted)
require.NoError(t, err)
assert.Equal(t, plaintext, decrypted)
})
t.Run("Decrypt empty string", func(t *testing.T) {
decrypted, err := service.DecryptString("")
require.Error(t, err)
assert.Equal(t, ErrEmptyCiphertext, err)
assert.Empty(t, decrypted)
})
}
func TestPkcs7Padding(t *testing.T) {
blockSize := 16
t.Run("Pad and unpad", func(t *testing.T) {
testCases := []struct {
input []byte
expected int // expected padding size
}{
{[]byte("testing"), 9}, // 7 bytes + 9 padding = 16 bytes (multiple of blockSize)
{[]byte("16 bytes exactly"), 16}, // 16 bytes + 16 padding = 32 bytes (multiple of blockSize)
{[]byte("this is a longer test string"), 4}, // 28 bytes + 4 padding = 32 bytes (multiple of blockSize)
{[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 2}, // 14 bytes + 2 padding = 16 bytes (multiple of blockSize)
{[]byte{}, 16}, // 0 bytes + 16 padding = 16 bytes (multiple of blockSize)
}
for _, tc := range testCases {
padded := pkcs7Pad(tc.input, blockSize)
// Check padding size
assert.Equal(t, len(tc.input)+tc.expected, len(padded))
// Check padding value
for i := len(tc.input); i < len(padded); i++ {
assert.Equal(t, byte(tc.expected), padded[i])
}
// Unpad and check
unpadded, err := pkcs7Unpad(padded, blockSize)
require.NoError(t, err)
assert.True(t, bytes.Equal(tc.input, unpadded))
}
})
t.Run("Invalid padding", func(t *testing.T) {
// Invalid padding value
invalid := []byte("test data with invalid padding")
paddedInvalid := pkcs7Pad(invalid, blockSize)
paddedInvalid[len(paddedInvalid)-1] = 99 // Invalid padding value
_, err := pkcs7Unpad(paddedInvalid, blockSize)
require.Error(t, err)
// Inconsistent padding
inconsistent := []byte("test data with inconsistent padding")
paddedInconsistent := pkcs7Pad(inconsistent, blockSize)
paddedInconsistent[len(paddedInconsistent)-2] = 99 // Make padding inconsistent
_, err = pkcs7Unpad(paddedInconsistent, blockSize)
require.Error(t, err)
// Empty data
_, err = pkcs7Unpad([]byte{}, blockSize)
require.Error(t, err)
// Invalid block size
invalidSize := []byte("invalid size")
_, err = pkcs7Unpad(invalidSize, blockSize)
require.Error(t, err)
})
}
func TestGetGlobalEncryptionService(t *testing.T) {
// Reset global key manager before test
globalKeyManager = nil
globalKeyManagerOnce = sync.Once{}
// Setup test environment
testEnvVar := DefaultKeyEnvVar
validKey := make([]byte, AES256KeySize)
for i := range validKey {
validKey[i] = byte(i % 256)
}
validKeyBase64 := base64.StdEncoding.EncodeToString(validKey)
// Set a valid key in environment
os.Setenv(testEnvVar, validKeyBase64)
t.Cleanup(func() {
os.Unsetenv(testEnvVar)
})
// Get global encryption service
service, err := GetGlobalEncryptionService()
require.NoError(t, err)
assert.NotNil(t, service)
// Test with actual encryption/decryption
plaintext := "Test with global encryption service"
encrypted, err := service.EncryptString(plaintext)
require.NoError(t, err)
decrypted, err := service.DecryptString(encrypted)
require.NoError(t, err)
assert.Equal(t, plaintext, decrypted)
}