s3_sse_copy_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. package s3api
  2. import (
  3. "bytes"
  4. "io"
  5. "net/http"
  6. "strings"
  7. "testing"
  8. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  9. )
  10. // TestSSECObjectCopy tests copying SSE-C encrypted objects with different keys
  11. func TestSSECObjectCopy(t *testing.T) {
  12. // Original key for source object
  13. sourceKey := GenerateTestSSECKey(1)
  14. sourceCustomerKey := &SSECustomerKey{
  15. Algorithm: "AES256",
  16. Key: sourceKey.Key,
  17. KeyMD5: sourceKey.KeyMD5,
  18. }
  19. // Destination key for target object
  20. destKey := GenerateTestSSECKey(2)
  21. destCustomerKey := &SSECustomerKey{
  22. Algorithm: "AES256",
  23. Key: destKey.Key,
  24. KeyMD5: destKey.KeyMD5,
  25. }
  26. testData := "Hello, SSE-C copy world!"
  27. // Encrypt with source key
  28. encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(testData), sourceCustomerKey)
  29. if err != nil {
  30. t.Fatalf("Failed to create encrypted reader: %v", err)
  31. }
  32. encryptedData, err := io.ReadAll(encryptedReader)
  33. if err != nil {
  34. t.Fatalf("Failed to read encrypted data: %v", err)
  35. }
  36. // Test copy strategy determination
  37. sourceMetadata := make(map[string][]byte)
  38. StoreIVInMetadata(sourceMetadata, iv)
  39. sourceMetadata[s3_constants.AmzServerSideEncryptionCustomerAlgorithm] = []byte("AES256")
  40. sourceMetadata[s3_constants.AmzServerSideEncryptionCustomerKeyMD5] = []byte(sourceKey.KeyMD5)
  41. t.Run("Same key copy (direct copy)", func(t *testing.T) {
  42. strategy, err := DetermineSSECCopyStrategy(sourceMetadata, sourceCustomerKey, sourceCustomerKey)
  43. if err != nil {
  44. t.Fatalf("Failed to determine copy strategy: %v", err)
  45. }
  46. if strategy != SSECCopyStrategyDirect {
  47. t.Errorf("Expected direct copy strategy for same key, got %v", strategy)
  48. }
  49. })
  50. t.Run("Different key copy (decrypt-encrypt)", func(t *testing.T) {
  51. strategy, err := DetermineSSECCopyStrategy(sourceMetadata, sourceCustomerKey, destCustomerKey)
  52. if err != nil {
  53. t.Fatalf("Failed to determine copy strategy: %v", err)
  54. }
  55. if strategy != SSECCopyStrategyDecryptEncrypt {
  56. t.Errorf("Expected decrypt-encrypt copy strategy for different keys, got %v", strategy)
  57. }
  58. })
  59. t.Run("Can direct copy check", func(t *testing.T) {
  60. // Same key should allow direct copy
  61. canDirect := CanDirectCopySSEC(sourceMetadata, sourceCustomerKey, sourceCustomerKey)
  62. if !canDirect {
  63. t.Error("Should allow direct copy with same key")
  64. }
  65. // Different key should not allow direct copy
  66. canDirect = CanDirectCopySSEC(sourceMetadata, sourceCustomerKey, destCustomerKey)
  67. if canDirect {
  68. t.Error("Should not allow direct copy with different keys")
  69. }
  70. })
  71. // Test actual copy operation (decrypt with source key, encrypt with dest key)
  72. t.Run("Full copy operation", func(t *testing.T) {
  73. // Decrypt with source key
  74. decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedData), sourceCustomerKey, iv)
  75. if err != nil {
  76. t.Fatalf("Failed to create decrypted reader: %v", err)
  77. }
  78. // Re-encrypt with destination key
  79. reEncryptedReader, destIV, err := CreateSSECEncryptedReader(decryptedReader, destCustomerKey)
  80. if err != nil {
  81. t.Fatalf("Failed to create re-encrypted reader: %v", err)
  82. }
  83. reEncryptedData, err := io.ReadAll(reEncryptedReader)
  84. if err != nil {
  85. t.Fatalf("Failed to read re-encrypted data: %v", err)
  86. }
  87. // Verify we can decrypt with destination key
  88. finalDecryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(reEncryptedData), destCustomerKey, destIV)
  89. if err != nil {
  90. t.Fatalf("Failed to create final decrypted reader: %v", err)
  91. }
  92. finalData, err := io.ReadAll(finalDecryptedReader)
  93. if err != nil {
  94. t.Fatalf("Failed to read final decrypted data: %v", err)
  95. }
  96. if string(finalData) != testData {
  97. t.Errorf("Expected %s, got %s", testData, string(finalData))
  98. }
  99. })
  100. }
  101. // TestSSEKMSObjectCopy tests copying SSE-KMS encrypted objects
  102. func TestSSEKMSObjectCopy(t *testing.T) {
  103. kmsKey := SetupTestKMS(t)
  104. defer kmsKey.Cleanup()
  105. testData := "Hello, SSE-KMS copy world!"
  106. encryptionContext := BuildEncryptionContext("test-bucket", "test-object", false)
  107. // Encrypt with SSE-KMS
  108. encryptedReader, sseKey, err := CreateSSEKMSEncryptedReader(strings.NewReader(testData), kmsKey.KeyID, encryptionContext)
  109. if err != nil {
  110. t.Fatalf("Failed to create encrypted reader: %v", err)
  111. }
  112. encryptedData, err := io.ReadAll(encryptedReader)
  113. if err != nil {
  114. t.Fatalf("Failed to read encrypted data: %v", err)
  115. }
  116. t.Run("Same KMS key copy", func(t *testing.T) {
  117. // Decrypt with original key
  118. decryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(encryptedData), sseKey)
  119. if err != nil {
  120. t.Fatalf("Failed to create decrypted reader: %v", err)
  121. }
  122. // Re-encrypt with same KMS key
  123. reEncryptedReader, newSseKey, err := CreateSSEKMSEncryptedReader(decryptedReader, kmsKey.KeyID, encryptionContext)
  124. if err != nil {
  125. t.Fatalf("Failed to create re-encrypted reader: %v", err)
  126. }
  127. reEncryptedData, err := io.ReadAll(reEncryptedReader)
  128. if err != nil {
  129. t.Fatalf("Failed to read re-encrypted data: %v", err)
  130. }
  131. // Verify we can decrypt with new key
  132. finalDecryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(reEncryptedData), newSseKey)
  133. if err != nil {
  134. t.Fatalf("Failed to create final decrypted reader: %v", err)
  135. }
  136. finalData, err := io.ReadAll(finalDecryptedReader)
  137. if err != nil {
  138. t.Fatalf("Failed to read final decrypted data: %v", err)
  139. }
  140. if string(finalData) != testData {
  141. t.Errorf("Expected %s, got %s", testData, string(finalData))
  142. }
  143. })
  144. }
  145. // TestSSECToSSEKMSCopy tests cross-encryption copy (SSE-C to SSE-KMS)
  146. func TestSSECToSSEKMSCopy(t *testing.T) {
  147. // Setup SSE-C key
  148. ssecKey := GenerateTestSSECKey(1)
  149. ssecCustomerKey := &SSECustomerKey{
  150. Algorithm: "AES256",
  151. Key: ssecKey.Key,
  152. KeyMD5: ssecKey.KeyMD5,
  153. }
  154. // Setup SSE-KMS
  155. kmsKey := SetupTestKMS(t)
  156. defer kmsKey.Cleanup()
  157. testData := "Hello, cross-encryption copy world!"
  158. // Encrypt with SSE-C
  159. encryptedReader, ssecIV, err := CreateSSECEncryptedReader(strings.NewReader(testData), ssecCustomerKey)
  160. if err != nil {
  161. t.Fatalf("Failed to create SSE-C encrypted reader: %v", err)
  162. }
  163. encryptedData, err := io.ReadAll(encryptedReader)
  164. if err != nil {
  165. t.Fatalf("Failed to read SSE-C encrypted data: %v", err)
  166. }
  167. // Decrypt SSE-C data
  168. decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(encryptedData), ssecCustomerKey, ssecIV)
  169. if err != nil {
  170. t.Fatalf("Failed to create SSE-C decrypted reader: %v", err)
  171. }
  172. // Re-encrypt with SSE-KMS
  173. encryptionContext := BuildEncryptionContext("test-bucket", "test-object", false)
  174. reEncryptedReader, sseKmsKey, err := CreateSSEKMSEncryptedReader(decryptedReader, kmsKey.KeyID, encryptionContext)
  175. if err != nil {
  176. t.Fatalf("Failed to create SSE-KMS encrypted reader: %v", err)
  177. }
  178. reEncryptedData, err := io.ReadAll(reEncryptedReader)
  179. if err != nil {
  180. t.Fatalf("Failed to read SSE-KMS encrypted data: %v", err)
  181. }
  182. // Decrypt with SSE-KMS
  183. finalDecryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(reEncryptedData), sseKmsKey)
  184. if err != nil {
  185. t.Fatalf("Failed to create SSE-KMS decrypted reader: %v", err)
  186. }
  187. finalData, err := io.ReadAll(finalDecryptedReader)
  188. if err != nil {
  189. t.Fatalf("Failed to read final decrypted data: %v", err)
  190. }
  191. if string(finalData) != testData {
  192. t.Errorf("Expected %s, got %s", testData, string(finalData))
  193. }
  194. }
  195. // TestSSEKMSToSSECCopy tests cross-encryption copy (SSE-KMS to SSE-C)
  196. func TestSSEKMSToSSECCopy(t *testing.T) {
  197. // Setup SSE-KMS
  198. kmsKey := SetupTestKMS(t)
  199. defer kmsKey.Cleanup()
  200. // Setup SSE-C key
  201. ssecKey := GenerateTestSSECKey(1)
  202. ssecCustomerKey := &SSECustomerKey{
  203. Algorithm: "AES256",
  204. Key: ssecKey.Key,
  205. KeyMD5: ssecKey.KeyMD5,
  206. }
  207. testData := "Hello, reverse cross-encryption copy world!"
  208. encryptionContext := BuildEncryptionContext("test-bucket", "test-object", false)
  209. // Encrypt with SSE-KMS
  210. encryptedReader, sseKmsKey, err := CreateSSEKMSEncryptedReader(strings.NewReader(testData), kmsKey.KeyID, encryptionContext)
  211. if err != nil {
  212. t.Fatalf("Failed to create SSE-KMS encrypted reader: %v", err)
  213. }
  214. encryptedData, err := io.ReadAll(encryptedReader)
  215. if err != nil {
  216. t.Fatalf("Failed to read SSE-KMS encrypted data: %v", err)
  217. }
  218. // Decrypt SSE-KMS data
  219. decryptedReader, err := CreateSSEKMSDecryptedReader(bytes.NewReader(encryptedData), sseKmsKey)
  220. if err != nil {
  221. t.Fatalf("Failed to create SSE-KMS decrypted reader: %v", err)
  222. }
  223. // Re-encrypt with SSE-C
  224. reEncryptedReader, reEncryptedIV, err := CreateSSECEncryptedReader(decryptedReader, ssecCustomerKey)
  225. if err != nil {
  226. t.Fatalf("Failed to create SSE-C encrypted reader: %v", err)
  227. }
  228. reEncryptedData, err := io.ReadAll(reEncryptedReader)
  229. if err != nil {
  230. t.Fatalf("Failed to read SSE-C encrypted data: %v", err)
  231. }
  232. // Decrypt with SSE-C
  233. finalDecryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(reEncryptedData), ssecCustomerKey, reEncryptedIV)
  234. if err != nil {
  235. t.Fatalf("Failed to create SSE-C decrypted reader: %v", err)
  236. }
  237. finalData, err := io.ReadAll(finalDecryptedReader)
  238. if err != nil {
  239. t.Fatalf("Failed to read final decrypted data: %v", err)
  240. }
  241. if string(finalData) != testData {
  242. t.Errorf("Expected %s, got %s", testData, string(finalData))
  243. }
  244. }
  245. // TestSSECopyWithCorruptedSource tests copy operations with corrupted source data
  246. func TestSSECopyWithCorruptedSource(t *testing.T) {
  247. ssecKey := GenerateTestSSECKey(1)
  248. ssecCustomerKey := &SSECustomerKey{
  249. Algorithm: "AES256",
  250. Key: ssecKey.Key,
  251. KeyMD5: ssecKey.KeyMD5,
  252. }
  253. testData := "Hello, corruption test!"
  254. // Encrypt data
  255. encryptedReader, iv, err := CreateSSECEncryptedReader(strings.NewReader(testData), ssecCustomerKey)
  256. if err != nil {
  257. t.Fatalf("Failed to create encrypted reader: %v", err)
  258. }
  259. encryptedData, err := io.ReadAll(encryptedReader)
  260. if err != nil {
  261. t.Fatalf("Failed to read encrypted data: %v", err)
  262. }
  263. // Corrupt the encrypted data
  264. corruptedData := make([]byte, len(encryptedData))
  265. copy(corruptedData, encryptedData)
  266. if len(corruptedData) > s3_constants.AESBlockSize {
  267. // Corrupt a byte after the IV
  268. corruptedData[s3_constants.AESBlockSize] ^= 0xFF
  269. }
  270. // Try to decrypt corrupted data
  271. decryptedReader, err := CreateSSECDecryptedReader(bytes.NewReader(corruptedData), ssecCustomerKey, iv)
  272. if err != nil {
  273. t.Fatalf("Failed to create decrypted reader for corrupted data: %v", err)
  274. }
  275. decryptedData, err := io.ReadAll(decryptedReader)
  276. if err != nil {
  277. // This is okay - corrupted data might cause read errors
  278. t.Logf("Read error for corrupted data (expected): %v", err)
  279. return
  280. }
  281. // If we can read it, the data should be different from original
  282. if string(decryptedData) == testData {
  283. t.Error("Decrypted corrupted data should not match original")
  284. }
  285. }
  286. // TestSSEKMSCopyStrategy tests SSE-KMS copy strategy determination
  287. func TestSSEKMSCopyStrategy(t *testing.T) {
  288. tests := []struct {
  289. name string
  290. srcMetadata map[string][]byte
  291. destKeyID string
  292. expectedStrategy SSEKMSCopyStrategy
  293. }{
  294. {
  295. name: "Unencrypted to unencrypted",
  296. srcMetadata: map[string][]byte{},
  297. destKeyID: "",
  298. expectedStrategy: SSEKMSCopyStrategyDirect,
  299. },
  300. {
  301. name: "Same KMS key",
  302. srcMetadata: map[string][]byte{
  303. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  304. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  305. },
  306. destKeyID: "test-key-123",
  307. expectedStrategy: SSEKMSCopyStrategyDirect,
  308. },
  309. {
  310. name: "Different KMS keys",
  311. srcMetadata: map[string][]byte{
  312. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  313. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  314. },
  315. destKeyID: "test-key-456",
  316. expectedStrategy: SSEKMSCopyStrategyDecryptEncrypt,
  317. },
  318. {
  319. name: "Encrypted to unencrypted",
  320. srcMetadata: map[string][]byte{
  321. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  322. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  323. },
  324. destKeyID: "",
  325. expectedStrategy: SSEKMSCopyStrategyDecryptEncrypt,
  326. },
  327. {
  328. name: "Unencrypted to encrypted",
  329. srcMetadata: map[string][]byte{},
  330. destKeyID: "test-key-123",
  331. expectedStrategy: SSEKMSCopyStrategyDecryptEncrypt,
  332. },
  333. }
  334. for _, tt := range tests {
  335. t.Run(tt.name, func(t *testing.T) {
  336. strategy, err := DetermineSSEKMSCopyStrategy(tt.srcMetadata, tt.destKeyID)
  337. if err != nil {
  338. t.Fatalf("DetermineSSEKMSCopyStrategy failed: %v", err)
  339. }
  340. if strategy != tt.expectedStrategy {
  341. t.Errorf("Expected strategy %v, got %v", tt.expectedStrategy, strategy)
  342. }
  343. })
  344. }
  345. }
  346. // TestSSEKMSCopyHeaders tests SSE-KMS copy header parsing
  347. func TestSSEKMSCopyHeaders(t *testing.T) {
  348. tests := []struct {
  349. name string
  350. headers map[string]string
  351. expectedKeyID string
  352. expectedContext map[string]string
  353. expectedBucketKey bool
  354. expectError bool
  355. }{
  356. {
  357. name: "No SSE-KMS headers",
  358. headers: map[string]string{},
  359. expectedKeyID: "",
  360. expectedContext: nil,
  361. expectedBucketKey: false,
  362. expectError: false,
  363. },
  364. {
  365. name: "SSE-KMS with key ID",
  366. headers: map[string]string{
  367. s3_constants.AmzServerSideEncryption: "aws:kms",
  368. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: "test-key-123",
  369. },
  370. expectedKeyID: "test-key-123",
  371. expectedContext: nil,
  372. expectedBucketKey: false,
  373. expectError: false,
  374. },
  375. {
  376. name: "SSE-KMS with all options",
  377. headers: map[string]string{
  378. s3_constants.AmzServerSideEncryption: "aws:kms",
  379. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: "test-key-123",
  380. s3_constants.AmzServerSideEncryptionContext: "eyJ0ZXN0IjoidmFsdWUifQ==", // base64 of {"test":"value"}
  381. s3_constants.AmzServerSideEncryptionBucketKeyEnabled: "true",
  382. },
  383. expectedKeyID: "test-key-123",
  384. expectedContext: map[string]string{"test": "value"},
  385. expectedBucketKey: true,
  386. expectError: false,
  387. },
  388. {
  389. name: "Invalid key ID",
  390. headers: map[string]string{
  391. s3_constants.AmzServerSideEncryption: "aws:kms",
  392. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: "invalid key id",
  393. },
  394. expectError: true,
  395. },
  396. {
  397. name: "Invalid encryption context",
  398. headers: map[string]string{
  399. s3_constants.AmzServerSideEncryption: "aws:kms",
  400. s3_constants.AmzServerSideEncryptionContext: "invalid-base64!",
  401. },
  402. expectError: true,
  403. },
  404. }
  405. for _, tt := range tests {
  406. t.Run(tt.name, func(t *testing.T) {
  407. req, _ := http.NewRequest("PUT", "/test", nil)
  408. for k, v := range tt.headers {
  409. req.Header.Set(k, v)
  410. }
  411. keyID, context, bucketKey, err := ParseSSEKMSCopyHeaders(req)
  412. if tt.expectError {
  413. if err == nil {
  414. t.Error("Expected error but got none")
  415. }
  416. return
  417. }
  418. if err != nil {
  419. t.Fatalf("Unexpected error: %v", err)
  420. }
  421. if keyID != tt.expectedKeyID {
  422. t.Errorf("Expected keyID %s, got %s", tt.expectedKeyID, keyID)
  423. }
  424. if !mapsEqual(context, tt.expectedContext) {
  425. t.Errorf("Expected context %v, got %v", tt.expectedContext, context)
  426. }
  427. if bucketKey != tt.expectedBucketKey {
  428. t.Errorf("Expected bucketKey %v, got %v", tt.expectedBucketKey, bucketKey)
  429. }
  430. })
  431. }
  432. }
  433. // TestSSEKMSDirectCopy tests direct copy scenarios
  434. func TestSSEKMSDirectCopy(t *testing.T) {
  435. tests := []struct {
  436. name string
  437. srcMetadata map[string][]byte
  438. destKeyID string
  439. canDirect bool
  440. }{
  441. {
  442. name: "Both unencrypted",
  443. srcMetadata: map[string][]byte{},
  444. destKeyID: "",
  445. canDirect: true,
  446. },
  447. {
  448. name: "Same key ID",
  449. srcMetadata: map[string][]byte{
  450. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  451. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  452. },
  453. destKeyID: "test-key-123",
  454. canDirect: true,
  455. },
  456. {
  457. name: "Different key IDs",
  458. srcMetadata: map[string][]byte{
  459. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  460. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  461. },
  462. destKeyID: "test-key-456",
  463. canDirect: false,
  464. },
  465. {
  466. name: "Source encrypted, dest unencrypted",
  467. srcMetadata: map[string][]byte{
  468. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  469. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  470. },
  471. destKeyID: "",
  472. canDirect: false,
  473. },
  474. {
  475. name: "Source unencrypted, dest encrypted",
  476. srcMetadata: map[string][]byte{},
  477. destKeyID: "test-key-123",
  478. canDirect: false,
  479. },
  480. }
  481. for _, tt := range tests {
  482. t.Run(tt.name, func(t *testing.T) {
  483. canDirect := CanDirectCopySSEKMS(tt.srcMetadata, tt.destKeyID)
  484. if canDirect != tt.canDirect {
  485. t.Errorf("Expected canDirect %v, got %v", tt.canDirect, canDirect)
  486. }
  487. })
  488. }
  489. }
  490. // TestGetSourceSSEKMSInfo tests extraction of SSE-KMS info from metadata
  491. func TestGetSourceSSEKMSInfo(t *testing.T) {
  492. tests := []struct {
  493. name string
  494. metadata map[string][]byte
  495. expectedKeyID string
  496. expectedEncrypted bool
  497. }{
  498. {
  499. name: "No encryption",
  500. metadata: map[string][]byte{},
  501. expectedKeyID: "",
  502. expectedEncrypted: false,
  503. },
  504. {
  505. name: "SSE-KMS with key ID",
  506. metadata: map[string][]byte{
  507. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  508. s3_constants.AmzServerSideEncryptionAwsKmsKeyId: []byte("test-key-123"),
  509. },
  510. expectedKeyID: "test-key-123",
  511. expectedEncrypted: true,
  512. },
  513. {
  514. name: "SSE-KMS without key ID (default key)",
  515. metadata: map[string][]byte{
  516. s3_constants.AmzServerSideEncryption: []byte("aws:kms"),
  517. },
  518. expectedKeyID: "",
  519. expectedEncrypted: true,
  520. },
  521. {
  522. name: "Non-KMS encryption",
  523. metadata: map[string][]byte{
  524. s3_constants.AmzServerSideEncryption: []byte("AES256"),
  525. },
  526. expectedKeyID: "",
  527. expectedEncrypted: false,
  528. },
  529. }
  530. for _, tt := range tests {
  531. t.Run(tt.name, func(t *testing.T) {
  532. keyID, encrypted := GetSourceSSEKMSInfo(tt.metadata)
  533. if keyID != tt.expectedKeyID {
  534. t.Errorf("Expected keyID %s, got %s", tt.expectedKeyID, keyID)
  535. }
  536. if encrypted != tt.expectedEncrypted {
  537. t.Errorf("Expected encrypted %v, got %v", tt.expectedEncrypted, encrypted)
  538. }
  539. })
  540. }
  541. }
  542. // Helper function to compare maps
  543. func mapsEqual(a, b map[string]string) bool {
  544. if len(a) != len(b) {
  545. return false
  546. }
  547. for k, v := range a {
  548. if b[k] != v {
  549. return false
  550. }
  551. }
  552. return true
  553. }