storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/crypto/sse-c.go (about)

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