crypto: add basic functionality for parsing SSE-C headers (#6148)

This commit adds basic support for SSE-C / SSE-C copy.
This includes functions for determining whether SSE-C
is requested by the S3 client and functions for parsing
such HTTP headers.

All S3 SSE-C parsing errors are exported such that callers
can pattern-match to forward the correct error to S3
clients.

Further the SSE-C related internal metadata entry-keys
are added by this commit.
This commit is contained in:
Andreas Auernhammer
2018-07-18 19:49:26 +02:00
committed by Dee Koder
parent 3ec4738955
commit 758a80e39b
4 changed files with 420 additions and 4 deletions

View File

@@ -15,12 +15,43 @@
package crypto
import (
"bytes"
"crypto/md5"
"encoding/base64"
"net/http"
)
// SSEHeader is the general AWS SSE HTTP header key.
const SSEHeader = "X-Amz-Server-Side-Encryption"
const (
// SSECAlgorithm is the HTTP header key referencing
// the SSE-C algorithm.
SSECAlgorithm = SSEHeader + "-Customer-Algorithm"
// SSECKey is the HTTP header key referencing the
// SSE-C client-provided key..
SSECKey = SSEHeader + "-Customer-Key"
// SSECKeyMD5 is the HTTP header key referencing
// the MD5 sum of the client-provided key.
SSECKeyMD5 = SSEHeader + "-Customer-Key-Md5"
)
const (
// SSECopyAlgorithm is the HTTP header key referencing
// the SSE-C algorithm for SSE-C copy requests.
SSECopyAlgorithm = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm"
// SSECopyKey is the HTTP header key referencing the SSE-C
// client-provided key for SSE-C copy requests.
SSECopyKey = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key"
// SSECopyKeyMD5 is the HTTP header key referencing the
// MD5 sum of the client key for SSE-C copy requests.
SSECopyKeyMD5 = "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5"
)
// SSEAlgorithmAES256 is the only supported value for the SSE-S3 or SSE-C algorithm header.
// For SSE-S3 see: https://docs.aws.amazon.com/AmazonS3/latest/dev/SSEUsingRESTAPI.html
// For SSE-C see: https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html
@@ -47,3 +78,99 @@ func (s3) Parse(h http.Header) (err error) {
}
return
}
var (
// SSEC represents AWS SSE-C. It provides functionality to handle
// SSE-C requests.
SSEC = ssec{}
// SSECopy represents AWS SSE-C for copy requests. It provides
// functionality to handle SSE-C copy requests.
SSECopy = ssecCopy{}
)
type ssec struct{}
type ssecCopy struct{}
// IsRequested returns true if the HTTP headers contains
// at least one SSE-C header. SSE-C copy headers are ignored.
func (ssec) IsRequested(h http.Header) bool {
if _, ok := h[SSECAlgorithm]; ok {
return true
}
if _, ok := h[SSECKey]; ok {
return true
}
if _, ok := h[SSECKeyMD5]; ok {
return true
}
return false
}
// IsRequested returns true if the HTTP headers contains
// at least one SSE-C copy header. Regular SSE-C headers
// are ignored.
func (ssecCopy) IsRequested(h http.Header) bool {
if _, ok := h[SSECopyAlgorithm]; ok {
return true
}
if _, ok := h[SSECopyKey]; ok {
return true
}
if _, ok := h[SSECopyKeyMD5]; ok {
return true
}
return false
}
// Parse parses the SSE-C headers and returns the SSE-C client key
// on success. SSE-C copy headers are ignored.
func (ssec) Parse(h http.Header) (key [32]byte, err error) {
defer h.Del(SSECKey) // remove SSE-C key from headers after parsing
if h.Get(SSECAlgorithm) != SSEAlgorithmAES256 {
return key, ErrInvalidCustomerAlgorithm
}
if h.Get(SSECKey) == "" {
return key, ErrMissingCustomerKey
}
if h.Get(SSECKeyMD5) == "" {
return key, ErrMissingCustomerKeyMD5
}
clientKey, err := base64.StdEncoding.DecodeString(h.Get(SSECKey))
if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long
return key, ErrInvalidCustomerKey
}
keyMD5, err := base64.StdEncoding.DecodeString(h.Get(SSECKeyMD5))
if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) {
return key, ErrCustomerKeyMD5Mismatch
}
copy(key[:], clientKey)
return key, nil
}
// Parse parses the SSE-C copy headers and returns the SSE-C client key
// on success. Regular SSE-C headers are ignored.
func (ssecCopy) Parse(h http.Header) (key [32]byte, err error) {
defer h.Del(SSECopyKey) // remove SSE-C copy key of source object from headers after parsing
if h.Get(SSECopyAlgorithm) != SSEAlgorithmAES256 {
return key, ErrInvalidCustomerAlgorithm
}
if h.Get(SSECopyKey) == "" {
return key, ErrMissingCustomerKey
}
if h.Get(SSECopyKeyMD5) == "" {
return key, ErrMissingCustomerKeyMD5
}
clientKey, err := base64.StdEncoding.DecodeString(h.Get(SSECopyKey))
if err != nil || len(clientKey) != 32 { // The client key must be 256 bits long
return key, ErrInvalidCustomerKey
}
keyMD5, err := base64.StdEncoding.DecodeString(h.Get(SSECopyKeyMD5))
if md5Sum := md5.Sum(clientKey); err != nil || !bytes.Equal(md5Sum[:], keyMD5) {
return key, ErrCustomerKeyMD5Mismatch
}
copy(key[:], clientKey)
return key, nil
}