diff options
Diffstat (limited to 'weed/s3api/s3_sse_s3.go')
| -rw-r--r-- | weed/s3api/s3_sse_s3.go | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/weed/s3api/s3_sse_s3.go b/weed/s3api/s3_sse_s3.go new file mode 100644 index 000000000..fc95b73bd --- /dev/null +++ b/weed/s3api/s3_sse_s3.go @@ -0,0 +1,258 @@ +package s3api + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/json" + "fmt" + "io" + mathrand "math/rand" + "net/http" + + "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" +) + +// SSE-S3 uses AES-256 encryption with server-managed keys +const ( + SSES3Algorithm = "AES256" + SSES3KeySize = 32 // 256 bits +) + +// SSES3Key represents a server-managed encryption key for SSE-S3 +type SSES3Key struct { + Key []byte + KeyID string + Algorithm string +} + +// IsSSES3RequestInternal checks if the request specifies SSE-S3 encryption +func IsSSES3RequestInternal(r *http.Request) bool { + return r.Header.Get(s3_constants.AmzServerSideEncryption) == SSES3Algorithm +} + +// 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) { + // 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, + } + + // In a production system, this would be more sophisticated + // For now, we'll use a simple JSON-like format + serialized := fmt.Sprintf(`{"algorithm":"%s","keyId":"%s"}`, + metadata["algorithm"], metadata["keyId"]) + + return []byte(serialized), 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 = "AES256" // 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) + } + + 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), + "sse-s3-key": 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["sse-s3-key"] + if !exists { + return nil, fmt.Errorf("SSE-S3 key not found in metadata") + } + + return DeserializeSSES3Metadata(keyData, keyManager) +} |
