github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/crypto/sse-c.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 crypto 19 20 import ( 21 "bytes" 22 "context" 23 "crypto/md5" 24 "encoding/base64" 25 "net/http" 26 27 xhttp "github.com/minio/minio/internal/http" 28 "github.com/minio/minio/internal/logger" 29 ) 30 31 type ssec struct{} 32 33 var ( 34 // SSEC represents AWS SSE-C. It provides functionality to handle 35 // SSE-C requests. 36 SSEC = ssec{} 37 38 _ Type = SSEC 39 ) 40 41 // String returns the SSE domain as string. For SSE-C the 42 // domain is "SSE-C". 43 func (ssec) String() string { return "SSE-C" } 44 45 // IsRequested returns true if the HTTP headers contains 46 // at least one SSE-C header. SSE-C copy headers are ignored. 47 func (ssec) IsRequested(h http.Header) bool { 48 if _, ok := h[xhttp.AmzServerSideEncryptionCustomerAlgorithm]; ok { 49 return true 50 } 51 if _, ok := h[xhttp.AmzServerSideEncryptionCustomerKey]; ok { 52 return true 53 } 54 if _, ok := h[xhttp.AmzServerSideEncryptionCustomerKeyMD5]; ok { 55 return true 56 } 57 return false 58 } 59 60 // IsEncrypted returns true if the metadata contains an SSE-C 61 // entry indicating that the object has been encrypted using 62 // SSE-C. 63 func (ssec) IsEncrypted(metadata map[string]string) bool { 64 if _, ok := metadata[MetaSealedKeySSEC]; ok { 65 return true 66 } 67 return false 68 } 69 70 // ParseHTTP parses the SSE-C headers and returns the SSE-C client key 71 // on success. SSE-C copy headers are ignored. 72 func (ssec) ParseHTTP(h http.Header) (key [32]byte, err error) { 73 if h.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm) != xhttp.AmzEncryptionAES { 74 return key, ErrInvalidCustomerAlgorithm 75 } 76 if h.Get(xhttp.AmzServerSideEncryptionCustomerKey) == "" { 77 return key, ErrMissingCustomerKey 78 } 79 if h.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5) == "" { 80 return key, ErrMissingCustomerKeyMD5 81 } 82 83 clientKey, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCustomerKey)) 84 if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long 85 return key, ErrInvalidCustomerKey 86 } 87 keyMD5, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5)) 88 if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) { 89 return key, ErrCustomerKeyMD5Mismatch 90 } 91 copy(key[:], clientKey) 92 return key, nil 93 } 94 95 // UnsealObjectKey extracts and decrypts the sealed object key 96 // from the metadata using the SSE-C client key of the HTTP headers 97 // and returns the decrypted object key. 98 func (s3 ssec) UnsealObjectKey(h http.Header, metadata map[string]string, bucket, object string) (key ObjectKey, err error) { 99 clientKey, err := s3.ParseHTTP(h) 100 if err != nil { 101 return 102 } 103 return unsealObjectKey(clientKey[:], metadata, bucket, object) 104 } 105 106 // CreateMetadata encodes the sealed key into the metadata 107 // and returns the modified metadata. It allocates a new 108 // metadata map if metadata is nil. 109 func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string { 110 if sealedKey.Algorithm != SealAlgorithm { 111 logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm)) 112 } 113 114 if metadata == nil { 115 metadata = make(map[string]string, 3) 116 } 117 metadata[MetaAlgorithm] = SealAlgorithm 118 metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) 119 metadata[MetaSealedKeySSEC] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) 120 return metadata 121 } 122 123 // ParseMetadata extracts all SSE-C related values from the object metadata 124 // and checks whether they are well-formed. It returns the sealed object key 125 // on success. 126 func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err error) { 127 // Extract all required values from object metadata 128 b64IV, ok := metadata[MetaIV] 129 if !ok { 130 return sealedKey, errMissingInternalIV 131 } 132 algorithm, ok := metadata[MetaAlgorithm] 133 if !ok { 134 return sealedKey, errMissingInternalSealAlgorithm 135 } 136 b64SealedKey, ok := metadata[MetaSealedKeySSEC] 137 if !ok { 138 return sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-C") 139 } 140 141 // Check whether all extracted values are well-formed 142 iv, err := base64.StdEncoding.DecodeString(b64IV) 143 if err != nil || len(iv) != 32 { 144 return sealedKey, errInvalidInternalIV 145 } 146 if algorithm != SealAlgorithm && algorithm != InsecureSealAlgorithm { 147 return sealedKey, errInvalidInternalSealAlgorithm 148 } 149 encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) 150 if err != nil || len(encryptedKey) != 64 { 151 return sealedKey, Errorf("The internal sealed key for SSE-C is invalid") 152 } 153 154 sealedKey.Algorithm = algorithm 155 copy(sealedKey.IV[:], iv) 156 copy(sealedKey.Key[:], encryptedKey) 157 return sealedKey, nil 158 }