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  }