github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/bucket/encryption/bucket-sse-config.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package sse 19 20 import ( 21 "encoding/xml" 22 "errors" 23 "io" 24 "net/http" 25 "strings" 26 27 "github.com/minio/minio/internal/crypto" 28 xhttp "github.com/minio/minio/internal/http" 29 ) 30 31 const ( 32 // AES256 is used with SSE-S3 33 AES256 Algorithm = "AES256" 34 // AWSKms is used with SSE-KMS 35 AWSKms Algorithm = "aws:kms" 36 ) 37 38 // Algorithm - represents valid SSE algorithms supported; currently only AES256 is supported 39 type Algorithm string 40 41 // UnmarshalXML - Unmarshals XML tag to valid SSE algorithm 42 func (alg *Algorithm) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 43 var s string 44 if err := d.DecodeElement(&s, &start); err != nil { 45 return err 46 } 47 48 switch s { 49 case string(AES256): 50 *alg = AES256 51 case string(AWSKms): 52 *alg = AWSKms 53 default: 54 return errors.New("Unknown SSE algorithm") 55 } 56 57 return nil 58 } 59 60 // MarshalXML - Marshals given SSE algorithm to valid XML 61 func (alg *Algorithm) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 62 return e.EncodeElement(string(*alg), start) 63 } 64 65 // EncryptionAction - for ApplyServerSideEncryptionByDefault XML tag 66 type EncryptionAction struct { 67 Algorithm Algorithm `xml:"SSEAlgorithm,omitempty"` 68 MasterKeyID string `xml:"KMSMasterKeyID,omitempty"` 69 } 70 71 // Rule - for ServerSideEncryptionConfiguration XML tag 72 type Rule struct { 73 DefaultEncryptionAction EncryptionAction `xml:"ApplyServerSideEncryptionByDefault"` 74 } 75 76 const xmlNS = "http://s3.amazonaws.com/doc/2006-03-01/" 77 78 // BucketSSEConfig - represents default bucket encryption configuration 79 type BucketSSEConfig struct { 80 XMLNS string `xml:"xmlns,attr,omitempty"` 81 XMLName xml.Name `xml:"ServerSideEncryptionConfiguration"` 82 Rules []Rule `xml:"Rule"` 83 } 84 85 // ParseBucketSSEConfig - Decodes given XML to a valid default bucket encryption config 86 func ParseBucketSSEConfig(r io.Reader) (*BucketSSEConfig, error) { 87 var config BucketSSEConfig 88 err := xml.NewDecoder(r).Decode(&config) 89 if err != nil { 90 return nil, err 91 } 92 93 // Validates server-side encryption config rules 94 // Only one rule is allowed on AWS S3 95 if len(config.Rules) != 1 { 96 return nil, errors.New("only one server-side encryption rule is allowed at a time") 97 } 98 99 for _, rule := range config.Rules { 100 switch rule.DefaultEncryptionAction.Algorithm { 101 case AES256: 102 if rule.DefaultEncryptionAction.MasterKeyID != "" { 103 return nil, errors.New("MasterKeyID is allowed with aws:kms only") 104 } 105 case AWSKms: 106 keyID := rule.DefaultEncryptionAction.MasterKeyID 107 if keyID == "" { 108 return nil, errors.New("MasterKeyID is missing with aws:kms") 109 } 110 spaces := strings.HasPrefix(keyID, " ") || strings.HasSuffix(keyID, " ") 111 if spaces { 112 return nil, errors.New("MasterKeyID contains unsupported characters") 113 } 114 } 115 } 116 117 if config.XMLNS == "" { 118 config.XMLNS = xmlNS 119 } 120 return &config, nil 121 } 122 123 // ApplyOptions ask for specific features to be enabled, 124 // when bucketSSEConfig is empty. 125 type ApplyOptions struct { 126 AutoEncrypt bool 127 } 128 129 // Apply applies the SSE bucket configuration on the given HTTP headers and 130 // sets the specified SSE headers. 131 // 132 // Apply does not overwrite any existing SSE headers. Further, it will 133 // set minimal SSE-KMS headers if autoEncrypt is true and the BucketSSEConfig 134 // is nil. 135 func (b *BucketSSEConfig) Apply(headers http.Header, opts ApplyOptions) { 136 if crypto.Requested(headers) { 137 return 138 } 139 if b == nil { 140 if opts.AutoEncrypt { 141 headers.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) 142 } 143 return 144 } 145 146 switch b.Algo() { 147 case xhttp.AmzEncryptionAES: 148 headers.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) 149 case xhttp.AmzEncryptionKMS: 150 headers.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionKMS) 151 headers.Set(xhttp.AmzServerSideEncryptionKmsID, b.KeyID()) 152 } 153 } 154 155 // Algo returns the SSE algorithm specified by the SSE configuration. 156 func (b *BucketSSEConfig) Algo() Algorithm { 157 for _, rule := range b.Rules { 158 return rule.DefaultEncryptionAction.Algorithm 159 } 160 return "" 161 } 162 163 // KeyID returns the KMS key ID specified by the SSE configuration. 164 // If the SSE configuration does not specify SSE-KMS it returns an 165 // empty key ID. 166 func (b *BucketSSEConfig) KeyID() string { 167 for _, rule := range b.Rules { 168 return strings.TrimPrefix(rule.DefaultEncryptionAction.MasterKeyID, crypto.ARNPrefix) 169 } 170 return "" 171 }