mirror of
https://github.com/minio/minio.git
synced 2025-04-20 18:44:21 -04:00
crypto: add helper functions for unsealing object keys (#6609)
This commit adds 3 helper functions for SSE-C and SSE-S3 to simplify object key unsealing in server code. See #6600
This commit is contained in:
parent
b0c9ae7490
commit
28e25eac78
@ -18,6 +18,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
"github.com/minio/minio/pkg/ioutil"
|
"github.com/minio/minio/pkg/ioutil"
|
||||||
@ -69,10 +71,59 @@ const (
|
|||||||
// domain is "SSE-S3".
|
// domain is "SSE-S3".
|
||||||
func (s3) String() string { return "SSE-S3" }
|
func (s3) String() string { return "SSE-S3" }
|
||||||
|
|
||||||
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
// from the metadata using KMS and returns the decrypted object
|
||||||
|
// key.
|
||||||
|
func (sse s3) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
keyID, kmsKey, sealedKey, err := sse.ParseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = key.Unseal(unsealKey, sealedKey, sse.String(), bucket, object)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the SSE domain as string. For SSE-C the
|
// String returns the SSE domain as string. For SSE-C the
|
||||||
// domain is "SSE-C".
|
// domain is "SSE-C".
|
||||||
func (ssec) String() string { return "SSE-C" }
|
func (ssec) String() string { return "SSE-C" }
|
||||||
|
|
||||||
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
// from the metadata using the SSE-C client key of the HTTP headers
|
||||||
|
// and returns the decrypted object key.
|
||||||
|
func (sse ssec) UnsealObjectKey(h http.Header, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
clientKey, err := sse.ParseHTTP(h)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return unsealObjectKey(clientKey, metadata, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsealObjectKey extracts and decrypts the sealed object key
|
||||||
|
// from the metadata using the SSE-Copy client key of the HTTP headers
|
||||||
|
// and returns the decrypted object key.
|
||||||
|
func (sse ssecCopy) UnsealObjectKey(h http.Header, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
clientKey, err := sse.ParseHTTP(h)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return unsealObjectKey(clientKey, metadata, bucket, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsealObjectKey decrypts and returns the sealed object key
|
||||||
|
// from the metadata using the SSE-C client key.
|
||||||
|
func unsealObjectKey(clientKey [32]byte, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||||
|
sealedKey, err := SSEC.ParseMetadata(metadata)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = key.Unseal(clientKey, sealedKey, SSEC.String(), bucket, object)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// EncryptSinglePart encrypts an io.Reader which must be the
|
// EncryptSinglePart encrypts an io.Reader which must be the
|
||||||
// the body of a single-part PUT request.
|
// the body of a single-part PUT request.
|
||||||
func EncryptSinglePart(r io.Reader, key ObjectKey) io.Reader {
|
func EncryptSinglePart(r io.Reader, key ObjectKey) io.Reader {
|
||||||
|
@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestS3String(t *testing.T) {
|
func TestS3String(t *testing.T) {
|
||||||
const Domain = "SSE-S3"
|
const Domain = "SSE-S3"
|
||||||
@ -29,3 +32,195 @@ func TestSSECString(t *testing.T) {
|
|||||||
t.Errorf("SSEC's string method returns wrong domain: got '%s' - want '%s'", domain, Domain)
|
t.Errorf("SSEC's string method returns wrong domain: got '%s' - want '%s'", domain, Domain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ssecUnsealObjectKeyTests = []struct {
|
||||||
|
Headers http.Header
|
||||||
|
Bucket, Object string
|
||||||
|
Metadata map[string]string
|
||||||
|
|
||||||
|
ExpectedErr error
|
||||||
|
}{
|
||||||
|
{ // 0 - Valid HTTP headers and valid metadata entries for bucket/object
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: nil,
|
||||||
|
},
|
||||||
|
{ // 1 - Valid HTTP headers but invalid metadata entries for bucket/object2
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object2",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: ErrSecretKeyMismatch,
|
||||||
|
},
|
||||||
|
{ // 2 - Valid HTTP headers but invalid metadata entries for bucket/object
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: errMissingInternalSealAlgorithm,
|
||||||
|
},
|
||||||
|
{ // 3 - Invalid HTTP headers for valid metadata entries for bucket/object
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: ErrMissingCustomerKeyMD5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSSECUnsealObjectKey(t *testing.T) {
|
||||||
|
for i, test := range ssecUnsealObjectKeyTests {
|
||||||
|
if _, err := SSEC.UnsealObjectKey(test.Headers, test.Metadata, test.Bucket, test.Object); err != test.ExpectedErr {
|
||||||
|
t.Errorf("Test %d: got: %v - want: %v", i, err, test.ExpectedErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sseCopyUnsealObjectKeyTests = []struct {
|
||||||
|
Headers http.Header
|
||||||
|
Bucket, Object string
|
||||||
|
Metadata map[string]string
|
||||||
|
|
||||||
|
ExpectedErr error
|
||||||
|
}{
|
||||||
|
{ // 0 - Valid HTTP headers and valid metadata entries for bucket/object
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: nil,
|
||||||
|
},
|
||||||
|
{ // 1 - Valid HTTP headers but invalid metadata entries for bucket/object2
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object2",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: ErrSecretKeyMismatch,
|
||||||
|
},
|
||||||
|
{ // 2 - Valid HTTP headers but invalid metadata entries for bucket/object
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: errMissingInternalSealAlgorithm,
|
||||||
|
},
|
||||||
|
{ // 3 - Invalid HTTP headers for valid metadata entries for bucket/object
|
||||||
|
Headers: http.Header{
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
|
||||||
|
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
|
||||||
|
},
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Sealed-Key": "IAAfAMBdYor5tf/UlVaQvwYlw5yKbPBeQqfygqsfHqhu1wHD9KDAP4bw38AhL12prFTS23JbbR9Re5Qv26ZnlQ==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "coVfGS3I/CTrqexX5vUN+PQPoP9aUFiPYYrSzqTWfBA=",
|
||||||
|
},
|
||||||
|
ExpectedErr: ErrMissingCustomerKeyMD5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSSECopyUnsealObjectKey(t *testing.T) {
|
||||||
|
for i, test := range sseCopyUnsealObjectKeyTests {
|
||||||
|
if _, err := SSECopy.UnsealObjectKey(test.Headers, test.Metadata, test.Bucket, test.Object); err != test.ExpectedErr {
|
||||||
|
t.Errorf("Test %d: got: %v - want: %v", i, err, test.ExpectedErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var s3UnsealObjectKeyTests = []struct {
|
||||||
|
KMS KMS
|
||||||
|
Bucket, Object string
|
||||||
|
Metadata map[string]string
|
||||||
|
|
||||||
|
ExpectedErr error
|
||||||
|
}{
|
||||||
|
{ // 0 - Valid KMS key-ID and valid metadata entries for bucket/object
|
||||||
|
KMS: NewKMS([32]byte{}),
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "hhVY0LKR1YtZbzAKxTWUfZt5enDfYX6Fxz1ma8Kiudc=",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key": "IAAfALhsOeD5AE3s5Zgq3DZ5VFGsOa3B0ksVC86veDcaj+fXv2U0VadhPaOKYr9Emd5ssOsO0uIhIIrKiOy9rA==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key": "IAAfAMRS2iw45FsfiF3QXajSYVWj1lxMpQm6DxDGPtADCX6fJQQ4atHBtfpgqJFyeQmIHsm0FBI+UlHw1Lv4ug==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id": "test-key-1",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
|
||||||
|
},
|
||||||
|
ExpectedErr: nil,
|
||||||
|
},
|
||||||
|
{ // 1 - Valid KMS key-ID for invalid metadata entries for bucket/object
|
||||||
|
KMS: NewKMS([32]byte{}),
|
||||||
|
Bucket: "bucket",
|
||||||
|
Object: "object",
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-Iv": "hhVY0LKR1YtZbzAKxTWUfZt5enDfYX6Fxz1ma8Kiudc=",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key": "IAAfALhsOeD5AE3s5Zgq3DZ5VFGsOa3B0ksVC86veDcaj+fXv2U0VadhPaOKYr9Emd5ssOsO0uIhIIrKiOy9rA==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key": "IAAfAMRS2iw45FsfiF3QXajSYVWj1lxMpQm6DxDGPtADCX6fJQQ4atHBtfpgqJFyeQmIHsm0FBI+UlHw1Lv4ug==",
|
||||||
|
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id": "test-key-1",
|
||||||
|
},
|
||||||
|
ExpectedErr: errMissingInternalSealAlgorithm,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestS3UnsealObjectKey(t *testing.T) {
|
||||||
|
for i, test := range s3UnsealObjectKeyTests {
|
||||||
|
if _, err := S3.UnsealObjectKey(test.KMS, test.Metadata, test.Bucket, test.Object); err != test.ExpectedErr {
|
||||||
|
t.Errorf("Test %d: got: %v - want: %v", i, err, test.ExpectedErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user