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