Skip to content

Commit

Permalink
change encryption key parsing to allow spaces in sse-c key
Browse files Browse the repository at this point in the history
Also vendoring in minio-go updates
  • Loading branch information
poornas committed Mar 13, 2018
1 parent 0ede95b commit 1cb3f51
Show file tree
Hide file tree
Showing 30 changed files with 288 additions and 822 deletions.
50 changes: 22 additions & 28 deletions cmd/client-s3.go
Expand Up @@ -19,7 +19,6 @@ package cmd
import (
"context"
"crypto/tls"
"encoding/base64"
"hash/fnv"
"io"
"net"
Expand All @@ -38,6 +37,7 @@ import (
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio-go"
"github.com/minio/minio-go/pkg/credentials"
"github.com/minio/minio-go/pkg/encrypt"
"github.com/minio/minio-go/pkg/policy"
"github.com/minio/minio-go/pkg/s3utils"
)
Expand Down Expand Up @@ -511,10 +511,8 @@ func (c *s3Client) Get(sseKey string) (io.Reader, *probe.Error) {
bucket, object := c.url2BucketAndObject()
var opts minio.GetObjectOptions
if sseKey != "" {
key := minio.NewSSEInfo([]byte(sseKey), "AES256")
for k, v := range key.GetSSEHeaders() {
opts.Set(k, v)
}
key := encrypt.DefaultPBKDF([]byte(sseKey), []byte(bucket+object))
opts.ServerSideEncryption = key
}
reader, e := c.api.GetObject(bucket, object, opts)
if e != nil {
Expand Down Expand Up @@ -545,22 +543,19 @@ func (c *s3Client) Copy(source string, size int64, progress io.Reader, srcSSEKey
}

tokens := splitStr(source, string(c.targetURL.Separator), 3)

var srcKeyPtr, tgtKeyPtr *minio.SSEInfo
var srcKey, tgtKey encrypt.ServerSide
if srcSSEKey != "" {
srcKey := minio.NewSSEInfo([]byte(srcSSEKey), "AES256")
srcKeyPtr = &srcKey
srcKey = encrypt.DefaultPBKDF([]byte(srcSSEKey), []byte(tokens[1]+tokens[2]))
}
if tgtSSEKey != "" {
tgtKey := minio.NewSSEInfo([]byte(tgtSSEKey), "AES256")
tgtKeyPtr = &tgtKey
tgtKey = encrypt.DefaultPBKDF([]byte(tgtSSEKey), []byte(dstBucket+dstObject))
}

// Source object
src := minio.NewSourceInfo(tokens[1], tokens[2], srcKeyPtr)
src := minio.NewSourceInfo(tokens[1], tokens[2], srcKey)

// Destination object
dst, e := minio.NewDestinationInfo(dstBucket, dstObject, tgtKeyPtr, nil)
dst, e := minio.NewDestinationInfo(dstBucket, dstObject, tgtKey, nil)
if e != nil {
return probe.NewError(e)
}
Expand Down Expand Up @@ -630,24 +625,24 @@ func (c *s3Client) Put(ctx context.Context, reader io.Reader, size int64, metada
if ok {
delete(metadata, "X-Amz-Storage-Class")
}
var encryption encrypt.ServerSide
if sseKey != "" {
metadata["x-amz-server-side-encryption-customer-algorithm"] = "AES256"
metadata["x-amz-server-side-encryption-customer-key"] = base64.StdEncoding.EncodeToString([]byte(sseKey))
metadata["x-amz-server-side-encryption-customer-key-MD5"] = sumMD5Base64([]byte(sseKey))
encryption = encrypt.DefaultPBKDF([]byte(sseKey), []byte(bucket+object))
}
if bucket == "" {
return 0, probe.NewError(BucketNameEmpty{})
}
opts := minio.PutObjectOptions{
UserMetadata: metadata,
Progress: progress,
NumThreads: defaultMultipartThreadsNum,
ContentType: contentType,
CacheControl: cacheControl,
ContentDisposition: contentDisposition,
ContentEncoding: contentEncoding,
ContentLanguage: contentLanguage,
StorageClass: strings.ToUpper(storageClass),
UserMetadata: metadata,
Progress: progress,
NumThreads: defaultMultipartThreadsNum,
ContentType: contentType,
CacheControl: cacheControl,
ContentDisposition: contentDisposition,
ContentEncoding: contentEncoding,
ContentLanguage: contentLanguage,
StorageClass: strings.ToUpper(storageClass),
ServerSideEncryption: encryption,
}
n, e := c.api.PutObjectWithContext(ctx, bucket, object, reader, size, opts)
if e != nil {
Expand Down Expand Up @@ -940,9 +935,8 @@ func (c *s3Client) Stat(isIncomplete, isFetchMeta bool, sseKey string) (*clientC
}
opts := minio.StatObjectOptions{}
if sseKey != "" {
opts.Set("x-amz-server-side-encryption-customer-algorithm", "AES256")
opts.Set("x-amz-server-side-encryption-customer-key", base64.StdEncoding.EncodeToString([]byte(sseKey)))
opts.Set("x-amz-server-side-encryption-customer-key-MD5", sumMD5Base64([]byte(sseKey)))
key := encrypt.DefaultPBKDF([]byte(sseKey), []byte(bucket+object))
opts.ServerSideEncryption = key
}
for objectStat := range c.listObjectWrapper(bucket, object, nonRecursive, nil) {
if objectStat.Err != nil {
Expand Down
51 changes: 29 additions & 22 deletions cmd/utils.go
Expand Up @@ -24,9 +24,7 @@ import (
"io"
"math/rand"
"os"
"regexp"
"runtime"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -178,37 +176,46 @@ type prefixSSEPair struct {

// parse list of comma separated alias/prefix=sse key values entered on command line and
// construct a map of alias to prefix and sse pairs.
func parseEncryptionKeys(ssekeys string) (encMap map[string][]prefixSSEPair, err *probe.Error) {
func parseEncryptionKeys(sseKeys string) (encMap map[string][]prefixSSEPair, err *probe.Error) {
encMap = make(map[string][]prefixSSEPair)
if ssekeys == "" {
if sseKeys == "" {
return
}
fields := strings.Split(ssekeys, ",")
r := regexp.MustCompile("([^=\\s]+)=([^\\s]+)")
for _, field := range fields {
field = strings.TrimSpace(field)
matches := r.FindStringSubmatch(field)
if len(matches) != 3 {
return nil, probe.NewError(errors.New("sse-c prefix should be of the form prefix=key"))
prefix := ""
ssekey := ""
index := 0 // start index of prefix
vs := 0 // start index of sse-c key
sseKeyLen := 32
delim := 1
k := len(sseKeys)
for index < k {
e := strings.Index(sseKeys[index:], "=")
if e == -1 {
return nil, probe.NewError(errors.New("sse-c prefix should be of the form prefix1=key1,... "))
}
key := matches[1]
value := matches[2]
alias, _ := url2Alias(key)
if len(value) != 32 {
prefix = sseKeys[index : index+e]
// validate alias
alias, _, hostCfg, err := expandAlias(prefix)
if hostCfg == nil || err != nil {
return nil, probe.NewError(errors.New("sse-c prefix " + prefix + " has invalid alias"))
}
vs = e + 1 + index
if vs+32 > k {
return nil, probe.NewError(errors.New("sse-c key should be 32 bytes long"))
}
prefix := strings.TrimSpace(key)
ssekey = sseKeys[vs : vs+sseKeyLen]
if (vs+sseKeyLen < k) && sseKeys[vs+sseKeyLen] != ',' {
return nil, probe.NewError(errors.New("sse-c prefix=secret should be delimited by , and secret should be 32 bytes long"))
}
if _, ok := encMap[alias]; !ok {
encMap[alias] = make([]prefixSSEPair, 0)
}
ps := prefixSSEPair{prefix: prefix, sseKey: value}
ps := prefixSSEPair{prefix: prefix, sseKey: ssekey}
encMap[alias] = append(encMap[alias], ps)
// advance index sseKeyLen + delim bytes for the next key start
index = vs + sseKeyLen + delim
}
// sort encryption keys in descending order of prefix length
for _, encKeys := range encMap {
sort.Sort(byPrefixLength(encKeys))
}
return
return encMap, nil
}

// byPrefixLength implements sort.Interface.
Expand Down
2 changes: 1 addition & 1 deletion functional-tests.sh
Expand Up @@ -694,7 +694,7 @@ function test_sse_key_rotation()
# create encrypted object on server
assert_success "$start_time" "${FUNCNAME[0]}" mc_cmd cp --encrypt-key "${cli_flag1}" "${FILE_1_MB}" "${SERVER_ALIAS}/${BUCKET_NAME}/${prefix}/${object_name}"
# now do a server side copy on same object and do a key rotation
assert_success "$start_time" "${FUNCNAME[0]}" mc_cmd cp --encrypt-key "${cli_flag1}, ${cli_flag2}" "${SERVER_ALIAS}/${BUCKET_NAME}/${prefix}/${object_name}" "${SERVER_ALIAS_TLS}/${BUCKET_NAME}/${object_name}"
assert_success "$start_time" "${FUNCNAME[0]}" mc_cmd cp --encrypt-key "${cli_flag1},${cli_flag2}" "${SERVER_ALIAS}/${BUCKET_NAME}/${prefix}/${object_name}" "${SERVER_ALIAS_TLS}/${BUCKET_NAME}/${object_name}"
# cat the object with the new key. should return data without any error
assert_success "$start_time" "${FUNCNAME[0]}" mc_cmd cat --encrypt-key "${cli_flag2}" "${SERVER_ALIAS_TLS}/${BUCKET_NAME}/${object_name}" > "${object_name}.downloaded"
assert_success "$start_time" "${FUNCNAME[0]}" show_on_failure $? "unable to download object using 'mc cat'"
Expand Down
4 changes: 2 additions & 2 deletions vendor/github.com/minio/minio-go/Makefile

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions vendor/github.com/minio/minio-go/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 17 additions & 67 deletions vendor/github.com/minio/minio-go/api-compose-object.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1cb3f51

Please sign in to comment.