storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/crypto/sse-s3.go (about) 1 /* 2 * Minio Cloud Storage, (C) 2019-2020 Minio, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package crypto 18 19 import ( 20 "context" 21 "encoding/base64" 22 "errors" 23 "net/http" 24 "path" 25 "strings" 26 27 xhttp "storj.io/minio/cmd/http" 28 "storj.io/minio/cmd/logger" 29 ) 30 31 type sses3 struct{} 32 33 var ( 34 // S3 represents AWS SSE-S3. It provides functionality to handle 35 // SSE-S3 requests. 36 S3 = sses3{} 37 38 _ Type = S3 39 ) 40 41 // String returns the SSE domain as string. For SSE-S3 the 42 // domain is "SSE-S3". 43 func (sses3) String() string { return "SSE-S3" } 44 45 func (sses3) IsRequested(h http.Header) bool { 46 _, ok := h[xhttp.AmzServerSideEncryption] 47 return ok && strings.ToLower(h.Get(xhttp.AmzServerSideEncryption)) != xhttp.AmzEncryptionKMS // Return only true if the SSE header is specified and does not contain the SSE-KMS value 48 } 49 50 // ParseHTTP parses the SSE-S3 related HTTP headers and checks 51 // whether they contain valid values. 52 func (sses3) ParseHTTP(h http.Header) error { 53 if h.Get(xhttp.AmzServerSideEncryption) != xhttp.AmzEncryptionAES { 54 return ErrInvalidEncryptionMethod 55 } 56 return nil 57 } 58 59 // IsEncrypted returns true if the object metadata indicates 60 // that the object was uploaded using SSE-S3. 61 func (sses3) IsEncrypted(metadata map[string]string) bool { 62 if _, ok := metadata[MetaSealedKeyS3]; ok { 63 return true 64 } 65 return false 66 } 67 68 // UnsealObjectKey extracts and decrypts the sealed object key 69 // from the metadata using KMS and returns the decrypted object 70 // key. 71 func (s3 sses3) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) { 72 keyID, kmsKey, sealedKey, err := s3.ParseMetadata(metadata) 73 if err != nil { 74 return key, err 75 } 76 unsealKey, err := kms.DecryptKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)}) 77 if err != nil { 78 return key, err 79 } 80 err = key.Unseal(unsealKey[:], sealedKey, s3.String(), bucket, object) 81 return key, err 82 } 83 84 // CreateMetadata encodes the sealed object key into the metadata and returns 85 // the modified metadata. If the keyID and the kmsKey is not empty it encodes 86 // both into the metadata as well. It allocates a new metadata map if metadata 87 // is nil. 88 func (sses3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string { 89 if sealedKey.Algorithm != SealAlgorithm { 90 logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) 91 } 92 93 // There are two possibilites: 94 // - We use a KMS -> There must be non-empty key ID and a KMS data key. 95 // - We use a K/V -> There must be no key ID and no KMS data key. 96 // Otherwise, the caller has passed an invalid argument combination. 97 if keyID == "" && len(kmsKey) != 0 { 98 logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present")) 99 } 100 if keyID != "" && len(kmsKey) == 0 { 101 logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present")) 102 } 103 104 if metadata == nil { 105 metadata = make(map[string]string, 5) 106 } 107 108 metadata[MetaAlgorithm] = sealedKey.Algorithm 109 metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) 110 metadata[MetaSealedKeyS3] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) 111 if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key. 112 metadata[MetaKeyID] = keyID 113 metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey) 114 } 115 return metadata 116 } 117 118 // ParseMetadata extracts all SSE-S3 related values from the object metadata 119 // and checks whether they are well-formed. It returns the sealed object key 120 // on success. If the metadata contains both, a KMS master key ID and a sealed 121 // KMS data key it returns both. If the metadata does not contain neither a 122 // KMS master key ID nor a sealed KMS data key it returns an empty keyID and 123 // KMS data key. Otherwise, it returns an error. 124 func (sses3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) { 125 // Extract all required values from object metadata 126 b64IV, ok := metadata[MetaIV] 127 if !ok { 128 return keyID, kmsKey, sealedKey, errMissingInternalIV 129 } 130 algorithm, ok := metadata[MetaAlgorithm] 131 if !ok { 132 return keyID, kmsKey, sealedKey, errMissingInternalSealAlgorithm 133 } 134 b64SealedKey, ok := metadata[MetaSealedKeyS3] 135 if !ok { 136 return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3") 137 } 138 139 // There are two possibilites: 140 // - We use a KMS -> There must be a key ID and a KMS data key. 141 // - We use a K/V -> There must be no key ID and no KMS data key. 142 // Otherwise, the metadata is corrupted. 143 keyID, idPresent := metadata[MetaKeyID] 144 b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey] 145 if !idPresent && kmsKeyPresent { 146 return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3") 147 } 148 if idPresent && !kmsKeyPresent { 149 return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3") 150 } 151 152 // Check whether all extracted values are well-formed 153 iv, err := base64.StdEncoding.DecodeString(b64IV) 154 if err != nil || len(iv) != 32 { 155 return keyID, kmsKey, sealedKey, errInvalidInternalIV 156 } 157 if algorithm != SealAlgorithm { 158 return keyID, kmsKey, sealedKey, errInvalidInternalSealAlgorithm 159 } 160 encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) 161 if err != nil || len(encryptedKey) != 64 { 162 return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid") 163 } 164 if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key. 165 kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey) 166 if err != nil { 167 return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid") 168 } 169 } 170 171 sealedKey.Algorithm = algorithm 172 copy(sealedKey.IV[:], iv) 173 copy(sealedKey.Key[:], encryptedKey) 174 return keyID, kmsKey, sealedKey, nil 175 }