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
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							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)
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										})
							 | 
						|
									}
							 | 
						|
								}
							 |