github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/encryption-v1.go (about)

     1  // Copyright (c) 2015-2023 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 cmd
    19  
    20  import (
    21  	"bufio"
    22  	"bytes"
    23  	"context"
    24  	"crypto/hmac"
    25  	"crypto/rand"
    26  	"crypto/subtle"
    27  	"encoding/binary"
    28  	"encoding/hex"
    29  	"errors"
    30  	"fmt"
    31  	"io"
    32  	"net/http"
    33  	"path"
    34  	"strconv"
    35  	"strings"
    36  
    37  	"github.com/minio/kms-go/kes"
    38  	"github.com/minio/minio/internal/crypto"
    39  	"github.com/minio/minio/internal/etag"
    40  	"github.com/minio/minio/internal/fips"
    41  	"github.com/minio/minio/internal/hash"
    42  	"github.com/minio/minio/internal/hash/sha256"
    43  	xhttp "github.com/minio/minio/internal/http"
    44  	"github.com/minio/minio/internal/kms"
    45  	"github.com/minio/minio/internal/logger"
    46  	"github.com/minio/sio"
    47  )
    48  
    49  var (
    50  	// AWS errors for invalid SSE-C requests.
    51  	errEncryptedObject                = errors.New("The object was stored using a form of SSE")
    52  	errInvalidSSEParameters           = errors.New("The SSE-C key for key-rotation is not correct") // special access denied
    53  	errKMSNotConfigured               = errors.New("KMS not configured for a server side encrypted objects")
    54  	errKMSKeyNotFound                 = errors.New("Unknown KMS key ID")
    55  	errKMSDefaultKeyAlreadyConfigured = errors.New("A default encryption already exists on KMS")
    56  	// Additional MinIO errors for SSE-C requests.
    57  	errObjectTampered = errors.New("The requested object was modified and may be compromised")
    58  	// error returned when invalid encryption parameters are specified
    59  	errInvalidEncryptionParameters     = errors.New("The encryption parameters are not applicable to this object")
    60  	errInvalidEncryptionParametersSSEC = errors.New("SSE-C encryption parameters are not supported on this bucket")
    61  )
    62  
    63  const (
    64  	// SSECustomerKeySize is the size of valid client provided encryption keys in bytes.
    65  	// Currently AWS supports only AES256. So the SSE-C key size is fixed to 32 bytes.
    66  	SSECustomerKeySize = 32
    67  
    68  	// SSEIVSize is the size of the IV data
    69  	SSEIVSize = 32 // 32 bytes
    70  
    71  	// SSEDAREPackageBlockSize - SSE dare package block size.
    72  	SSEDAREPackageBlockSize = 64 * 1024 // 64KiB bytes
    73  
    74  	// SSEDAREPackageMetaSize - SSE dare package meta padding bytes.
    75  	SSEDAREPackageMetaSize = 32 // 32 bytes
    76  
    77  )
    78  
    79  // KMSKeyID returns in AWS compatible KMS KeyID() format.
    80  func (o *ObjectInfo) KMSKeyID() string { return kmsKeyIDFromMetadata(o.UserDefined) }
    81  
    82  // KMSKeyID returns in AWS compatible KMS KeyID() format.
    83  func (o *MultipartInfo) KMSKeyID() string { return kmsKeyIDFromMetadata(o.UserDefined) }
    84  
    85  // kmsKeyIDFromMetadata returns any AWS S3 KMS key ID in the
    86  // metadata, if any. It returns an empty ID if no key ID is
    87  // present.
    88  func kmsKeyIDFromMetadata(metadata map[string]string) string {
    89  	const ARNPrefix = crypto.ARNPrefix
    90  	if len(metadata) == 0 {
    91  		return ""
    92  	}
    93  	kmsID, ok := metadata[crypto.MetaKeyID]
    94  	if !ok {
    95  		return ""
    96  	}
    97  	if strings.HasPrefix(kmsID, ARNPrefix) {
    98  		return kmsID
    99  	}
   100  	return ARNPrefix + kmsID
   101  }
   102  
   103  // DecryptETags decryptes the ETag of all ObjectInfos using the KMS.
   104  //
   105  // It adjusts the size of all encrypted objects since encrypted
   106  // objects are slightly larger due to encryption overhead.
   107  // Further, it decrypts all single-part SSE-S3 encrypted objects
   108  // and formats ETags of SSE-C / SSE-KMS encrypted objects to
   109  // be AWS S3 compliant.
   110  //
   111  // DecryptETags uses a KMS bulk decryption API, if available, which
   112  // is more efficient than decrypting ETags sequentually.
   113  func DecryptETags(ctx context.Context, k kms.KMS, objects []ObjectInfo) error {
   114  	const BatchSize = 250 // We process the objects in batches - 250 is a reasonable default.
   115  	var (
   116  		metadata = make([]map[string]string, 0, BatchSize)
   117  		buckets  = make([]string, 0, BatchSize)
   118  		names    = make([]string, 0, BatchSize)
   119  	)
   120  	for len(objects) > 0 {
   121  		N := BatchSize
   122  		if len(objects) < BatchSize {
   123  			N = len(objects)
   124  		}
   125  		batch := objects[:N]
   126  
   127  		// We have to decrypt only ETags of SSE-S3 single-part
   128  		// objects.
   129  		// Therefore, we remember which objects (there index)
   130  		// in the current batch are single-part SSE-S3 objects.
   131  		metadata = metadata[:0:N]
   132  		buckets = buckets[:0:N]
   133  		names = names[:0:N]
   134  		SSES3SinglePartObjects := make(map[int]bool)
   135  		for i, object := range batch {
   136  			if kind, ok := crypto.IsEncrypted(object.UserDefined); ok && kind == crypto.S3 && !crypto.IsMultiPart(object.UserDefined) {
   137  				SSES3SinglePartObjects[i] = true
   138  
   139  				metadata = append(metadata, object.UserDefined)
   140  				buckets = append(buckets, object.Bucket)
   141  				names = append(names, object.Name)
   142  			}
   143  		}
   144  
   145  		// If there are no SSE-S3 single-part objects
   146  		// we can skip the decryption process. However,
   147  		// we still have to adjust the size and ETag
   148  		// of SSE-C and SSE-KMS objects.
   149  		if len(SSES3SinglePartObjects) == 0 {
   150  			for i := range batch {
   151  				size, err := batch[i].GetActualSize()
   152  				if err != nil {
   153  					return err
   154  				}
   155  				batch[i].Size = size
   156  
   157  				if _, ok := crypto.IsEncrypted(batch[i].UserDefined); ok {
   158  					ETag, err := etag.Parse(batch[i].ETag)
   159  					if err != nil {
   160  						return err
   161  					}
   162  					batch[i].ETag = ETag.Format().String()
   163  				}
   164  			}
   165  			objects = objects[N:]
   166  			continue
   167  		}
   168  
   169  		// There is at least one SSE-S3 single-part object.
   170  		// For all SSE-S3 single-part objects we have to
   171  		// fetch their decryption keys. We do this using
   172  		// a Bulk-Decryption API call, if available.
   173  		keys, err := crypto.S3.UnsealObjectKeys(ctx, k, metadata, buckets, names)
   174  		if err != nil {
   175  			return err
   176  		}
   177  
   178  		// Now, we have to decrypt the ETags of SSE-S3 single-part
   179  		// objects and adjust the size and ETags of all encrypted
   180  		// objects.
   181  		for i := range batch {
   182  			size, err := batch[i].GetActualSize()
   183  			if err != nil {
   184  				return err
   185  			}
   186  			batch[i].Size = size
   187  
   188  			if _, ok := crypto.IsEncrypted(batch[i].UserDefined); ok {
   189  				ETag, err := etag.Parse(batch[i].ETag)
   190  				if err != nil {
   191  					return err
   192  				}
   193  				if SSES3SinglePartObjects[i] && ETag.IsEncrypted() {
   194  					ETag, err = etag.Decrypt(keys[0][:], ETag)
   195  					if err != nil {
   196  						return err
   197  					}
   198  					keys = keys[1:]
   199  				}
   200  				batch[i].ETag = ETag.Format().String()
   201  			}
   202  		}
   203  		objects = objects[N:]
   204  	}
   205  	return nil
   206  }
   207  
   208  // isMultipart returns true if the current object is
   209  // uploaded by the user using multipart mechanism:
   210  // initiate new multipart, upload part, complete upload
   211  func (o *ObjectInfo) isMultipart() bool {
   212  	_, encrypted := crypto.IsEncrypted(o.UserDefined)
   213  	if encrypted {
   214  		if !crypto.IsMultiPart(o.UserDefined) {
   215  			return false
   216  		}
   217  		for _, part := range o.Parts {
   218  			_, err := sio.DecryptedSize(uint64(part.Size))
   219  			if err != nil {
   220  				return false
   221  			}
   222  		}
   223  	}
   224  
   225  	// Further check if this object is uploaded using multipart mechanism
   226  	// by the user and it is not about Erasure internally splitting the
   227  	// object into parts in PutObject()
   228  	return len(o.ETag) != 32
   229  }
   230  
   231  // ParseSSECopyCustomerRequest parses the SSE-C header fields of the provided request.
   232  // It returns the client provided key on success.
   233  func ParseSSECopyCustomerRequest(h http.Header, metadata map[string]string) (key []byte, err error) {
   234  	if crypto.S3.IsEncrypted(metadata) && crypto.SSECopy.IsRequested(h) {
   235  		return nil, crypto.ErrIncompatibleEncryptionMethod
   236  	}
   237  	k, err := crypto.SSECopy.ParseHTTP(h)
   238  	return k[:], err
   239  }
   240  
   241  // ParseSSECustomerRequest parses the SSE-C header fields of the provided request.
   242  // It returns the client provided key on success.
   243  func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) {
   244  	return ParseSSECustomerHeader(r.Header)
   245  }
   246  
   247  // ParseSSECustomerHeader parses the SSE-C header fields and returns
   248  // the client provided key on success.
   249  func ParseSSECustomerHeader(header http.Header) (key []byte, err error) {
   250  	if crypto.S3.IsRequested(header) && crypto.SSEC.IsRequested(header) {
   251  		return key, crypto.ErrIncompatibleEncryptionMethod
   252  	}
   253  
   254  	k, err := crypto.SSEC.ParseHTTP(header)
   255  	return k[:], err
   256  }
   257  
   258  // This function rotates old to new key.
   259  func rotateKey(ctx context.Context, oldKey []byte, newKeyID string, newKey []byte, bucket, object string, metadata map[string]string, cryptoCtx kms.Context) error {
   260  	kind, _ := crypto.IsEncrypted(metadata)
   261  	switch kind {
   262  	case crypto.S3:
   263  		if GlobalKMS == nil {
   264  			return errKMSNotConfigured
   265  		}
   266  		keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata)
   267  		if err != nil {
   268  			return err
   269  		}
   270  		oldKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, kms.Context{bucket: path.Join(bucket, object)})
   271  		if err != nil {
   272  			return err
   273  		}
   274  		var objectKey crypto.ObjectKey
   275  		if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
   276  			return err
   277  		}
   278  
   279  		newKey, err := GlobalKMS.GenerateKey(ctx, "", kms.Context{bucket: path.Join(bucket, object)})
   280  		if err != nil {
   281  			return err
   282  		}
   283  		sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
   284  		crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey)
   285  		return nil
   286  	case crypto.S3KMS:
   287  		if GlobalKMS == nil {
   288  			return errKMSNotConfigured
   289  		}
   290  		objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object)
   291  		if err != nil {
   292  			return err
   293  		}
   294  
   295  		if len(cryptoCtx) == 0 {
   296  			_, _, _, cryptoCtx, err = crypto.S3KMS.ParseMetadata(metadata)
   297  			if err != nil {
   298  				return err
   299  			}
   300  		}
   301  
   302  		// If the context does not contain the bucket key
   303  		// we must add it for key generation. However,
   304  		// the context must be stored exactly like the
   305  		// client provided it. Therefore, we create a copy
   306  		// of the client provided context and add the bucket
   307  		// key, if not present.
   308  		kmsCtx := kms.Context{}
   309  		for k, v := range cryptoCtx {
   310  			kmsCtx[k] = v
   311  		}
   312  		if _, ok := kmsCtx[bucket]; !ok {
   313  			kmsCtx[bucket] = path.Join(bucket, object)
   314  		}
   315  		newKey, err := GlobalKMS.GenerateKey(ctx, newKeyID, kmsCtx)
   316  		if err != nil {
   317  			return err
   318  		}
   319  
   320  		sealedKey := objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object)
   321  		crypto.S3KMS.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey, cryptoCtx)
   322  		return nil
   323  	case crypto.SSEC:
   324  		sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
   325  		if err != nil {
   326  			return err
   327  		}
   328  
   329  		var objectKey crypto.ObjectKey
   330  		if err = objectKey.Unseal(oldKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
   331  			if subtle.ConstantTimeCompare(oldKey, newKey) == 1 {
   332  				return errInvalidSSEParameters // AWS returns special error for equal but invalid keys.
   333  			}
   334  			return crypto.ErrInvalidCustomerKey // To provide strict AWS S3 compatibility we return: access denied.
   335  
   336  		}
   337  		if subtle.ConstantTimeCompare(oldKey, newKey) == 1 && sealedKey.Algorithm == crypto.SealAlgorithm {
   338  			return nil // don't rotate on equal keys if seal algorithm is latest
   339  		}
   340  		sealedKey = objectKey.Seal(newKey, sealedKey.IV, crypto.SSEC.String(), bucket, object)
   341  		crypto.SSEC.CreateMetadata(metadata, sealedKey)
   342  		return nil
   343  	default:
   344  		return errObjectTampered
   345  	}
   346  }
   347  
   348  func newEncryptMetadata(ctx context.Context, kind crypto.Type, keyID string, key []byte, bucket, object string, metadata map[string]string, cryptoCtx kms.Context) (crypto.ObjectKey, error) {
   349  	var sealedKey crypto.SealedKey
   350  	switch kind {
   351  	case crypto.S3:
   352  		if GlobalKMS == nil {
   353  			return crypto.ObjectKey{}, errKMSNotConfigured
   354  		}
   355  		key, err := GlobalKMS.GenerateKey(ctx, "", kms.Context{bucket: path.Join(bucket, object)})
   356  		if err != nil {
   357  			return crypto.ObjectKey{}, err
   358  		}
   359  
   360  		objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader)
   361  		sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object)
   362  		crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey)
   363  		return objectKey, nil
   364  	case crypto.S3KMS:
   365  		if GlobalKMS == nil {
   366  			return crypto.ObjectKey{}, errKMSNotConfigured
   367  		}
   368  
   369  		// If the context does not contain the bucket key
   370  		// we must add it for key generation. However,
   371  		// the context must be stored exactly like the
   372  		// client provided it. Therefore, we create a copy
   373  		// of the client provided context and add the bucket
   374  		// key, if not present.
   375  		kmsCtx := kms.Context{}
   376  		for k, v := range cryptoCtx {
   377  			kmsCtx[k] = v
   378  		}
   379  		if _, ok := kmsCtx[bucket]; !ok {
   380  			kmsCtx[bucket] = path.Join(bucket, object)
   381  		}
   382  		key, err := GlobalKMS.GenerateKey(ctx, keyID, kmsCtx)
   383  		if err != nil {
   384  			if errors.Is(err, kes.ErrKeyNotFound) {
   385  				return crypto.ObjectKey{}, errKMSKeyNotFound
   386  			}
   387  			return crypto.ObjectKey{}, err
   388  		}
   389  
   390  		objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader)
   391  		sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object)
   392  		crypto.S3KMS.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey, cryptoCtx)
   393  		return objectKey, nil
   394  	case crypto.SSEC:
   395  		objectKey := crypto.GenerateKey(key, rand.Reader)
   396  		sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object)
   397  		crypto.SSEC.CreateMetadata(metadata, sealedKey)
   398  		return objectKey, nil
   399  	default:
   400  		return crypto.ObjectKey{}, fmt.Errorf("encryption type '%v' not supported", kind)
   401  	}
   402  }
   403  
   404  func newEncryptReader(ctx context.Context, content io.Reader, kind crypto.Type, keyID string, key []byte, bucket, object string, metadata map[string]string, cryptoCtx kms.Context) (io.Reader, crypto.ObjectKey, error) {
   405  	objectEncryptionKey, err := newEncryptMetadata(ctx, kind, keyID, key, bucket, object, metadata, cryptoCtx)
   406  	if err != nil {
   407  		return nil, crypto.ObjectKey{}, err
   408  	}
   409  
   410  	reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20, CipherSuites: fips.DARECiphers()})
   411  	if err != nil {
   412  		return nil, crypto.ObjectKey{}, crypto.ErrInvalidCustomerKey
   413  	}
   414  
   415  	return reader, objectEncryptionKey, nil
   416  }
   417  
   418  // set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of
   419  // SSE-S3
   420  func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) {
   421  	var (
   422  		key    []byte
   423  		keyID  string
   424  		kmsCtx kms.Context
   425  	)
   426  	kind, _ := crypto.IsRequested(r.Header)
   427  	switch kind {
   428  	case crypto.SSEC:
   429  		key, err = ParseSSECustomerRequest(r)
   430  		if err != nil {
   431  			return err
   432  		}
   433  	case crypto.S3KMS:
   434  		keyID, kmsCtx, err = crypto.S3KMS.ParseHTTP(r.Header)
   435  		if err != nil {
   436  			return err
   437  		}
   438  	}
   439  	_, err = newEncryptMetadata(r.Context(), kind, keyID, key, bucket, object, metadata, kmsCtx)
   440  	return
   441  }
   442  
   443  // EncryptRequest takes the client provided content and encrypts the data
   444  // with the client provided key. It also marks the object as client-side-encrypted
   445  // and sets the correct headers.
   446  func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, crypto.ObjectKey, error) {
   447  	if r.ContentLength > encryptBufferThreshold {
   448  		// The encryption reads in blocks of 64KB.
   449  		// We add a buffer on bigger files to reduce the number of syscalls upstream.
   450  		content = bufio.NewReaderSize(content, encryptBufferSize)
   451  	}
   452  
   453  	var (
   454  		key   []byte
   455  		keyID string
   456  		ctx   kms.Context
   457  		err   error
   458  	)
   459  	kind, _ := crypto.IsRequested(r.Header)
   460  	if kind == crypto.SSEC {
   461  		key, err = ParseSSECustomerRequest(r)
   462  		if err != nil {
   463  			return nil, crypto.ObjectKey{}, err
   464  		}
   465  	}
   466  	if kind == crypto.S3KMS {
   467  		keyID, ctx, err = crypto.S3KMS.ParseHTTP(r.Header)
   468  		if err != nil {
   469  			return nil, crypto.ObjectKey{}, err
   470  		}
   471  	}
   472  	return newEncryptReader(r.Context(), content, kind, keyID, key, bucket, object, metadata, ctx)
   473  }
   474  
   475  func decryptObjectMeta(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
   476  	switch kind, _ := crypto.IsEncrypted(metadata); kind {
   477  	case crypto.S3:
   478  		KMS := GlobalKMS
   479  		if KMS == nil {
   480  			return nil, errKMSNotConfigured
   481  		}
   482  		objectKey, err := crypto.S3.UnsealObjectKey(KMS, metadata, bucket, object)
   483  		if err != nil {
   484  			return nil, err
   485  		}
   486  		return objectKey[:], nil
   487  	case crypto.S3KMS:
   488  		if GlobalKMS == nil {
   489  			return nil, errKMSNotConfigured
   490  		}
   491  		objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object)
   492  		if err != nil {
   493  			return nil, err
   494  		}
   495  		return objectKey[:], nil
   496  	case crypto.SSEC:
   497  		sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
   498  		if err != nil {
   499  			return nil, err
   500  		}
   501  		var objectKey crypto.ObjectKey
   502  		if err = objectKey.Unseal(key, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
   503  			return nil, err
   504  		}
   505  		return objectKey[:], nil
   506  	default:
   507  		return nil, errObjectTampered
   508  	}
   509  }
   510  
   511  // Adding support for reader based interface
   512  
   513  // DecryptRequestWithSequenceNumberR - same as
   514  // DecryptRequestWithSequenceNumber but with a reader
   515  func DecryptRequestWithSequenceNumberR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
   516  	if crypto.SSEC.IsEncrypted(metadata) {
   517  		key, err := ParseSSECustomerHeader(h)
   518  		if err != nil {
   519  			return nil, err
   520  		}
   521  		return newDecryptReader(client, key, bucket, object, seqNumber, metadata)
   522  	}
   523  	return newDecryptReader(client, nil, bucket, object, seqNumber, metadata)
   524  }
   525  
   526  // DecryptCopyRequestR - same as DecryptCopyRequest, but with a
   527  // Reader
   528  func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
   529  	var (
   530  		key []byte
   531  		err error
   532  	)
   533  	if crypto.SSECopy.IsRequested(h) {
   534  		key, err = ParseSSECopyCustomerRequest(h, metadata)
   535  		if err != nil {
   536  			return nil, err
   537  		}
   538  	}
   539  	return newDecryptReader(client, key, bucket, object, seqNumber, metadata)
   540  }
   541  
   542  func newDecryptReader(client io.Reader, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
   543  	objectEncryptionKey, err := decryptObjectMeta(key, bucket, object, metadata)
   544  	if err != nil {
   545  		return nil, err
   546  	}
   547  	return newDecryptReaderWithObjectKey(client, objectEncryptionKey, seqNumber)
   548  }
   549  
   550  func newDecryptReaderWithObjectKey(client io.Reader, objectEncryptionKey []byte, seqNumber uint32) (io.Reader, error) {
   551  	reader, err := sio.DecryptReader(client, sio.Config{
   552  		Key:            objectEncryptionKey,
   553  		SequenceNumber: seqNumber,
   554  		CipherSuites:   fips.DARECiphers(),
   555  	})
   556  	if err != nil {
   557  		return nil, crypto.ErrInvalidCustomerKey
   558  	}
   559  	return reader, nil
   560  }
   561  
   562  // DecryptBlocksRequestR - same as DecryptBlocksRequest but with a
   563  // reader
   564  func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint32, partStart int, oi ObjectInfo, copySource bool) (io.Reader, error) {
   565  	bucket, object := oi.Bucket, oi.Name
   566  	// Single part case
   567  	if !oi.isMultipart() {
   568  		var reader io.Reader
   569  		var err error
   570  		if copySource {
   571  			reader, err = DecryptCopyRequestR(inputReader, h, bucket, object, seqNumber, oi.UserDefined)
   572  		} else {
   573  			reader, err = DecryptRequestWithSequenceNumberR(inputReader, h, bucket, object, seqNumber, oi.UserDefined)
   574  		}
   575  		if err != nil {
   576  			return nil, err
   577  		}
   578  		return reader, nil
   579  	}
   580  
   581  	partDecRelOffset := int64(seqNumber) * SSEDAREPackageBlockSize
   582  	partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
   583  
   584  	w := &DecryptBlocksReader{
   585  		reader:            inputReader,
   586  		startSeqNum:       seqNumber,
   587  		partDecRelOffset:  partDecRelOffset,
   588  		partEncRelOffset:  partEncRelOffset,
   589  		parts:             oi.Parts,
   590  		partIndex:         partStart,
   591  		header:            h,
   592  		bucket:            bucket,
   593  		object:            object,
   594  		customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey),
   595  		copySource:        copySource,
   596  		metadata:          cloneMSS(oi.UserDefined),
   597  	}
   598  
   599  	if w.copySource {
   600  		w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey)
   601  	}
   602  
   603  	if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
   604  		return nil, err
   605  	}
   606  
   607  	return w, nil
   608  }
   609  
   610  // DecryptBlocksReader - decrypts multipart parts, while implementing
   611  // a io.Reader compatible interface.
   612  type DecryptBlocksReader struct {
   613  	// Source of the encrypted content that will be decrypted
   614  	reader io.Reader
   615  	// Current decrypter for the current encrypted data block
   616  	decrypter io.Reader
   617  	// Start sequence number
   618  	startSeqNum uint32
   619  	// Current part index
   620  	partIndex int
   621  	// Parts information
   622  	parts          []ObjectPartInfo
   623  	header         http.Header
   624  	bucket, object string
   625  	metadata       map[string]string
   626  
   627  	partDecRelOffset, partEncRelOffset int64
   628  
   629  	copySource bool
   630  	// Customer Key
   631  	customerKeyHeader string
   632  }
   633  
   634  func (d *DecryptBlocksReader) buildDecrypter(partID int) error {
   635  	m := cloneMSS(d.metadata)
   636  	// Initialize the first decrypter; new decrypters will be
   637  	// initialized in Read() operation as needed.
   638  	var key []byte
   639  	var err error
   640  	if d.copySource {
   641  		if crypto.SSEC.IsEncrypted(d.metadata) {
   642  			d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader)
   643  			key, err = ParseSSECopyCustomerRequest(d.header, d.metadata)
   644  		}
   645  	} else {
   646  		if crypto.SSEC.IsEncrypted(d.metadata) {
   647  			d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader)
   648  			key, err = ParseSSECustomerHeader(d.header)
   649  		}
   650  	}
   651  	if err != nil {
   652  		return err
   653  	}
   654  
   655  	objectEncryptionKey, err := decryptObjectMeta(key, d.bucket, d.object, m)
   656  	if err != nil {
   657  		return err
   658  	}
   659  
   660  	var partIDbin [4]byte
   661  	binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID
   662  
   663  	mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key
   664  	mac.Write(partIDbin[:])
   665  	partEncryptionKey := mac.Sum(nil)
   666  
   667  	// Limit the reader, so the decryptor doesn't receive bytes
   668  	// from the next part (different DARE stream)
   669  	encLenToRead := d.parts[d.partIndex].Size - d.partEncRelOffset
   670  	decrypter, err := newDecryptReaderWithObjectKey(io.LimitReader(d.reader, encLenToRead), partEncryptionKey, d.startSeqNum)
   671  	if err != nil {
   672  		return err
   673  	}
   674  
   675  	d.decrypter = decrypter
   676  	return nil
   677  }
   678  
   679  func (d *DecryptBlocksReader) Read(p []byte) (int, error) {
   680  	var err error
   681  	var n1 int
   682  	decPartSize, _ := sio.DecryptedSize(uint64(d.parts[d.partIndex].Size))
   683  	unreadPartLen := int64(decPartSize) - d.partDecRelOffset
   684  	if int64(len(p)) < unreadPartLen {
   685  		n1, err = d.decrypter.Read(p)
   686  		if err != nil {
   687  			return 0, err
   688  		}
   689  		d.partDecRelOffset += int64(n1)
   690  	} else {
   691  		n1, err = io.ReadFull(d.decrypter, p[:unreadPartLen])
   692  		if err != nil {
   693  			return 0, err
   694  		}
   695  
   696  		// We should now proceed to next part, reset all
   697  		// values appropriately.
   698  		d.partEncRelOffset = 0
   699  		d.partDecRelOffset = 0
   700  		d.startSeqNum = 0
   701  
   702  		d.partIndex++
   703  		if d.partIndex == len(d.parts) {
   704  			return n1, io.EOF
   705  		}
   706  
   707  		err = d.buildDecrypter(d.parts[d.partIndex].Number)
   708  		if err != nil {
   709  			return 0, err
   710  		}
   711  
   712  		n1, err = d.decrypter.Read(p[n1:])
   713  		if err != nil {
   714  			return 0, err
   715  		}
   716  
   717  		d.partDecRelOffset += int64(n1)
   718  	}
   719  	return len(p), nil
   720  }
   721  
   722  // DecryptedSize returns the size of the object after decryption in bytes.
   723  // It returns an error if the object is not encrypted or marked as encrypted
   724  // but has an invalid size.
   725  func (o ObjectInfo) DecryptedSize() (int64, error) {
   726  	if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
   727  		return 0, errors.New("Cannot compute decrypted size of an unencrypted object")
   728  	}
   729  	if !o.isMultipart() {
   730  		size, err := sio.DecryptedSize(uint64(o.Size))
   731  		if err != nil {
   732  			err = errObjectTampered // assign correct error type
   733  		}
   734  		return int64(size), err
   735  	}
   736  
   737  	var size int64
   738  	for _, part := range o.Parts {
   739  		partSize, err := sio.DecryptedSize(uint64(part.Size))
   740  		if err != nil {
   741  			return 0, errObjectTampered
   742  		}
   743  		size += int64(partSize)
   744  	}
   745  	return size, nil
   746  }
   747  
   748  // DecryptETag decrypts the ETag that is part of given object
   749  // with the given object encryption key.
   750  //
   751  // However, DecryptETag does not try to decrypt the ETag if
   752  // it consists of a 128 bit hex value (32 hex chars) and exactly
   753  // one '-' followed by a 32-bit number.
   754  // This special case addresses randomly-generated ETags generated
   755  // by the MinIO server when running in non-compat mode. These
   756  // random ETags are not encrypt.
   757  //
   758  // Calling DecryptETag with a non-randomly generated ETag will
   759  // fail.
   760  func DecryptETag(key crypto.ObjectKey, object ObjectInfo) (string, error) {
   761  	if n := strings.Count(object.ETag, "-"); n > 0 {
   762  		if n != 1 {
   763  			return "", errObjectTampered
   764  		}
   765  		i := strings.IndexByte(object.ETag, '-')
   766  		if len(object.ETag[:i]) != 32 {
   767  			return "", errObjectTampered
   768  		}
   769  		if _, err := hex.DecodeString(object.ETag[:32]); err != nil {
   770  			return "", errObjectTampered
   771  		}
   772  		if _, err := strconv.ParseInt(object.ETag[i+1:], 10, 32); err != nil {
   773  			return "", errObjectTampered
   774  		}
   775  		return object.ETag, nil
   776  	}
   777  
   778  	etag, err := hex.DecodeString(object.ETag)
   779  	if err != nil {
   780  		return "", err
   781  	}
   782  	etag, err = key.UnsealETag(etag)
   783  	if err != nil {
   784  		return "", err
   785  	}
   786  	return hex.EncodeToString(etag), nil
   787  }
   788  
   789  // For encrypted objects, the ETag sent by client if available
   790  // is stored in encrypted form in the backend. Decrypt the ETag
   791  // if ETag was previously encrypted.
   792  func getDecryptedETag(headers http.Header, objInfo ObjectInfo, copySource bool) (decryptedETag string) {
   793  	var (
   794  		key [32]byte
   795  		err error
   796  	)
   797  	// If ETag is contentMD5Sum return it as is.
   798  	if len(objInfo.ETag) == 32 {
   799  		return objInfo.ETag
   800  	}
   801  
   802  	if crypto.IsMultiPart(objInfo.UserDefined) {
   803  		return objInfo.ETag
   804  	}
   805  
   806  	if crypto.SSECopy.IsRequested(headers) {
   807  		key, err = crypto.SSECopy.ParseHTTP(headers)
   808  		if err != nil {
   809  			return objInfo.ETag
   810  		}
   811  	}
   812  
   813  	// As per AWS S3 Spec, ETag for SSE-C encrypted objects need not be MD5Sum of the data.
   814  	// Since server side copy with same source and dest just replaces the ETag, we save
   815  	// encrypted content MD5Sum as ETag for both SSE-C and SSE-KMS, we standardize the ETag
   816  	// encryption across SSE-C and SSE-KMS, and only return last 32 bytes for SSE-C
   817  	if (crypto.SSEC.IsEncrypted(objInfo.UserDefined) || crypto.S3KMS.IsEncrypted(objInfo.UserDefined)) && !copySource {
   818  		return objInfo.ETag[len(objInfo.ETag)-32:]
   819  	}
   820  
   821  	objectEncryptionKey, err := decryptObjectMeta(key[:], objInfo.Bucket, objInfo.Name, objInfo.UserDefined)
   822  	if err != nil {
   823  		return objInfo.ETag
   824  	}
   825  	return tryDecryptETag(objectEncryptionKey, objInfo.ETag, true)
   826  }
   827  
   828  // helper to decrypt Etag given object encryption key and encrypted ETag
   829  func tryDecryptETag(key []byte, encryptedETag string, sses3 bool) string {
   830  	// ETag for SSE-C or SSE-KMS encrypted objects need not be content MD5Sum.While encrypted
   831  	// md5sum is stored internally, return just the last 32 bytes of hex-encoded and
   832  	// encrypted md5sum string for SSE-C
   833  	if !sses3 {
   834  		return encryptedETag[len(encryptedETag)-32:]
   835  	}
   836  	var objectKey crypto.ObjectKey
   837  	copy(objectKey[:], key)
   838  	encBytes, err := hex.DecodeString(encryptedETag)
   839  	if err != nil {
   840  		return encryptedETag
   841  	}
   842  	etagBytes, err := objectKey.UnsealETag(encBytes)
   843  	if err != nil {
   844  		return encryptedETag
   845  	}
   846  	return hex.EncodeToString(etagBytes)
   847  }
   848  
   849  // GetDecryptedRange - To decrypt the range (off, length) of the
   850  // decrypted object stream, we need to read the range (encOff,
   851  // encLength) of the encrypted object stream to decrypt it, and
   852  // compute skipLen, the number of bytes to skip in the beginning of
   853  // the encrypted range.
   854  //
   855  // In addition we also compute the object part number for where the
   856  // requested range starts, along with the DARE sequence number within
   857  // that part. For single part objects, the partStart will be 0.
   858  func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) {
   859  	if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
   860  		err = errors.New("Object is not encrypted")
   861  		return
   862  	}
   863  
   864  	if rs == nil {
   865  		// No range, so offsets refer to the whole object.
   866  		return 0, o.Size, 0, 0, 0, nil
   867  	}
   868  
   869  	// Assemble slice of (decrypted) part sizes in `sizes`
   870  	var sizes []int64
   871  	var decObjSize int64 // decrypted total object size
   872  	if o.isMultipart() {
   873  		sizes = make([]int64, len(o.Parts))
   874  		for i, part := range o.Parts {
   875  			var partSize uint64
   876  			partSize, err = sio.DecryptedSize(uint64(part.Size))
   877  			if err != nil {
   878  				err = errObjectTampered
   879  				return
   880  			}
   881  			sizes[i] = int64(partSize)
   882  			decObjSize += int64(partSize)
   883  		}
   884  	} else {
   885  		var partSize uint64
   886  		partSize, err = sio.DecryptedSize(uint64(o.Size))
   887  		if err != nil {
   888  			err = errObjectTampered
   889  			return
   890  		}
   891  		sizes = []int64{int64(partSize)}
   892  		decObjSize = sizes[0]
   893  	}
   894  
   895  	var off, length int64
   896  	off, length, err = rs.GetOffsetLength(decObjSize)
   897  	if err != nil {
   898  		return
   899  	}
   900  
   901  	// At this point, we have:
   902  	//
   903  	// 1. the decrypted part sizes in `sizes` (single element for
   904  	//    single part object) and total decrypted object size `decObjSize`
   905  	//
   906  	// 2. the (decrypted) start offset `off` and (decrypted)
   907  	//    length to read `length`
   908  	//
   909  	// These are the inputs to the rest of the algorithm below.
   910  
   911  	// Locate the part containing the start of the required range
   912  	var partEnd int
   913  	var cumulativeSum, encCumulativeSum int64
   914  	for i, size := range sizes {
   915  		if off < cumulativeSum+size {
   916  			partStart = i
   917  			break
   918  		}
   919  		cumulativeSum += size
   920  		encPartSize, _ := sio.EncryptedSize(uint64(size))
   921  		encCumulativeSum += int64(encPartSize)
   922  	}
   923  	// partStart is always found in the loop above,
   924  	// because off is validated.
   925  
   926  	sseDAREEncPackageBlockSize := int64(SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
   927  	startPkgNum := (off - cumulativeSum) / SSEDAREPackageBlockSize
   928  
   929  	// Now we can calculate the number of bytes to skip
   930  	skipLen = (off - cumulativeSum) % SSEDAREPackageBlockSize
   931  
   932  	encOff = encCumulativeSum + startPkgNum*sseDAREEncPackageBlockSize
   933  	// Locate the part containing the end of the required range
   934  	endOffset := off + length - 1
   935  	for i1, size := range sizes[partStart:] {
   936  		i := partStart + i1
   937  		if endOffset < cumulativeSum+size {
   938  			partEnd = i
   939  			break
   940  		}
   941  		cumulativeSum += size
   942  		encPartSize, _ := sio.EncryptedSize(uint64(size))
   943  		encCumulativeSum += int64(encPartSize)
   944  	}
   945  	// partEnd is always found in the loop above, because off and
   946  	// length are validated.
   947  	endPkgNum := (endOffset - cumulativeSum) / SSEDAREPackageBlockSize
   948  	// Compute endEncOffset with one additional DARE package (so
   949  	// we read the package containing the last desired byte).
   950  	endEncOffset := encCumulativeSum + (endPkgNum+1)*sseDAREEncPackageBlockSize
   951  	// Check if the DARE package containing the end offset is a
   952  	// full sized package (as the last package in the part may be
   953  	// smaller)
   954  	lastPartSize, _ := sio.EncryptedSize(uint64(sizes[partEnd]))
   955  	if endEncOffset > encCumulativeSum+int64(lastPartSize) {
   956  		endEncOffset = encCumulativeSum + int64(lastPartSize)
   957  	}
   958  	encLength = endEncOffset - encOff
   959  	// Set the sequence number as the starting package number of
   960  	// the requested block
   961  	seqNumber = uint32(startPkgNum)
   962  	return encOff, encLength, skipLen, seqNumber, partStart, nil
   963  }
   964  
   965  // EncryptedSize returns the size of the object after encryption.
   966  // An encrypted object is always larger than a plain object
   967  // except for zero size objects.
   968  func (o *ObjectInfo) EncryptedSize() int64 {
   969  	size, err := sio.EncryptedSize(uint64(o.Size))
   970  	if err != nil {
   971  		// This cannot happen since AWS S3 allows parts to be 5GB at most
   972  		// sio max. size is 256 TB
   973  		reqInfo := (&logger.ReqInfo{}).AppendTags("size", strconv.FormatUint(size, 10))
   974  		ctx := logger.SetReqInfo(GlobalContext, reqInfo)
   975  		logger.CriticalIf(ctx, err)
   976  	}
   977  	return int64(size)
   978  }
   979  
   980  // DecryptObjectInfo tries to decrypt the provided object if it is encrypted.
   981  // It fails if the object is encrypted and the HTTP headers don't contain
   982  // SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior)
   983  // DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the
   984  // decryption succeeded.
   985  //
   986  // DecryptObjectInfo also returns whether the object is encrypted or not.
   987  func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err error) {
   988  	// Directories are never encrypted.
   989  	if info.IsDir {
   990  		return false, nil
   991  	}
   992  	if r == nil {
   993  		return false, errInvalidArgument
   994  	}
   995  
   996  	headers := r.Header
   997  
   998  	// disallow X-Amz-Server-Side-Encryption header on HEAD and GET
   999  	switch r.Method {
  1000  	case http.MethodGet, http.MethodHead:
  1001  		if crypto.S3.IsRequested(headers) || crypto.S3KMS.IsRequested(headers) {
  1002  			return false, errInvalidEncryptionParameters
  1003  		}
  1004  	}
  1005  
  1006  	_, encrypted = crypto.IsEncrypted(info.UserDefined)
  1007  	if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" {
  1008  		return false, errInvalidEncryptionParameters
  1009  	}
  1010  
  1011  	if encrypted {
  1012  		if crypto.SSEC.IsEncrypted(info.UserDefined) {
  1013  			if !(crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers)) {
  1014  				return encrypted, errEncryptedObject
  1015  			}
  1016  		}
  1017  
  1018  		if crypto.S3.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" {
  1019  			if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) {
  1020  				return encrypted, errEncryptedObject
  1021  			}
  1022  		}
  1023  
  1024  		if crypto.S3KMS.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" {
  1025  			if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) {
  1026  				return encrypted, errEncryptedObject
  1027  			}
  1028  		}
  1029  
  1030  		if _, err = info.DecryptedSize(); err != nil {
  1031  			return encrypted, err
  1032  		}
  1033  
  1034  		if _, ok := crypto.IsEncrypted(info.UserDefined); ok && !crypto.IsMultiPart(info.UserDefined) {
  1035  			info.ETag = getDecryptedETag(headers, *info, false)
  1036  		}
  1037  	}
  1038  
  1039  	return encrypted, nil
  1040  }
  1041  
  1042  type (
  1043  	objectMetaEncryptFn func(baseKey string, data []byte) []byte
  1044  	objectMetaDecryptFn func(baseKey string, data []byte) ([]byte, error)
  1045  )
  1046  
  1047  // metadataEncrypter returns a function that will read data from input,
  1048  // encrypt it using the provided key and return the result.
  1049  // 0 sized inputs are passed through.
  1050  func metadataEncrypter(key crypto.ObjectKey) objectMetaEncryptFn {
  1051  	return func(baseKey string, data []byte) []byte {
  1052  		if len(data) == 0 {
  1053  			return data
  1054  		}
  1055  		var buffer bytes.Buffer
  1056  		mac := hmac.New(sha256.New, key[:])
  1057  		mac.Write([]byte(baseKey))
  1058  		if _, err := sio.Encrypt(&buffer, bytes.NewReader(data), sio.Config{Key: mac.Sum(nil), CipherSuites: fips.DARECiphers()}); err != nil {
  1059  			logger.CriticalIf(context.Background(), errors.New("unable to encrypt using object key"))
  1060  		}
  1061  		return buffer.Bytes()
  1062  	}
  1063  }
  1064  
  1065  // metadataDecrypter reverses metadataEncrypter.
  1066  func (o *ObjectInfo) metadataDecrypter() objectMetaDecryptFn {
  1067  	return func(baseKey string, input []byte) ([]byte, error) {
  1068  		if len(input) == 0 {
  1069  			return input, nil
  1070  		}
  1071  
  1072  		key, err := decryptObjectMeta(nil, o.Bucket, o.Name, o.UserDefined)
  1073  		if err != nil {
  1074  			return nil, err
  1075  		}
  1076  		mac := hmac.New(sha256.New, key)
  1077  		mac.Write([]byte(baseKey))
  1078  		return sio.DecryptBuffer(nil, input, sio.Config{Key: mac.Sum(nil), CipherSuites: fips.DARECiphers()})
  1079  	}
  1080  }
  1081  
  1082  // decryptChecksums will attempt to decode checksums and return it/them if set.
  1083  // if part > 0, and we have the checksum for the part that will be returned.
  1084  func (o *ObjectInfo) decryptPartsChecksums() {
  1085  	data := o.Checksum
  1086  	if len(data) == 0 {
  1087  		return
  1088  	}
  1089  	if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted {
  1090  		decrypted, err := o.metadataDecrypter()("object-checksum", data)
  1091  		if err != nil {
  1092  			logger.LogIf(GlobalContext, err)
  1093  			return
  1094  		}
  1095  		data = decrypted
  1096  	}
  1097  	cs := hash.ReadPartCheckSums(data)
  1098  	if len(cs) == len(o.Parts) {
  1099  		for i := range o.Parts {
  1100  			o.Parts[i].Checksums = cs[i]
  1101  		}
  1102  	}
  1103  	return
  1104  }
  1105  
  1106  // metadataEncryptFn provides an encryption function for metadata.
  1107  // Will return nil, nil if unencrypted.
  1108  func (o *ObjectInfo) metadataEncryptFn(headers http.Header) (objectMetaEncryptFn, error) {
  1109  	kind, _ := crypto.IsEncrypted(o.UserDefined)
  1110  	switch kind {
  1111  	case crypto.SSEC:
  1112  		if crypto.SSECopy.IsRequested(headers) {
  1113  			key, err := crypto.SSECopy.ParseHTTP(headers)
  1114  			if err != nil {
  1115  				return nil, err
  1116  			}
  1117  			objectEncryptionKey, err := decryptObjectMeta(key[:], o.Bucket, o.Name, o.UserDefined)
  1118  			if err != nil {
  1119  				return nil, err
  1120  			}
  1121  			if len(objectEncryptionKey) == 32 {
  1122  				var key crypto.ObjectKey
  1123  				copy(key[:], objectEncryptionKey)
  1124  				return metadataEncrypter(key), nil
  1125  			}
  1126  			return nil, errors.New("metadataEncryptFn: unexpected key size")
  1127  		}
  1128  	case crypto.S3, crypto.S3KMS:
  1129  		objectEncryptionKey, err := decryptObjectMeta(nil, o.Bucket, o.Name, o.UserDefined)
  1130  		if err != nil {
  1131  			return nil, err
  1132  		}
  1133  		if len(objectEncryptionKey) == 32 {
  1134  			var key crypto.ObjectKey
  1135  			copy(key[:], objectEncryptionKey)
  1136  			return metadataEncrypter(key), nil
  1137  		}
  1138  		return nil, errors.New("metadataEncryptFn: unexpected key size")
  1139  	}
  1140  
  1141  	return nil, nil
  1142  }
  1143  
  1144  // decryptChecksums will attempt to decode checksums and return it/them if set.
  1145  // if part > 0, and we have the checksum for the part that will be returned.
  1146  func (o *ObjectInfo) decryptChecksums(part int) map[string]string {
  1147  	data := o.Checksum
  1148  	if len(data) == 0 {
  1149  		return nil
  1150  	}
  1151  	if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted {
  1152  		decrypted, err := o.metadataDecrypter()("object-checksum", data)
  1153  		if err != nil {
  1154  			logger.LogIf(GlobalContext, err)
  1155  			return nil
  1156  		}
  1157  		data = decrypted
  1158  	}
  1159  	return hash.ReadCheckSums(data, part)
  1160  }