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.
		
		
		
		
		
			
		
			
				
					
					
						
							316 lines
						
					
					
						
							9.0 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							316 lines
						
					
					
						
							9.0 KiB
						
					
					
				
								package s3api
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"crypto/aes"
							 | 
						|
									"crypto/cipher"
							 | 
						|
									"crypto/rand"
							 | 
						|
									"encoding/base64"
							 | 
						|
									"encoding/json"
							 | 
						|
									"fmt"
							 | 
						|
									"io"
							 | 
						|
									mathrand "math/rand"
							 | 
						|
									"net/http"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// SSE-S3 uses AES-256 encryption with server-managed keys
							 | 
						|
								const (
							 | 
						|
									SSES3Algorithm = s3_constants.SSEAlgorithmAES256
							 | 
						|
									SSES3KeySize   = 32 // 256 bits
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								// SSES3Key represents a server-managed encryption key for SSE-S3
							 | 
						|
								type SSES3Key struct {
							 | 
						|
									Key       []byte
							 | 
						|
									KeyID     string
							 | 
						|
									Algorithm string
							 | 
						|
									IV        []byte // Initialization Vector for this key
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// IsSSES3RequestInternal checks if the request specifies SSE-S3 encryption
							 | 
						|
								func IsSSES3RequestInternal(r *http.Request) bool {
							 | 
						|
									sseHeader := r.Header.Get(s3_constants.AmzServerSideEncryption)
							 | 
						|
									result := sseHeader == SSES3Algorithm
							 | 
						|
								
							 | 
						|
									// Debug: log header detection for SSE-S3 requests
							 | 
						|
									if result {
							 | 
						|
										glog.V(4).Infof("SSE-S3 detection: method=%s, header=%q, expected=%q, result=%t, copySource=%q", r.Method, sseHeader, SSES3Algorithm, result, r.Header.Get("X-Amz-Copy-Source"))
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return result
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// IsSSES3EncryptedInternal checks if the object metadata indicates SSE-S3 encryption
							 | 
						|
								func IsSSES3EncryptedInternal(metadata map[string][]byte) bool {
							 | 
						|
									if sseAlgorithm, exists := metadata[s3_constants.AmzServerSideEncryption]; exists {
							 | 
						|
										return string(sseAlgorithm) == SSES3Algorithm
							 | 
						|
									}
							 | 
						|
									return false
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GenerateSSES3Key generates a new SSE-S3 encryption key
							 | 
						|
								func GenerateSSES3Key() (*SSES3Key, error) {
							 | 
						|
									key := make([]byte, SSES3KeySize)
							 | 
						|
									if _, err := io.ReadFull(rand.Reader, key); err != nil {
							 | 
						|
										return nil, fmt.Errorf("failed to generate SSE-S3 key: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Generate a key ID for tracking
							 | 
						|
									keyID := fmt.Sprintf("sse-s3-key-%d", mathrand.Int63())
							 | 
						|
								
							 | 
						|
									return &SSES3Key{
							 | 
						|
										Key:       key,
							 | 
						|
										KeyID:     keyID,
							 | 
						|
										Algorithm: SSES3Algorithm,
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// CreateSSES3EncryptedReader creates an encrypted reader for SSE-S3
							 | 
						|
								// Returns the encrypted reader and the IV for metadata storage
							 | 
						|
								func CreateSSES3EncryptedReader(reader io.Reader, key *SSES3Key) (io.Reader, []byte, error) {
							 | 
						|
									// Create AES cipher
							 | 
						|
									block, err := aes.NewCipher(key.Key)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, nil, fmt.Errorf("create AES cipher: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Generate random IV
							 | 
						|
									iv := make([]byte, aes.BlockSize)
							 | 
						|
									if _, err := io.ReadFull(rand.Reader, iv); err != nil {
							 | 
						|
										return nil, nil, fmt.Errorf("generate IV: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Create CTR mode cipher
							 | 
						|
									stream := cipher.NewCTR(block, iv)
							 | 
						|
								
							 | 
						|
									// Return encrypted reader and IV separately for metadata storage
							 | 
						|
									encryptedReader := &cipher.StreamReader{S: stream, R: reader}
							 | 
						|
								
							 | 
						|
									return encryptedReader, iv, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// CreateSSES3DecryptedReader creates a decrypted reader for SSE-S3 using IV from metadata
							 | 
						|
								func CreateSSES3DecryptedReader(reader io.Reader, key *SSES3Key, iv []byte) (io.Reader, error) {
							 | 
						|
									// Create AES cipher
							 | 
						|
									block, err := aes.NewCipher(key.Key)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("create AES cipher: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Create CTR mode cipher with the provided IV
							 | 
						|
									stream := cipher.NewCTR(block, iv)
							 | 
						|
								
							 | 
						|
									return &cipher.StreamReader{S: stream, R: reader}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetSSES3Headers returns the headers for SSE-S3 encrypted objects
							 | 
						|
								func GetSSES3Headers() map[string]string {
							 | 
						|
									return map[string]string{
							 | 
						|
										s3_constants.AmzServerSideEncryption: SSES3Algorithm,
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// SerializeSSES3Metadata serializes SSE-S3 metadata for storage
							 | 
						|
								func SerializeSSES3Metadata(key *SSES3Key) ([]byte, error) {
							 | 
						|
									if err := ValidateSSES3Key(key); err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// For SSE-S3, we typically don't store the actual key in metadata
							 | 
						|
									// Instead, we store a key ID or reference that can be used to retrieve the key
							 | 
						|
									// from a secure key management system
							 | 
						|
								
							 | 
						|
									metadata := map[string]string{
							 | 
						|
										"algorithm": key.Algorithm,
							 | 
						|
										"keyId":     key.KeyID,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Include IV if present (needed for chunk-level decryption)
							 | 
						|
									if key.IV != nil {
							 | 
						|
										metadata["iv"] = base64.StdEncoding.EncodeToString(key.IV)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Use JSON for proper serialization
							 | 
						|
									data, err := json.Marshal(metadata)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("marshal SSE-S3 metadata: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return data, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// DeserializeSSES3Metadata deserializes SSE-S3 metadata from storage and retrieves the actual key
							 | 
						|
								func DeserializeSSES3Metadata(data []byte, keyManager *SSES3KeyManager) (*SSES3Key, error) {
							 | 
						|
									if len(data) == 0 {
							 | 
						|
										return nil, fmt.Errorf("empty SSE-S3 metadata")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Parse the JSON metadata to extract keyId
							 | 
						|
									var metadata map[string]string
							 | 
						|
									if err := json.Unmarshal(data, &metadata); err != nil {
							 | 
						|
										return nil, fmt.Errorf("failed to parse SSE-S3 metadata: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									keyID, exists := metadata["keyId"]
							 | 
						|
									if !exists {
							 | 
						|
										return nil, fmt.Errorf("keyId not found in SSE-S3 metadata")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									algorithm, exists := metadata["algorithm"]
							 | 
						|
									if !exists {
							 | 
						|
										algorithm = s3_constants.SSEAlgorithmAES256 // Default algorithm
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Retrieve the actual key using the keyId
							 | 
						|
									if keyManager == nil {
							 | 
						|
										return nil, fmt.Errorf("key manager is required for SSE-S3 key retrieval")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									key, err := keyManager.GetOrCreateKey(keyID)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("failed to retrieve SSE-S3 key with ID %s: %w", keyID, err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Verify the algorithm matches
							 | 
						|
									if key.Algorithm != algorithm {
							 | 
						|
										return nil, fmt.Errorf("algorithm mismatch: expected %s, got %s", algorithm, key.Algorithm)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Restore IV if present in metadata (for chunk-level decryption)
							 | 
						|
									if ivStr, exists := metadata["iv"]; exists {
							 | 
						|
										iv, err := base64.StdEncoding.DecodeString(ivStr)
							 | 
						|
										if err != nil {
							 | 
						|
											return nil, fmt.Errorf("failed to decode IV: %w", err)
							 | 
						|
										}
							 | 
						|
										key.IV = iv
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return key, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// SSES3KeyManager manages SSE-S3 encryption keys
							 | 
						|
								type SSES3KeyManager struct {
							 | 
						|
									// In a production system, this would interface with a secure key management system
							 | 
						|
									keys map[string]*SSES3Key
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// NewSSES3KeyManager creates a new SSE-S3 key manager
							 | 
						|
								func NewSSES3KeyManager() *SSES3KeyManager {
							 | 
						|
									return &SSES3KeyManager{
							 | 
						|
										keys: make(map[string]*SSES3Key),
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetOrCreateKey gets an existing key or creates a new one
							 | 
						|
								func (km *SSES3KeyManager) GetOrCreateKey(keyID string) (*SSES3Key, error) {
							 | 
						|
									if keyID == "" {
							 | 
						|
										// Generate new key
							 | 
						|
										return GenerateSSES3Key()
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Check if key exists
							 | 
						|
									if key, exists := km.keys[keyID]; exists {
							 | 
						|
										return key, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Create new key
							 | 
						|
									key, err := GenerateSSES3Key()
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									key.KeyID = keyID
							 | 
						|
									km.keys[keyID] = key
							 | 
						|
								
							 | 
						|
									return key, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// StoreKey stores a key in the manager
							 | 
						|
								func (km *SSES3KeyManager) StoreKey(key *SSES3Key) {
							 | 
						|
									km.keys[key.KeyID] = key
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetKey retrieves a key by ID
							 | 
						|
								func (km *SSES3KeyManager) GetKey(keyID string) (*SSES3Key, bool) {
							 | 
						|
									key, exists := km.keys[keyID]
							 | 
						|
									return key, exists
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Global SSE-S3 key manager instance
							 | 
						|
								var globalSSES3KeyManager = NewSSES3KeyManager()
							 | 
						|
								
							 | 
						|
								// GetSSES3KeyManager returns the global SSE-S3 key manager
							 | 
						|
								func GetSSES3KeyManager() *SSES3KeyManager {
							 | 
						|
									return globalSSES3KeyManager
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// ProcessSSES3Request processes an SSE-S3 request and returns encryption metadata
							 | 
						|
								func ProcessSSES3Request(r *http.Request) (map[string][]byte, error) {
							 | 
						|
									if !IsSSES3RequestInternal(r) {
							 | 
						|
										return nil, nil
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Generate or retrieve encryption key
							 | 
						|
									keyManager := GetSSES3KeyManager()
							 | 
						|
									key, err := keyManager.GetOrCreateKey("")
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("get SSE-S3 key: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Serialize key metadata
							 | 
						|
									keyData, err := SerializeSSES3Metadata(key)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, fmt.Errorf("serialize SSE-S3 metadata: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Store key in manager
							 | 
						|
									keyManager.StoreKey(key)
							 | 
						|
								
							 | 
						|
									// Return metadata
							 | 
						|
									metadata := map[string][]byte{
							 | 
						|
										s3_constants.AmzServerSideEncryption: []byte(SSES3Algorithm),
							 | 
						|
										s3_constants.SeaweedFSSSES3Key:       keyData,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return metadata, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetSSES3KeyFromMetadata extracts SSE-S3 key from object metadata
							 | 
						|
								func GetSSES3KeyFromMetadata(metadata map[string][]byte, keyManager *SSES3KeyManager) (*SSES3Key, error) {
							 | 
						|
									keyData, exists := metadata[s3_constants.SeaweedFSSSES3Key]
							 | 
						|
									if !exists {
							 | 
						|
										return nil, fmt.Errorf("SSE-S3 key not found in metadata")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return DeserializeSSES3Metadata(keyData, keyManager)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// CreateSSES3EncryptedReaderWithBaseIV creates an encrypted reader using a base IV for multipart upload consistency.
							 | 
						|
								// The returned IV is the offset-derived IV, calculated from the input baseIV and offset.
							 | 
						|
								func CreateSSES3EncryptedReaderWithBaseIV(reader io.Reader, key *SSES3Key, baseIV []byte, offset int64) (io.Reader, []byte /* derivedIV */, error) {
							 | 
						|
									// Validate key to prevent panics and security issues
							 | 
						|
									if key == nil {
							 | 
						|
										return nil, nil, fmt.Errorf("SSES3Key is nil")
							 | 
						|
									}
							 | 
						|
									if key.Key == nil || len(key.Key) != SSES3KeySize {
							 | 
						|
										return nil, nil, fmt.Errorf("invalid SSES3Key: must be %d bytes, got %d", SSES3KeySize, len(key.Key))
							 | 
						|
									}
							 | 
						|
									if err := ValidateSSES3Key(key); err != nil {
							 | 
						|
										return nil, nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									block, err := aes.NewCipher(key.Key)
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, nil, fmt.Errorf("create AES cipher: %w", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Calculate the proper IV with offset to ensure unique IV per chunk/part
							 | 
						|
									// This prevents the severe security vulnerability of IV reuse in CTR mode
							 | 
						|
									iv := calculateIVWithOffset(baseIV, offset)
							 | 
						|
								
							 | 
						|
									stream := cipher.NewCTR(block, iv)
							 | 
						|
									encryptedReader := &cipher.StreamReader{S: stream, R: reader}
							 | 
						|
									return encryptedReader, iv, nil
							 | 
						|
								}
							 |