crypto: add support for parsing/creating SSE-C/SSE-S3 metadata (#6169)
* crypto: add support for parsing SSE-C/SSE-S3 metadata This commit adds support for detecting and parsing SSE-C/SSE-S3 object metadata. With the `IsEncrypted` functions it is possible to determine whether an object seems to be encrypted. With the `ParseMetadata` functions it is possible to validate such metadata and extract the SSE-C/SSE-S3 related values. It also fixes some naming issues. * crypto: add functions for creating SSE object metadata This commit adds functions for creating SSE-S3 and SSE-C metadata. It also adds a `CreateMultipartMetadata` for creating multipart metadata. For all functions unit tests are included.master
parent
2debe77586
commit
644c2ce326
@ -0,0 +1,213 @@ |
|||||||
|
// Minio Cloud Storage, (C) 2015, 2016, 2017, 2018 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" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/logger" |
||||||
|
) |
||||||
|
|
||||||
|
// IsMultiPart returns true if the object metadata indicates
|
||||||
|
// that it was uploaded using some form of server-side-encryption
|
||||||
|
// and the S3 multipart API.
|
||||||
|
func IsMultiPart(metadata map[string]string) bool { |
||||||
|
if _, ok := metadata[SSEMultipart]; ok { |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// IsEncrypted returns true if the object metadata indicates
|
||||||
|
// that it was uploaded using some form of server-side-encryption.
|
||||||
|
//
|
||||||
|
// IsEncrypted only checks whether the metadata contains at least
|
||||||
|
// one entry indicating SSE-C or SSE-S3.
|
||||||
|
func IsEncrypted(metadata map[string]string) bool { |
||||||
|
if _, ok := metadata[SSEIV]; ok { |
||||||
|
return true |
||||||
|
} |
||||||
|
if _, ok := metadata[SSESealAlgorithm]; ok { |
||||||
|
return true |
||||||
|
} |
||||||
|
if IsMultiPart(metadata) { |
||||||
|
return true |
||||||
|
} |
||||||
|
if S3.IsEncrypted(metadata) { |
||||||
|
return true |
||||||
|
} |
||||||
|
if SSEC.IsEncrypted(metadata) { |
||||||
|
return true |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// 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 false |
||||||
|
} |
||||||
|
|
||||||
|
// CreateMultipartMetadata adds the multipart flag entry to metadata
|
||||||
|
// and returns modifed metadata. It allocates a new metadata map if
|
||||||
|
// metadata is nil.
|
||||||
|
func CreateMultipartMetadata(metadata map[string]string) map[string]string { |
||||||
|
if metadata == nil { |
||||||
|
metadata = map[string]string{} |
||||||
|
} |
||||||
|
metadata[SSEMultipart] = "" |
||||||
|
return metadata |
||||||
|
} |
||||||
|
|
||||||
|
// CreateMetadata encodes the keyID, the sealed kms data key and the sealed key
|
||||||
|
// into the metadata and returns the modified metadata. 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(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) |
||||||
|
} |
||||||
|
|
||||||
|
if metadata == nil { |
||||||
|
metadata = map[string]string{} |
||||||
|
} |
||||||
|
metadata[S3KMSKeyID] = keyID |
||||||
|
metadata[SSESealAlgorithm] = sealedKey.Algorithm |
||||||
|
metadata[SSEIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) |
||||||
|
metadata[S3SealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) |
||||||
|
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 KMS key-ID, the
|
||||||
|
// sealed KMS key and the sealed object key on success.
|
||||||
|
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, Error{"The object metadata is missing the internal sealed key for SSE-S3"} |
||||||
|
} |
||||||
|
keyID, ok = metadata[S3KMSKeyID] |
||||||
|
if !ok { |
||||||
|
return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal KMS key-ID for SSE-S3"} |
||||||
|
} |
||||||
|
b64KMSSealedKey, ok := metadata[S3KMSSealedKey] |
||||||
|
if !ok { |
||||||
|
return keyID, kmsKey, sealedKey, Error{"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, Error{"The internal sealed key for SSE-S3 is invalid"} |
||||||
|
} |
||||||
|
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey) |
||||||
|
if err != nil { |
||||||
|
return keyID, kmsKey, sealedKey, Error{"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 |
||||||
|
} |
||||||
|
|
||||||
|
// 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(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm)) |
||||||
|
} |
||||||
|
|
||||||
|
if metadata == nil { |
||||||
|
metadata = map[string]string{} |
||||||
|
} |
||||||
|
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, Error{"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, Error{"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,361 @@ |
|||||||
|
// Minio Cloud Storage, (C) 2015, 2016, 2017, 2018 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" |
||||||
|
"encoding/base64" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/logger" |
||||||
|
) |
||||||
|
|
||||||
|
var isMultipartTests = []struct { |
||||||
|
Metadata map[string]string |
||||||
|
Multipart bool |
||||||
|
}{ |
||||||
|
{Multipart: true, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
||||||
|
{Multipart: true, Metadata: map[string]string{"X-Minio-Internal-Encrypted-Multipart": ""}}, // 1
|
||||||
|
{Multipart: true, Metadata: map[string]string{SSEMultipart: "some-value"}}, // 2
|
||||||
|
{Multipart: false, Metadata: map[string]string{"": ""}}, // 3
|
||||||
|
{Multipart: false, Metadata: map[string]string{"X-Minio-Internal-EncryptedMultipart": ""}}, // 4
|
||||||
|
} |
||||||
|
|
||||||
|
func TestIsMultipart(t *testing.T) { |
||||||
|
for i, test := range isMultipartTests { |
||||||
|
if isMultipart := IsMultiPart(test.Metadata); isMultipart != test.Multipart { |
||||||
|
t.Errorf("Test %d: got '%v' - want '%v'", i, isMultipart, test.Multipart) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var isEncryptedTests = []struct { |
||||||
|
Metadata map[string]string |
||||||
|
Encrypted bool |
||||||
|
}{ |
||||||
|
{Encrypted: true, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
||||||
|
{Encrypted: true, Metadata: map[string]string{SSEIV: ""}}, // 1
|
||||||
|
{Encrypted: true, Metadata: map[string]string{SSESealAlgorithm: ""}}, // 2
|
||||||
|
{Encrypted: true, Metadata: map[string]string{SSECSealedKey: ""}}, // 3
|
||||||
|
{Encrypted: true, Metadata: map[string]string{S3SealedKey: ""}}, // 4
|
||||||
|
{Encrypted: true, Metadata: map[string]string{S3KMSKeyID: ""}}, // 5
|
||||||
|
{Encrypted: true, Metadata: map[string]string{S3KMSSealedKey: ""}}, // 6
|
||||||
|
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
||||||
|
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
||||||
|
} |
||||||
|
|
||||||
|
func TestIsEncrypted(t *testing.T) { |
||||||
|
for i, test := range isEncryptedTests { |
||||||
|
if isEncrypted := IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { |
||||||
|
t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var s3IsEncryptedTests = []struct { |
||||||
|
Metadata map[string]string |
||||||
|
Encrypted bool |
||||||
|
}{ |
||||||
|
{Encrypted: false, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
||||||
|
{Encrypted: false, Metadata: map[string]string{SSEIV: ""}}, // 1
|
||||||
|
{Encrypted: false, Metadata: map[string]string{SSESealAlgorithm: ""}}, // 2
|
||||||
|
{Encrypted: false, Metadata: map[string]string{SSECSealedKey: ""}}, // 3
|
||||||
|
{Encrypted: true, Metadata: map[string]string{S3SealedKey: ""}}, // 4
|
||||||
|
{Encrypted: true, Metadata: map[string]string{S3KMSKeyID: ""}}, // 5
|
||||||
|
{Encrypted: true, Metadata: map[string]string{S3KMSSealedKey: ""}}, // 6
|
||||||
|
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
||||||
|
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
||||||
|
} |
||||||
|
|
||||||
|
func TestS3IsEncrypted(t *testing.T) { |
||||||
|
for i, test := range s3IsEncryptedTests { |
||||||
|
if isEncrypted := S3.IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { |
||||||
|
t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var ssecIsEncryptedTests = []struct { |
||||||
|
Metadata map[string]string |
||||||
|
Encrypted bool |
||||||
|
}{ |
||||||
|
{Encrypted: false, Metadata: map[string]string{SSEMultipart: ""}}, // 0
|
||||||
|
{Encrypted: false, Metadata: map[string]string{SSEIV: ""}}, // 1
|
||||||
|
{Encrypted: false, Metadata: map[string]string{SSESealAlgorithm: ""}}, // 2
|
||||||
|
{Encrypted: true, Metadata: map[string]string{SSECSealedKey: ""}}, // 3
|
||||||
|
{Encrypted: false, Metadata: map[string]string{S3SealedKey: ""}}, // 4
|
||||||
|
{Encrypted: false, Metadata: map[string]string{S3KMSKeyID: ""}}, // 5
|
||||||
|
{Encrypted: false, Metadata: map[string]string{S3KMSSealedKey: ""}}, // 6
|
||||||
|
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
|
||||||
|
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
|
||||||
|
} |
||||||
|
|
||||||
|
func TestSSECIsEncrypted(t *testing.T) { |
||||||
|
for i, test := range ssecIsEncryptedTests { |
||||||
|
if isEncrypted := SSEC.IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { |
||||||
|
t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var s3ParseMetadataTests = []struct { |
||||||
|
Metadata map[string]string |
||||||
|
ExpectedErr error |
||||||
|
|
||||||
|
DataKey []byte |
||||||
|
KeyID string |
||||||
|
SealedKey SealedKey |
||||||
|
}{ |
||||||
|
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}}, // 0
|
||||||
|
{ |
||||||
|
ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, |
||||||
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 1
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The object metadata is missing the internal sealed key for SSE-S3"}, |
||||||
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 2
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The object metadata is missing the internal KMS key-ID for SSE-S3"}, |
||||||
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 3
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The object metadata is missing the internal sealed KMS data key for SSE-S3"}, |
||||||
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: ""}, |
||||||
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 4
|
||||||
|
{ |
||||||
|
ExpectedErr: errInvalidInternalIV, |
||||||
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: "", S3KMSSealedKey: ""}, |
||||||
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 5
|
||||||
|
{ |
||||||
|
ExpectedErr: errInvalidInternalSealAlgorithm, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: "", S3KMSSealedKey: "", |
||||||
|
}, |
||||||
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 6
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The internal sealed key for SSE-S3 is invalid"}, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "", |
||||||
|
S3KMSKeyID: "", S3KMSSealedKey: "", |
||||||
|
}, |
||||||
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, |
||||||
|
}, // 7
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The internal sealed KMS data key for SSE-S3 is invalid"}, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, |
||||||
|
S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "key-1", |
||||||
|
S3KMSSealedKey: ".MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=", // invalid base64
|
||||||
|
}, |
||||||
|
DataKey: []byte{}, KeyID: "key-1", SealedKey: SealedKey{}, |
||||||
|
}, // 8
|
||||||
|
{ |
||||||
|
ExpectedErr: nil, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, |
||||||
|
S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "", S3KMSSealedKey: "", |
||||||
|
}, |
||||||
|
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{Algorithm: SealAlgorithm}, |
||||||
|
}, // 9
|
||||||
|
{ |
||||||
|
ExpectedErr: nil, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), SSESealAlgorithm: SealAlgorithm, |
||||||
|
S3SealedKey: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)), S3KMSKeyID: "key-1", |
||||||
|
S3KMSSealedKey: 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}}, |
||||||
|
}, // 10
|
||||||
|
} |
||||||
|
|
||||||
|
func TestS3ParseMetadata(t *testing.T) { |
||||||
|
for i, test := range s3ParseMetadataTests { |
||||||
|
keyID, dataKey, sealedKey, err := S3.ParseMetadata(test.Metadata) |
||||||
|
if err != test.ExpectedErr { |
||||||
|
t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) |
||||||
|
} |
||||||
|
if !bytes.Equal(dataKey, test.DataKey) { |
||||||
|
t.Errorf("Test %d: got data key '%v' - want data key '%v'", i, dataKey, test.DataKey) |
||||||
|
} |
||||||
|
if keyID != test.KeyID { |
||||||
|
t.Errorf("Test %d: got key-ID '%v' - want key-ID '%v'", i, keyID, test.KeyID) |
||||||
|
} |
||||||
|
if sealedKey.Algorithm != test.SealedKey.Algorithm { |
||||||
|
t.Errorf("Test %d: got sealed key algorithm '%v' - want sealed key algorithm '%v'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { |
||||||
|
t.Errorf("Test %d: got sealed key '%v' - want sealed key '%v'", i, sealedKey.Key, test.SealedKey.Key) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { |
||||||
|
t.Errorf("Test %d: got sealed key IV '%v' - want sealed key IV '%v'", i, sealedKey.IV, test.SealedKey.IV) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var ssecParseMetadataTests = []struct { |
||||||
|
Metadata map[string]string |
||||||
|
ExpectedErr error |
||||||
|
|
||||||
|
SealedKey SealedKey |
||||||
|
}{ |
||||||
|
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0
|
||||||
|
{ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, SealedKey: SealedKey{}}, // 1
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The object metadata is missing the internal sealed key for SSE-C"}, |
||||||
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, SealedKey: SealedKey{}, |
||||||
|
}, // 2
|
||||||
|
{ |
||||||
|
ExpectedErr: errInvalidInternalIV, |
||||||
|
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", SSECSealedKey: ""}, SealedKey: SealedKey{}, |
||||||
|
}, // 3
|
||||||
|
{ |
||||||
|
ExpectedErr: errInvalidInternalSealAlgorithm, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: "", SSECSealedKey: "", |
||||||
|
}, |
||||||
|
SealedKey: SealedKey{}, |
||||||
|
}, // 4
|
||||||
|
{ |
||||||
|
ExpectedErr: Error{"The internal sealed key for SSE-C is invalid"}, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "", |
||||||
|
}, |
||||||
|
SealedKey: SealedKey{}, |
||||||
|
}, // 5
|
||||||
|
{ |
||||||
|
ExpectedErr: nil, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, |
||||||
|
SSECSealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), |
||||||
|
}, |
||||||
|
SealedKey: SealedKey{Algorithm: SealAlgorithm}, |
||||||
|
}, // 6
|
||||||
|
{ |
||||||
|
ExpectedErr: nil, |
||||||
|
Metadata: map[string]string{ |
||||||
|
SSEIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), SSESealAlgorithm: InsecureSealAlgorithm, |
||||||
|
SSECSealedKey: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)), |
||||||
|
}, |
||||||
|
SealedKey: SealedKey{Algorithm: InsecureSealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}}, |
||||||
|
}, // 7
|
||||||
|
} |
||||||
|
|
||||||
|
func TestCreateMultipartMetadata(t *testing.T) { |
||||||
|
metadata := CreateMultipartMetadata(nil) |
||||||
|
if v, ok := metadata[SSEMultipart]; !ok || v != "" { |
||||||
|
t.Errorf("Metadata is missing the correct value for '%s': got '%s' - want '%s'", SSEMultipart, v, "") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestSSECParseMetadata(t *testing.T) { |
||||||
|
for i, test := range ssecParseMetadataTests { |
||||||
|
sealedKey, err := SSEC.ParseMetadata(test.Metadata) |
||||||
|
if err != test.ExpectedErr { |
||||||
|
t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) |
||||||
|
} |
||||||
|
if sealedKey.Algorithm != test.SealedKey.Algorithm { |
||||||
|
t.Errorf("Test %d: got sealed key algorithm '%v' - want sealed key algorithm '%v'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { |
||||||
|
t.Errorf("Test %d: got sealed key '%v' - want sealed key '%v'", i, sealedKey.Key, test.SealedKey.Key) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { |
||||||
|
t.Errorf("Test %d: got sealed key IV '%v' - want sealed key IV '%v'", i, sealedKey.IV, test.SealedKey.IV) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var s3CreateMetadataTests = []struct { |
||||||
|
KeyID string |
||||||
|
SealedDataKey []byte |
||||||
|
SealedKey SealedKey |
||||||
|
}{ |
||||||
|
{KeyID: "", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, |
||||||
|
{KeyID: "cafebabe", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, |
||||||
|
{KeyID: "deadbeef", SealedDataKey: make([]byte, 32), SealedKey: SealedKey{IV: [32]byte{0xf7}, Key: [64]byte{0xea}, Algorithm: SealAlgorithm}}, |
||||||
|
} |
||||||
|
|
||||||
|
func TestS3CreateMetadata(t *testing.T) { |
||||||
|
defer func(disableLog bool) { logger.Disable = disableLog }(logger.Disable) |
||||||
|
logger.Disable = true |
||||||
|
for i, test := range s3CreateMetadataTests { |
||||||
|
metadata := S3.CreateMetadata(nil, test.KeyID, test.SealedDataKey, test.SealedKey) |
||||||
|
keyID, kmsKey, sealedKey, err := S3.ParseMetadata(metadata) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Test %d: failed to parse metadata: %v", i, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
if keyID != test.KeyID { |
||||||
|
t.Errorf("Test %d: Key-ID mismatch: got '%s' - want '%s'", i, keyID, test.KeyID) |
||||||
|
} |
||||||
|
if !bytes.Equal(kmsKey, test.SealedDataKey) { |
||||||
|
t.Errorf("Test %d: sealed KMS data mismatch: got '%v' - want '%v'", i, kmsKey, test.SealedDataKey) |
||||||
|
} |
||||||
|
if sealedKey.Algorithm != test.SealedKey.Algorithm { |
||||||
|
t.Errorf("Test %d: seal algorithm mismatch: got '%s' - want '%s'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { |
||||||
|
t.Errorf("Test %d: IV mismatch: got '%v' - want '%v'", i, sealedKey.IV, test.SealedKey.IV) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { |
||||||
|
t.Errorf("Test %d: sealed key mismatch: got '%v' - want '%v'", i, sealedKey.Key, test.SealedKey.Key) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defer func() { |
||||||
|
if err := recover(); err == nil || err != logger.ErrCritical { |
||||||
|
t.Errorf("Expected '%s' panic for invalid seal algorithm but got '%s'", logger.ErrCritical, err) |
||||||
|
} |
||||||
|
}() |
||||||
|
_ = S3.CreateMetadata(nil, "", []byte{}, SealedKey{Algorithm: InsecureSealAlgorithm}) |
||||||
|
} |
||||||
|
|
||||||
|
var ssecCreateMetadataTests = []SealedKey{ |
||||||
|
{Algorithm: SealAlgorithm}, |
||||||
|
{IV: [32]byte{0xff}, Key: [64]byte{0x7e}, Algorithm: SealAlgorithm}, |
||||||
|
} |
||||||
|
|
||||||
|
func TestSSECCreateMetadata(t *testing.T) { |
||||||
|
defer func(disableLog bool) { logger.Disable = disableLog }(logger.Disable) |
||||||
|
logger.Disable = true |
||||||
|
for i, test := range s3CreateMetadataTests { |
||||||
|
metadata := SSEC.CreateMetadata(nil, test.SealedKey) |
||||||
|
sealedKey, err := SSEC.ParseMetadata(metadata) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("Test %d: failed to parse metadata: %v", i, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
if sealedKey.Algorithm != test.SealedKey.Algorithm { |
||||||
|
t.Errorf("Test %d: seal algorithm mismatch: got '%s' - want '%s'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { |
||||||
|
t.Errorf("Test %d: IV mismatch: got '%v' - want '%v'", i, sealedKey.IV, test.SealedKey.IV) |
||||||
|
} |
||||||
|
if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { |
||||||
|
t.Errorf("Test %d: sealed key mismatch: got '%v' - want '%v'", i, sealedKey.Key, test.SealedKey.Key) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defer func() { |
||||||
|
if err := recover(); err == nil || err != logger.ErrCritical { |
||||||
|
t.Errorf("Expected '%s' panic for invalid seal algorithm but got '%s'", logger.ErrCritical, err) |
||||||
|
} |
||||||
|
}() |
||||||
|
_ = SSEC.CreateMetadata(nil, SealedKey{Algorithm: InsecureSealAlgorithm}) |
||||||
|
} |
Loading…
Reference in new issue