mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
crypto: add support for decrypting SSE-KMS metadata (#11415)
This commit refactors the SSE implementation and add S3-compatible SSE-KMS context handling. SSE-KMS differs from SSE-S3 in two main aspects: 1. The client can request a particular key and specify a KMS context as part of the request. 2. The ETag of an SSE-KMS encrypted object is not the MD5 sum of the object content. This commit only focuses on the 1st aspect. A client can send an optional SSE context when using SSE-KMS. This context is remembered by the S3 server such that the client does not have to specify the context again (during multipart PUT / GET / HEAD ...). The crypto. context also includes the bucket/object name to prevent renaming objects at the backend. Now, AWS S3 behaves as following: - If the user does not provide a SSE-KMS context it does not store one - resp. does not include the SSE-KMS context header in the response (e.g. HEAD). - If the user specifies a SSE-KMS context without the bucket/object name then AWS stores the exact context the client provided but adds the bucket/object name internally. The response contains the KMS context without the bucket/object name. - If the user specifies a SSE-KMS context with the bucket/object name then AWS again stores the exact context provided by the client. The response contains the KMS context with the bucket/object name. This commit implements this behavior w.r.t. SSE-KMS. However, as of now, no such object can be created since the server rejects SSE-KMS encryption requests. This commit is one stepping stone for SSE-KMS support. Co-authored-by: Harshavardhana <harsha@minio.io>
This commit is contained in:
parent
f71e192343
commit
871b450dbd
@ -44,6 +44,15 @@ const (
|
|||||||
// MetaDataEncryptionKey is the sealed data encryption key (DEK) received from
|
// MetaDataEncryptionKey is the sealed data encryption key (DEK) received from
|
||||||
// the KMS.
|
// the KMS.
|
||||||
MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key"
|
MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key"
|
||||||
|
|
||||||
|
// MetaContext is the KMS context provided by a client when encrypting an
|
||||||
|
// object with SSE-KMS. A client may not send a context in which case the
|
||||||
|
// MetaContext will not be present.
|
||||||
|
// MetaContext only contains the bucket/object name if the client explicitly
|
||||||
|
// added it. However, when decrypting an object the bucket/object name must
|
||||||
|
// be part of the object. Therefore, the bucket/object name must be added
|
||||||
|
// to the context, if not present, whenever a decryption is performed.
|
||||||
|
MetaContext = "X-Minio-Internal-Server-Side-Encryption-Context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsMultiPart returns true if the object metadata indicates
|
// IsMultiPart returns true if the object metadata indicates
|
||||||
@ -109,26 +118,35 @@ 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) (Type, bool) {
|
||||||
if _, ok := metadata[MetaIV]; ok {
|
if S3KMS.IsEncrypted(metadata) {
|
||||||
return true
|
return S3KMS, true
|
||||||
}
|
|
||||||
if _, ok := metadata[MetaAlgorithm]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if IsMultiPart(metadata) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
if S3.IsEncrypted(metadata) {
|
if S3.IsEncrypted(metadata) {
|
||||||
return true
|
return S3, true
|
||||||
}
|
}
|
||||||
if SSEC.IsEncrypted(metadata) {
|
if SSEC.IsEncrypted(metadata) {
|
||||||
return true
|
return SSEC, true
|
||||||
}
|
}
|
||||||
if S3KMS.IsEncrypted(metadata) {
|
if IsMultiPart(metadata) {
|
||||||
return true
|
return nil, true
|
||||||
}
|
}
|
||||||
return false
|
if _, ok := metadata[MetaIV]; ok {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaAlgorithm]; ok {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaKeyID]; ok {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaDataEncryptionKey]; ok {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
if _, ok := metadata[MetaContext]; ok {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateMultipartMetadata adds the multipart flag entry to metadata
|
// CreateMultipartMetadata adds the multipart flag entry to metadata
|
||||||
|
@ -59,7 +59,7 @@ var isEncryptedTests = []struct {
|
|||||||
|
|
||||||
func TestIsEncrypted(t *testing.T) {
|
func TestIsEncrypted(t *testing.T) {
|
||||||
for i, test := range isEncryptedTests {
|
for i, test := range isEncryptedTests {
|
||||||
if isEncrypted := IsEncrypted(test.Metadata); isEncrypted != test.Encrypted {
|
if _, isEncrypted := IsEncrypted(test.Metadata); isEncrypted != test.Encrypted {
|
||||||
t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted)
|
t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,8 +74,8 @@ var s3IsEncryptedTests = []struct {
|
|||||||
{Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2
|
{Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2
|
||||||
{Encrypted: false, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3
|
{Encrypted: false, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3
|
||||||
{Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4
|
{Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4
|
||||||
{Encrypted: true, Metadata: map[string]string{MetaKeyID: ""}}, // 5
|
{Encrypted: false, Metadata: map[string]string{MetaKeyID: ""}}, // 5
|
||||||
{Encrypted: true, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 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
|
||||||
}
|
}
|
||||||
|
@ -82,12 +82,6 @@ func (ssekms) IsEncrypted(metadata map[string]string) bool {
|
|||||||
if _, ok := metadata[MetaSealedKeyKMS]; ok {
|
if _, ok := metadata[MetaSealedKeyKMS]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := metadata[MetaKeyID]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := metadata[MetaDataEncryptionKey]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +89,14 @@ func (ssekms) IsEncrypted(metadata map[string]string) bool {
|
|||||||
// from the metadata using KMS and returns the decrypted object
|
// from the metadata using KMS and returns the decrypted object
|
||||||
// key.
|
// key.
|
||||||
func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
keyID, kmsKey, sealedKey, err := s3.ParseMetadata(metadata)
|
keyID, kmsKey, sealedKey, ctx, err := s3.ParseMetadata(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
if _, ok := ctx[bucket]; !ok {
|
||||||
|
ctx[bucket] = path.Join(bucket, object)
|
||||||
|
}
|
||||||
|
unsealKey, err := kms.UnsealKey(keyID, kmsKey, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
@ -147,19 +144,19 @@ func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []
|
|||||||
// KMS data key it returns both. If the metadata does not contain neither a
|
// 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 master key ID nor a sealed KMS data key it returns an empty keyID and
|
||||||
// KMS data key. Otherwise, it returns an error.
|
// KMS data key. Otherwise, it returns an error.
|
||||||
func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) {
|
func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, ctx Context, err error) {
|
||||||
// Extract all required values from object metadata
|
// Extract all required values from object metadata
|
||||||
b64IV, ok := metadata[MetaIV]
|
b64IV, ok := metadata[MetaIV]
|
||||||
if !ok {
|
if !ok {
|
||||||
return keyID, kmsKey, sealedKey, errMissingInternalIV
|
return keyID, kmsKey, sealedKey, ctx, errMissingInternalIV
|
||||||
}
|
}
|
||||||
algorithm, ok := metadata[MetaAlgorithm]
|
algorithm, ok := metadata[MetaAlgorithm]
|
||||||
if !ok {
|
if !ok {
|
||||||
return keyID, kmsKey, sealedKey, errMissingInternalSealAlgorithm
|
return keyID, kmsKey, sealedKey, ctx, errMissingInternalSealAlgorithm
|
||||||
}
|
}
|
||||||
b64SealedKey, ok := metadata[MetaSealedKeyKMS]
|
b64SealedKey, ok := metadata[MetaSealedKeyKMS]
|
||||||
if !ok {
|
if !ok {
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are two possibilites:
|
// There are two possibilites:
|
||||||
@ -169,33 +166,44 @@ func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []
|
|||||||
keyID, idPresent := metadata[MetaKeyID]
|
keyID, idPresent := metadata[MetaKeyID]
|
||||||
b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey]
|
b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey]
|
||||||
if !idPresent && kmsKeyPresent {
|
if !idPresent && kmsKeyPresent {
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
|
||||||
}
|
}
|
||||||
if idPresent && !kmsKeyPresent {
|
if idPresent && !kmsKeyPresent {
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether all extracted values are well-formed
|
// Check whether all extracted values are well-formed
|
||||||
iv, err := base64.StdEncoding.DecodeString(b64IV)
|
iv, err := base64.StdEncoding.DecodeString(b64IV)
|
||||||
if err != nil || len(iv) != 32 {
|
if err != nil || len(iv) != 32 {
|
||||||
return keyID, kmsKey, sealedKey, errInvalidInternalIV
|
return keyID, kmsKey, sealedKey, ctx, errInvalidInternalIV
|
||||||
}
|
}
|
||||||
if algorithm != SealAlgorithm {
|
if algorithm != SealAlgorithm {
|
||||||
return keyID, kmsKey, sealedKey, errInvalidInternalSealAlgorithm
|
return keyID, kmsKey, sealedKey, ctx, errInvalidInternalSealAlgorithm
|
||||||
}
|
}
|
||||||
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
|
||||||
if err != nil || len(encryptedKey) != 64 {
|
if err != nil || len(encryptedKey) != 64 {
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid")
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed key for SSE-KMS is invalid")
|
||||||
}
|
}
|
||||||
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
|
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
|
||||||
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid")
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed KMS data key for SSE-KMS is invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b64Ctx, ok := metadata[MetaContext]
|
||||||
|
if ok {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(b64Ctx)
|
||||||
|
if err != nil {
|
||||||
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The internal KMS context is not base64-encoded")
|
||||||
|
}
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
if err = json.Unmarshal(b, ctx); err != nil {
|
||||||
|
return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed KMS context is invalid")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealedKey.Algorithm = algorithm
|
sealedKey.Algorithm = algorithm
|
||||||
copy(sealedKey.IV[:], iv)
|
copy(sealedKey.IV[:], iv)
|
||||||
copy(sealedKey.Key[:], encryptedKey)
|
copy(sealedKey.Key[:], encryptedKey)
|
||||||
return keyID, kmsKey, sealedKey, nil
|
return keyID, kmsKey, sealedKey, ctx, nil
|
||||||
}
|
}
|
||||||
|
@ -62,12 +62,6 @@ func (sses3) IsEncrypted(metadata map[string]string) bool {
|
|||||||
if _, ok := metadata[MetaSealedKeyS3]; ok {
|
if _, ok := metadata[MetaSealedKeyS3]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := metadata[MetaKeyID]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := metadata[MetaDataEncryptionKey]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,11 @@ func backendDownError(err error) bool {
|
|||||||
|
|
||||||
// IsCacheable returns if the object should be saved in the cache.
|
// IsCacheable returns if the object should be saved in the cache.
|
||||||
func (o ObjectInfo) IsCacheable() bool {
|
func (o ObjectInfo) IsCacheable() bool {
|
||||||
return !crypto.IsEncrypted(o.UserDefined) || globalCacheKMS != nil
|
if globalCacheKMS != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, ok := crypto.IsEncrypted(o.UserDefined)
|
||||||
|
return !ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// reads file cached on disk from offset upto length
|
// reads file cached on disk from offset upto length
|
||||||
|
@ -247,56 +247,45 @@ func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
|
func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
|
||||||
switch {
|
switch kind, _ := crypto.IsEncrypted(metadata); kind {
|
||||||
default:
|
case crypto.S3:
|
||||||
return nil, errObjectTampered
|
var KMS crypto.KMS = GlobalKMS
|
||||||
case crypto.S3.IsEncrypted(metadata) && isCacheEncrypted(metadata):
|
if isCacheEncrypted(metadata) {
|
||||||
if globalCacheKMS == nil {
|
KMS = globalCacheKMS
|
||||||
|
}
|
||||||
|
if KMS == nil {
|
||||||
return nil, errKMSNotConfigured
|
return nil, errKMSNotConfigured
|
||||||
}
|
}
|
||||||
keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata)
|
objectKey, err := crypto.S3.UnsealObjectKey(KMS, metadata, bucket, object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
extKey, err := globalCacheKMS.UnsealKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var objectKey crypto.ObjectKey
|
|
||||||
if err = objectKey.Unseal(extKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return objectKey[:], nil
|
return objectKey[:], nil
|
||||||
case crypto.S3.IsEncrypted(metadata):
|
case crypto.S3KMS:
|
||||||
if GlobalKMS == nil {
|
if GlobalKMS == nil {
|
||||||
return nil, errKMSNotConfigured
|
return nil, errKMSNotConfigured
|
||||||
}
|
}
|
||||||
keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata)
|
objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
extKey, err := GlobalKMS.UnsealKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var objectKey crypto.ObjectKey
|
|
||||||
if err = objectKey.Unseal(extKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return objectKey[:], nil
|
return objectKey[:], nil
|
||||||
case crypto.SSEC.IsEncrypted(metadata):
|
case crypto.SSEC:
|
||||||
var extKey [32]byte
|
|
||||||
copy(extKey[:], key)
|
|
||||||
sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
|
sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var objectKey crypto.ObjectKey
|
var (
|
||||||
|
objectKey crypto.ObjectKey
|
||||||
|
extKey [32]byte
|
||||||
|
)
|
||||||
|
copy(extKey[:], key)
|
||||||
if err = objectKey.Unseal(extKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
|
if err = objectKey.Unseal(extKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return objectKey[:], nil
|
return objectKey[:], nil
|
||||||
|
default:
|
||||||
|
return nil, errObjectTampered
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +505,7 @@ func (d *DecryptBlocksReader) Read(p []byte) (int, error) {
|
|||||||
// It returns an error if the object is not encrypted or marked as encrypted
|
// It returns an error if the object is not encrypted or marked as encrypted
|
||||||
// but has an invalid size.
|
// but has an invalid size.
|
||||||
func (o *ObjectInfo) DecryptedSize() (int64, error) {
|
func (o *ObjectInfo) DecryptedSize() (int64, error) {
|
||||||
if !crypto.IsEncrypted(o.UserDefined) {
|
if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
|
||||||
return 0, errors.New("Cannot compute decrypted size of an unencrypted object")
|
return 0, errors.New("Cannot compute decrypted size of an unencrypted object")
|
||||||
}
|
}
|
||||||
if !isEncryptedMultipart(*o) {
|
if !isEncryptedMultipart(*o) {
|
||||||
@ -649,7 +638,7 @@ func tryDecryptETag(key []byte, encryptedETag string, ssec bool) string {
|
|||||||
// requested range starts, along with the DARE sequence number within
|
// requested range starts, along with the DARE sequence number within
|
||||||
// that part. For single part objects, the partStart will be 0.
|
// that part. For single part objects, the partStart will be 0.
|
||||||
func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) {
|
func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) {
|
||||||
if !crypto.IsEncrypted(o.UserDefined) {
|
if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
|
||||||
err = errors.New("Object is not encrypted")
|
err = errors.New("Object is not encrypted")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -796,7 +785,7 @@ func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encrypted = crypto.IsEncrypted(info.UserDefined)
|
_, encrypted = crypto.IsEncrypted(info.UserDefined)
|
||||||
if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" {
|
if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" {
|
||||||
return false, errInvalidEncryptionParameters
|
return false, errInvalidEncryptionParameters
|
||||||
}
|
}
|
||||||
@ -818,7 +807,7 @@ func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err e
|
|||||||
return encrypted, err
|
return encrypted, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if crypto.IsEncrypted(info.UserDefined) && !crypto.IsMultiPart(info.UserDefined) {
|
if _, ok := crypto.IsEncrypted(info.UserDefined); ok && !crypto.IsMultiPart(info.UserDefined) {
|
||||||
info.ETag = getDecryptedETag(headers, *info, false)
|
info.ETag = getDecryptedETag(headers, *info, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func TestDecryptObjectInfo(t *testing.T) {
|
|||||||
for i, test := range decryptObjectInfoTests {
|
for i, test := range decryptObjectInfoTests {
|
||||||
if encrypted, err := DecryptObjectInfo(&test.info, test.request); err != test.expErr {
|
if encrypted, err := DecryptObjectInfo(&test.info, test.request); err != test.expErr {
|
||||||
t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr)
|
t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr)
|
||||||
} else if enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted {
|
} else if _, enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted {
|
||||||
t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i)
|
t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i)
|
||||||
} else if !encrypted && enc != encrypted {
|
} else if !encrypted && enc != encrypted {
|
||||||
t.Errorf("Test %d: Decryption thinks object is not encrypted but it is", i)
|
t.Errorf("Test %d: Decryption thinks object is not encrypted but it is", i)
|
||||||
|
@ -404,7 +404,7 @@ func (o ObjectInfo) IsCompressedOK() (bool, error) {
|
|||||||
// GetActualETag - returns the actual etag of the stored object
|
// GetActualETag - returns the actual etag of the stored object
|
||||||
// decrypts SSE objects.
|
// decrypts SSE objects.
|
||||||
func (o ObjectInfo) GetActualETag(h http.Header) string {
|
func (o ObjectInfo) GetActualETag(h http.Header) string {
|
||||||
if !crypto.IsEncrypted(o.UserDefined) {
|
if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
|
||||||
return o.ETag
|
return o.ETag
|
||||||
}
|
}
|
||||||
return getDecryptedETag(h, o, false)
|
return getDecryptedETag(h, o, false)
|
||||||
@ -423,7 +423,7 @@ func (o ObjectInfo) GetActualSize() (int64, error) {
|
|||||||
}
|
}
|
||||||
return size, nil
|
return size, nil
|
||||||
}
|
}
|
||||||
if crypto.IsEncrypted(o.UserDefined) {
|
if _, ok := crypto.IsEncrypted(o.UserDefined); ok {
|
||||||
return o.DecryptedSize()
|
return o.DecryptedSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +600,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions, cl
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
isEncrypted := crypto.IsEncrypted(oi.UserDefined)
|
_, isEncrypted := crypto.IsEncrypted(oi.UserDefined)
|
||||||
isCompressed, err := oi.IsCompressedOK()
|
isCompressed, err := oi.IsCompressedOK()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
|
@ -265,19 +265,17 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
|||||||
|
|
||||||
// Set encryption response headers
|
// Set encryption response headers
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
switch {
|
case crypto.S3:
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
case crypto.SSEC:
|
||||||
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(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
|
||||||
}
|
}
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,14 +448,12 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
|
|
||||||
// Set encryption response headers
|
// Set encryption response headers
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
switch {
|
case crypto.S3:
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
case crypto.SSEC:
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,19 +650,17 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
// Set encryption response headers
|
// Set encryption response headers
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
switch {
|
case crypto.S3:
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
case crypto.SSEC:
|
||||||
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(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
|
||||||
}
|
}
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1047,7 +1041,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
var encMetadata = make(map[string]string)
|
var encMetadata = make(map[string]string)
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
// Encryption parameters not applicable for this object.
|
// Encryption parameters not applicable for this object.
|
||||||
if !crypto.IsEncrypted(srcInfo.UserDefined) && crypto.SSECopy.IsRequested(r.Header) {
|
if _, ok := crypto.IsEncrypted(srcInfo.UserDefined); ok && 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
|
||||||
}
|
}
|
||||||
@ -1584,13 +1578,13 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch kind, encrypted := crypto.IsEncrypted(objInfo.UserDefined); {
|
||||||
case crypto.IsEncrypted(objInfo.UserDefined):
|
case encrypted:
|
||||||
switch {
|
switch kind {
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
case crypto.S3:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
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:
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
|
|
||||||
@ -1912,7 +1906,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
|||||||
srcInfo := gr.ObjInfo
|
srcInfo := gr.ObjInfo
|
||||||
|
|
||||||
actualPartSize := srcInfo.Size
|
actualPartSize := srcInfo.Size
|
||||||
if crypto.IsEncrypted(srcInfo.UserDefined) {
|
if _, ok := crypto.IsEncrypted(srcInfo.UserDefined); ok {
|
||||||
actualPartSize, err = srcInfo.GetActualSize()
|
actualPartSize, err = srcInfo.GetActualSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
@ -2010,7 +2004,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
|||||||
rawReader := srcInfo.Reader
|
rawReader := srcInfo.Reader
|
||||||
pReader := NewPutObjReader(rawReader, nil, nil)
|
pReader := NewPutObjReader(rawReader, nil, nil)
|
||||||
|
|
||||||
isEncrypted := crypto.IsEncrypted(mi.UserDefined)
|
_, isEncrypted := crypto.IsEncrypted(mi.UserDefined)
|
||||||
var objectEncryptionKey crypto.ObjectKey
|
var objectEncryptionKey crypto.ObjectKey
|
||||||
if objectAPI.IsEncryptionSupported() && isEncrypted {
|
if objectAPI.IsEncryptionSupported() && isEncrypted {
|
||||||
if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) {
|
if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) {
|
||||||
@ -2250,7 +2244,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
rawReader := hashReader
|
rawReader := hashReader
|
||||||
pReader := NewPutObjReader(rawReader, nil, nil)
|
pReader := NewPutObjReader(rawReader, nil, nil)
|
||||||
|
|
||||||
isEncrypted := crypto.IsEncrypted(mi.UserDefined)
|
_, isEncrypted := crypto.IsEncrypted(mi.UserDefined)
|
||||||
var objectEncryptionKey crypto.ObjectKey
|
var objectEncryptionKey crypto.ObjectKey
|
||||||
if objectAPI.IsEncryptionSupported() && isEncrypted {
|
if objectAPI.IsEncryptionSupported() && isEncrypted {
|
||||||
if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) {
|
if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) {
|
||||||
@ -2416,7 +2410,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ssec bool
|
var ssec bool
|
||||||
if objectAPI.IsEncryptionSupported() && crypto.IsEncrypted(listPartsInfo.UserDefined) {
|
if _, ok := crypto.IsEncrypted(listPartsInfo.UserDefined); ok && objectAPI.IsEncryptionSupported() {
|
||||||
var key []byte
|
var key []byte
|
||||||
if crypto.SSEC.IsEncrypted(listPartsInfo.UserDefined) {
|
if crypto.SSEC.IsEncrypted(listPartsInfo.UserDefined) {
|
||||||
ssec = true
|
ssec = true
|
||||||
@ -2580,7 +2574,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
|
|||||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if crypto.IsEncrypted(mi.UserDefined) {
|
if _, ok := crypto.IsEncrypted(mi.UserDefined); ok {
|
||||||
var key []byte
|
var key []byte
|
||||||
isEncrypted = true
|
isEncrypted = true
|
||||||
ssec = crypto.SSEC.IsEncrypted(mi.UserDefined)
|
ssec = crypto.SSEC.IsEncrypted(mi.UserDefined)
|
||||||
|
@ -1289,14 +1289,12 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
switch {
|
case crypto.S3:
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
case crypto.SSEC:
|
||||||
case crypto.SSEC.IsRequested(r.Header):
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mustReplicate {
|
if mustReplicate {
|
||||||
@ -1448,14 +1446,12 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Set encryption response headers
|
// Set encryption response headers
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if crypto.IsEncrypted(objInfo.UserDefined) {
|
switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
|
||||||
switch {
|
case crypto.S3:
|
||||||
case crypto.S3.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
|
case crypto.SSEC:
|
||||||
case crypto.SSEC.IsEncrypted(objInfo.UserDefined):
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
|
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
||||||
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user