github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/crypto/sse.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 "context" 22 "errors" 23 "fmt" 24 "io" 25 "net/http" 26 27 "github.com/minio/minio/internal/fips" 28 "github.com/minio/minio/internal/ioutil" 29 "github.com/minio/minio/internal/logger" 30 "github.com/minio/sio" 31 ) 32 33 const ( 34 // SealAlgorithm is the encryption/sealing algorithm used to derive & seal 35 // the key-encryption-key and to en/decrypt the object data. 36 SealAlgorithm = "DAREv2-HMAC-SHA256" 37 38 // InsecureSealAlgorithm is the legacy encryption/sealing algorithm used 39 // to derive & seal the key-encryption-key and to en/decrypt the object data. 40 // This algorithm should not be used for new objects because its key derivation 41 // is not optimal. See: https://github.com/minio/minio/pull/6121 42 InsecureSealAlgorithm = "DARE-SHA256" 43 ) 44 45 // Type represents an AWS SSE type: 46 // - SSE-C 47 // - SSE-S3 48 // - SSE-KMS 49 type Type interface { 50 fmt.Stringer 51 52 IsRequested(http.Header) bool 53 IsEncrypted(map[string]string) bool 54 } 55 56 // IsRequested returns true and the SSE Type if the HTTP headers 57 // indicate that some form server-side encryption is requested. 58 // 59 // If no SSE headers are present then IsRequested returns false 60 // and no Type. 61 func IsRequested(h http.Header) (Type, bool) { 62 switch { 63 case S3.IsRequested(h): 64 return S3, true 65 case S3KMS.IsRequested(h): 66 return S3KMS, true 67 case SSEC.IsRequested(h): 68 return SSEC, true 69 default: 70 return nil, false 71 } 72 } 73 74 // Requested returns whether any type of encryption is requested. 75 func Requested(h http.Header) bool { 76 return S3.IsRequested(h) || S3KMS.IsRequested(h) || SSEC.IsRequested(h) 77 } 78 79 // UnsealObjectKey extracts and decrypts the sealed object key 80 // from the metadata using the SSE-Copy client key of the HTTP headers 81 // and returns the decrypted object key. 82 func (sse ssecCopy) UnsealObjectKey(h http.Header, metadata map[string]string, bucket, object string) (key ObjectKey, err error) { 83 clientKey, err := sse.ParseHTTP(h) 84 if err != nil { 85 return 86 } 87 return unsealObjectKey(clientKey[:], metadata, bucket, object) 88 } 89 90 // unsealObjectKey decrypts and returns the sealed object key 91 // from the metadata using the SSE-C client key. 92 func unsealObjectKey(clientKey []byte, metadata map[string]string, bucket, object string) (key ObjectKey, err error) { 93 sealedKey, err := SSEC.ParseMetadata(metadata) 94 if err != nil { 95 return 96 } 97 err = key.Unseal(clientKey, sealedKey, SSEC.String(), bucket, object) 98 return 99 } 100 101 // EncryptSinglePart encrypts an io.Reader which must be the 102 // body of a single-part PUT request. 103 func EncryptSinglePart(r io.Reader, key ObjectKey) io.Reader { 104 r, err := sio.EncryptReader(r, sio.Config{MinVersion: sio.Version20, Key: key[:], CipherSuites: fips.DARECiphers()}) 105 if err != nil { 106 logger.CriticalIf(context.Background(), errors.New("Unable to encrypt io.Reader using object key")) 107 } 108 return r 109 } 110 111 // EncryptMultiPart encrypts an io.Reader which must be the body of 112 // multi-part PUT request. It derives an unique encryption key from 113 // the partID and the object key. 114 func EncryptMultiPart(r io.Reader, partID int, key ObjectKey) io.Reader { 115 partKey := key.DerivePartKey(uint32(partID)) 116 return EncryptSinglePart(r, ObjectKey(partKey)) 117 } 118 119 // DecryptSinglePart decrypts an io.Writer which must an object 120 // uploaded with the single-part PUT API. The offset and length 121 // specify the requested range. 122 func DecryptSinglePart(w io.Writer, offset, length int64, key ObjectKey) io.WriteCloser { 123 const PayloadSize = 1 << 16 // DARE 2.0 124 w = ioutil.LimitedWriter(w, offset%PayloadSize, length) 125 126 decWriter, err := sio.DecryptWriter(w, sio.Config{Key: key[:], CipherSuites: fips.DARECiphers()}) 127 if err != nil { 128 logger.CriticalIf(context.Background(), errors.New("Unable to decrypt io.Writer using object key")) 129 } 130 return decWriter 131 }