You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

984 lines
25 KiB

package s3api
import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
)
// TestSSES3EncryptionDecryption tests basic SSE-S3 encryption and decryption
func TestSSES3EncryptionDecryption(t *testing.T) {
// Generate SSE-S3 key
sseS3Key, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key: %v", err)
}
// Test data
testData := []byte("Hello, World! This is a test of SSE-S3 encryption.")
// Create encrypted reader
dataReader := bytes.NewReader(testData)
encryptedReader, iv, err := CreateSSES3EncryptedReader(dataReader, sseS3Key)
if err != nil {
t.Fatalf("Failed to create encrypted reader: %v", err)
}
// Read encrypted data
encryptedData, err := io.ReadAll(encryptedReader)
if err != nil {
t.Fatalf("Failed to read encrypted data: %v", err)
}
// Verify data is actually encrypted (different from original)
if bytes.Equal(encryptedData, testData) {
t.Error("Data doesn't appear to be encrypted")
}
// Create decrypted reader
encryptedReader2 := bytes.NewReader(encryptedData)
decryptedReader, err := CreateSSES3DecryptedReader(encryptedReader2, sseS3Key, iv)
if err != nil {
t.Fatalf("Failed to create decrypted reader: %v", err)
}
// Read decrypted data
decryptedData, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatalf("Failed to read decrypted data: %v", err)
}
// Verify decrypted data matches original
if !bytes.Equal(decryptedData, testData) {
t.Errorf("Decrypted data doesn't match original.\nOriginal: %s\nDecrypted: %s", testData, decryptedData)
}
}
// TestSSES3IsRequestInternal tests detection of SSE-S3 requests
func TestSSES3IsRequestInternal(t *testing.T) {
testCases := []struct {
name string
headers map[string]string
expected bool
}{
{
name: "Valid SSE-S3 request",
headers: map[string]string{
s3_constants.AmzServerSideEncryption: "AES256",
},
expected: true,
},
{
name: "No SSE headers",
headers: map[string]string{},
expected: false,
},
{
name: "SSE-KMS request",
headers: map[string]string{
s3_constants.AmzServerSideEncryption: "aws:kms",
},
expected: false,
},
{
name: "SSE-C request",
headers: map[string]string{
s3_constants.AmzServerSideEncryptionCustomerAlgorithm: "AES256",
},
expected: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
req := &http.Request{Header: make(http.Header)}
for k, v := range tc.headers {
req.Header.Set(k, v)
}
result := IsSSES3RequestInternal(req)
if result != tc.expected {
t.Errorf("Expected %v, got %v", tc.expected, result)
}
})
}
}
// TestSSES3MetadataSerialization tests SSE-S3 metadata serialization and deserialization
func TestSSES3MetadataSerialization(t *testing.T) {
// Initialize global key manager
globalSSES3KeyManager = NewSSES3KeyManager()
defer func() {
globalSSES3KeyManager = NewSSES3KeyManager()
}()
// Set up the key manager with a super key for testing
keyManager := GetSSES3KeyManager()
keyManager.superKey = make([]byte, 32)
for i := range keyManager.superKey {
keyManager.superKey[i] = byte(i)
}
// Generate SSE-S3 key
sseS3Key, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key: %v", err)
}
// Add IV to the key
sseS3Key.IV = make([]byte, 16)
for i := range sseS3Key.IV {
sseS3Key.IV[i] = byte(i * 2)
}
// Serialize metadata
serialized, err := SerializeSSES3Metadata(sseS3Key)
if err != nil {
t.Fatalf("Failed to serialize SSE-S3 metadata: %v", err)
}
if len(serialized) == 0 {
t.Error("Serialized metadata is empty")
}
// Deserialize metadata
deserializedKey, err := DeserializeSSES3Metadata(serialized, keyManager)
if err != nil {
t.Fatalf("Failed to deserialize SSE-S3 metadata: %v", err)
}
// Verify key matches
if !bytes.Equal(deserializedKey.Key, sseS3Key.Key) {
t.Error("Deserialized key doesn't match original key")
}
// Verify IV matches
if !bytes.Equal(deserializedKey.IV, sseS3Key.IV) {
t.Error("Deserialized IV doesn't match original IV")
}
// Verify algorithm matches
if deserializedKey.Algorithm != sseS3Key.Algorithm {
t.Errorf("Algorithm mismatch: expected %s, got %s", sseS3Key.Algorithm, deserializedKey.Algorithm)
}
// Verify key ID matches
if deserializedKey.KeyID != sseS3Key.KeyID {
t.Errorf("Key ID mismatch: expected %s, got %s", sseS3Key.KeyID, deserializedKey.KeyID)
}
}
// TestDetectPrimarySSETypeS3 tests detection of SSE-S3 as primary encryption type
func TestDetectPrimarySSETypeS3(t *testing.T) {
s3a := &S3ApiServer{}
testCases := []struct {
name string
entry *filer_pb.Entry
expected string
}{
{
name: "Single SSE-S3 chunk",
entry: &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{
{
FileId: "1,123",
Offset: 0,
Size: 1024,
SseType: filer_pb.SSEType_SSE_S3,
SseMetadata: []byte("metadata"),
},
},
},
expected: s3_constants.SSETypeS3,
},
{
name: "Multiple SSE-S3 chunks",
entry: &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{
{
FileId: "1,123",
Offset: 0,
Size: 1024,
SseType: filer_pb.SSEType_SSE_S3,
SseMetadata: []byte("metadata1"),
},
{
FileId: "2,456",
Offset: 1024,
Size: 1024,
SseType: filer_pb.SSEType_SSE_S3,
SseMetadata: []byte("metadata2"),
},
},
},
expected: s3_constants.SSETypeS3,
},
{
name: "Mixed SSE-S3 and SSE-KMS chunks (SSE-S3 majority)",
entry: &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{
{
FileId: "1,123",
Offset: 0,
Size: 1024,
SseType: filer_pb.SSEType_SSE_S3,
SseMetadata: []byte("metadata1"),
},
{
FileId: "2,456",
Offset: 1024,
Size: 1024,
SseType: filer_pb.SSEType_SSE_S3,
SseMetadata: []byte("metadata2"),
},
{
FileId: "3,789",
Offset: 2048,
Size: 1024,
SseType: filer_pb.SSEType_SSE_KMS,
SseMetadata: []byte("metadata3"),
},
},
},
expected: s3_constants.SSETypeS3,
},
{
name: "No chunks, SSE-S3 metadata without KMS key ID",
entry: &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{},
},
expected: s3_constants.SSETypeS3,
},
{
name: "No chunks, SSE-KMS metadata with KMS key ID",
entry: &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-id"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{},
},
expected: s3_constants.SSETypeKMS,
},
{
name: "SSE-C chunks",
entry: &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryptionCustomerAlgorithm: []byte("AES256"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{
{
FileId: "1,123",
Offset: 0,
Size: 1024,
SseType: filer_pb.SSEType_SSE_C,
SseMetadata: []byte("metadata"),
},
},
},
expected: s3_constants.SSETypeC,
},
{
name: "Unencrypted",
entry: &filer_pb.Entry{
Extended: map[string][]byte{},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{
{
FileId: "1,123",
Offset: 0,
Size: 1024,
},
},
},
expected: "None",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := s3a.detectPrimarySSEType(tc.entry)
if result != tc.expected {
t.Errorf("Expected %s, got %s", tc.expected, result)
}
})
}
}
// TestAddSSES3HeadersToResponse tests that SSE-S3 headers are added to responses
func TestAddSSES3HeadersToResponse(t *testing.T) {
s3a := &S3ApiServer{}
entry := &filer_pb.Entry{
Extended: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
},
Attributes: &filer_pb.FuseAttributes{},
Chunks: []*filer_pb.FileChunk{
{
FileId: "1,123",
Offset: 0,
Size: 1024,
SseType: filer_pb.SSEType_SSE_S3,
SseMetadata: []byte("metadata"),
},
},
}
proxyResponse := &http.Response{
Header: make(http.Header),
}
s3a.addSSEHeadersToResponse(proxyResponse, entry)
algorithm := proxyResponse.Header.Get(s3_constants.AmzServerSideEncryption)
if algorithm != "AES256" {
t.Errorf("Expected SSE algorithm AES256, got %s", algorithm)
}
// Should NOT have SSE-C or SSE-KMS specific headers
if proxyResponse.Header.Get(s3_constants.AmzServerSideEncryptionCustomerAlgorithm) != "" {
t.Error("Should not have SSE-C customer algorithm header")
}
if proxyResponse.Header.Get(s3_constants.AmzServerSideEncryptionAwsKmsKeyId) != "" {
t.Error("Should not have SSE-KMS key ID header")
}
}
// TestSSES3EncryptionWithBaseIV tests multipart encryption with base IV
func TestSSES3EncryptionWithBaseIV(t *testing.T) {
// Generate SSE-S3 key
sseS3Key, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key: %v", err)
}
// Generate base IV
baseIV := make([]byte, 16)
for i := range baseIV {
baseIV[i] = byte(i)
}
// Test data for two parts
testData1 := []byte("Part 1 of multipart upload test.")
testData2 := []byte("Part 2 of multipart upload test.")
// Encrypt part 1 at offset 0
dataReader1 := bytes.NewReader(testData1)
encryptedReader1, iv1, err := CreateSSES3EncryptedReaderWithBaseIV(dataReader1, sseS3Key, baseIV, 0)
if err != nil {
t.Fatalf("Failed to create encrypted reader for part 1: %v", err)
}
encryptedData1, err := io.ReadAll(encryptedReader1)
if err != nil {
t.Fatalf("Failed to read encrypted data for part 1: %v", err)
}
// Encrypt part 2 at offset (simulating second part)
dataReader2 := bytes.NewReader(testData2)
offset2 := int64(len(testData1))
encryptedReader2, iv2, err := CreateSSES3EncryptedReaderWithBaseIV(dataReader2, sseS3Key, baseIV, offset2)
if err != nil {
t.Fatalf("Failed to create encrypted reader for part 2: %v", err)
}
encryptedData2, err := io.ReadAll(encryptedReader2)
if err != nil {
t.Fatalf("Failed to read encrypted data for part 2: %v", err)
}
// IVs should be different (offset-based)
if bytes.Equal(iv1, iv2) {
t.Error("IVs should be different for different offsets")
}
// Decrypt part 1
decryptedReader1, err := CreateSSES3DecryptedReader(bytes.NewReader(encryptedData1), sseS3Key, iv1)
if err != nil {
t.Fatalf("Failed to create decrypted reader for part 1: %v", err)
}
decryptedData1, err := io.ReadAll(decryptedReader1)
if err != nil {
t.Fatalf("Failed to read decrypted data for part 1: %v", err)
}
// Decrypt part 2
decryptedReader2, err := CreateSSES3DecryptedReader(bytes.NewReader(encryptedData2), sseS3Key, iv2)
if err != nil {
t.Fatalf("Failed to create decrypted reader for part 2: %v", err)
}
decryptedData2, err := io.ReadAll(decryptedReader2)
if err != nil {
t.Fatalf("Failed to read decrypted data for part 2: %v", err)
}
// Verify decrypted data matches original
if !bytes.Equal(decryptedData1, testData1) {
t.Errorf("Decrypted part 1 doesn't match original.\nOriginal: %s\nDecrypted: %s", testData1, decryptedData1)
}
if !bytes.Equal(decryptedData2, testData2) {
t.Errorf("Decrypted part 2 doesn't match original.\nOriginal: %s\nDecrypted: %s", testData2, decryptedData2)
}
}
// TestSSES3WrongKeyDecryption tests that wrong key fails decryption
func TestSSES3WrongKeyDecryption(t *testing.T) {
// Generate two different keys
sseS3Key1, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key 1: %v", err)
}
sseS3Key2, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key 2: %v", err)
}
// Test data
testData := []byte("Secret data encrypted with key 1")
// Encrypt with key 1
dataReader := bytes.NewReader(testData)
encryptedReader, iv, err := CreateSSES3EncryptedReader(dataReader, sseS3Key1)
if err != nil {
t.Fatalf("Failed to create encrypted reader: %v", err)
}
encryptedData, err := io.ReadAll(encryptedReader)
if err != nil {
t.Fatalf("Failed to read encrypted data: %v", err)
}
// Try to decrypt with key 2 (wrong key)
decryptedReader, err := CreateSSES3DecryptedReader(bytes.NewReader(encryptedData), sseS3Key2, iv)
if err != nil {
t.Fatalf("Failed to create decrypted reader: %v", err)
}
decryptedData, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatalf("Failed to read decrypted data: %v", err)
}
// Decrypted data should NOT match original (wrong key produces garbage)
if bytes.Equal(decryptedData, testData) {
t.Error("Decryption with wrong key should not produce correct plaintext")
}
}
// TestSSES3KeyGeneration tests SSE-S3 key generation
func TestSSES3KeyGeneration(t *testing.T) {
// Generate multiple keys
keys := make([]*SSES3Key, 10)
for i := range keys {
key, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key %d: %v", i, err)
}
keys[i] = key
// Verify key properties
if len(key.Key) != SSES3KeySize {
t.Errorf("Key %d has wrong size: expected %d, got %d", i, SSES3KeySize, len(key.Key))
}
if key.Algorithm != SSES3Algorithm {
t.Errorf("Key %d has wrong algorithm: expected %s, got %s", i, SSES3Algorithm, key.Algorithm)
}
if key.KeyID == "" {
t.Errorf("Key %d has empty key ID", i)
}
}
// Verify keys are unique
for i := 0; i < len(keys); i++ {
for j := i + 1; j < len(keys); j++ {
if bytes.Equal(keys[i].Key, keys[j].Key) {
t.Errorf("Keys %d and %d are identical (should be unique)", i, j)
}
if keys[i].KeyID == keys[j].KeyID {
t.Errorf("Key IDs %d and %d are identical (should be unique)", i, j)
}
}
}
}
// TestSSES3VariousSizes tests SSE-S3 encryption/decryption with various data sizes
func TestSSES3VariousSizes(t *testing.T) {
sizes := []int{1, 15, 16, 17, 100, 1024, 4096, 1048576}
for _, size := range sizes {
t.Run(fmt.Sprintf("size_%d", size), func(t *testing.T) {
// Generate test data
testData := make([]byte, size)
for i := range testData {
testData[i] = byte(i % 256)
}
// Generate key
sseS3Key, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key: %v", err)
}
// Encrypt
dataReader := bytes.NewReader(testData)
encryptedReader, iv, err := CreateSSES3EncryptedReader(dataReader, sseS3Key)
if err != nil {
t.Fatalf("Failed to create encrypted reader: %v", err)
}
encryptedData, err := io.ReadAll(encryptedReader)
if err != nil {
t.Fatalf("Failed to read encrypted data: %v", err)
}
// Verify encrypted size matches original
if len(encryptedData) != size {
t.Errorf("Encrypted size mismatch: expected %d, got %d", size, len(encryptedData))
}
// Decrypt
decryptedReader, err := CreateSSES3DecryptedReader(bytes.NewReader(encryptedData), sseS3Key, iv)
if err != nil {
t.Fatalf("Failed to create decrypted reader: %v", err)
}
decryptedData, err := io.ReadAll(decryptedReader)
if err != nil {
t.Fatalf("Failed to read decrypted data: %v", err)
}
// Verify
if !bytes.Equal(decryptedData, testData) {
t.Errorf("Decrypted data doesn't match original for size %d", size)
}
})
}
}
// TestSSES3ResponseHeaders tests that SSE-S3 response headers are set correctly
func TestSSES3ResponseHeaders(t *testing.T) {
w := httptest.NewRecorder()
// Simulate setting SSE-S3 response headers
w.Header().Set(s3_constants.AmzServerSideEncryption, SSES3Algorithm)
// Verify headers
algorithm := w.Header().Get(s3_constants.AmzServerSideEncryption)
if algorithm != "AES256" {
t.Errorf("Expected algorithm AES256, got %s", algorithm)
}
// Should NOT have customer key headers
if w.Header().Get(s3_constants.AmzServerSideEncryptionCustomerAlgorithm) != "" {
t.Error("Should not have SSE-C customer algorithm header")
}
if w.Header().Get(s3_constants.AmzServerSideEncryptionCustomerKeyMD5) != "" {
t.Error("Should not have SSE-C customer key MD5 header")
}
// Should NOT have KMS key ID
if w.Header().Get(s3_constants.AmzServerSideEncryptionAwsKmsKeyId) != "" {
t.Error("Should not have SSE-KMS key ID header")
}
}
// TestSSES3IsEncryptedInternal tests detection of SSE-S3 encryption from metadata
func TestSSES3IsEncryptedInternal(t *testing.T) {
testCases := []struct {
name string
metadata map[string][]byte
expected bool
}{
{
name: "Empty metadata",
metadata: map[string][]byte{},
expected: false,
},
{
name: "Valid SSE-S3 metadata",
metadata: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("AES256"),
},
expected: true,
},
{
name: "SSE-KMS metadata",
metadata: map[string][]byte{
s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
},
expected: false,
},
{
name: "SSE-C metadata",
metadata: map[string][]byte{
s3_constants.AmzServerSideEncryptionCustomerAlgorithm: []byte("AES256"),
},
expected: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := IsSSES3EncryptedInternal(tc.metadata)
if result != tc.expected {
t.Errorf("Expected %v, got %v", tc.expected, result)
}
})
}
}
// TestSSES3InvalidMetadataDeserialization tests error handling for invalid metadata
func TestSSES3InvalidMetadataDeserialization(t *testing.T) {
keyManager := NewSSES3KeyManager()
keyManager.superKey = make([]byte, 32)
testCases := []struct {
name string
metadata []byte
shouldError bool
}{
{
name: "Empty metadata",
metadata: []byte{},
shouldError: true,
},
{
name: "Invalid JSON",
metadata: []byte("not valid json"),
shouldError: true,
},
{
name: "Missing keyId",
metadata: []byte(`{"algorithm":"AES256"}`),
shouldError: true,
},
{
name: "Invalid base64 encrypted DEK",
metadata: []byte(`{"keyId":"test","algorithm":"AES256","encryptedDEK":"not-valid-base64!","nonce":"dGVzdA=="}`),
shouldError: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
_, err := DeserializeSSES3Metadata(tc.metadata, keyManager)
if tc.shouldError && err == nil {
t.Error("Expected error but got none")
}
if !tc.shouldError && err != nil {
t.Errorf("Unexpected error: %v", err)
}
})
}
}
// TestGetSSES3Headers tests SSE-S3 header generation
func TestGetSSES3Headers(t *testing.T) {
headers := GetSSES3Headers()
if len(headers) == 0 {
t.Error("Expected headers to be non-empty")
}
algorithm, exists := headers[s3_constants.AmzServerSideEncryption]
if !exists {
t.Error("Expected AmzServerSideEncryption header to exist")
}
if algorithm != "AES256" {
t.Errorf("Expected algorithm AES256, got %s", algorithm)
}
}
// TestProcessSSES3Request tests processing of SSE-S3 requests
func TestProcessSSES3Request(t *testing.T) {
// Initialize global key manager
globalSSES3KeyManager = NewSSES3KeyManager()
defer func() {
globalSSES3KeyManager = NewSSES3KeyManager()
}()
// Set up the key manager with a super key for testing
keyManager := GetSSES3KeyManager()
keyManager.superKey = make([]byte, 32)
for i := range keyManager.superKey {
keyManager.superKey[i] = byte(i)
}
// Create SSE-S3 request
req := httptest.NewRequest("PUT", "/bucket/object", nil)
req.Header.Set(s3_constants.AmzServerSideEncryption, "AES256")
// Process request
metadata, err := ProcessSSES3Request(req)
if err != nil {
t.Fatalf("Failed to process SSE-S3 request: %v", err)
}
if metadata == nil {
t.Fatal("Expected metadata to be non-nil")
}
// Verify metadata contains SSE algorithm
if sseAlgo, exists := metadata[s3_constants.AmzServerSideEncryption]; !exists {
t.Error("Expected SSE algorithm in metadata")
} else if string(sseAlgo) != "AES256" {
t.Errorf("Expected AES256, got %s", string(sseAlgo))
}
// Verify metadata contains key data
if _, exists := metadata[s3_constants.SeaweedFSSSES3Key]; !exists {
t.Error("Expected SSE-S3 key data in metadata")
}
}
// TestGetSSES3KeyFromMetadata tests extraction of SSE-S3 key from metadata
func TestGetSSES3KeyFromMetadata(t *testing.T) {
// Initialize global key manager
globalSSES3KeyManager = NewSSES3KeyManager()
defer func() {
globalSSES3KeyManager = NewSSES3KeyManager()
}()
// Set up the key manager with a super key for testing
keyManager := GetSSES3KeyManager()
keyManager.superKey = make([]byte, 32)
for i := range keyManager.superKey {
keyManager.superKey[i] = byte(i)
}
// Generate and serialize key
sseS3Key, err := GenerateSSES3Key()
if err != nil {
t.Fatalf("Failed to generate SSE-S3 key: %v", err)
}
sseS3Key.IV = make([]byte, 16)
for i := range sseS3Key.IV {
sseS3Key.IV[i] = byte(i)
}
serialized, err := SerializeSSES3Metadata(sseS3Key)
if err != nil {
t.Fatalf("Failed to serialize SSE-S3 metadata: %v", err)
}
metadata := map[string][]byte{
s3_constants.SeaweedFSSSES3Key: serialized,
}
// Extract key
extractedKey, err := GetSSES3KeyFromMetadata(metadata, keyManager)
if err != nil {
t.Fatalf("Failed to get SSE-S3 key from metadata: %v", err)
}
// Verify key matches
if !bytes.Equal(extractedKey.Key, sseS3Key.Key) {
t.Error("Extracted key doesn't match original key")
}
if !bytes.Equal(extractedKey.IV, sseS3Key.IV) {
t.Error("Extracted IV doesn't match original IV")
}
}
// TestSSES3EnvelopeEncryption tests that envelope encryption works correctly
func TestSSES3EnvelopeEncryption(t *testing.T) {
// Initialize key manager with a super key
keyManager := NewSSES3KeyManager()
keyManager.superKey = make([]byte, 32)
for i := range keyManager.superKey {
keyManager.superKey[i] = byte(i + 100)
}
// Generate a DEK
dek := make([]byte, 32)
for i := range dek {
dek[i] = byte(i)
}
// Encrypt DEK with super key
encryptedDEK, nonce, err := keyManager.encryptKeyWithSuperKey(dek)
if err != nil {
t.Fatalf("Failed to encrypt DEK: %v", err)
}
if len(encryptedDEK) == 0 {
t.Error("Encrypted DEK is empty")
}
if len(nonce) == 0 {
t.Error("Nonce is empty")
}
// Decrypt DEK with super key
decryptedDEK, err := keyManager.decryptKeyWithSuperKey(encryptedDEK, nonce)
if err != nil {
t.Fatalf("Failed to decrypt DEK: %v", err)
}
// Verify DEK matches
if !bytes.Equal(decryptedDEK, dek) {
t.Error("Decrypted DEK doesn't match original DEK")
}
}
// TestValidateSSES3Key tests SSE-S3 key validation
func TestValidateSSES3Key(t *testing.T) {
testCases := []struct {
name string
key *SSES3Key
shouldError bool
errorMsg string
}{
{
name: "Nil key",
key: nil,
shouldError: true,
errorMsg: "SSE-S3 key cannot be nil",
},
{
name: "Valid key",
key: &SSES3Key{
Key: make([]byte, 32),
KeyID: "test-key",
Algorithm: "AES256",
},
shouldError: false,
},
{
name: "Valid key with IV",
key: &SSES3Key{
Key: make([]byte, 32),
KeyID: "test-key",
Algorithm: "AES256",
IV: make([]byte, 16),
},
shouldError: false,
},
{
name: "Invalid key size (too small)",
key: &SSES3Key{
Key: make([]byte, 16),
KeyID: "test-key",
Algorithm: "AES256",
},
shouldError: true,
errorMsg: "invalid SSE-S3 key size",
},
{
name: "Invalid key size (too large)",
key: &SSES3Key{
Key: make([]byte, 64),
KeyID: "test-key",
Algorithm: "AES256",
},
shouldError: true,
errorMsg: "invalid SSE-S3 key size",
},
{
name: "Nil key bytes",
key: &SSES3Key{
Key: nil,
KeyID: "test-key",
Algorithm: "AES256",
},
shouldError: true,
errorMsg: "SSE-S3 key bytes cannot be nil",
},
{
name: "Empty key ID",
key: &SSES3Key{
Key: make([]byte, 32),
KeyID: "",
Algorithm: "AES256",
},
shouldError: true,
errorMsg: "SSE-S3 key ID cannot be empty",
},
{
name: "Invalid algorithm",
key: &SSES3Key{
Key: make([]byte, 32),
KeyID: "test-key",
Algorithm: "INVALID",
},
shouldError: true,
errorMsg: "invalid SSE-S3 algorithm",
},
{
name: "Invalid IV length",
key: &SSES3Key{
Key: make([]byte, 32),
KeyID: "test-key",
Algorithm: "AES256",
IV: make([]byte, 8), // Wrong size
},
shouldError: true,
errorMsg: "invalid SSE-S3 IV length",
},
{
name: "Empty IV is allowed (set during encryption)",
key: &SSES3Key{
Key: make([]byte, 32),
KeyID: "test-key",
Algorithm: "AES256",
IV: []byte{}, // Empty is OK
},
shouldError: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateSSES3Key(tc.key)
if tc.shouldError {
if err == nil {
t.Error("Expected error but got none")
} else if tc.errorMsg != "" && !strings.Contains(err.Error(), tc.errorMsg) {
t.Errorf("Expected error containing %q, got: %v", tc.errorMsg, err)
}
} else {
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
})
}
}