s3_sse_kms_utils.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package s3api
  2. import (
  3. "context"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "fmt"
  7. "strings"
  8. "github.com/seaweedfs/seaweedfs/weed/kms"
  9. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  10. )
  11. // KMSDataKeyResult holds the result of data key generation
  12. type KMSDataKeyResult struct {
  13. Response *kms.GenerateDataKeyResponse
  14. Block cipher.Block
  15. }
  16. // generateKMSDataKey generates a new data encryption key using KMS
  17. // This function encapsulates the common pattern used across all SSE-KMS functions
  18. func generateKMSDataKey(keyID string, encryptionContext map[string]string) (*KMSDataKeyResult, error) {
  19. // Validate keyID to prevent injection attacks and malformed requests to KMS service
  20. if !isValidKMSKeyID(keyID) {
  21. return nil, fmt.Errorf("invalid KMS key ID format: key ID must be non-empty, without spaces or control characters")
  22. }
  23. // Validate encryption context to prevent malformed requests to KMS service
  24. if encryptionContext != nil {
  25. for key, value := range encryptionContext {
  26. // Validate context keys and values for basic security
  27. if strings.TrimSpace(key) == "" {
  28. return nil, fmt.Errorf("invalid encryption context: keys cannot be empty or whitespace-only")
  29. }
  30. if strings.ContainsAny(key, "\x00\n\r\t") || strings.ContainsAny(value, "\x00\n\r\t") {
  31. return nil, fmt.Errorf("invalid encryption context: keys and values cannot contain control characters")
  32. }
  33. // AWS KMS has limits on key/value lengths
  34. if len(key) > 2048 || len(value) > 2048 {
  35. return nil, fmt.Errorf("invalid encryption context: keys and values must be ≤ 2048 characters (key=%d, value=%d)", len(key), len(value))
  36. }
  37. }
  38. // AWS KMS has a limit on the total number of context pairs
  39. if len(encryptionContext) > s3_constants.MaxKMSEncryptionContextPairs {
  40. return nil, fmt.Errorf("invalid encryption context: cannot exceed %d key-value pairs, got %d", s3_constants.MaxKMSEncryptionContextPairs, len(encryptionContext))
  41. }
  42. }
  43. // Get KMS provider
  44. kmsProvider := kms.GetGlobalKMS()
  45. if kmsProvider == nil {
  46. return nil, fmt.Errorf("KMS is not configured")
  47. }
  48. // Create data key request
  49. generateDataKeyReq := &kms.GenerateDataKeyRequest{
  50. KeyID: keyID,
  51. KeySpec: kms.KeySpecAES256,
  52. EncryptionContext: encryptionContext,
  53. }
  54. // Generate the data key
  55. dataKeyResp, err := kmsProvider.GenerateDataKey(context.Background(), generateDataKeyReq)
  56. if err != nil {
  57. return nil, fmt.Errorf("failed to generate KMS data key: %v", err)
  58. }
  59. // Create AES cipher with the plaintext data key
  60. block, err := aes.NewCipher(dataKeyResp.Plaintext)
  61. if err != nil {
  62. // Clear sensitive data before returning error
  63. kms.ClearSensitiveData(dataKeyResp.Plaintext)
  64. return nil, fmt.Errorf("failed to create AES cipher: %v", err)
  65. }
  66. return &KMSDataKeyResult{
  67. Response: dataKeyResp,
  68. Block: block,
  69. }, nil
  70. }
  71. // clearKMSDataKey safely clears sensitive data from a KMSDataKeyResult
  72. func clearKMSDataKey(result *KMSDataKeyResult) {
  73. if result != nil && result.Response != nil {
  74. kms.ClearSensitiveData(result.Response.Plaintext)
  75. }
  76. }
  77. // createSSEKMSKey creates an SSEKMSKey struct from data key result and parameters
  78. func createSSEKMSKey(result *KMSDataKeyResult, encryptionContext map[string]string, bucketKeyEnabled bool, iv []byte, chunkOffset int64) *SSEKMSKey {
  79. return &SSEKMSKey{
  80. KeyID: result.Response.KeyID,
  81. EncryptedDataKey: result.Response.CiphertextBlob,
  82. EncryptionContext: encryptionContext,
  83. BucketKeyEnabled: bucketKeyEnabled,
  84. IV: iv,
  85. ChunkOffset: chunkOffset,
  86. }
  87. }