mirror of https://github.com/minio/minio.git
refactor cmd/crypto code for SSE handling and parsing (#11045)
This commit refactors the code in `cmd/crypto` and separates SSE-S3, SSE-C and SSE-KMS. This commit should not cause any behavior change except for: - `IsRequested(http.Header)` which now returns the requested type {SSE-C, SSE-S3, SSE-KMS} and does not consider SSE-C copy headers. However, SSE-C copy headers alone are anyway not valid.
This commit is contained in:
parent
35fafb837b
commit
8cdf2106b0
|
@ -748,7 +748,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -895,7 +895,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
||||||
if _, err = globalBucketSSEConfigSys.Get(bucket); err == nil || globalAutoEncryption {
|
if _, err = globalBucketSSEConfigSys.Get(bucket); err == nil || globalAutoEncryption {
|
||||||
// This request header needs to be set prior to setting ObjectOptions
|
// This request header needs to be set prior to setting ObjectOptions
|
||||||
if !crypto.SSEC.IsRequested(r.Header) {
|
if !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,7 +907,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsRequested(formValues) && !HasSuffix(object, SlashSeparator) { // handle SSE requests
|
if _, ok := crypto.IsRequested(formValues); ok && !HasSuffix(object, SlashSeparator) { // handle SSE requests
|
||||||
if crypto.SSECopy.IsRequested(r.Header) {
|
if crypto.SSECopy.IsRequested(r.Header) {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
|
|
|
@ -28,7 +28,6 @@ import (
|
||||||
|
|
||||||
miniogo "github.com/minio/minio-go/v7"
|
miniogo "github.com/minio/minio-go/v7"
|
||||||
"github.com/minio/minio-go/v7/pkg/tags"
|
"github.com/minio/minio-go/v7/pkg/tags"
|
||||||
"github.com/minio/minio/cmd/crypto"
|
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
sse "github.com/minio/minio/pkg/bucket/encryption"
|
sse "github.com/minio/minio/pkg/bucket/encryption"
|
||||||
|
@ -547,7 +546,7 @@ func (r *RestoreObjectRequest) validate(ctx context.Context, objAPI ObjectLayer)
|
||||||
if r.OutputLocation.S3.Prefix == "" {
|
if r.OutputLocation.S3.Prefix == "" {
|
||||||
return fmt.Errorf("Prefix is a required parameter in OutputLocation")
|
return fmt.Errorf("Prefix is a required parameter in OutputLocation")
|
||||||
}
|
}
|
||||||
if r.OutputLocation.S3.Encryption.EncryptionType != crypto.SSEAlgorithmAES256 {
|
if r.OutputLocation.S3.Encryption.EncryptionType != xhttp.AmzEncryptionAES {
|
||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,7 +572,7 @@ func putRestoreOpts(bucket, object string, rreq *RestoreObjectRequest, objInfo O
|
||||||
}
|
}
|
||||||
meta[xhttp.AmzObjectTagging] = rreq.OutputLocation.S3.Tagging.String()
|
meta[xhttp.AmzObjectTagging] = rreq.OutputLocation.S3.Tagging.String()
|
||||||
if rreq.OutputLocation.S3.Encryption.EncryptionType != "" {
|
if rreq.OutputLocation.S3.Encryption.EncryptionType != "" {
|
||||||
meta[crypto.SSEHeader] = crypto.SSEAlgorithmAES256
|
meta[xhttp.AmzServerSideEncryption] = xhttp.AmzEncryptionAES
|
||||||
}
|
}
|
||||||
return ObjectOptions{
|
return ObjectOptions{
|
||||||
Versioned: globalBucketVersioningSys.Enabled(bucket),
|
Versioned: globalBucketVersioningSys.Enabled(bucket),
|
||||||
|
|
|
@ -19,231 +19,62 @@ import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SSEHeader is the general AWS SSE HTTP header key.
|
|
||||||
const SSEHeader = "X-Amz-Server-Side-Encryption"
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SSEKmsID is the HTTP header key referencing the SSE-KMS
|
|
||||||
// key ID.
|
|
||||||
SSEKmsID = SSEHeader + "-Aws-Kms-Key-Id"
|
|
||||||
|
|
||||||
// SSEKmsContext is the HTTP header key referencing the
|
|
||||||
// SSE-KMS encryption context.
|
|
||||||
SSEKmsContext = SSEHeader + "-Context"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SSECAlgorithm is the HTTP header key referencing
|
|
||||||
// the SSE-C algorithm.
|
|
||||||
SSECAlgorithm = SSEHeader + "-Customer-Algorithm"
|
|
||||||
|
|
||||||
// SSECKey is the HTTP header key referencing the
|
|
||||||
// SSE-C client-provided key..
|
|
||||||
SSECKey = SSEHeader + "-Customer-Key"
|
|
||||||
|
|
||||||
// SSECKeyMD5 is the HTTP header key referencing
|
|
||||||
// the MD5 sum of the client-provided key.
|
|
||||||
SSECKeyMD5 = SSEHeader + "-Customer-Key-Md5"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SSECopyAlgorithm is the HTTP header key referencing
|
|
||||||
// the SSE-C algorithm for SSE-C copy requests.
|
|
||||||
SSECopyAlgorithm = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm"
|
|
||||||
|
|
||||||
// SSECopyKey is the HTTP header key referencing the SSE-C
|
|
||||||
// client-provided key for SSE-C copy requests.
|
|
||||||
SSECopyKey = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key"
|
|
||||||
|
|
||||||
// SSECopyKeyMD5 is the HTTP header key referencing the
|
|
||||||
// MD5 sum of the client key for SSE-C copy requests.
|
|
||||||
SSECopyKeyMD5 = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SSEAlgorithmAES256 is the only supported value for the SSE-S3 or SSE-C algorithm header.
|
|
||||||
// For SSE-S3 see: https://docs.aws.amazon.com/AmazonS3/latest/dev/SSEUsingRESTAPI.html
|
|
||||||
// For SSE-C see: https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html
|
|
||||||
SSEAlgorithmAES256 = "AES256"
|
|
||||||
|
|
||||||
// SSEAlgorithmKMS is the value of 'X-Amz-Server-Side-Encryption' for SSE-KMS.
|
|
||||||
// See: https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html
|
|
||||||
SSEAlgorithmKMS = "aws:kms"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RemoveSensitiveHeaders removes confidential encryption
|
// RemoveSensitiveHeaders removes confidential encryption
|
||||||
// information - e.g. the SSE-C key - from the HTTP headers.
|
// information - e.g. the SSE-C key - from the HTTP headers.
|
||||||
// It has the same semantics as RemoveSensitiveEntires.
|
// It has the same semantics as RemoveSensitiveEntires.
|
||||||
func RemoveSensitiveHeaders(h http.Header) {
|
func RemoveSensitiveHeaders(h http.Header) {
|
||||||
h.Del(SSECKey)
|
h.Del(xhttp.AmzServerSideEncryptionCustomerKey)
|
||||||
h.Del(SSECopyKey)
|
h.Del(xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
||||||
h.Del(xhttp.AmzMetaUnencryptedContentLength)
|
h.Del(xhttp.AmzMetaUnencryptedContentLength)
|
||||||
h.Del(xhttp.AmzMetaUnencryptedContentMD5)
|
h.Del(xhttp.AmzMetaUnencryptedContentMD5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRequested returns true if the HTTP headers indicates
|
|
||||||
// that any form server-side encryption (SSE-C, SSE-S3 or SSE-KMS)
|
|
||||||
// is requested.
|
|
||||||
func IsRequested(h http.Header) bool {
|
|
||||||
return S3.IsRequested(h) || SSEC.IsRequested(h) || SSECopy.IsRequested(h) || S3KMS.IsRequested(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
// S3 represents AWS SSE-S3. It provides functionality to handle
|
|
||||||
// SSE-S3 requests.
|
|
||||||
var S3 = s3{}
|
|
||||||
|
|
||||||
type s3 struct{}
|
|
||||||
|
|
||||||
// IsRequested returns true if the HTTP headers indicates that
|
|
||||||
// the S3 client requests SSE-S3.
|
|
||||||
func (s3) IsRequested(h http.Header) bool {
|
|
||||||
_, ok := h[SSEHeader]
|
|
||||||
return ok && strings.ToLower(h.Get(SSEHeader)) != SSEAlgorithmKMS // Return only true if the SSE header is specified and does not contain the SSE-KMS value
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseHTTP parses the SSE-S3 related HTTP headers and checks
|
|
||||||
// whether they contain valid values.
|
|
||||||
func (s3) ParseHTTP(h http.Header) (err error) {
|
|
||||||
if h.Get(SSEHeader) != SSEAlgorithmAES256 {
|
|
||||||
err = ErrInvalidEncryptionMethod
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// S3KMS represents AWS SSE-KMS. It provides functionality to
|
|
||||||
// handle SSE-KMS requests.
|
|
||||||
var S3KMS = s3KMS{}
|
|
||||||
|
|
||||||
type s3KMS struct{}
|
|
||||||
|
|
||||||
// IsRequested returns true if the HTTP headers indicates that
|
|
||||||
// the S3 client requests SSE-KMS.
|
|
||||||
func (s3KMS) IsRequested(h http.Header) bool {
|
|
||||||
if _, ok := h[SSEKmsID]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := h[SSEKmsContext]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := h[SSEHeader]; ok {
|
|
||||||
return strings.ToUpper(h.Get(SSEHeader)) != SSEAlgorithmAES256 // Return only true if the SSE header is specified and does not contain the SSE-S3 value
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseHTTP parses the SSE-KMS headers and returns the SSE-KMS key ID
|
|
||||||
// and context, if present, on success.
|
|
||||||
func (s3KMS) ParseHTTP(h http.Header) (string, interface{}, error) {
|
|
||||||
algorithm := h.Get(SSEHeader)
|
|
||||||
if algorithm != SSEAlgorithmKMS {
|
|
||||||
return "", nil, ErrInvalidEncryptionMethod
|
|
||||||
}
|
|
||||||
|
|
||||||
contextStr, ok := h[SSEKmsContext]
|
|
||||||
if ok {
|
|
||||||
var context map[string]interface{}
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
if err := json.Unmarshal([]byte(contextStr[0]), &context); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
return h.Get(SSEKmsID), context, nil
|
|
||||||
}
|
|
||||||
return h.Get(SSEKmsID), nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// SSEC represents AWS SSE-C. It provides functionality to handle
|
|
||||||
// SSE-C requests.
|
|
||||||
SSEC = ssec{}
|
|
||||||
|
|
||||||
// SSECopy represents AWS SSE-C for copy requests. It provides
|
// SSECopy represents AWS SSE-C for copy requests. It provides
|
||||||
// functionality to handle SSE-C copy requests.
|
// functionality to handle SSE-C copy requests.
|
||||||
SSECopy = ssecCopy{}
|
SSECopy = ssecCopy{}
|
||||||
)
|
)
|
||||||
|
|
||||||
type ssec struct{}
|
|
||||||
type ssecCopy struct{}
|
type ssecCopy struct{}
|
||||||
|
|
||||||
// IsRequested returns true if the HTTP headers contains
|
|
||||||
// at least one SSE-C header. SSE-C copy headers are ignored.
|
|
||||||
func (ssec) IsRequested(h http.Header) bool {
|
|
||||||
if _, ok := h[SSECAlgorithm]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := h[SSECKey]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := h[SSECKeyMD5]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRequested returns true if the HTTP headers contains
|
// IsRequested returns true if the HTTP headers contains
|
||||||
// at least one SSE-C copy header. Regular SSE-C headers
|
// at least one SSE-C copy header. Regular SSE-C headers
|
||||||
// are ignored.
|
// are ignored.
|
||||||
func (ssecCopy) IsRequested(h http.Header) bool {
|
func (ssecCopy) IsRequested(h http.Header) bool {
|
||||||
if _, ok := h[SSECopyAlgorithm]; ok {
|
if _, ok := h[xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := h[SSECopyKey]; ok {
|
if _, ok := h[xhttp.AmzServerSideEncryptionCopyCustomerKey]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := h[SSECopyKeyMD5]; ok {
|
if _, ok := h[xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseHTTP parses the SSE-C headers and returns the SSE-C client key
|
|
||||||
// on success. SSE-C copy headers are ignored.
|
|
||||||
func (ssec) ParseHTTP(h http.Header) (key [32]byte, err error) {
|
|
||||||
if h.Get(SSECAlgorithm) != SSEAlgorithmAES256 {
|
|
||||||
return key, ErrInvalidCustomerAlgorithm
|
|
||||||
}
|
|
||||||
if h.Get(SSECKey) == "" {
|
|
||||||
return key, ErrMissingCustomerKey
|
|
||||||
}
|
|
||||||
if h.Get(SSECKeyMD5) == "" {
|
|
||||||
return key, ErrMissingCustomerKeyMD5
|
|
||||||
}
|
|
||||||
|
|
||||||
clientKey, err := base64.StdEncoding.DecodeString(h.Get(SSECKey))
|
|
||||||
if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long
|
|
||||||
return key, ErrInvalidCustomerKey
|
|
||||||
}
|
|
||||||
keyMD5, err := base64.StdEncoding.DecodeString(h.Get(SSECKeyMD5))
|
|
||||||
if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) {
|
|
||||||
return key, ErrCustomerKeyMD5Mismatch
|
|
||||||
}
|
|
||||||
copy(key[:], clientKey)
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseHTTP parses the SSE-C copy headers and returns the SSE-C client key
|
// ParseHTTP parses the SSE-C copy headers and returns the SSE-C client key
|
||||||
// on success. Regular SSE-C headers are ignored.
|
// on success. Regular SSE-C headers are ignored.
|
||||||
func (ssecCopy) ParseHTTP(h http.Header) (key [32]byte, err error) {
|
func (ssecCopy) ParseHTTP(h http.Header) (key [32]byte, err error) {
|
||||||
if h.Get(SSECopyAlgorithm) != SSEAlgorithmAES256 {
|
if h.Get(xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm) != xhttp.AmzEncryptionAES {
|
||||||
return key, ErrInvalidCustomerAlgorithm
|
return key, ErrInvalidCustomerAlgorithm
|
||||||
}
|
}
|
||||||
if h.Get(SSECopyKey) == "" {
|
if h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey) == "" {
|
||||||
return key, ErrMissingCustomerKey
|
return key, ErrMissingCustomerKey
|
||||||
}
|
}
|
||||||
if h.Get(SSECopyKeyMD5) == "" {
|
if h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5) == "" {
|
||||||
return key, ErrMissingCustomerKeyMD5
|
return key, ErrMissingCustomerKeyMD5
|
||||||
}
|
}
|
||||||
|
|
||||||
clientKey, err := base64.StdEncoding.DecodeString(h.Get(SSECopyKey))
|
clientKey, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey))
|
||||||
if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long
|
if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long
|
||||||
return key, ErrInvalidCustomerKey
|
return key, ErrInvalidCustomerKey
|
||||||
}
|
}
|
||||||
keyMD5, err := base64.StdEncoding.DecodeString(h.Get(SSECopyKeyMD5))
|
keyMD5, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5))
|
||||||
if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) {
|
if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) {
|
||||||
return key, ErrCustomerKeyMD5Mismatch
|
return key, ErrCustomerKeyMD5Mismatch
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,26 +18,29 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsRequested(t *testing.T) {
|
func TestIsRequested(t *testing.T) {
|
||||||
for i, test := range kmsIsRequestedTests {
|
for i, test := range kmsIsRequestedTests {
|
||||||
if got := IsRequested(test.Header) && S3KMS.IsRequested(test.Header); got != test.Expected {
|
_, got := IsRequested(test.Header)
|
||||||
|
got = got && S3KMS.IsRequested(test.Header)
|
||||||
|
if got != test.Expected {
|
||||||
t.Errorf("SSE-KMS: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
t.Errorf("SSE-KMS: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, test := range s3IsRequestedTests {
|
for i, test := range s3IsRequestedTests {
|
||||||
if got := IsRequested(test.Header) && S3.IsRequested(test.Header); got != test.Expected {
|
_, got := IsRequested(test.Header)
|
||||||
|
got = got && S3.IsRequested(test.Header)
|
||||||
|
if got != test.Expected {
|
||||||
t.Errorf("SSE-S3: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
t.Errorf("SSE-S3: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, test := range ssecIsRequestedTests {
|
for i, test := range ssecIsRequestedTests {
|
||||||
if got := IsRequested(test.Header) && SSEC.IsRequested(test.Header); got != test.Expected {
|
_, got := IsRequested(test.Header)
|
||||||
t.Errorf("SSE-C: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
got = got && SSEC.IsRequested(test.Header)
|
||||||
}
|
if got != test.Expected {
|
||||||
}
|
|
||||||
for i, test := range ssecCopyIsRequestedTests {
|
|
||||||
if got := IsRequested(test.Header) && SSECopy.IsRequested(test.Header); got != test.Expected {
|
|
||||||
t.Errorf("SSE-C: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
t.Errorf("SSE-C: Test %d: Wanted %v but got %v", i, test.Expected, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,11 +134,11 @@ var s3IsRequestedTests = []struct {
|
||||||
Header http.Header
|
Header http.Header
|
||||||
Expected bool
|
Expected bool
|
||||||
}{
|
}{
|
||||||
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES256"}}, Expected: true}, // 0
|
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES256"}}, Expected: true}, // 0
|
||||||
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES-256"}}, Expected: true}, // 1
|
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES-256"}}, Expected: true}, // 1
|
||||||
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{""}}, Expected: true}, // 2
|
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{""}}, Expected: true}, // 2
|
||||||
{Header: http.Header{"X-Amz-Server-Side-Encryptio": []string{"AES256"}}, Expected: false}, // 3
|
{Header: http.Header{"X-Amz-Server-Side-Encryptio": []string{"AES256"}}, Expected: false}, // 3
|
||||||
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{SSEAlgorithmKMS}}, Expected: false}, // 4
|
{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{xhttp.AmzEncryptionKMS}}, Expected: false}, // 4
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestS3IsRequested(t *testing.T) {
|
func TestS3IsRequested(t *testing.T) {
|
||||||
|
@ -403,7 +406,7 @@ func TestSSECopyParse(t *testing.T) {
|
||||||
if err == nil && key == zeroKey {
|
if err == nil && key == zeroKey {
|
||||||
t.Errorf("Test %d: parsed client key is zero key", i)
|
t.Errorf("Test %d: parsed client key is zero key", i)
|
||||||
}
|
}
|
||||||
if _, ok := test.Header[SSECKey]; ok {
|
if _, ok := test.Header[xhttp.AmzServerSideEncryptionCustomerKey]; ok {
|
||||||
t.Errorf("Test %d: client key is not removed from HTTP headers after parsing", i)
|
t.Errorf("Test %d: client key is not removed from HTTP headers after parsing", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,47 +417,47 @@ var removeSensitiveHeadersTests = []struct {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
SSECKey: []string{""},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{""},
|
||||||
SSECopyKey: []string{""},
|
xhttp.AmzServerSideEncryptionCopyCustomerKey: []string{""},
|
||||||
},
|
},
|
||||||
ExpectedHeader: http.Header{},
|
ExpectedHeader: http.Header{},
|
||||||
},
|
},
|
||||||
{ // Standard SSE-C request headers
|
{ // Standard SSE-C request headers
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
SSECAlgorithm: []string{SSEAlgorithmAES256},
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
SSECKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
},
|
},
|
||||||
ExpectedHeader: http.Header{
|
ExpectedHeader: http.Header{
|
||||||
SSECAlgorithm: []string{SSEAlgorithmAES256},
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ // Standard SSE-C + SSE-C-copy request headers
|
{ // Standard SSE-C + SSE-C-copy request headers
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
SSECAlgorithm: []string{SSEAlgorithmAES256},
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
SSECKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
SSECopyKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCopyCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
SSECopyKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
},
|
},
|
||||||
ExpectedHeader: http.Header{
|
ExpectedHeader: http.Header{
|
||||||
SSECAlgorithm: []string{SSEAlgorithmAES256},
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
SSECopyKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ // Standard SSE-C + metadata request headers
|
{ // Standard SSE-C + metadata request headers
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
SSECAlgorithm: []string{SSEAlgorithmAES256},
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
SSECKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
"X-Amz-Meta-Test-1": []string{"Test-1"},
|
"X-Amz-Meta-Test-1": []string{"Test-1"},
|
||||||
},
|
},
|
||||||
ExpectedHeader: http.Header{
|
ExpectedHeader: http.Header{
|
||||||
SSECAlgorithm: []string{SSEAlgorithmAES256},
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
"X-Amz-Meta-Test-1": []string{"Test-1"},
|
"X-Amz-Meta-Test-1": []string{"Test-1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ // https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w
|
{ // https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w
|
||||||
|
|
|
@ -15,19 +15,42 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/cmd/logger"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MetaMultipart indicates that the object has been uploaded
|
||||||
|
// in multiple parts - via the S3 multipart API.
|
||||||
|
MetaMultipart = "X-Minio-Internal-Encrypted-Multipart"
|
||||||
|
|
||||||
|
// MetaIV is the random initialization vector (IV) used for
|
||||||
|
// the MinIO-internal key derivation.
|
||||||
|
MetaIV = "X-Minio-Internal-Server-Side-Encryption-Iv"
|
||||||
|
|
||||||
|
// MetaAlgorithm is the algorithm used to derive internal keys
|
||||||
|
// and encrypt the objects.
|
||||||
|
MetaAlgorithm = "X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm"
|
||||||
|
|
||||||
|
// MetaSealedKeySSEC is the sealed object encryption key in case of SSE-C.
|
||||||
|
MetaSealedKeySSEC = "X-Minio-Internal-Server-Side-Encryption-Sealed-Key"
|
||||||
|
// MetaSealedKeyS3 is the sealed object encryption key in case of SSE-S3
|
||||||
|
MetaSealedKeyS3 = "X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key"
|
||||||
|
// MetaSealedKeyKMS is the sealed object encryption key in case of SSE-KMS
|
||||||
|
MetaSealedKeyKMS = "X-Minio-Internal-Server-Side-Encryption-Kms-Sealed-Key"
|
||||||
|
|
||||||
|
// MetaKeyID is the KMS master key ID used to generate/encrypt the data
|
||||||
|
// encryption key (DEK).
|
||||||
|
MetaKeyID = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id"
|
||||||
|
// MetaDataEncryptionKey is the sealed data encryption key (DEK) received from
|
||||||
|
// the KMS.
|
||||||
|
MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsMultiPart returns true if the object metadata indicates
|
// IsMultiPart returns true if the object metadata indicates
|
||||||
// that it was uploaded using some form of server-side-encryption
|
// that it was uploaded using some form of server-side-encryption
|
||||||
// and the S3 multipart API.
|
// and the S3 multipart API.
|
||||||
func IsMultiPart(metadata map[string]string) bool {
|
func IsMultiPart(metadata map[string]string) bool {
|
||||||
if _, ok := metadata[SSEMultipart]; ok {
|
if _, ok := metadata[MetaMultipart]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -37,8 +60,8 @@ func IsMultiPart(metadata map[string]string) bool {
|
||||||
// information - e.g. the SSE-C key - from the metadata map.
|
// information - e.g. the SSE-C key - from the metadata map.
|
||||||
// It has the same semantics as RemoveSensitiveHeaders.
|
// It has the same semantics as RemoveSensitiveHeaders.
|
||||||
func RemoveSensitiveEntries(metadata map[string]string) { // The functions is tested in TestRemoveSensitiveHeaders for compatibility reasons
|
func RemoveSensitiveEntries(metadata map[string]string) { // The functions is tested in TestRemoveSensitiveHeaders for compatibility reasons
|
||||||
delete(metadata, SSECKey)
|
delete(metadata, xhttp.AmzServerSideEncryptionCustomerKey)
|
||||||
delete(metadata, SSECopyKey)
|
delete(metadata, xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
||||||
delete(metadata, xhttp.AmzMetaUnencryptedContentLength)
|
delete(metadata, xhttp.AmzMetaUnencryptedContentLength)
|
||||||
delete(metadata, xhttp.AmzMetaUnencryptedContentMD5)
|
delete(metadata, xhttp.AmzMetaUnencryptedContentMD5)
|
||||||
}
|
}
|
||||||
|
@ -46,31 +69,36 @@ func RemoveSensitiveEntries(metadata map[string]string) { // The functions is te
|
||||||
// RemoveSSEHeaders removes all crypto-specific SSE
|
// RemoveSSEHeaders removes all crypto-specific SSE
|
||||||
// header entries from the metadata map.
|
// header entries from the metadata map.
|
||||||
func RemoveSSEHeaders(metadata map[string]string) {
|
func RemoveSSEHeaders(metadata map[string]string) {
|
||||||
delete(metadata, SSEHeader)
|
delete(metadata, xhttp.AmzServerSideEncryption)
|
||||||
delete(metadata, SSEKmsID)
|
delete(metadata, xhttp.AmzServerSideEncryptionKmsID)
|
||||||
delete(metadata, SSEKmsContext)
|
delete(metadata, xhttp.AmzServerSideEncryptionKmsContext)
|
||||||
delete(metadata, SSECKeyMD5)
|
delete(metadata, xhttp.AmzServerSideEncryptionCustomerAlgorithm)
|
||||||
delete(metadata, SSECAlgorithm)
|
delete(metadata, xhttp.AmzServerSideEncryptionCustomerKey)
|
||||||
|
delete(metadata, xhttp.AmzServerSideEncryptionCustomerKeyMD5)
|
||||||
|
delete(metadata, xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm)
|
||||||
|
delete(metadata, xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
||||||
|
delete(metadata, xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveInternalEntries removes all crypto-specific internal
|
// RemoveInternalEntries removes all crypto-specific internal
|
||||||
// metadata entries from the metadata map.
|
// metadata entries from the metadata map.
|
||||||
func RemoveInternalEntries(metadata map[string]string) {
|
func RemoveInternalEntries(metadata map[string]string) {
|
||||||
delete(metadata, SSEMultipart)
|
delete(metadata, MetaMultipart)
|
||||||
delete(metadata, SSEIV)
|
delete(metadata, MetaAlgorithm)
|
||||||
delete(metadata, SSESealAlgorithm)
|
delete(metadata, MetaIV)
|
||||||
delete(metadata, SSECSealedKey)
|
delete(metadata, MetaSealedKeySSEC)
|
||||||
delete(metadata, S3SealedKey)
|
delete(metadata, MetaSealedKeyS3)
|
||||||
delete(metadata, S3KMSKeyID)
|
delete(metadata, MetaSealedKeyKMS)
|
||||||
delete(metadata, S3KMSSealedKey)
|
delete(metadata, MetaKeyID)
|
||||||
|
delete(metadata, MetaDataEncryptionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSourceEncrypted returns true if the source is encrypted
|
// IsSourceEncrypted returns true if the source is encrypted
|
||||||
func IsSourceEncrypted(metadata map[string]string) bool {
|
func IsSourceEncrypted(metadata map[string]string) bool {
|
||||||
if _, ok := metadata[SSECAlgorithm]; ok {
|
if _, ok := metadata[xhttp.AmzServerSideEncryptionCustomerAlgorithm]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := metadata[SSEHeader]; ok {
|
if _, ok := metadata[xhttp.AmzServerSideEncryption]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -82,10 +110,10 @@ func IsSourceEncrypted(metadata map[string]string) bool {
|
||||||
// IsEncrypted only checks whether the metadata contains at least
|
// IsEncrypted only checks whether the metadata contains at least
|
||||||
// one entry indicating SSE-C or SSE-S3.
|
// one entry indicating SSE-C or SSE-S3.
|
||||||
func IsEncrypted(metadata map[string]string) bool {
|
func IsEncrypted(metadata map[string]string) bool {
|
||||||
if _, ok := metadata[SSEIV]; ok {
|
if _, ok := metadata[MetaIV]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := metadata[SSESealAlgorithm]; ok {
|
if _, ok := metadata[MetaAlgorithm]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if IsMultiPart(metadata) {
|
if IsMultiPart(metadata) {
|
||||||
|
@ -97,28 +125,7 @@ func IsEncrypted(metadata map[string]string) bool {
|
||||||
if SSEC.IsEncrypted(metadata) {
|
if SSEC.IsEncrypted(metadata) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
if S3KMS.IsEncrypted(metadata) {
|
||||||
}
|
|
||||||
|
|
||||||
// IsEncrypted returns true if the object metadata indicates
|
|
||||||
// that the object was uploaded using SSE-S3.
|
|
||||||
func (s3) IsEncrypted(metadata map[string]string) bool {
|
|
||||||
if _, ok := metadata[S3SealedKey]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := metadata[S3KMSKeyID]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := metadata[S3KMSSealedKey]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEncrypted returns true if the object metadata indicates
|
|
||||||
// that the object was uploaded using SSE-C.
|
|
||||||
func (ssec) IsEncrypted(metadata map[string]string) bool {
|
|
||||||
if _, ok := metadata[SSECSealedKey]; ok {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -129,159 +136,11 @@ func (ssec) IsEncrypted(metadata map[string]string) bool {
|
||||||
// metadata is nil.
|
// metadata is nil.
|
||||||
func CreateMultipartMetadata(metadata map[string]string) map[string]string {
|
func CreateMultipartMetadata(metadata map[string]string) map[string]string {
|
||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
return map[string]string{SSEMultipart: ""}
|
return map[string]string{MetaMultipart: ""}
|
||||||
}
|
}
|
||||||
metadata[SSEMultipart] = ""
|
metadata[MetaMultipart] = ""
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateMetadata encodes the sealed object key into the metadata and returns
|
|
||||||
// the modified metadata. If the keyID and the kmsKey is not empty it encodes
|
|
||||||
// both into the metadata as well. It allocates a new metadata map if metadata
|
|
||||||
// is nil.
|
|
||||||
func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
|
|
||||||
if sealedKey.Algorithm != SealAlgorithm {
|
|
||||||
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are two possibilites:
|
|
||||||
// - We use a KMS -> There must be non-empty key ID and a KMS data key.
|
|
||||||
// - We use a K/V -> There must be no key ID and no KMS data key.
|
|
||||||
// Otherwise, the caller has passed an invalid argument combination.
|
|
||||||
if keyID == "" && len(kmsKey) != 0 {
|
|
||||||
logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present"))
|
|
||||||
}
|
|
||||||
if keyID != "" && len(kmsKey) == 0 {
|
|
||||||
logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if metadata == nil {
|
|
||||||
metadata = make(map[string]string, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata[SSESealAlgorithm] = sealedKey.Algorithm
|
|
||||||
metadata[SSEIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
|
||||||
metadata[S3SealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
|
||||||
if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key.
|
|
||||||
metadata[S3KMSKeyID] = keyID
|
|
||||||
metadata[S3KMSSealedKey] = base64.StdEncoding.EncodeToString(kmsKey)
|
|
||||||
}
|
|
||||||
return metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseMetadata extracts all SSE-S3 related values from the object metadata
|
|
||||||
// and checks whether they are well-formed. It returns the sealed object key
|
|
||||||
// on success. If the metadata contains both, a KMS master key ID and a sealed
|
|
||||||
// KMS data key it returns both. If the metadata does not contain neither a
|
|
||||||
// KMS master key ID nor a sealed KMS data key it returns an empty keyID and
|
|
||||||
// KMS data key. Otherwise, it returns an error.
|
|
||||||
func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) {
|
|
||||||
// Extract all required values from object metadata
|
|
||||||
b64IV, ok := metadata[SSEIV]
|
|
||||||
if !ok {
|
|
||||||
return keyID, kmsKey, sealedKey, errMissingInternalIV
|
|
||||||
}
|
|
||||||
algorithm, ok := metadata[SSESealAlgorithm]
|
|
||||||
if !ok {
|
|
||||||
return keyID, kmsKey, sealedKey, errMissingInternalSealAlgorithm
|
|
||||||
}
|
|
||||||
b64SealedKey, ok := metadata[S3SealedKey]
|
|
||||||
if !ok {
|
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are two possibilites:
|
|
||||||
// - We use a KMS -> There must be a key ID and a KMS data key.
|
|
||||||
// - We use a K/V -> There must be no key ID and no KMS data key.
|
|
||||||
// Otherwise, the metadata is corrupted.
|
|
||||||
keyID, idPresent := metadata[S3KMSKeyID]
|
|
||||||
b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey]
|
|
||||||
if !idPresent && kmsKeyPresent {
|
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
|
|
||||||
}
|
|
||||||
if idPresent && !kmsKeyPresent {
|
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether all extracted values are well-formed
|
|
||||||
var iv [32]byte
|
|
||||||
n, err := base64.StdEncoding.Decode(iv[:], []byte(b64IV))
|
|
||||||
if err != nil || n != 32 {
|
|
||||||
return keyID, kmsKey, sealedKey, errInvalidInternalIV
|
|
||||||
}
|
|
||||||
if algorithm != SealAlgorithm {
|
|
||||||
return keyID, kmsKey, sealedKey, errInvalidInternalSealAlgorithm
|
|
||||||
}
|
|
||||||
var encryptedKey [64]byte
|
|
||||||
n, err = base64.StdEncoding.Decode(encryptedKey[:], []byte(b64SealedKey))
|
|
||||||
if err != nil || n != 64 {
|
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid")
|
|
||||||
}
|
|
||||||
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
|
|
||||||
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
|
||||||
if err != nil {
|
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealedKey.Algorithm = algorithm
|
|
||||||
sealedKey.IV = iv
|
|
||||||
sealedKey.Key = encryptedKey
|
|
||||||
return keyID, kmsKey, sealedKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateMetadata encodes the sealed key into the metadata and returns the modified metadata.
|
|
||||||
// It allocates a new metadata map if metadata is nil.
|
|
||||||
func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string {
|
|
||||||
if sealedKey.Algorithm != SealAlgorithm {
|
|
||||||
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm))
|
|
||||||
}
|
|
||||||
|
|
||||||
if metadata == nil {
|
|
||||||
metadata = make(map[string]string, 3)
|
|
||||||
}
|
|
||||||
metadata[SSESealAlgorithm] = SealAlgorithm
|
|
||||||
metadata[SSEIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
|
||||||
metadata[SSECSealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
|
||||||
return metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseMetadata extracts all SSE-C related values from the object metadata
|
|
||||||
// and checks whether they are well-formed. It returns the sealed object key
|
|
||||||
// on success.
|
|
||||||
func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err error) {
|
|
||||||
// Extract all required values from object metadata
|
|
||||||
b64IV, ok := metadata[SSEIV]
|
|
||||||
if !ok {
|
|
||||||
return sealedKey, errMissingInternalIV
|
|
||||||
}
|
|
||||||
algorithm, ok := metadata[SSESealAlgorithm]
|
|
||||||
if !ok {
|
|
||||||
return sealedKey, errMissingInternalSealAlgorithm
|
|
||||||
}
|
|
||||||
b64SealedKey, ok := metadata[SSECSealedKey]
|
|
||||||
if !ok {
|
|
||||||
return sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-C")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether all extracted values are well-formed
|
|
||||||
iv, err := base64.StdEncoding.DecodeString(b64IV)
|
|
||||||
if err != nil || len(iv) != 32 {
|
|
||||||
return sealedKey, errInvalidInternalIV
|
|
||||||
}
|
|
||||||
if algorithm != SealAlgorithm && algorithm != InsecureSealAlgorithm {
|
|
||||||
return sealedKey, errInvalidInternalSealAlgorithm
|
|
||||||
}
|
|
||||||
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
|
||||||
if err != nil || len(encryptedKey) != 64 {
|
|
||||||
return sealedKey, Errorf("The internal sealed key for SSE-C is invalid")
|
|
||||||
}
|
|
||||||
|
|
||||||
sealedKey.Algorithm = algorithm
|
|
||||||
copy(sealedKey.IV[:], iv)
|
|
||||||
copy(sealedKey.Key[:], encryptedKey)
|
|
||||||
return sealedKey, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsETagSealed returns true if the etag seems to be encrypted.
|
// IsETagSealed returns true if the etag seems to be encrypted.
|
||||||
func IsETagSealed(etag []byte) bool { return len(etag) > 16 }
|
func IsETagSealed(etag []byte) bool { return len(etag) > 16 }
|
||||||
|
|
|
@ -27,9 +27,9 @@ var isMultipartTests = []struct {
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
Multipart bool
|
Multipart bool
|
||||||
}{
|
}{
|
||||||
{Multipart: true, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
{Multipart: true, Metadata: map[string]string{MetaMultipart: ""}}, // 0
|
||||||
{Multipart: true, Metadata: map[string]string{"X-Minio-Internal-Encrypted-Multipart": ""}}, // 1
|
{Multipart: true, Metadata: map[string]string{"X-Minio-Internal-Encrypted-Multipart": ""}}, // 1
|
||||||
{Multipart: true, Metadata: map[string]string{SSEMultipart: "some-value"}}, // 2
|
{Multipart: true, Metadata: map[string]string{MetaMultipart: "some-value"}}, // 2
|
||||||
{Multipart: false, Metadata: map[string]string{"": ""}}, // 3
|
{Multipart: false, Metadata: map[string]string{"": ""}}, // 3
|
||||||
{Multipart: false, Metadata: map[string]string{"X-Minio-Internal-EncryptedMultipart": ""}}, // 4
|
{Multipart: false, Metadata: map[string]string{"X-Minio-Internal-EncryptedMultipart": ""}}, // 4
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,13 @@ var isEncryptedTests = []struct {
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
Encrypted bool
|
Encrypted bool
|
||||||
}{
|
}{
|
||||||
{Encrypted: true, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
{Encrypted: true, Metadata: map[string]string{MetaMultipart: ""}}, // 0
|
||||||
{Encrypted: true, Metadata: map[string]string{SSEIV: ""}}, // 1
|
{Encrypted: true, Metadata: map[string]string{MetaIV: ""}}, // 1
|
||||||
{Encrypted: true, Metadata: map[string]string{SSESealAlgorithm: ""}}, // 2
|
{Encrypted: true, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2
|
||||||
{Encrypted: true, Metadata: map[string]string{SSECSealedKey: ""}}, // 3
|
{Encrypted: true, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3
|
||||||
{Encrypted: true, Metadata: map[string]string{S3SealedKey: ""}}, // 4
|
{Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4
|
||||||
{Encrypted: true, Metadata: map[string]string{S3KMSKeyID: ""}}, // 5
|
{Encrypted: true, Metadata: map[string]string{MetaKeyID: ""}}, // 5
|
||||||
{Encrypted: true, Metadata: map[string]string{S3KMSSealedKey: ""}}, // 6
|
{Encrypted: true, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6
|
||||||
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
||||||
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
||||||
}
|
}
|
||||||
|
@ -69,13 +69,13 @@ var s3IsEncryptedTests = []struct {
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
Encrypted bool
|
Encrypted bool
|
||||||
}{
|
}{
|
||||||
{Encrypted: false, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
{Encrypted: false, Metadata: map[string]string{MetaMultipart: ""}}, // 0
|
||||||
{Encrypted: false, Metadata: map[string]string{SSEIV: ""}}, // 1
|
{Encrypted: false, Metadata: map[string]string{MetaIV: ""}}, // 1
|
||||||
{Encrypted: false, Metadata: map[string]string{SSESealAlgorithm: ""}}, // 2
|
{Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2
|
||||||
{Encrypted: false, Metadata: map[string]string{SSECSealedKey: ""}}, // 3
|
{Encrypted: false, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3
|
||||||
{Encrypted: true, Metadata: map[string]string{S3SealedKey: ""}}, // 4
|
{Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4
|
||||||
{Encrypted: true, Metadata: map[string]string{S3KMSKeyID: ""}}, // 5
|
{Encrypted: true, Metadata: map[string]string{MetaKeyID: ""}}, // 5
|
||||||
{Encrypted: true, Metadata: map[string]string{S3KMSSealedKey: ""}}, // 6
|
{Encrypted: true, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6
|
||||||
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
||||||
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
||||||
}
|
}
|
||||||
|
@ -92,13 +92,13 @@ var ssecIsEncryptedTests = []struct {
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
Encrypted bool
|
Encrypted bool
|
||||||
}{
|
}{
|
||||||
{Encrypted: false, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
{Encrypted: false, Metadata: map[string]string{MetaMultipart: ""}}, // 0
|
||||||
{Encrypted: false, Metadata: map[string]string{SSEIV: ""}}, // 1
|
{Encrypted: false, Metadata: map[string]string{MetaIV: ""}}, // 1
|
||||||
{Encrypted: false, Metadata: map[string]string{SSESealAlgorithm: ""}}, // 2
|
{Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2
|
||||||
{Encrypted: true, Metadata: map[string]string{SSECSealedKey: ""}}, // 3
|
{Encrypted: true, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3
|
||||||
{Encrypted: false, Metadata: map[string]string{S3SealedKey: ""}}, // 4
|
{Encrypted: false, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4
|
||||||
{Encrypted: false, Metadata: map[string]string{S3KMSKeyID: ""}}, // 5
|
{Encrypted: false, Metadata: map[string]string{MetaKeyID: ""}}, // 5
|
||||||
{Encrypted: false, Metadata: map[string]string{S3KMSSealedKey: ""}}, // 6
|
{Encrypted: false, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6
|
||||||
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
||||||
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
||||||
}
|
}
|
||||||
|
@ -121,65 +121,65 @@ var s3ParseMetadataTests = []struct {
|
||||||
}{
|
}{
|
||||||
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}}, // 0
|
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}}, // 0
|
||||||
{
|
{
|
||||||
ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""},
|
ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{MetaIV: ""},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 1
|
}, // 1
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-S3"),
|
ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-S3"),
|
||||||
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
Metadata: map[string]string{MetaIV: "", MetaAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 2
|
}, // 2
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3"),
|
ExpectedErr: Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3"),
|
||||||
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSSealedKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeyS3: "", MetaDataEncryptionKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 3
|
}, // 3
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3"),
|
ExpectedErr: Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3"),
|
||||||
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: ""},
|
Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeyS3: "", MetaKeyID: ""},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 4
|
}, // 4
|
||||||
{
|
{
|
||||||
ExpectedErr: errInvalidInternalIV,
|
ExpectedErr: errInvalidInternalIV,
|
||||||
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: "", S3KMSSealedKey: ""},
|
Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeyS3: "", MetaKeyID: "", MetaDataEncryptionKey: ""},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 5
|
}, // 5
|
||||||
{
|
{
|
||||||
ExpectedErr: errInvalidInternalSealAlgorithm,
|
ExpectedErr: errInvalidInternalSealAlgorithm,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: "", S3KMSSealedKey: "",
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: "", MetaSealedKeyS3: "", MetaKeyID: "", MetaDataEncryptionKey: "",
|
||||||
},
|
},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 6
|
}, // 6
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The internal sealed key for SSE-S3 is invalid"),
|
ExpectedErr: Errorf("The internal sealed key for SSE-S3 is invalid"),
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "",
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, MetaSealedKeyS3: "",
|
||||||
S3KMSKeyID: "", S3KMSSealedKey: "",
|
MetaKeyID: "", MetaDataEncryptionKey: "",
|
||||||
},
|
},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
|
||||||
}, // 7
|
}, // 7
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The internal sealed KMS data key for SSE-S3 is invalid"),
|
ExpectedErr: Errorf("The internal sealed KMS data key for SSE-S3 is invalid"),
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm,
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm,
|
||||||
S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "key-1",
|
MetaSealedKeyS3: base64.StdEncoding.EncodeToString(make([]byte, 64)), MetaKeyID: "key-1",
|
||||||
S3KMSSealedKey: ".MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=", // invalid base64
|
MetaDataEncryptionKey: ".MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=", // invalid base64
|
||||||
},
|
},
|
||||||
DataKey: []byte{}, KeyID: "key-1", SealedKey: SealedKey{},
|
DataKey: []byte{}, KeyID: "key-1", SealedKey: SealedKey{},
|
||||||
}, // 8
|
}, // 8
|
||||||
{
|
{
|
||||||
ExpectedErr: nil,
|
ExpectedErr: nil,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm,
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm,
|
||||||
S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "", S3KMSSealedKey: "",
|
MetaSealedKeyS3: base64.StdEncoding.EncodeToString(make([]byte, 64)), MetaKeyID: "", MetaDataEncryptionKey: "",
|
||||||
},
|
},
|
||||||
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{Algorithm: SealAlgorithm},
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{Algorithm: SealAlgorithm},
|
||||||
}, // 9
|
}, // 9
|
||||||
{
|
{
|
||||||
ExpectedErr: nil,
|
ExpectedErr: nil,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), SSESealAlgorithm: SealAlgorithm,
|
MetaIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), MetaAlgorithm: SealAlgorithm,
|
||||||
S3SealedKey: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)), S3KMSKeyID: "key-1",
|
MetaSealedKeyS3: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)), MetaKeyID: "key-1",
|
||||||
S3KMSSealedKey: base64.StdEncoding.EncodeToString(make([]byte, 48)),
|
MetaDataEncryptionKey: base64.StdEncoding.EncodeToString(make([]byte, 48)),
|
||||||
},
|
},
|
||||||
DataKey: make([]byte, 48), KeyID: "key-1", SealedKey: SealedKey{Algorithm: SealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}},
|
DataKey: make([]byte, 48), KeyID: "key-1", SealedKey: SealedKey{Algorithm: SealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}},
|
||||||
}, // 10
|
}, // 10
|
||||||
|
@ -223,43 +223,43 @@ var ssecParseMetadataTests = []struct {
|
||||||
|
|
||||||
SealedKey SealedKey
|
SealedKey SealedKey
|
||||||
}{
|
}{
|
||||||
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0
|
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0
|
||||||
{ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, SealedKey: SealedKey{}}, // 1
|
{ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{MetaIV: ""}, SealedKey: SealedKey{}}, // 1
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-C"),
|
ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-C"),
|
||||||
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, SealedKey: SealedKey{},
|
Metadata: map[string]string{MetaIV: "", MetaAlgorithm: ""}, SealedKey: SealedKey{},
|
||||||
}, // 2
|
}, // 2
|
||||||
{
|
{
|
||||||
ExpectedErr: errInvalidInternalIV,
|
ExpectedErr: errInvalidInternalIV,
|
||||||
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", SSECSealedKey: ""}, SealedKey: SealedKey{},
|
Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeySSEC: ""}, SealedKey: SealedKey{},
|
||||||
}, // 3
|
}, // 3
|
||||||
{
|
{
|
||||||
ExpectedErr: errInvalidInternalSealAlgorithm,
|
ExpectedErr: errInvalidInternalSealAlgorithm,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: "", SSECSealedKey: "",
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: "", MetaSealedKeySSEC: "",
|
||||||
},
|
},
|
||||||
SealedKey: SealedKey{},
|
SealedKey: SealedKey{},
|
||||||
}, // 4
|
}, // 4
|
||||||
{
|
{
|
||||||
ExpectedErr: Errorf("The internal sealed key for SSE-C is invalid"),
|
ExpectedErr: Errorf("The internal sealed key for SSE-C is invalid"),
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "",
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, MetaSealedKeySSEC: "",
|
||||||
},
|
},
|
||||||
SealedKey: SealedKey{},
|
SealedKey: SealedKey{},
|
||||||
}, // 5
|
}, // 5
|
||||||
{
|
{
|
||||||
ExpectedErr: nil,
|
ExpectedErr: nil,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm,
|
MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm,
|
||||||
SSECSealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)),
|
MetaSealedKeySSEC: base64.StdEncoding.EncodeToString(make([]byte, 64)),
|
||||||
},
|
},
|
||||||
SealedKey: SealedKey{Algorithm: SealAlgorithm},
|
SealedKey: SealedKey{Algorithm: SealAlgorithm},
|
||||||
}, // 6
|
}, // 6
|
||||||
{
|
{
|
||||||
ExpectedErr: nil,
|
ExpectedErr: nil,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), SSESealAlgorithm: InsecureSealAlgorithm,
|
MetaIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), MetaAlgorithm: InsecureSealAlgorithm,
|
||||||
SSECSealedKey: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)),
|
MetaSealedKeySSEC: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)),
|
||||||
},
|
},
|
||||||
SealedKey: SealedKey{Algorithm: InsecureSealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}},
|
SealedKey: SealedKey{Algorithm: InsecureSealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}},
|
||||||
}, // 7
|
}, // 7
|
||||||
|
@ -267,8 +267,8 @@ var ssecParseMetadataTests = []struct {
|
||||||
|
|
||||||
func TestCreateMultipartMetadata(t *testing.T) {
|
func TestCreateMultipartMetadata(t *testing.T) {
|
||||||
metadata := CreateMultipartMetadata(nil)
|
metadata := CreateMultipartMetadata(nil)
|
||||||
if v, ok := metadata[SSEMultipart]; !ok || v != "" {
|
if v, ok := metadata[MetaMultipart]; !ok || v != "" {
|
||||||
t.Errorf("Metadata is missing the correct value for '%s': got '%s' - want '%s'", SSEMultipart, v, "")
|
t.Errorf("Metadata is missing the correct value for '%s': got '%s' - want '%s'", MetaMultipart, v, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,20 +411,20 @@ var removeInternalEntriesTests = []struct {
|
||||||
}{
|
}{
|
||||||
{ // 0
|
{ // 0
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEMultipart: "",
|
MetaMultipart: "",
|
||||||
SSEIV: "",
|
MetaIV: "",
|
||||||
SSESealAlgorithm: "",
|
MetaAlgorithm: "",
|
||||||
SSECSealedKey: "",
|
MetaSealedKeySSEC: "",
|
||||||
S3SealedKey: "",
|
MetaSealedKeyS3: "",
|
||||||
S3KMSKeyID: "",
|
MetaKeyID: "",
|
||||||
S3KMSSealedKey: "",
|
MetaDataEncryptionKey: "",
|
||||||
},
|
},
|
||||||
Expected: map[string]string{},
|
Expected: map[string]string{},
|
||||||
},
|
},
|
||||||
{ // 1
|
{ // 1
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
SSEMultipart: "",
|
MetaMultipart: "",
|
||||||
SSEIV: "",
|
MetaIV: "",
|
||||||
"X-Amz-Meta-A": "X",
|
"X-Amz-Meta-A": "X",
|
||||||
"X-Minio-Internal-B": "Y",
|
"X-Minio-Internal-B": "Y",
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2019-2020 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ssec struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SSEC represents AWS SSE-C. It provides functionality to handle
|
||||||
|
// SSE-C requests.
|
||||||
|
SSEC = ssec{}
|
||||||
|
|
||||||
|
_ Type = SSEC
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the SSE domain as string. For SSE-C the
|
||||||
|
// domain is "SSE-C".
|
||||||
|
func (ssec) String() string { return "SSE-C" }
|
||||||
|
|
||||||
|
// IsRequested returns true if the HTTP headers contains
|
||||||
|
// at least one SSE-C header. SSE-C copy headers are ignored.
|
||||||
|
func (ssec) IsRequested(h http.Header) bool {
|
||||||
|
if _, ok := h[xhttp.AmzServerSideEncryptionCustomerAlgorithm]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := h[xhttp.AmzServerSideEncryptionCustomerKey]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := h[xhttp.AmzServerSideEncryptionCustomerKeyMD5]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEncrypted returns true if the metadata contains an SSE-C
|
||||||
|
// entry inidicating that the object has been encrypted using
|
||||||
|
// SSE-C.
|
||||||
|
func (ssec) IsEncrypted(metadata map[string]string) bool {
|
||||||
|
if _, ok := metadata[MetaSealedKeySSEC]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHTTP parses the SSE-C headers and returns the SSE-C client key
|
||||||
|
// on success. SSE-C copy headers are ignored.
|
||||||
|
func (ssec) ParseHTTP(h http.Header) (key [32]byte, err error) {
|
||||||
|
if h.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm) != xhttp.AmzEncryptionAES {
|
||||||
|
return key, ErrInvalidCustomerAlgorithm
|
||||||
|
}
|
||||||
|
if h.Get(xhttp.AmzServerSideEncryptionCustomerKey) == "" {
|
||||||
|
return key, ErrMissingCustomerKey
|
||||||
|
}
|
||||||
|
if h.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5) == "" {
|
||||||
|
return key, ErrMissingCustomerKeyMD5
|
||||||
|
}
|
||||||
|
|
||||||
|
clientKey, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCustomerKey))
|
||||||
|
if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long
|
||||||
|
return key, ErrInvalidCustomerKey
|
||||||
|
}
|
||||||
|
keyMD5, err := base64.StdEncoding.DecodeString(h.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
|
if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) {
|
||||||
|
return key, ErrCustomerKeyMD5Mismatch
|
||||||
|
}
|
||||||
|
copy(key[:], clientKey)
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
// from the metadata using the SSE-C client key of the HTTP headers
|
||||||
|
// and returns the decrypted object key.
|
||||||
|
func (s3 ssec) UnsealObjectKey(h http.Header, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
clientKey, err := s3.ParseHTTP(h)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return unsealObjectKey(clientKey, metadata, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMetadata encodes the sealed key into the metadata
|
||||||
|
// and returns the modified metadata. It allocates a new
|
||||||
|
// metadata map if metadata is nil.
|
||||||
|
func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string {
|
||||||
|
if sealedKey.Algorithm != SealAlgorithm {
|
||||||
|
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm))
|
||||||
|
}
|
||||||
|
|
||||||
|
if metadata == nil {
|
||||||
|
metadata = make(map[string]string, 3)
|
||||||
|
}
|
||||||
|
metadata[MetaAlgorithm] = SealAlgorithm
|
||||||
|
metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
||||||
|
metadata[MetaSealedKeySSEC] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseMetadata extracts all SSE-C related values from the object metadata
|
||||||
|
// and checks whether they are well-formed. It returns the sealed object key
|
||||||
|
// on success.
|
||||||
|
func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err error) {
|
||||||
|
// Extract all required values from object metadata
|
||||||
|
b64IV, ok := metadata[MetaIV]
|
||||||
|
if !ok {
|
||||||
|
return sealedKey, errMissingInternalIV
|
||||||
|
}
|
||||||
|
algorithm, ok := metadata[MetaAlgorithm]
|
||||||
|
if !ok {
|
||||||
|
return sealedKey, errMissingInternalSealAlgorithm
|
||||||
|
}
|
||||||
|
b64SealedKey, ok := metadata[MetaSealedKeySSEC]
|
||||||
|
if !ok {
|
||||||
|
return sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-C")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether all extracted values are well-formed
|
||||||
|
iv, err := base64.StdEncoding.DecodeString(b64IV)
|
||||||
|
if err != nil || len(iv) != 32 {
|
||||||
|
return sealedKey, errInvalidInternalIV
|
||||||
|
}
|
||||||
|
if algorithm != SealAlgorithm && algorithm != InsecureSealAlgorithm {
|
||||||
|
return sealedKey, errInvalidInternalSealAlgorithm
|
||||||
|
}
|
||||||
|
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
||||||
|
if err != nil || len(encryptedKey) != 64 {
|
||||||
|
return sealedKey, Errorf("The internal sealed key for SSE-C is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
sealedKey.Algorithm = algorithm
|
||||||
|
copy(sealedKey.IV[:], iv)
|
||||||
|
copy(sealedKey.Key[:], encryptedKey)
|
||||||
|
return sealedKey, nil
|
||||||
|
}
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2019-2020 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ssekms struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// S3KMS represents AWS SSE-KMS. It provides functionality to
|
||||||
|
// handle SSE-KMS requests.
|
||||||
|
S3KMS = ssekms{}
|
||||||
|
|
||||||
|
_ Type = S3KMS
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the SSE domain as string. For SSE-KMS the
|
||||||
|
// domain is "SSE-KMS".
|
||||||
|
func (ssekms) String() string { return "SSE-KMS" }
|
||||||
|
|
||||||
|
// IsRequested returns true if the HTTP headers contains
|
||||||
|
// at least one SSE-KMS header.
|
||||||
|
func (ssekms) IsRequested(h http.Header) bool {
|
||||||
|
if _, ok := h[xhttp.AmzServerSideEncryptionKmsID]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := h[xhttp.AmzServerSideEncryptionKmsContext]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := h[xhttp.AmzServerSideEncryption]; ok {
|
||||||
|
return strings.ToUpper(h.Get(xhttp.AmzServerSideEncryption)) != xhttp.AmzEncryptionAES // Return only true if the SSE header is specified and does not contain the SSE-S3 value
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHTTP parses the SSE-KMS headers and returns the SSE-KMS key ID
|
||||||
|
// and the KMS context on success.
|
||||||
|
func (ssekms) ParseHTTP(h http.Header) (string, Context, error) {
|
||||||
|
algorithm := h.Get(xhttp.AmzServerSideEncryption)
|
||||||
|
if algorithm != xhttp.AmzEncryptionKMS {
|
||||||
|
return "", nil, ErrInvalidEncryptionMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx Context
|
||||||
|
if context, ok := h[xhttp.AmzServerSideEncryptionKmsContext]; ok {
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
if err := json.Unmarshal([]byte(context[0]), &ctx); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return h.Get(xhttp.AmzServerSideEncryptionKmsID), ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEncrypted returns true if the object metadata indicates
|
||||||
|
// that the object was uploaded using SSE-KMS.
|
||||||
|
func (ssekms) IsEncrypted(metadata map[string]string) bool {
|
||||||
|
if _, ok := metadata[MetaSealedKeyKMS]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaKeyID]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaDataEncryptionKey]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
// from the metadata using KMS and returns the decrypted object
|
||||||
|
// key.
|
||||||
|
func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
keyID, kmsKey, sealedKey, err := s3.ParseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
||||||
|
if err != nil {
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
err = key.Unseal(unsealKey, sealedKey, s3.String(), bucket, object)
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMetadata encodes the sealed object key into the metadata and returns
|
||||||
|
// the modified metadata. If the keyID and the kmsKey is not empty it encodes
|
||||||
|
// both into the metadata as well. It allocates a new metadata map if metadata
|
||||||
|
// is nil.
|
||||||
|
func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
|
||||||
|
if sealedKey.Algorithm != SealAlgorithm {
|
||||||
|
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are two possibilites:
|
||||||
|
// - We use a KMS -> There must be non-empty key ID and a KMS data key.
|
||||||
|
// - We use a K/V -> There must be no key ID and no KMS data key.
|
||||||
|
// Otherwise, the caller has passed an invalid argument combination.
|
||||||
|
if keyID == "" && len(kmsKey) != 0 {
|
||||||
|
logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present"))
|
||||||
|
}
|
||||||
|
if keyID != "" && len(kmsKey) == 0 {
|
||||||
|
logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if metadata == nil {
|
||||||
|
metadata = make(map[string]string, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata[MetaAlgorithm] = sealedKey.Algorithm
|
||||||
|
metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
||||||
|
metadata[MetaSealedKeyKMS] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
||||||
|
if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key.
|
||||||
|
metadata[MetaKeyID] = keyID
|
||||||
|
metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey)
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseMetadata extracts all SSE-KMS related values from the object metadata
|
||||||
|
// and checks whether they are well-formed. It returns the sealed object key
|
||||||
|
// on success. If the metadata contains both, a KMS master key ID and a sealed
|
||||||
|
// KMS data key it returns both. If the metadata does not contain neither a
|
||||||
|
// KMS master key ID nor a sealed KMS data key it returns an empty keyID and
|
||||||
|
// KMS data key. Otherwise, it returns an error.
|
||||||
|
func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) {
|
||||||
|
// Extract all required values from object metadata
|
||||||
|
b64IV, ok := metadata[MetaIV]
|
||||||
|
if !ok {
|
||||||
|
return keyID, kmsKey, sealedKey, errMissingInternalIV
|
||||||
|
}
|
||||||
|
algorithm, ok := metadata[MetaAlgorithm]
|
||||||
|
if !ok {
|
||||||
|
return keyID, kmsKey, sealedKey, errMissingInternalSealAlgorithm
|
||||||
|
}
|
||||||
|
b64SealedKey, ok := metadata[MetaSealedKeyKMS]
|
||||||
|
if !ok {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are two possibilites:
|
||||||
|
// - We use a KMS -> There must be a key ID and a KMS data key.
|
||||||
|
// - We use a K/V -> There must be no key ID and no KMS data key.
|
||||||
|
// Otherwise, the metadata is corrupted.
|
||||||
|
keyID, idPresent := metadata[MetaKeyID]
|
||||||
|
b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey]
|
||||||
|
if !idPresent && kmsKeyPresent {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
|
||||||
|
}
|
||||||
|
if idPresent && !kmsKeyPresent {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether all extracted values are well-formed
|
||||||
|
iv, err := base64.StdEncoding.DecodeString(b64IV)
|
||||||
|
if err != nil || len(iv) != 32 {
|
||||||
|
return keyID, kmsKey, sealedKey, errInvalidInternalIV
|
||||||
|
}
|
||||||
|
if algorithm != SealAlgorithm {
|
||||||
|
return keyID, kmsKey, sealedKey, errInvalidInternalSealAlgorithm
|
||||||
|
}
|
||||||
|
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
||||||
|
if err != nil || len(encryptedKey) != 64 {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid")
|
||||||
|
}
|
||||||
|
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
|
||||||
|
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
||||||
|
if err != nil {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealedKey.Algorithm = algorithm
|
||||||
|
copy(sealedKey.IV[:], iv)
|
||||||
|
copy(sealedKey.Key[:], encryptedKey)
|
||||||
|
return keyID, kmsKey, sealedKey, nil
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2019-2020 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sses3 struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// S3 represents AWS SSE-S3. It provides functionality to handle
|
||||||
|
// SSE-S3 requests.
|
||||||
|
S3 = sses3{}
|
||||||
|
|
||||||
|
_ Type = S3
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the SSE domain as string. For SSE-S3 the
|
||||||
|
// domain is "SSE-S3".
|
||||||
|
func (sses3) String() string { return "SSE-S3" }
|
||||||
|
|
||||||
|
func (sses3) IsRequested(h http.Header) bool {
|
||||||
|
_, ok := h[xhttp.AmzServerSideEncryption]
|
||||||
|
return ok && strings.ToLower(h.Get(xhttp.AmzServerSideEncryption)) != xhttp.AmzEncryptionKMS // Return only true if the SSE header is specified and does not contain the SSE-KMS value
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHTTP parses the SSE-S3 related HTTP headers and checks
|
||||||
|
// whether they contain valid values.
|
||||||
|
func (sses3) ParseHTTP(h http.Header) error {
|
||||||
|
if h.Get(xhttp.AmzServerSideEncryption) != xhttp.AmzEncryptionAES {
|
||||||
|
return ErrInvalidEncryptionMethod
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEncrypted returns true if the object metadata indicates
|
||||||
|
// that the object was uploaded using SSE-S3.
|
||||||
|
func (sses3) IsEncrypted(metadata map[string]string) bool {
|
||||||
|
if _, ok := metadata[MetaSealedKeyS3]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaKeyID]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaDataEncryptionKey]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
// from the metadata using KMS and returns the decrypted object
|
||||||
|
// key.
|
||||||
|
func (s3 sses3) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
keyID, kmsKey, sealedKey, err := s3.ParseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
||||||
|
if err != nil {
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
err = key.Unseal(unsealKey, sealedKey, s3.String(), bucket, object)
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMetadata encodes the sealed object key into the metadata and returns
|
||||||
|
// the modified metadata. If the keyID and the kmsKey is not empty it encodes
|
||||||
|
// both into the metadata as well. It allocates a new metadata map if metadata
|
||||||
|
// is nil.
|
||||||
|
func (sses3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
|
||||||
|
if sealedKey.Algorithm != SealAlgorithm {
|
||||||
|
logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are two possibilites:
|
||||||
|
// - We use a KMS -> There must be non-empty key ID and a KMS data key.
|
||||||
|
// - We use a K/V -> There must be no key ID and no KMS data key.
|
||||||
|
// Otherwise, the caller has passed an invalid argument combination.
|
||||||
|
if keyID == "" && len(kmsKey) != 0 {
|
||||||
|
logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present"))
|
||||||
|
}
|
||||||
|
if keyID != "" && len(kmsKey) == 0 {
|
||||||
|
logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if metadata == nil {
|
||||||
|
metadata = make(map[string]string, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata[MetaAlgorithm] = sealedKey.Algorithm
|
||||||
|
metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
|
||||||
|
metadata[MetaSealedKeyS3] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
|
||||||
|
if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key.
|
||||||
|
metadata[MetaKeyID] = keyID
|
||||||
|
metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey)
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseMetadata extracts all SSE-S3 related values from the object metadata
|
||||||
|
// and checks whether they are well-formed. It returns the sealed object key
|
||||||
|
// on success. If the metadata contains both, a KMS master key ID and a sealed
|
||||||
|
// KMS data key it returns both. If the metadata does not contain neither a
|
||||||
|
// KMS master key ID nor a sealed KMS data key it returns an empty keyID and
|
||||||
|
// KMS data key. Otherwise, it returns an error.
|
||||||
|
func (sses3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) {
|
||||||
|
// Extract all required values from object metadata
|
||||||
|
b64IV, ok := metadata[MetaIV]
|
||||||
|
if !ok {
|
||||||
|
return keyID, kmsKey, sealedKey, errMissingInternalIV
|
||||||
|
}
|
||||||
|
algorithm, ok := metadata[MetaAlgorithm]
|
||||||
|
if !ok {
|
||||||
|
return keyID, kmsKey, sealedKey, errMissingInternalSealAlgorithm
|
||||||
|
}
|
||||||
|
b64SealedKey, ok := metadata[MetaSealedKeyS3]
|
||||||
|
if !ok {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are two possibilites:
|
||||||
|
// - We use a KMS -> There must be a key ID and a KMS data key.
|
||||||
|
// - We use a K/V -> There must be no key ID and no KMS data key.
|
||||||
|
// Otherwise, the metadata is corrupted.
|
||||||
|
keyID, idPresent := metadata[MetaKeyID]
|
||||||
|
b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey]
|
||||||
|
if !idPresent && kmsKeyPresent {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
|
||||||
|
}
|
||||||
|
if idPresent && !kmsKeyPresent {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether all extracted values are well-formed
|
||||||
|
iv, err := base64.StdEncoding.DecodeString(b64IV)
|
||||||
|
if err != nil || len(iv) != 32 {
|
||||||
|
return keyID, kmsKey, sealedKey, errInvalidInternalIV
|
||||||
|
}
|
||||||
|
if algorithm != SealAlgorithm {
|
||||||
|
return keyID, kmsKey, sealedKey, errInvalidInternalSealAlgorithm
|
||||||
|
}
|
||||||
|
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
||||||
|
if err != nil || len(encryptedKey) != 64 {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid")
|
||||||
|
}
|
||||||
|
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
|
||||||
|
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
||||||
|
if err != nil {
|
||||||
|
return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealedKey.Algorithm = algorithm
|
||||||
|
copy(sealedKey.IV[:], iv)
|
||||||
|
copy(sealedKey.Key[:], encryptedKey)
|
||||||
|
return keyID, kmsKey, sealedKey, nil
|
||||||
|
}
|
|
@ -17,44 +17,15 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/ioutil"
|
"github.com/minio/minio/pkg/ioutil"
|
||||||
"github.com/minio/sio"
|
"github.com/minio/sio"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// SSEMultipart is the metadata key indicating that the object
|
|
||||||
// was uploaded using the S3 multipart API and stored using
|
|
||||||
// some from of server-side-encryption.
|
|
||||||
SSEMultipart = "X-Minio-Internal-Encrypted-Multipart"
|
|
||||||
|
|
||||||
// SSEIV is the metadata key referencing the random initialization
|
|
||||||
// vector (IV) used for SSE-S3 and SSE-C key derivation.
|
|
||||||
SSEIV = "X-Minio-Internal-Server-Side-Encryption-Iv"
|
|
||||||
|
|
||||||
// SSESealAlgorithm is the metadata key referencing the algorithm
|
|
||||||
// used by SSE-C and SSE-S3 to encrypt the object.
|
|
||||||
SSESealAlgorithm = "X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm"
|
|
||||||
|
|
||||||
// SSECSealedKey is the metadata key referencing the sealed object-key for SSE-C.
|
|
||||||
SSECSealedKey = "X-Minio-Internal-Server-Side-Encryption-Sealed-Key"
|
|
||||||
|
|
||||||
// S3SealedKey is the metadata key referencing the sealed object-key for SSE-S3.
|
|
||||||
S3SealedKey = "X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key"
|
|
||||||
|
|
||||||
// S3KMSKeyID is the metadata key referencing the KMS key-id used to
|
|
||||||
// generate/decrypt the S3-KMS-Sealed-Key. It is only used for SSE-S3 + KMS.
|
|
||||||
S3KMSKeyID = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id"
|
|
||||||
|
|
||||||
// S3KMSSealedKey is the metadata key referencing the encrypted key generated
|
|
||||||
// by KMS. It is only used for SSE-S3 + KMS.
|
|
||||||
S3KMSSealedKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// SealAlgorithm is the encryption/sealing algorithm used to derive & seal
|
// SealAlgorithm is the encryption/sealing algorithm used to derive & seal
|
||||||
// the key-encryption-key and to en/decrypt the object data.
|
// the key-encryption-key and to en/decrypt the object data.
|
||||||
|
@ -67,39 +38,34 @@ const (
|
||||||
InsecureSealAlgorithm = "DARE-SHA256"
|
InsecureSealAlgorithm = "DARE-SHA256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the SSE domain as string. For SSE-S3 the
|
// Type represents an AWS SSE type:
|
||||||
// domain is "SSE-S3".
|
// • SSE-C
|
||||||
func (s3) String() string { return "SSE-S3" }
|
// • SSE-S3
|
||||||
|
// • SSE-KMS
|
||||||
|
type Type interface {
|
||||||
|
fmt.Stringer
|
||||||
|
|
||||||
// UnsealObjectKey extracts and decrypts the sealed object key
|
IsRequested(http.Header) bool
|
||||||
// from the metadata using KMS and returns the decrypted object
|
|
||||||
// key.
|
IsEncrypted(map[string]string) bool
|
||||||
func (sse s3) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
|
||||||
keyID, kmsKey, sealedKey, err := sse.ParseMetadata(metadata)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = key.Unseal(unsealKey, sealedKey, sse.String(), bucket, object)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the SSE domain as string. For SSE-C the
|
// IsRequested returns true and the SSE Type if the HTTP headers
|
||||||
// domain is "SSE-C".
|
// indicate that some form server-side encryption is requested.
|
||||||
func (ssec) String() string { return "SSE-C" }
|
//
|
||||||
|
// If no SSE headers are present then IsRequested returns false
|
||||||
// UnsealObjectKey extracts and decrypts the sealed object key
|
// and no Type.
|
||||||
// from the metadata using the SSE-C client key of the HTTP headers
|
func IsRequested(h http.Header) (Type, bool) {
|
||||||
// and returns the decrypted object key.
|
switch {
|
||||||
func (sse ssec) UnsealObjectKey(h http.Header, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
case S3.IsRequested(h):
|
||||||
clientKey, err := sse.ParseHTTP(h)
|
return S3, true
|
||||||
if err != nil {
|
case S3KMS.IsRequested(h):
|
||||||
return
|
return S3KMS, true
|
||||||
|
case SSEC.IsRequested(h):
|
||||||
|
return SSEC, true
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
return unsealObjectKey(clientKey, metadata, bucket, object)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnsealObjectKey extracts and decrypts the sealed object key
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
|
|
@ -962,7 +962,7 @@ func (c *diskCache) Get(ctx context.Context, bucket, object string, rs *HTTPRang
|
||||||
}
|
}
|
||||||
if globalCacheKMS != nil {
|
if globalCacheKMS != nil {
|
||||||
// clean up internal SSE cache metadata
|
// clean up internal SSE cache metadata
|
||||||
delete(gr.ObjInfo.UserDefined, crypto.SSEHeader)
|
delete(gr.ObjInfo.UserDefined, xhttp.AmzServerSideEncryption)
|
||||||
}
|
}
|
||||||
if !rngInfo.Empty() {
|
if !rngInfo.Empty() {
|
||||||
// overlay Size with actual object size and not the range size
|
// overlay Size with actual object size and not the range size
|
||||||
|
|
|
@ -386,13 +386,13 @@ func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, offset,
|
||||||
header: h,
|
header: h,
|
||||||
bucket: bucket,
|
bucket: bucket,
|
||||||
object: object,
|
object: object,
|
||||||
customerKeyHeader: h.Get(crypto.SSECKey),
|
customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey),
|
||||||
copySource: copySource,
|
copySource: copySource,
|
||||||
metadata: cloneMSS(oi.UserDefined),
|
metadata: cloneMSS(oi.UserDefined),
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.copySource {
|
if w.copySource {
|
||||||
w.customerKeyHeader = h.Get(crypto.SSECopyKey)
|
w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
|
if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
|
||||||
|
@ -434,12 +434,12 @@ func (d *DecryptBlocksReader) buildDecrypter(partID int) error {
|
||||||
var err error
|
var err error
|
||||||
if d.copySource {
|
if d.copySource {
|
||||||
if crypto.SSEC.IsEncrypted(d.metadata) {
|
if crypto.SSEC.IsEncrypted(d.metadata) {
|
||||||
d.header.Set(crypto.SSECopyKey, d.customerKeyHeader)
|
d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader)
|
||||||
key, err = ParseSSECopyCustomerRequest(d.header, d.metadata)
|
key, err = ParseSSECopyCustomerRequest(d.header, d.metadata)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if crypto.SSEC.IsEncrypted(d.metadata) {
|
if crypto.SSEC.IsEncrypted(d.metadata) {
|
||||||
d.header.Set(crypto.SSECKey, d.customerKeyHeader)
|
d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader)
|
||||||
key, err = ParseSSECustomerHeader(d.header)
|
key, err = ParseSSECustomerHeader(d.header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/klauspost/compress/zstd"
|
"github.com/klauspost/compress/zstd"
|
||||||
"github.com/minio/minio-go/v7/pkg/encrypt"
|
"github.com/minio/minio-go/v7/pkg/encrypt"
|
||||||
"github.com/minio/minio/cmd/crypto"
|
"github.com/minio/minio/cmd/crypto"
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/sio"
|
"github.com/minio/sio"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,20 +39,20 @@ var encryptRequestTests = []struct {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
header: map[string]string{
|
header: map[string]string{
|
||||||
crypto.SSECAlgorithm: "AES256",
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: "AES256",
|
||||||
crypto.SSECKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
xhttp.AmzServerSideEncryptionCustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||||
crypto.SSECKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||||
},
|
},
|
||||||
metadata: map[string]string{},
|
metadata: map[string]string{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: map[string]string{
|
header: map[string]string{
|
||||||
crypto.SSECAlgorithm: "AES256",
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: "AES256",
|
||||||
crypto.SSECKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
xhttp.AmzServerSideEncryptionCustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||||
crypto.SSECKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||||
},
|
},
|
||||||
metadata: map[string]string{
|
metadata: map[string]string{
|
||||||
crypto.SSECKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
xhttp.AmzServerSideEncryptionCustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -70,13 +71,13 @@ func TestEncryptRequest(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
||||||
}
|
}
|
||||||
if kdf, ok := test.metadata[crypto.SSESealAlgorithm]; !ok {
|
if kdf, ok := test.metadata[crypto.MetaAlgorithm]; !ok {
|
||||||
t.Errorf("Test %d: ServerSideEncryptionKDF must be part of metadata: %v", i, kdf)
|
t.Errorf("Test %d: ServerSideEncryptionKDF must be part of metadata: %v", i, kdf)
|
||||||
}
|
}
|
||||||
if iv, ok := test.metadata[crypto.SSEIV]; !ok {
|
if iv, ok := test.metadata[crypto.MetaIV]; !ok {
|
||||||
t.Errorf("Test %d: crypto.SSEIV must be part of metadata: %v", i, iv)
|
t.Errorf("Test %d: crypto.SSEIV must be part of metadata: %v", i, iv)
|
||||||
}
|
}
|
||||||
if mac, ok := test.metadata[crypto.SSECSealedKey]; !ok {
|
if mac, ok := test.metadata[crypto.MetaSealedKeySSEC]; !ok {
|
||||||
t.Errorf("Test %d: ServerSideEncryptionKeyMAC must be part of metadata: %v", i, mac)
|
t.Errorf("Test %d: ServerSideEncryptionKeyMAC must be part of metadata: %v", i, mac)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,33 +94,33 @@ var decryptObjectInfoTests = []struct {
|
||||||
expErr: nil,
|
expErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{crypto.SSESealAlgorithm: crypto.InsecureSealAlgorithm}},
|
info: ObjectInfo{Size: 100, UserDefined: map[string]string{crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm}},
|
||||||
request: &http.Request{Header: http.Header{crypto.SSECAlgorithm: []string{crypto.SSEAlgorithmAES256}}},
|
request: &http.Request{Header: http.Header{xhttp.AmzServerSideEncryption: []string{xhttp.AmzEncryptionAES}}},
|
||||||
expErr: nil,
|
expErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
info: ObjectInfo{Size: 0, UserDefined: map[string]string{crypto.SSESealAlgorithm: crypto.InsecureSealAlgorithm}},
|
info: ObjectInfo{Size: 0, UserDefined: map[string]string{crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm}},
|
||||||
request: &http.Request{Header: http.Header{crypto.SSECAlgorithm: []string{crypto.SSEAlgorithmAES256}}},
|
request: &http.Request{Header: http.Header{xhttp.AmzServerSideEncryption: []string{xhttp.AmzEncryptionAES}}},
|
||||||
expErr: nil,
|
expErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{crypto.SSECSealedKey: "EAAfAAAAAAD7v1hQq3PFRUHsItalxmrJqrOq6FwnbXNarxOOpb8jTWONPPKyM3Gfjkjyj6NCf+aB/VpHCLCTBA=="}},
|
info: ObjectInfo{Size: 100, UserDefined: map[string]string{crypto.MetaSealedKeySSEC: "EAAfAAAAAAD7v1hQq3PFRUHsItalxmrJqrOq6FwnbXNarxOOpb8jTWONPPKyM3Gfjkjyj6NCf+aB/VpHCLCTBA=="}},
|
||||||
request: &http.Request{Header: http.Header{}},
|
request: &http.Request{Header: http.Header{}},
|
||||||
expErr: errEncryptedObject,
|
expErr: errEncryptedObject,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{}},
|
info: ObjectInfo{Size: 100, UserDefined: map[string]string{}},
|
||||||
request: &http.Request{Method: http.MethodGet, Header: http.Header{crypto.SSECAlgorithm: []string{crypto.SSEAlgorithmAES256}}},
|
request: &http.Request{Method: http.MethodGet, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES}}},
|
||||||
expErr: errInvalidEncryptionParameters,
|
expErr: errInvalidEncryptionParameters,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{}},
|
info: ObjectInfo{Size: 100, UserDefined: map[string]string{}},
|
||||||
request: &http.Request{Method: http.MethodHead, Header: http.Header{crypto.SSECAlgorithm: []string{crypto.SSEAlgorithmAES256}}},
|
request: &http.Request{Method: http.MethodHead, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES}}},
|
||||||
expErr: errInvalidEncryptionParameters,
|
expErr: errInvalidEncryptionParameters,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
info: ObjectInfo{Size: 31, UserDefined: map[string]string{crypto.SSESealAlgorithm: crypto.InsecureSealAlgorithm}},
|
info: ObjectInfo{Size: 31, UserDefined: map[string]string{crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm}},
|
||||||
request: &http.Request{Header: http.Header{crypto.SSECAlgorithm: []string{crypto.SSEAlgorithmAES256}}},
|
request: &http.Request{Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES}}},
|
||||||
expErr: errObjectTampered,
|
expErr: errObjectTampered,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -221,10 +222,10 @@ func TestGetDecryptedRange_Issue50(t *testing.T) {
|
||||||
Name: "object",
|
Name: "object",
|
||||||
Size: 595160760,
|
Size: 595160760,
|
||||||
UserDefined: map[string]string{
|
UserDefined: map[string]string{
|
||||||
crypto.SSEMultipart: "",
|
crypto.MetaMultipart: "",
|
||||||
crypto.SSEIV: "HTexa=",
|
crypto.MetaIV: "HTexa=",
|
||||||
crypto.SSESealAlgorithm: "DAREv2-HMAC-SHA256",
|
crypto.MetaAlgorithm: "DAREv2-HMAC-SHA256",
|
||||||
crypto.SSECSealedKey: "IAA8PGAA==",
|
crypto.MetaSealedKeySSEC: "IAA8PGAA==",
|
||||||
ReservedMetadataPrefix + "actual-size": "594870264",
|
ReservedMetadataPrefix + "actual-size": "594870264",
|
||||||
"content-type": "application/octet-stream",
|
"content-type": "application/octet-stream",
|
||||||
"etag": "166b1545b4c1535294ee0686678bea8c-2",
|
"etag": "166b1545b4c1535294ee0686678bea8c-2",
|
||||||
|
@ -276,11 +277,11 @@ func TestGetDecryptedRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
udMap = func(isMulti bool) map[string]string {
|
udMap = func(isMulti bool) map[string]string {
|
||||||
m := map[string]string{
|
m := map[string]string{
|
||||||
crypto.SSESealAlgorithm: crypto.InsecureSealAlgorithm,
|
crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm,
|
||||||
crypto.SSEMultipart: "1",
|
crypto.MetaMultipart: "1",
|
||||||
}
|
}
|
||||||
if !isMulti {
|
if !isMulti {
|
||||||
delete(m, crypto.SSEMultipart)
|
delete(m, crypto.MetaMultipart)
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -553,56 +554,56 @@ var getDefaultOptsTests = []struct {
|
||||||
encryptionType encrypt.Type
|
encryptionType encrypt.Type
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
{headers: http.Header{crypto.SSECAlgorithm: []string{"AES256"},
|
{headers: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"},
|
||||||
crypto.SSECKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
crypto.SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
||||||
copySource: false,
|
copySource: false,
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
encryptionType: encrypt.SSEC,
|
encryptionType: encrypt.SSEC,
|
||||||
err: nil}, // 0
|
err: nil}, // 0
|
||||||
{headers: http.Header{crypto.SSECAlgorithm: []string{"AES256"},
|
{headers: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"},
|
||||||
crypto.SSECKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
crypto.SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
||||||
copySource: true,
|
copySource: true,
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
encryptionType: "",
|
encryptionType: "",
|
||||||
err: nil}, // 1
|
err: nil}, // 1
|
||||||
{headers: http.Header{crypto.SSECAlgorithm: []string{"AES256"},
|
{headers: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"},
|
||||||
crypto.SSECKey: []string{"Mz"},
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{"Mz"},
|
||||||
crypto.SSECKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
||||||
copySource: false,
|
copySource: false,
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
encryptionType: "",
|
encryptionType: "",
|
||||||
err: crypto.ErrInvalidCustomerKey}, // 2
|
err: crypto.ErrInvalidCustomerKey}, // 2
|
||||||
{headers: http.Header{crypto.SSEHeader: []string{"AES256"}},
|
{headers: http.Header{xhttp.AmzServerSideEncryption: []string{"AES256"}},
|
||||||
copySource: false,
|
copySource: false,
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
encryptionType: encrypt.S3,
|
encryptionType: encrypt.S3,
|
||||||
err: nil}, // 3
|
err: nil}, // 3
|
||||||
{headers: http.Header{},
|
{headers: http.Header{},
|
||||||
copySource: false,
|
copySource: false,
|
||||||
metadata: map[string]string{crypto.S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)),
|
metadata: map[string]string{crypto.MetaSealedKeyS3: base64.StdEncoding.EncodeToString(make([]byte, 64)),
|
||||||
crypto.S3KMSKeyID: "kms-key",
|
crypto.MetaKeyID: "kms-key",
|
||||||
crypto.S3KMSSealedKey: "m-key"},
|
crypto.MetaDataEncryptionKey: "m-key"},
|
||||||
encryptionType: encrypt.S3,
|
encryptionType: encrypt.S3,
|
||||||
err: nil}, // 4
|
err: nil}, // 4
|
||||||
{headers: http.Header{},
|
{headers: http.Header{},
|
||||||
copySource: true,
|
copySource: true,
|
||||||
metadata: map[string]string{crypto.S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)),
|
metadata: map[string]string{crypto.MetaSealedKeyS3: base64.StdEncoding.EncodeToString(make([]byte, 64)),
|
||||||
crypto.S3KMSKeyID: "kms-key",
|
crypto.MetaKeyID: "kms-key",
|
||||||
crypto.S3KMSSealedKey: "m-key"},
|
crypto.MetaDataEncryptionKey: "m-key"},
|
||||||
encryptionType: "",
|
encryptionType: "",
|
||||||
err: nil}, // 5
|
err: nil}, // 5
|
||||||
{headers: http.Header{crypto.SSECopyAlgorithm: []string{"AES256"},
|
{headers: http.Header{xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm: []string{"AES256"},
|
||||||
crypto.SSECopyKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCopyCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
crypto.SSECopyKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
||||||
copySource: true,
|
copySource: true,
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
encryptionType: encrypt.SSEC,
|
encryptionType: encrypt.SSEC,
|
||||||
err: nil}, // 6
|
err: nil}, // 6
|
||||||
{headers: http.Header{crypto.SSECopyAlgorithm: []string{"AES256"},
|
{headers: http.Header{xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm: []string{"AES256"},
|
||||||
crypto.SSECopyKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
xhttp.AmzServerSideEncryptionCopyCustomerKey: []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
crypto.SSECopyKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="}},
|
||||||
copySource: false,
|
copySource: false,
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
encryptionType: "",
|
encryptionType: "",
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/crypto"
|
"github.com/minio/minio/cmd/crypto"
|
||||||
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests getRedirectLocation function for all its criteria.
|
// Tests getRedirectLocation function for all its criteria.
|
||||||
|
@ -181,15 +182,15 @@ var containsReservedMetadataTests = []struct {
|
||||||
header: http.Header{"X-Minio-Key": []string{"value"}},
|
header: http.Header{"X-Minio-Key": []string{"value"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: http.Header{crypto.SSEIV: []string{"iv"}},
|
header: http.Header{crypto.MetaIV: []string{"iv"}},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: http.Header{crypto.SSESealAlgorithm: []string{crypto.InsecureSealAlgorithm}},
|
header: http.Header{crypto.MetaAlgorithm: []string{crypto.InsecureSealAlgorithm}},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: http.Header{crypto.SSECSealedKey: []string{"mac"}},
|
header: http.Header{crypto.MetaSealedKeySSEC: []string{"mac"}},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -217,11 +218,11 @@ var sseTLSHandlerTests = []struct {
|
||||||
Header http.Header
|
Header http.Header
|
||||||
IsTLS, ShouldFail bool
|
IsTLS, ShouldFail bool
|
||||||
}{
|
}{
|
||||||
{URL: &url.URL{}, Header: http.Header{}, IsTLS: false, ShouldFail: false}, // 0
|
{URL: &url.URL{}, Header: http.Header{}, IsTLS: false, ShouldFail: false}, // 0
|
||||||
{URL: &url.URL{}, Header: http.Header{crypto.SSECAlgorithm: []string{"AES256"}}, IsTLS: false, ShouldFail: true}, // 1
|
{URL: &url.URL{}, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"}}, IsTLS: false, ShouldFail: true}, // 1
|
||||||
{URL: &url.URL{}, Header: http.Header{crypto.SSECAlgorithm: []string{"AES256"}}, IsTLS: true, ShouldFail: false}, // 2
|
{URL: &url.URL{}, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"}}, IsTLS: true, ShouldFail: false}, // 2
|
||||||
{URL: &url.URL{}, Header: http.Header{crypto.SSECKey: []string{""}}, IsTLS: true, ShouldFail: false}, // 3
|
{URL: &url.URL{}, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerKey: []string{""}}, IsTLS: true, ShouldFail: false}, // 3
|
||||||
{URL: &url.URL{}, Header: http.Header{crypto.SSECopyAlgorithm: []string{""}}, IsTLS: false, ShouldFail: true}, // 4
|
{URL: &url.URL{}, Header: http.Header{xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm: []string{""}}, IsTLS: false, ShouldFail: true}, // 4
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSSETLSHandler(t *testing.T) {
|
func TestSSETLSHandler(t *testing.T) {
|
||||||
|
|
|
@ -111,6 +111,20 @@ const (
|
||||||
AmzMetaUnencryptedContentLength = "X-Amz-Meta-X-Amz-Unencrypted-Content-Length"
|
AmzMetaUnencryptedContentLength = "X-Amz-Meta-X-Amz-Unencrypted-Content-Length"
|
||||||
AmzMetaUnencryptedContentMD5 = "X-Amz-Meta-X-Amz-Unencrypted-Content-Md5"
|
AmzMetaUnencryptedContentMD5 = "X-Amz-Meta-X-Amz-Unencrypted-Content-Md5"
|
||||||
|
|
||||||
|
// AWS server-side encryption headers for SSE-S3, SSE-KMS and SSE-C.
|
||||||
|
AmzServerSideEncryption = "X-Amz-Server-Side-Encryption"
|
||||||
|
AmzServerSideEncryptionKmsID = AmzServerSideEncryption + "-Aws-Kms-Key-Id"
|
||||||
|
AmzServerSideEncryptionKmsContext = AmzServerSideEncryption + "-Context"
|
||||||
|
AmzServerSideEncryptionCustomerAlgorithm = AmzServerSideEncryption + "-Customer-Algorithm"
|
||||||
|
AmzServerSideEncryptionCustomerKey = AmzServerSideEncryption + "-Customer-Key"
|
||||||
|
AmzServerSideEncryptionCustomerKeyMD5 = AmzServerSideEncryption + "-Customer-Key-Md5"
|
||||||
|
AmzServerSideEncryptionCopyCustomerAlgorithm = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm"
|
||||||
|
AmzServerSideEncryptionCopyCustomerKey = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key"
|
||||||
|
AmzServerSideEncryptionCopyCustomerKeyMD5 = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5"
|
||||||
|
|
||||||
|
AmzEncryptionAES = "AES256"
|
||||||
|
AmzEncryptionKMS = "aws:kms"
|
||||||
|
|
||||||
// Signature v2 related constants
|
// Signature v2 related constants
|
||||||
AmzSignatureV2 = "Signature"
|
AmzSignatureV2 = "Signature"
|
||||||
AmzAccessKeyID = "AWSAccessKeyId"
|
AmzAccessKeyID = "AWSAccessKeyId"
|
||||||
|
|
|
@ -439,7 +439,9 @@ func isCompressible(header http.Header, object string) bool {
|
||||||
globalCompressConfigMu.Lock()
|
globalCompressConfigMu.Lock()
|
||||||
cfg := globalCompressConfig
|
cfg := globalCompressConfig
|
||||||
globalCompressConfigMu.Unlock()
|
globalCompressConfigMu.Unlock()
|
||||||
if !cfg.Enabled || crypto.IsRequested(header) || excludeForCompression(header, object, cfg) {
|
|
||||||
|
_, ok := crypto.IsRequested(header)
|
||||||
|
if !cfg.Enabled || ok || excludeForCompression(header, object, cfg) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -628,7 +630,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions, cl
|
||||||
// encrypted bytes. The header parameter is used to
|
// encrypted bytes. The header parameter is used to
|
||||||
// provide encryption parameters.
|
// provide encryption parameters.
|
||||||
fn = func(inputReader io.Reader, h http.Header, pcfn CheckPreconditionFn, cFns ...func()) (r *GetObjectReader, err error) {
|
fn = func(inputReader io.Reader, h http.Header, pcfn CheckPreconditionFn, cFns ...func()) (r *GetObjectReader, err error) {
|
||||||
copySource := h.Get(crypto.SSECopyAlgorithm) != ""
|
copySource := h.Get(xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm) != ""
|
||||||
|
|
||||||
cFns = append(cleanUpFns, cFns...)
|
cFns = append(cleanUpFns, cFns...)
|
||||||
// Attach decrypter on inputReader
|
// Attach decrypter on inputReader
|
||||||
|
|
|
@ -347,7 +347,7 @@ func TestIsCompressed(t *testing.T) {
|
||||||
UserDefined: map[string]string{"X-Minio-Internal-compression": compressionAlgorithmV2,
|
UserDefined: map[string]string{"X-Minio-Internal-compression": compressionAlgorithmV2,
|
||||||
"content-type": "application/octet-stream",
|
"content-type": "application/octet-stream",
|
||||||
"etag": "b3ff3ef3789147152fbfbc50efba4bfd-2",
|
"etag": "b3ff3ef3789147152fbfbc50efba4bfd-2",
|
||||||
crypto.SSEIV: "yes",
|
crypto.MetaIV: "yes",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: true,
|
result: true,
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); ok && !objectAPI.IsEncryptionSupported() {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -267,15 +267,15 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
if crypto.IsEncrypted(objInfo.UserDefined) {
|
||||||
switch {
|
switch {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
||||||
// Validate the SSE-C Key set in the header.
|
// Validate the SSE-C Key set in the header.
|
||||||
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
|
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set(crypto.SSECAlgorithm, r.Header.Get(crypto.SSECAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(crypto.SSECKeyMD5, r.Header.Get(crypto.SSECKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +313,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -446,10 +446,10 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
if crypto.IsEncrypted(objInfo.UserDefined) {
|
||||||
switch {
|
switch {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSECAlgorithm, r.Header.Get(crypto.SSECAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(crypto.SSECKeyMD5, r.Header.Get(crypto.SSECKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -518,7 +518,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
||||||
writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest))
|
writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrBadRequest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrBadRequest), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -640,15 +640,15 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
if crypto.IsEncrypted(objInfo.UserDefined) {
|
||||||
switch {
|
switch {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
||||||
// Validate the SSE-C Key set in the header.
|
// Validate the SSE-C Key set in the header.
|
||||||
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
|
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
|
||||||
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err))
|
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set(crypto.SSECAlgorithm, r.Header.Get(crypto.SSECAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(crypto.SSECKeyMD5, r.Header.Get(crypto.SSECKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -811,7 +811,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -884,7 +884,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||||
_, err = globalBucketSSEConfigSys.Get(dstBucket)
|
_, err = globalBucketSSEConfigSys.Get(dstBucket)
|
||||||
// This request header needs to be set prior to setting ObjectOptions
|
// This request header needs to be set prior to setting ObjectOptions
|
||||||
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
}
|
}
|
||||||
|
|
||||||
var srcOpts, dstOpts ObjectOptions
|
var srcOpts, dstOpts ObjectOptions
|
||||||
|
@ -1030,7 +1030,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
||||||
pReader := NewPutObjReader(srcInfo.Reader, nil, nil)
|
pReader := NewPutObjReader(srcInfo.Reader, nil, nil)
|
||||||
|
|
||||||
// Check if either the source is encrypted or the destination will be encrypted.
|
// Check if either the source is encrypted or the destination will be encrypted.
|
||||||
objectEncryption := crypto.IsSourceEncrypted(srcInfo.UserDefined) || crypto.IsRequested(r.Header)
|
_, objectEncryption := crypto.IsRequested(r.Header)
|
||||||
|
objectEncryption = objectEncryption || crypto.IsSourceEncrypted(srcInfo.UserDefined)
|
||||||
var encMetadata = make(map[string]string)
|
var encMetadata = make(map[string]string)
|
||||||
if objectAPI.IsEncryptionSupported() && !isCompressed {
|
if objectAPI.IsEncryptionSupported() && !isCompressed {
|
||||||
// Encryption parameters not applicable for this object.
|
// Encryption parameters not applicable for this object.
|
||||||
|
@ -1321,7 +1322,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1451,7 +1452,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||||
_, err = globalBucketSSEConfigSys.Get(bucket)
|
_, err = globalBucketSSEConfigSys.Get(bucket)
|
||||||
// This request header needs to be set prior to setting ObjectOptions
|
// This request header needs to be set prior to setting ObjectOptions
|
||||||
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualSize := size
|
actualSize := size
|
||||||
|
@ -1528,7 +1529,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||||
}
|
}
|
||||||
var objectEncryptionKey crypto.ObjectKey
|
var objectEncryptionKey crypto.ObjectKey
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsRequested(r.Header) && !HasSuffix(object, SlashSeparator) { // handle SSE requests
|
if _, ok := crypto.IsRequested(r.Header); ok && !HasSuffix(object, SlashSeparator) { // handle SSE requests
|
||||||
if crypto.SSECopy.IsRequested(r.Header) {
|
if crypto.SSECopy.IsRequested(r.Header) {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
|
@ -1569,11 +1570,11 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||||
case crypto.IsEncrypted(objInfo.UserDefined):
|
case crypto.IsEncrypted(objInfo.UserDefined):
|
||||||
switch {
|
switch {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag})
|
objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag})
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSECAlgorithm, r.Header.Get(crypto.SSECAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(crypto.SSECKeyMD5, r.Header.Get(crypto.SSECKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
|
|
||||||
if len(objInfo.ETag) >= 32 && strings.Count(objInfo.ETag, "-") != 1 {
|
if len(objInfo.ETag) >= 32 && strings.Count(objInfo.ETag, "-") != 1 {
|
||||||
objInfo.ETag = objInfo.ETag[len(objInfo.ETag)-32:]
|
objInfo.ETag = objInfo.ETag[len(objInfo.ETag)-32:]
|
||||||
|
@ -1623,7 +1624,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1645,7 +1646,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
||||||
_, err = globalBucketSSEConfigSys.Get(bucket)
|
_, err = globalBucketSSEConfigSys.Get(bucket)
|
||||||
// This request header needs to be set prior to setting ObjectOptions
|
// This request header needs to be set prior to setting ObjectOptions
|
||||||
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate storage class metadata if present
|
// Validate storage class metadata if present
|
||||||
|
@ -1659,7 +1660,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
||||||
var encMetadata = map[string]string{}
|
var encMetadata = map[string]string{}
|
||||||
|
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); ok {
|
||||||
if err = setEncryptionMetadata(r, bucket, object, encMetadata); err != nil {
|
if err = setEncryptionMetadata(r, bucket, object, encMetadata); err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
|
@ -1751,7 +1752,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2067,7 +2068,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !objectAPI.IsEncryptionSupported() && crypto.IsRequested(r.Header) {
|
if _, ok := crypto.IsRequested(r.Header); !objectAPI.IsEncryptionSupported() && ok {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
"github.com/minio/minio/cmd/crypto"
|
|
||||||
xhttp "github.com/minio/minio/cmd/http"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
ioutilx "github.com/minio/minio/pkg/ioutil"
|
ioutilx "github.com/minio/minio/pkg/ioutil"
|
||||||
|
@ -221,9 +220,9 @@ func testAPIHeadObjectHandlerWithEncryption(obj ObjectLayer, instanceType, bucke
|
||||||
key32Bytes = generateBytesData(32 * humanize.Byte)
|
key32Bytes = generateBytesData(32 * humanize.Byte)
|
||||||
key32BytesMd5 = md5.Sum(key32Bytes)
|
key32BytesMd5 = md5.Sum(key32Bytes)
|
||||||
metaWithSSEC = map[string]string{
|
metaWithSSEC = map[string]string{
|
||||||
crypto.SSECAlgorithm: crypto.SSEAlgorithmAES256,
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
crypto.SSECKey: base64.StdEncoding.EncodeToString(key32Bytes),
|
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(key32Bytes),
|
||||||
crypto.SSECKeyMD5: base64.StdEncoding.EncodeToString(key32BytesMd5[:]),
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(key32BytesMd5[:]),
|
||||||
}
|
}
|
||||||
mapCopy = func(m map[string]string) map[string]string {
|
mapCopy = func(m map[string]string) map[string]string {
|
||||||
r := make(map[string]string, len(m))
|
r := make(map[string]string, len(m))
|
||||||
|
@ -667,9 +666,9 @@ func testAPIGetObjectWithMPHandler(obj ObjectLayer, instanceType, bucketName str
|
||||||
key32Bytes = generateBytesData(32 * humanize.Byte)
|
key32Bytes = generateBytesData(32 * humanize.Byte)
|
||||||
key32BytesMd5 = md5.Sum(key32Bytes)
|
key32BytesMd5 = md5.Sum(key32Bytes)
|
||||||
metaWithSSEC = map[string]string{
|
metaWithSSEC = map[string]string{
|
||||||
crypto.SSECAlgorithm: crypto.SSEAlgorithmAES256,
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
crypto.SSECKey: base64.StdEncoding.EncodeToString(key32Bytes),
|
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(key32Bytes),
|
||||||
crypto.SSECKeyMD5: base64.StdEncoding.EncodeToString(key32BytesMd5[:]),
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(key32BytesMd5[:]),
|
||||||
}
|
}
|
||||||
mapCopy = func(m map[string]string) map[string]string {
|
mapCopy = func(m map[string]string) map[string]string {
|
||||||
r := make(map[string]string, len(m))
|
r := make(map[string]string, len(m))
|
||||||
|
@ -865,9 +864,9 @@ func testAPIGetObjectWithPartNumberHandler(obj ObjectLayer, instanceType, bucket
|
||||||
key32Bytes = generateBytesData(32 * humanize.Byte)
|
key32Bytes = generateBytesData(32 * humanize.Byte)
|
||||||
key32BytesMd5 = md5.Sum(key32Bytes)
|
key32BytesMd5 = md5.Sum(key32Bytes)
|
||||||
metaWithSSEC = map[string]string{
|
metaWithSSEC = map[string]string{
|
||||||
crypto.SSECAlgorithm: crypto.SSEAlgorithmAES256,
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
crypto.SSECKey: base64.StdEncoding.EncodeToString(key32Bytes),
|
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(key32Bytes),
|
||||||
crypto.SSECKeyMD5: base64.StdEncoding.EncodeToString(key32BytesMd5[:]),
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(key32BytesMd5[:]),
|
||||||
}
|
}
|
||||||
mapCopy = func(m map[string]string) map[string]string {
|
mapCopy = func(m map[string]string) map[string]string {
|
||||||
r := make(map[string]string, len(m))
|
r := make(map[string]string, len(m))
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/crypto"
|
xhttp "github.com/minio/minio/cmd/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSkipContentSha256Cksum - Test validate the logic which decides whether
|
// TestSkipContentSha256Cksum - Test validate the logic which decides whether
|
||||||
|
@ -160,7 +160,7 @@ func TestExtractSignedHeaders(t *testing.T) {
|
||||||
t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode)
|
t.Fatalf("Expected the APIErrorCode to %d, but got %d", ErrUnsignedHeaders, errCode)
|
||||||
}
|
}
|
||||||
// set headers value through Get parameter
|
// set headers value through Get parameter
|
||||||
inputQuery.Add("x-amz-server-side-encryption", crypto.SSEAlgorithmAES256)
|
inputQuery.Add("x-amz-server-side-encryption", xhttp.AmzEncryptionAES)
|
||||||
r.URL.RawQuery = inputQuery.Encode()
|
r.URL.RawQuery = inputQuery.Encode()
|
||||||
_, errCode = extractSignedHeaders(signedHeaders, r)
|
_, errCode = extractSignedHeaders(signedHeaders, r)
|
||||||
if errCode != ErrNone {
|
if errCode != ErrNone {
|
||||||
|
|
|
@ -1171,7 +1171,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||||
// Check if bucket encryption is enabled
|
// Check if bucket encryption is enabled
|
||||||
_, err = globalBucketSSEConfigSys.Get(bucket)
|
_, err = globalBucketSSEConfigSys.Get(bucket)
|
||||||
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) {
|
||||||
r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
r.Header.Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Require Content-Length to be set in the request
|
// Require Content-Length to be set in the request
|
||||||
|
@ -1239,7 +1239,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsRequested(r.Header) && !HasSuffix(object, SlashSeparator) { // handle SSE requests
|
if _, ok := crypto.IsRequested(r.Header); ok && !HasSuffix(object, SlashSeparator) { // handle SSE requests
|
||||||
rawReader := hashReader
|
rawReader := hashReader
|
||||||
var objectEncryptionKey crypto.ObjectKey
|
var objectEncryptionKey crypto.ObjectKey
|
||||||
reader, objectEncryptionKey, err = EncryptRequest(hashReader, r, bucket, object, metadata)
|
reader, objectEncryptionKey, err = EncryptRequest(hashReader, r, bucket, object, metadata)
|
||||||
|
@ -1288,10 +1288,10 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
if crypto.IsEncrypted(objInfo.UserDefined) {
|
||||||
switch {
|
switch {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
case crypto.SSEC.IsRequested(r.Header):
|
case crypto.SSEC.IsRequested(r.Header):
|
||||||
w.Header().Set(crypto.SSECAlgorithm, r.Header.Get(crypto.SSECAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(crypto.SSECKeyMD5, r.Header.Get(crypto.SSECKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1447,10 +1447,10 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
if crypto.IsEncrypted(objInfo.UserDefined) {
|
||||||
switch {
|
switch {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256)
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
||||||
w.Header().Set(crypto.SSECAlgorithm, r.Header.Get(crypto.SSECAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(crypto.SSECKeyMD5, r.Header.Get(crypto.SSECKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue