storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/crypto/sse-kms.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 jsoniter "github.com/json-iterator/go" 28 29 xhttp "storj.io/minio/cmd/http" 30 "storj.io/minio/cmd/logger" 31 ) 32 33 type ssekms struct{} 34 35 var ( 36 // S3KMS represents AWS SSE-KMS. It provides functionality to 37 // handle SSE-KMS requests. 38 S3KMS = ssekms{} 39 40 _ Type = S3KMS 41 ) 42 43 // String returns the SSE domain as string. For SSE-KMS the 44 // domain is "SSE-KMS". 45 func (ssekms) String() string { return "SSE-KMS" } 46 47 // IsRequested returns true if the HTTP headers contains 48 // at least one SSE-KMS header. 49 func (ssekms) IsRequested(h http.Header) bool { 50 if _, ok := h[xhttp.AmzServerSideEncryptionKmsID]; ok { 51 return true 52 } 53 if _, ok := h[xhttp.AmzServerSideEncryptionKmsContext]; ok { 54 return true 55 } 56 if _, ok := h[xhttp.AmzServerSideEncryption]; ok { 57 return strings.ToUpper(h.Get(xhttp.AmzServerSideEncryption)) != xhttp.AmzEncryptionAES // Return only true if the SSE header is specified and does not contain the SSE-S3 value 58 } 59 return false 60 } 61 62 // ParseHTTP parses the SSE-KMS headers and returns the SSE-KMS key ID 63 // and the KMS context on success. 64 func (ssekms) ParseHTTP(h http.Header) (string, Context, error) { 65 algorithm := h.Get(xhttp.AmzServerSideEncryption) 66 if algorithm != xhttp.AmzEncryptionKMS { 67 return "", nil, ErrInvalidEncryptionMethod 68 } 69 70 var ctx Context 71 if context, ok := h[xhttp.AmzServerSideEncryptionKmsContext]; ok { 72 var json = jsoniter.ConfigCompatibleWithStandardLibrary 73 if err := json.Unmarshal([]byte(context[0]), &ctx); err != nil { 74 return "", nil, err 75 } 76 } 77 return h.Get(xhttp.AmzServerSideEncryptionKmsID), ctx, nil 78 } 79 80 // IsEncrypted returns true if the object metadata indicates 81 // that the object was uploaded using SSE-KMS. 82 func (ssekms) IsEncrypted(metadata map[string]string) bool { 83 if _, ok := metadata[MetaSealedKeyKMS]; ok { 84 return true 85 } 86 return false 87 } 88 89 // UnsealObjectKey extracts and decrypts the sealed object key 90 // from the metadata using KMS and returns the decrypted object 91 // key. 92 func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) { 93 keyID, kmsKey, sealedKey, ctx, err := s3.ParseMetadata(metadata) 94 if err != nil { 95 return key, err 96 } 97 if _, ok := ctx[bucket]; !ok { 98 ctx[bucket] = path.Join(bucket, object) 99 } 100 unsealKey, err := kms.DecryptKey(keyID, kmsKey, ctx) 101 if err != nil { 102 return key, err 103 } 104 err = key.Unseal(unsealKey[:], sealedKey, s3.String(), bucket, object) 105 return key, err 106 } 107 108 // CreateMetadata encodes the sealed object key into the metadata and returns 109 // the modified metadata. If the keyID and the kmsKey is not empty it encodes 110 // both into the metadata as well. It allocates a new metadata map if metadata 111 // is nil. 112 func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string { 113 if sealedKey.Algorithm != SealAlgorithm { 114 logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) 115 } 116 117 // There are two possibilites: 118 // - We use a KMS -> There must be non-empty key ID and a KMS data key. 119 // - We use a K/V -> There must be no key ID and no KMS data key. 120 // Otherwise, the caller has passed an invalid argument combination. 121 if keyID == "" && len(kmsKey) != 0 { 122 logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present")) 123 } 124 if keyID != "" && len(kmsKey) == 0 { 125 logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present")) 126 } 127 128 if metadata == nil { 129 metadata = make(map[string]string, 5) 130 } 131 132 metadata[MetaAlgorithm] = sealedKey.Algorithm 133 metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) 134 metadata[MetaSealedKeyKMS] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) 135 if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key. 136 metadata[MetaKeyID] = keyID 137 metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey) 138 } 139 return metadata 140 } 141 142 // ParseMetadata extracts all SSE-KMS related values from the object metadata 143 // and checks whether they are well-formed. It returns the sealed object key 144 // on success. If the metadata contains both, a KMS master key ID and a sealed 145 // KMS data key it returns both. If the metadata does not contain neither a 146 // KMS master key ID nor a sealed KMS data key it returns an empty keyID and 147 // KMS data key. Otherwise, it returns an error. 148 func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, ctx Context, err error) { 149 // Extract all required values from object metadata 150 b64IV, ok := metadata[MetaIV] 151 if !ok { 152 return keyID, kmsKey, sealedKey, ctx, errMissingInternalIV 153 } 154 algorithm, ok := metadata[MetaAlgorithm] 155 if !ok { 156 return keyID, kmsKey, sealedKey, ctx, errMissingInternalSealAlgorithm 157 } 158 b64SealedKey, ok := metadata[MetaSealedKeyKMS] 159 if !ok { 160 return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal sealed key for SSE-S3") 161 } 162 163 // There are two possibilites: 164 // - We use a KMS -> There must be a key ID and a KMS data key. 165 // - We use a K/V -> There must be no key ID and no KMS data key. 166 // Otherwise, the metadata is corrupted. 167 keyID, idPresent := metadata[MetaKeyID] 168 b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey] 169 if !idPresent && kmsKeyPresent { 170 return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3") 171 } 172 if idPresent && !kmsKeyPresent { 173 return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3") 174 } 175 176 // Check whether all extracted values are well-formed 177 iv, err := base64.StdEncoding.DecodeString(b64IV) 178 if err != nil || len(iv) != 32 { 179 return keyID, kmsKey, sealedKey, ctx, errInvalidInternalIV 180 } 181 if algorithm != SealAlgorithm { 182 return keyID, kmsKey, sealedKey, ctx, errInvalidInternalSealAlgorithm 183 } 184 encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) 185 if err != nil || len(encryptedKey) != 64 { 186 return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed key for SSE-KMS is invalid") 187 } 188 if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key. 189 kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey) 190 if err != nil { 191 return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed KMS data key for SSE-KMS is invalid") 192 } 193 } 194 b64Ctx, ok := metadata[MetaContext] 195 if ok { 196 b, err := base64.StdEncoding.DecodeString(b64Ctx) 197 if err != nil { 198 return keyID, kmsKey, sealedKey, ctx, Errorf("The internal KMS context is not base64-encoded") 199 } 200 var json = jsoniter.ConfigCompatibleWithStandardLibrary 201 if err = json.Unmarshal(b, ctx); err != nil { 202 return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed KMS context is invalid") 203 } 204 } 205 206 sealedKey.Algorithm = algorithm 207 copy(sealedKey.IV[:], iv) 208 copy(sealedKey.Key[:], encryptedKey) 209 return keyID, kmsKey, sealedKey, ctx, nil 210 }