storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/encryption-v1.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2017-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 cmd
    18  
    19  import (
    20  	"bufio"
    21  	"crypto/hmac"
    22  	"crypto/rand"
    23  	"crypto/sha256"
    24  	"crypto/subtle"
    25  	"encoding/binary"
    26  	"encoding/hex"
    27  	"errors"
    28  	"io"
    29  	"net/http"
    30  	"path"
    31  	"strconv"
    32  	"strings"
    33  
    34  	"github.com/minio/sio"
    35  
    36  	"storj.io/minio/cmd/crypto"
    37  	xhttp "storj.io/minio/cmd/http"
    38  	"storj.io/minio/cmd/logger"
    39  	"storj.io/minio/pkg/fips"
    40  )
    41  
    42  var (
    43  	// AWS errors for invalid SSE-C requests.
    44  	errEncryptedObject      = errors.New("The object was stored using a form of SSE")
    45  	errInvalidSSEParameters = errors.New("The SSE-C key for key-rotation is not correct") // special access denied
    46  	errKMSNotConfigured     = errors.New("KMS not configured for a server side encrypted object")
    47  	// Additional MinIO errors for SSE-C requests.
    48  	errObjectTampered = errors.New("The requested object was modified and may be compromised")
    49  	// error returned when invalid encryption parameters are specified
    50  	errInvalidEncryptionParameters = errors.New("The encryption parameters are not applicable to this object")
    51  )
    52  
    53  const (
    54  	// SSECustomerKeySize is the size of valid client provided encryption keys in bytes.
    55  	// Currently AWS supports only AES256. So the SSE-C key size is fixed to 32 bytes.
    56  	SSECustomerKeySize = 32
    57  
    58  	// SSEIVSize is the size of the IV data
    59  	SSEIVSize = 32 // 32 bytes
    60  
    61  	// SSEDAREPackageBlockSize - SSE dare package block size.
    62  	SSEDAREPackageBlockSize = 64 * 1024 // 64KiB bytes
    63  
    64  	// SSEDAREPackageMetaSize - SSE dare package meta padding bytes.
    65  	SSEDAREPackageMetaSize = 32 // 32 bytes
    66  
    67  )
    68  
    69  // isEncryptedMultipart returns true if the current object is
    70  // uploaded by the user using multipart mechanism:
    71  // initiate new multipart, upload part, complete upload
    72  func isEncryptedMultipart(objInfo ObjectInfo) bool {
    73  	if len(objInfo.Parts) == 0 {
    74  		return false
    75  	}
    76  	if !crypto.IsMultiPart(objInfo.UserDefined) {
    77  		return false
    78  	}
    79  	for _, part := range objInfo.Parts {
    80  		_, err := sio.DecryptedSize(uint64(part.Size))
    81  		if err != nil {
    82  			return false
    83  		}
    84  	}
    85  	// Further check if this object is uploaded using multipart mechanism
    86  	// by the user and it is not about Erasure internally splitting the
    87  	// object into parts in PutObject()
    88  	return !(objInfo.backendType == BackendErasure && len(objInfo.ETag) == 32)
    89  }
    90  
    91  // ParseSSECopyCustomerRequest parses the SSE-C header fields of the provided request.
    92  // It returns the client provided key on success.
    93  func ParseSSECopyCustomerRequest(h http.Header, metadata map[string]string) (key []byte, err error) {
    94  	if crypto.S3.IsEncrypted(metadata) && crypto.SSECopy.IsRequested(h) {
    95  		return nil, crypto.ErrIncompatibleEncryptionMethod
    96  	}
    97  	k, err := crypto.SSECopy.ParseHTTP(h)
    98  	return k[:], err
    99  }
   100  
   101  // ParseSSECustomerRequest parses the SSE-C header fields of the provided request.
   102  // It returns the client provided key on success.
   103  func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) {
   104  	return ParseSSECustomerHeader(r.Header)
   105  }
   106  
   107  // ParseSSECustomerHeader parses the SSE-C header fields and returns
   108  // the client provided key on success.
   109  func ParseSSECustomerHeader(header http.Header) (key []byte, err error) {
   110  	if crypto.S3.IsRequested(header) && crypto.SSEC.IsRequested(header) {
   111  		return key, crypto.ErrIncompatibleEncryptionMethod
   112  	}
   113  
   114  	k, err := crypto.SSEC.ParseHTTP(header)
   115  	return k[:], err
   116  }
   117  
   118  // This function rotates old to new key.
   119  func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map[string]string) error {
   120  	switch {
   121  	default:
   122  		return errObjectTampered
   123  	case crypto.SSEC.IsEncrypted(metadata):
   124  		sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
   125  		if err != nil {
   126  			return err
   127  		}
   128  
   129  		var objectKey crypto.ObjectKey
   130  		if err = objectKey.Unseal(oldKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
   131  			if subtle.ConstantTimeCompare(oldKey, newKey) == 1 {
   132  				return errInvalidSSEParameters // AWS returns special error for equal but invalid keys.
   133  			}
   134  			return crypto.ErrInvalidCustomerKey // To provide strict AWS S3 compatibility we return: access denied.
   135  
   136  		}
   137  		if subtle.ConstantTimeCompare(oldKey, newKey) == 1 && sealedKey.Algorithm == crypto.SealAlgorithm {
   138  			return nil // don't rotate on equal keys if seal algorithm is latest
   139  		}
   140  		sealedKey = objectKey.Seal(newKey, sealedKey.IV, crypto.SSEC.String(), bucket, object)
   141  		crypto.SSEC.CreateMetadata(metadata, sealedKey)
   142  		return nil
   143  	case crypto.S3.IsEncrypted(metadata):
   144  		if GlobalKMS == nil {
   145  			return errKMSNotConfigured
   146  		}
   147  		keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata)
   148  		if err != nil {
   149  			return err
   150  		}
   151  		oldKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)})
   152  		if err != nil {
   153  			return err
   154  		}
   155  		var objectKey crypto.ObjectKey
   156  		if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
   157  			return err
   158  		}
   159  
   160  		newKey, err := GlobalKMS.GenerateKey("", crypto.Context{bucket: path.Join(bucket, object)})
   161  		if err != nil {
   162  			return err
   163  		}
   164  		sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
   165  		crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey)
   166  		return nil
   167  	}
   168  }
   169  
   170  func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (crypto.ObjectKey, error) {
   171  	var sealedKey crypto.SealedKey
   172  	if sseS3 {
   173  		if GlobalKMS == nil {
   174  			return crypto.ObjectKey{}, errKMSNotConfigured
   175  		}
   176  		key, err := GlobalKMS.GenerateKey("", crypto.Context{bucket: path.Join(bucket, object)})
   177  		if err != nil {
   178  			return crypto.ObjectKey{}, err
   179  		}
   180  
   181  		objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader)
   182  		sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
   183  		crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey)
   184  		return objectKey, nil
   185  	}
   186  	objectKey := crypto.GenerateKey(key, rand.Reader)
   187  	sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object)
   188  	crypto.SSEC.CreateMetadata(metadata, sealedKey)
   189  	return objectKey, nil
   190  }
   191  
   192  func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (io.Reader, crypto.ObjectKey, error) {
   193  	objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata, sseS3)
   194  	if err != nil {
   195  		return nil, crypto.ObjectKey{}, err
   196  	}
   197  
   198  	reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20, CipherSuites: fips.CipherSuitesDARE()})
   199  	if err != nil {
   200  		return nil, crypto.ObjectKey{}, crypto.ErrInvalidCustomerKey
   201  	}
   202  
   203  	return reader, objectEncryptionKey, nil
   204  }
   205  
   206  // set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of
   207  // SSE-S3
   208  func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) {
   209  	var (
   210  		key []byte
   211  	)
   212  	if crypto.SSEC.IsRequested(r.Header) {
   213  		key, err = ParseSSECustomerRequest(r)
   214  		if err != nil {
   215  			return
   216  		}
   217  	}
   218  	_, err = newEncryptMetadata(key, bucket, object, metadata, crypto.S3.IsRequested(r.Header))
   219  	return
   220  }
   221  
   222  // EncryptRequest takes the client provided content and encrypts the data
   223  // with the client provided key. It also marks the object as client-side-encrypted
   224  // and sets the correct headers.
   225  func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, crypto.ObjectKey, error) {
   226  	if crypto.S3.IsRequested(r.Header) && crypto.SSEC.IsRequested(r.Header) {
   227  		return nil, crypto.ObjectKey{}, crypto.ErrIncompatibleEncryptionMethod
   228  	}
   229  	if r.ContentLength > encryptBufferThreshold {
   230  		// The encryption reads in blocks of 64KB.
   231  		// We add a buffer on bigger files to reduce the number of syscalls upstream.
   232  		content = bufio.NewReaderSize(content, encryptBufferSize)
   233  	}
   234  
   235  	var key []byte
   236  	if crypto.SSEC.IsRequested(r.Header) {
   237  		var err error
   238  		key, err = ParseSSECustomerRequest(r)
   239  		if err != nil {
   240  			return nil, crypto.ObjectKey{}, err
   241  		}
   242  	}
   243  	return newEncryptReader(content, key, bucket, object, metadata, crypto.S3.IsRequested(r.Header))
   244  }
   245  
   246  func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
   247  	switch kind, _ := crypto.IsEncrypted(metadata); kind {
   248  	case crypto.S3:
   249  		var KMS crypto.KMS = GlobalKMS
   250  		if isCacheEncrypted(metadata) {
   251  			KMS = globalCacheKMS
   252  		}
   253  		if KMS == nil {
   254  			return nil, errKMSNotConfigured
   255  		}
   256  		objectKey, err := crypto.S3.UnsealObjectKey(KMS, metadata, bucket, object)
   257  		if err != nil {
   258  			return nil, err
   259  		}
   260  		return objectKey[:], nil
   261  	case crypto.S3KMS:
   262  		if GlobalKMS == nil {
   263  			return nil, errKMSNotConfigured
   264  		}
   265  		objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object)
   266  		if err != nil {
   267  			return nil, err
   268  		}
   269  		return objectKey[:], nil
   270  	case crypto.SSEC:
   271  		sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  		var objectKey crypto.ObjectKey
   276  		if err = objectKey.Unseal(key, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
   277  			return nil, err
   278  		}
   279  		return objectKey[:], nil
   280  	default:
   281  		return nil, errObjectTampered
   282  	}
   283  }
   284  
   285  // Adding support for reader based interface
   286  
   287  // DecryptRequestWithSequenceNumberR - same as
   288  // DecryptRequestWithSequenceNumber but with a reader
   289  func DecryptRequestWithSequenceNumberR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
   290  	if crypto.S3.IsEncrypted(metadata) {
   291  		return newDecryptReader(client, nil, bucket, object, seqNumber, metadata)
   292  	}
   293  
   294  	key, err := ParseSSECustomerHeader(h)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	return newDecryptReader(client, key, bucket, object, seqNumber, metadata)
   299  }
   300  
   301  // DecryptCopyRequestR - same as DecryptCopyRequest, but with a
   302  // Reader
   303  func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
   304  	var (
   305  		key []byte
   306  		err error
   307  	)
   308  	if crypto.SSECopy.IsRequested(h) {
   309  		key, err = ParseSSECopyCustomerRequest(h, metadata)
   310  		if err != nil {
   311  			return nil, err
   312  		}
   313  	}
   314  	return newDecryptReader(client, key, bucket, object, seqNumber, metadata)
   315  }
   316  
   317  func newDecryptReader(client io.Reader, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
   318  	objectEncryptionKey, err := decryptObjectInfo(key, bucket, object, metadata)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	return newDecryptReaderWithObjectKey(client, objectEncryptionKey, seqNumber)
   323  }
   324  
   325  func newDecryptReaderWithObjectKey(client io.Reader, objectEncryptionKey []byte, seqNumber uint32) (io.Reader, error) {
   326  	reader, err := sio.DecryptReader(client, sio.Config{
   327  		Key:            objectEncryptionKey,
   328  		SequenceNumber: seqNumber,
   329  		CipherSuites:   fips.CipherSuitesDARE(),
   330  	})
   331  	if err != nil {
   332  		return nil, crypto.ErrInvalidCustomerKey
   333  	}
   334  	return reader, nil
   335  }
   336  
   337  // DecryptBlocksRequestR - same as DecryptBlocksRequest but with a
   338  // reader
   339  func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint32, partStart int, oi ObjectInfo, copySource bool) (io.Reader, error) {
   340  
   341  	bucket, object := oi.Bucket, oi.Name
   342  	// Single part case
   343  	if !isEncryptedMultipart(oi) {
   344  		var reader io.Reader
   345  		var err error
   346  		if copySource {
   347  			reader, err = DecryptCopyRequestR(inputReader, h, bucket, object, seqNumber, oi.UserDefined)
   348  		} else {
   349  			reader, err = DecryptRequestWithSequenceNumberR(inputReader, h, bucket, object, seqNumber, oi.UserDefined)
   350  		}
   351  		if err != nil {
   352  			return nil, err
   353  		}
   354  		return reader, nil
   355  	}
   356  
   357  	partDecRelOffset := int64(seqNumber) * SSEDAREPackageBlockSize
   358  	partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
   359  
   360  	w := &DecryptBlocksReader{
   361  		reader:            inputReader,
   362  		startSeqNum:       seqNumber,
   363  		partDecRelOffset:  partDecRelOffset,
   364  		partEncRelOffset:  partEncRelOffset,
   365  		parts:             oi.Parts,
   366  		partIndex:         partStart,
   367  		header:            h,
   368  		bucket:            bucket,
   369  		object:            object,
   370  		customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey),
   371  		copySource:        copySource,
   372  		metadata:          cloneMSS(oi.UserDefined),
   373  	}
   374  
   375  	if w.copySource {
   376  		w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey)
   377  	}
   378  
   379  	if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
   380  		return nil, err
   381  	}
   382  
   383  	return w, nil
   384  }
   385  
   386  // DecryptBlocksReader - decrypts multipart parts, while implementing
   387  // a io.Reader compatible interface.
   388  type DecryptBlocksReader struct {
   389  	// Source of the encrypted content that will be decrypted
   390  	reader io.Reader
   391  	// Current decrypter for the current encrypted data block
   392  	decrypter io.Reader
   393  	// Start sequence number
   394  	startSeqNum uint32
   395  	// Current part index
   396  	partIndex int
   397  	// Parts information
   398  	parts          []ObjectPartInfo
   399  	header         http.Header
   400  	bucket, object string
   401  	metadata       map[string]string
   402  
   403  	partDecRelOffset, partEncRelOffset int64
   404  
   405  	copySource bool
   406  	// Customer Key
   407  	customerKeyHeader string
   408  }
   409  
   410  func (d *DecryptBlocksReader) buildDecrypter(partID int) error {
   411  	m := cloneMSS(d.metadata)
   412  	// Initialize the first decrypter; new decrypters will be
   413  	// initialized in Read() operation as needed.
   414  	var key []byte
   415  	var err error
   416  	if d.copySource {
   417  		if crypto.SSEC.IsEncrypted(d.metadata) {
   418  			d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader)
   419  			key, err = ParseSSECopyCustomerRequest(d.header, d.metadata)
   420  		}
   421  	} else {
   422  		if crypto.SSEC.IsEncrypted(d.metadata) {
   423  			d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader)
   424  			key, err = ParseSSECustomerHeader(d.header)
   425  		}
   426  	}
   427  	if err != nil {
   428  		return err
   429  	}
   430  
   431  	objectEncryptionKey, err := decryptObjectInfo(key, d.bucket, d.object, m)
   432  	if err != nil {
   433  		return err
   434  	}
   435  
   436  	var partIDbin [4]byte
   437  	binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID
   438  
   439  	mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key
   440  	mac.Write(partIDbin[:])
   441  	partEncryptionKey := mac.Sum(nil)
   442  
   443  	// Limit the reader, so the decryptor doesnt receive bytes
   444  	// from the next part (different DARE stream)
   445  	encLenToRead := d.parts[d.partIndex].Size - d.partEncRelOffset
   446  	decrypter, err := newDecryptReaderWithObjectKey(io.LimitReader(d.reader, encLenToRead), partEncryptionKey, d.startSeqNum)
   447  	if err != nil {
   448  		return err
   449  	}
   450  
   451  	d.decrypter = decrypter
   452  	return nil
   453  }
   454  
   455  func (d *DecryptBlocksReader) Read(p []byte) (int, error) {
   456  	var err error
   457  	var n1 int
   458  	decPartSize, _ := sio.DecryptedSize(uint64(d.parts[d.partIndex].Size))
   459  	unreadPartLen := int64(decPartSize) - d.partDecRelOffset
   460  	if int64(len(p)) < unreadPartLen {
   461  		n1, err = d.decrypter.Read(p)
   462  		if err != nil {
   463  			return 0, err
   464  		}
   465  		d.partDecRelOffset += int64(n1)
   466  	} else {
   467  		n1, err = io.ReadFull(d.decrypter, p[:unreadPartLen])
   468  		if err != nil {
   469  			return 0, err
   470  		}
   471  
   472  		// We should now proceed to next part, reset all
   473  		// values appropriately.
   474  		d.partEncRelOffset = 0
   475  		d.partDecRelOffset = 0
   476  		d.startSeqNum = 0
   477  
   478  		d.partIndex++
   479  		if d.partIndex == len(d.parts) {
   480  			return n1, io.EOF
   481  		}
   482  
   483  		err = d.buildDecrypter(d.parts[d.partIndex].Number)
   484  		if err != nil {
   485  			return 0, err
   486  		}
   487  
   488  		n1, err = d.decrypter.Read(p[n1:])
   489  		if err != nil {
   490  			return 0, err
   491  		}
   492  
   493  		d.partDecRelOffset += int64(n1)
   494  	}
   495  	return len(p), nil
   496  }
   497  
   498  // DecryptedSize returns the size of the object after decryption in bytes.
   499  // It returns an error if the object is not encrypted or marked as encrypted
   500  // but has an invalid size.
   501  func (o *ObjectInfo) DecryptedSize() (int64, error) {
   502  	if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
   503  		return 0, errors.New("Cannot compute decrypted size of an unencrypted object")
   504  	}
   505  	if !isEncryptedMultipart(*o) {
   506  		size, err := sio.DecryptedSize(uint64(o.Size))
   507  		if err != nil {
   508  			err = errObjectTampered // assign correct error type
   509  		}
   510  		return int64(size), err
   511  	}
   512  
   513  	var size int64
   514  	for _, part := range o.Parts {
   515  		partSize, err := sio.DecryptedSize(uint64(part.Size))
   516  		if err != nil {
   517  			return 0, errObjectTampered
   518  		}
   519  		size += int64(partSize)
   520  	}
   521  	return size, nil
   522  }
   523  
   524  // DecryptETag decrypts the ETag that is part of given object
   525  // with the given object encryption key.
   526  //
   527  // However, DecryptETag does not try to decrypt the ETag if
   528  // it consists of a 128 bit hex value (32 hex chars) and exactly
   529  // one '-' followed by a 32-bit number.
   530  // This special case adresses randomly-generated ETags generated
   531  // by the MinIO server when running in non-compat mode. These
   532  // random ETags are not encrypt.
   533  //
   534  // Calling DecryptETag with a non-randomly generated ETag will
   535  // fail.
   536  func DecryptETag(key crypto.ObjectKey, object ObjectInfo) (string, error) {
   537  	if n := strings.Count(object.ETag, "-"); n > 0 {
   538  		if n != 1 {
   539  			return "", errObjectTampered
   540  		}
   541  		i := strings.IndexByte(object.ETag, '-')
   542  		if len(object.ETag[:i]) != 32 {
   543  			return "", errObjectTampered
   544  		}
   545  		if _, err := hex.DecodeString(object.ETag[:32]); err != nil {
   546  			return "", errObjectTampered
   547  		}
   548  		if _, err := strconv.ParseInt(object.ETag[i+1:], 10, 32); err != nil {
   549  			return "", errObjectTampered
   550  		}
   551  		return object.ETag, nil
   552  	}
   553  
   554  	etag, err := hex.DecodeString(object.ETag)
   555  	if err != nil {
   556  		return "", err
   557  	}
   558  	etag, err = key.UnsealETag(etag)
   559  	if err != nil {
   560  		return "", err
   561  	}
   562  	return hex.EncodeToString(etag), nil
   563  }
   564  
   565  // For encrypted objects, the ETag sent by client if available
   566  // is stored in encrypted form in the backend. Decrypt the ETag
   567  // if ETag was previously encrypted.
   568  func getDecryptedETag(headers http.Header, objInfo ObjectInfo, copySource bool) (decryptedETag string) {
   569  	var (
   570  		key [32]byte
   571  		err error
   572  	)
   573  	// If ETag is contentMD5Sum return it as is.
   574  	if len(objInfo.ETag) == 32 {
   575  		return objInfo.ETag
   576  	}
   577  
   578  	if crypto.IsMultiPart(objInfo.UserDefined) {
   579  		return objInfo.ETag
   580  	}
   581  
   582  	if crypto.SSECopy.IsRequested(headers) {
   583  		key, err = crypto.SSECopy.ParseHTTP(headers)
   584  		if err != nil {
   585  			return objInfo.ETag
   586  		}
   587  	}
   588  
   589  	// As per AWS S3 Spec, ETag for SSE-C encrypted objects need not be MD5Sum of the data.
   590  	// Since server side copy with same source and dest just replaces the ETag, we save
   591  	// encrypted content MD5Sum as ETag for both SSE-C and SSE-S3, we standardize the ETag
   592  	// encryption across SSE-C and SSE-S3, and only return last 32 bytes for SSE-C
   593  	if crypto.SSEC.IsEncrypted(objInfo.UserDefined) && !copySource {
   594  		return objInfo.ETag[len(objInfo.ETag)-32:]
   595  	}
   596  
   597  	objectEncryptionKey, err := decryptObjectInfo(key[:], objInfo.Bucket, objInfo.Name, objInfo.UserDefined)
   598  	if err != nil {
   599  		return objInfo.ETag
   600  	}
   601  	return tryDecryptETag(objectEncryptionKey, objInfo.ETag, false)
   602  }
   603  
   604  // helper to decrypt Etag given object encryption key and encrypted ETag
   605  func tryDecryptETag(key []byte, encryptedETag string, ssec bool) string {
   606  	// ETag for SSE-C encrypted objects need not be content MD5Sum.While encrypted
   607  	// md5sum is stored internally, return just the last 32 bytes of hex-encoded and
   608  	// encrypted md5sum string for SSE-C
   609  	if ssec {
   610  		return encryptedETag[len(encryptedETag)-32:]
   611  	}
   612  	var objectKey crypto.ObjectKey
   613  	copy(objectKey[:], key)
   614  	encBytes, err := hex.DecodeString(encryptedETag)
   615  	if err != nil {
   616  		return encryptedETag
   617  	}
   618  	etagBytes, err := objectKey.UnsealETag(encBytes)
   619  	if err != nil {
   620  		return encryptedETag
   621  	}
   622  	return hex.EncodeToString(etagBytes)
   623  }
   624  
   625  // GetDecryptedRange - To decrypt the range (off, length) of the
   626  // decrypted object stream, we need to read the range (encOff,
   627  // encLength) of the encrypted object stream to decrypt it, and
   628  // compute skipLen, the number of bytes to skip in the beginning of
   629  // the encrypted range.
   630  //
   631  // In addition we also compute the object part number for where the
   632  // requested range starts, along with the DARE sequence number within
   633  // that part. For single part objects, the partStart will be 0.
   634  func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) {
   635  	if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
   636  		err = errors.New("Object is not encrypted")
   637  		return
   638  	}
   639  
   640  	if rs == nil {
   641  		// No range, so offsets refer to the whole object.
   642  		return 0, o.Size, 0, 0, 0, nil
   643  	}
   644  
   645  	// Assemble slice of (decrypted) part sizes in `sizes`
   646  	var sizes []int64
   647  	var decObjSize int64 // decrypted total object size
   648  	if isEncryptedMultipart(*o) {
   649  		sizes = make([]int64, len(o.Parts))
   650  		for i, part := range o.Parts {
   651  			var partSize uint64
   652  			partSize, err = sio.DecryptedSize(uint64(part.Size))
   653  			if err != nil {
   654  				err = errObjectTampered
   655  				return
   656  			}
   657  			sizes[i] = int64(partSize)
   658  			decObjSize += int64(partSize)
   659  		}
   660  	} else {
   661  		var partSize uint64
   662  		partSize, err = sio.DecryptedSize(uint64(o.Size))
   663  		if err != nil {
   664  			err = errObjectTampered
   665  			return
   666  		}
   667  		sizes = []int64{int64(partSize)}
   668  		decObjSize = sizes[0]
   669  	}
   670  
   671  	var off, length int64
   672  	off, length, err = rs.GetOffsetLength(decObjSize)
   673  	if err != nil {
   674  		return
   675  	}
   676  
   677  	// At this point, we have:
   678  	//
   679  	// 1. the decrypted part sizes in `sizes` (single element for
   680  	//    single part object) and total decrypted object size `decObjSize`
   681  	//
   682  	// 2. the (decrypted) start offset `off` and (decrypted)
   683  	//    length to read `length`
   684  	//
   685  	// These are the inputs to the rest of the algorithm below.
   686  
   687  	// Locate the part containing the start of the required range
   688  	var partEnd int
   689  	var cumulativeSum, encCumulativeSum int64
   690  	for i, size := range sizes {
   691  		if off < cumulativeSum+size {
   692  			partStart = i
   693  			break
   694  		}
   695  		cumulativeSum += size
   696  		encPartSize, _ := sio.EncryptedSize(uint64(size))
   697  		encCumulativeSum += int64(encPartSize)
   698  	}
   699  	// partStart is always found in the loop above,
   700  	// because off is validated.
   701  
   702  	sseDAREEncPackageBlockSize := int64(SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
   703  	startPkgNum := (off - cumulativeSum) / SSEDAREPackageBlockSize
   704  
   705  	// Now we can calculate the number of bytes to skip
   706  	skipLen = (off - cumulativeSum) % SSEDAREPackageBlockSize
   707  
   708  	encOff = encCumulativeSum + startPkgNum*sseDAREEncPackageBlockSize
   709  	// Locate the part containing the end of the required range
   710  	endOffset := off + length - 1
   711  	for i1, size := range sizes[partStart:] {
   712  		i := partStart + i1
   713  		if endOffset < cumulativeSum+size {
   714  			partEnd = i
   715  			break
   716  		}
   717  		cumulativeSum += size
   718  		encPartSize, _ := sio.EncryptedSize(uint64(size))
   719  		encCumulativeSum += int64(encPartSize)
   720  	}
   721  	// partEnd is always found in the loop above, because off and
   722  	// length are validated.
   723  	endPkgNum := (endOffset - cumulativeSum) / SSEDAREPackageBlockSize
   724  	// Compute endEncOffset with one additional DARE package (so
   725  	// we read the package containing the last desired byte).
   726  	endEncOffset := encCumulativeSum + (endPkgNum+1)*sseDAREEncPackageBlockSize
   727  	// Check if the DARE package containing the end offset is a
   728  	// full sized package (as the last package in the part may be
   729  	// smaller)
   730  	lastPartSize, _ := sio.EncryptedSize(uint64(sizes[partEnd]))
   731  	if endEncOffset > encCumulativeSum+int64(lastPartSize) {
   732  		endEncOffset = encCumulativeSum + int64(lastPartSize)
   733  	}
   734  	encLength = endEncOffset - encOff
   735  	// Set the sequence number as the starting package number of
   736  	// the requested block
   737  	seqNumber = uint32(startPkgNum)
   738  	return encOff, encLength, skipLen, seqNumber, partStart, nil
   739  }
   740  
   741  // EncryptedSize returns the size of the object after encryption.
   742  // An encrypted object is always larger than a plain object
   743  // except for zero size objects.
   744  func (o *ObjectInfo) EncryptedSize() int64 {
   745  	size, err := sio.EncryptedSize(uint64(o.Size))
   746  	if err != nil {
   747  		// This cannot happen since AWS S3 allows parts to be 5GB at most
   748  		// sio max. size is 256 TB
   749  		reqInfo := (&logger.ReqInfo{}).AppendTags("size", strconv.FormatUint(size, 10))
   750  		ctx := logger.SetReqInfo(GlobalContext, reqInfo)
   751  		logger.CriticalIf(ctx, err)
   752  	}
   753  	return int64(size)
   754  }
   755  
   756  // DecryptObjectInfo tries to decrypt the provided object if it is encrypted.
   757  // It fails if the object is encrypted and the HTTP headers don't contain
   758  // SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior)
   759  // DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the
   760  // decryption succeeded.
   761  //
   762  // DecryptObjectInfo also returns whether the object is encrypted or not.
   763  func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err error) {
   764  	// Directories are never encrypted.
   765  	if info.IsDir {
   766  		return false, nil
   767  	}
   768  	if r == nil {
   769  		return false, errInvalidArgument
   770  	}
   771  
   772  	headers := r.Header
   773  
   774  	// disallow X-Amz-Server-Side-Encryption header on HEAD and GET
   775  	switch r.Method {
   776  	case http.MethodGet, http.MethodHead:
   777  		if crypto.S3.IsRequested(headers) {
   778  			return false, errInvalidEncryptionParameters
   779  		}
   780  	}
   781  
   782  	_, encrypted = crypto.IsEncrypted(info.UserDefined)
   783  	if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" {
   784  		return false, errInvalidEncryptionParameters
   785  	}
   786  
   787  	if encrypted {
   788  		if crypto.SSEC.IsEncrypted(info.UserDefined) {
   789  			if !(crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers)) {
   790  				return encrypted, errEncryptedObject
   791  			}
   792  		}
   793  
   794  		if crypto.S3.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" {
   795  			if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) {
   796  				return encrypted, errEncryptedObject
   797  			}
   798  		}
   799  
   800  		if _, err = info.DecryptedSize(); err != nil {
   801  			return encrypted, err
   802  		}
   803  
   804  		if _, ok := crypto.IsEncrypted(info.UserDefined); ok && !crypto.IsMultiPart(info.UserDefined) {
   805  			info.ETag = getDecryptedETag(headers, *info, false)
   806  		}
   807  	}
   808  
   809  	return encrypted, nil
   810  }
   811  
   812  // The customer key in the header is used by the gateway for encryption in the case of
   813  // s3 gateway double encryption. A new client key is derived from the customer provided
   814  // key to be sent to the s3 backend for encryption at the backend.
   815  func deriveClientKey(clientKey [32]byte, bucket, object string) [32]byte {
   816  	var key [32]byte
   817  	mac := hmac.New(sha256.New, clientKey[:])
   818  	mac.Write([]byte(crypto.SSEC.String()))
   819  	mac.Write([]byte(path.Join(bucket, object)))
   820  	mac.Sum(key[:0])
   821  	return key
   822  }