mirror of
https://github.com/pgsty/minio.git
synced 2026-09-05 18:16:16 +03:00
fix: decrypt CopyObject checksums with destination key
Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
+1
-2
@@ -777,8 +777,7 @@ func generateListObjectsV2Response(ctx context.Context, bucket, prefix, token, n
|
|||||||
type metaCheckFn = func(name string, action policy.Action) (s3Err APIErrorCode)
|
type metaCheckFn = func(name string, action policy.Action) (s3Err APIErrorCode)
|
||||||
|
|
||||||
// generates CopyObjectResponse from the committed object information.
|
// generates CopyObjectResponse from the committed object information.
|
||||||
func generateCopyObjectResponse(oi ObjectInfo, h http.Header) CopyObjectResponse {
|
func generateCopyObjectResponse(oi ObjectInfo, cs map[string]string) CopyObjectResponse {
|
||||||
cs, _ := oi.decryptChecksums(0, h)
|
|
||||||
return CopyObjectResponse{
|
return CopyObjectResponse{
|
||||||
ETag: "\"" + oi.ETag + "\"",
|
ETag: "\"" + oi.ETag + "\"",
|
||||||
LastModified: amztime.ISO8601Format(oi.ModTime.UTC()),
|
LastModified: amztime.ISO8601Format(oi.ModTime.UTC()),
|
||||||
|
|||||||
@@ -387,6 +387,44 @@ func testAPICopyObjectServerSideChecksumEncryption(obj ObjectLayer, instanceType
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldKey := bytes.Repeat([]byte{0x31}, 32)
|
||||||
|
oldKeyMD5 := md5.Sum(oldKey)
|
||||||
|
newKey := bytes.Repeat([]byte{0x42}, 32)
|
||||||
|
newKeyMD5 := md5.Sum(newKey)
|
||||||
|
encryptedSource := "copy-checksum/sse-c-different-key-source.bin"
|
||||||
|
putCopyChecksumSource(t, apiRouter, credentials, bucketName, encryptedSource, data, map[string]string{
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(oldKey),
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(oldKeyMD5[:]),
|
||||||
|
})
|
||||||
|
|
||||||
|
destination := "copy-checksum/sse-c-different-key-destination.bin"
|
||||||
|
rec := copyChecksumRequest(t, apiRouter, credentials, bucketName, encryptedSource, destination, map[string]string{
|
||||||
|
xhttp.AmzChecksumAlgo: hash.ChecksumCRC32.String(),
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(newKey),
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(newKeyMD5[:]),
|
||||||
|
xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
|
xhttp.AmzServerSideEncryptionCopyCustomerKey: base64.StdEncoding.EncodeToString(oldKey),
|
||||||
|
xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: base64.StdEncoding.EncodeToString(oldKeyMD5[:]),
|
||||||
|
})
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("%s: different-key SSE-C CopyObject failed: %d %s", instanceType, rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
assertCopyChecksumResponse(t, rec, hash.ChecksumCRC32, data)
|
||||||
|
if got, want := rec.Header().Get(hash.ChecksumCRC32.Key()), mustChecksum(t, hash.ChecksumCRC32, data); got != want {
|
||||||
|
t.Fatalf("%s: different-key SSE-C response header checksum %q, want %q", instanceType, got, want)
|
||||||
|
}
|
||||||
|
if got := rec.Header().Get(xhttp.AmzChecksumType); got != xhttp.AmzChecksumTypeFullObject {
|
||||||
|
t.Fatalf("%s: different-key SSE-C response checksum type %q, want %q", instanceType, got, xhttp.AmzChecksumTypeFullObject)
|
||||||
|
}
|
||||||
|
newKeyHeaders := http.Header{
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerKey: []string{base64.StdEncoding.EncodeToString(newKey)},
|
||||||
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: []string{base64.StdEncoding.EncodeToString(newKeyMD5[:])},
|
||||||
|
}
|
||||||
|
assertCopyChecksum(t, obj, bucketName, destination, hash.ChecksumCRC32, data, false, newKeyHeaders)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ func testAPICopyObjectSSECKeyRotationKeepsCompressionState(obj ObjectLayer, inst
|
|||||||
newMD5 := md5.Sum(newKey)
|
newMD5 := md5.Sum(newKey)
|
||||||
|
|
||||||
putCopyChecksumSource(t, apiRouter, credentials, bucketName, object, data, map[string]string{
|
putCopyChecksumSource(t, apiRouter, credentials, bucketName, object, data, map[string]string{
|
||||||
|
xhttp.AmzChecksumCRC32: mustChecksum(t, hash.ChecksumCRC32, data),
|
||||||
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
xhttp.AmzServerSideEncryptionCustomerAlgorithm: xhttp.AmzEncryptionAES,
|
||||||
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(oldKey),
|
xhttp.AmzServerSideEncryptionCustomerKey: base64.StdEncoding.EncodeToString(oldKey),
|
||||||
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(oldMD5[:]),
|
xhttp.AmzServerSideEncryptionCustomerKeyMD5: base64.StdEncoding.EncodeToString(oldMD5[:]),
|
||||||
@@ -172,6 +173,7 @@ func testAPICopyObjectSSECKeyRotationKeepsCompressionState(obj ObjectLayer, inst
|
|||||||
if rec.Code != http.StatusOK {
|
if rec.Code != http.StatusOK {
|
||||||
t.Fatalf("%s: key rotation failed: %d %s", instanceType, rec.Code, rec.Body.String())
|
t.Fatalf("%s: key rotation failed: %d %s", instanceType, rec.Code, rec.Body.String())
|
||||||
}
|
}
|
||||||
|
assertCopyChecksumResponse(t, rec, hash.ChecksumCRC32, data)
|
||||||
after, err := obj.GetObjectInfo(t.Context(), bucketName, object, ObjectOptions{})
|
after, err := obj.GetObjectInfo(t.Context(), bucketName, object, ObjectOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -353,6 +353,11 @@ func isETagEqual(left, right string) bool {
|
|||||||
// upon a success Put/Copy/CompleteMultipart/Delete requests
|
// upon a success Put/Copy/CompleteMultipart/Delete requests
|
||||||
// to activate delete only headers set delete as true
|
// to activate delete only headers set delete as true
|
||||||
func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, del bool, h http.Header) {
|
func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, del bool, h http.Header) {
|
||||||
|
cs, _ := objInfo.decryptChecksums(0, h)
|
||||||
|
setPutObjHeadersWithChecksum(w, objInfo, del, cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPutObjHeadersWithChecksum(w http.ResponseWriter, objInfo ObjectInfo, del bool, cs map[string]string) {
|
||||||
// We must not use the http.Header().Set method here because some (broken)
|
// We must not use the http.Header().Set method here because some (broken)
|
||||||
// clients expect the ETag header key to be literally "ETag" - not "Etag" (case-sensitive).
|
// clients expect the ETag header key to be literally "ETag" - not "Etag" (case-sensitive).
|
||||||
// Therefore, we have to set the ETag directly as map entry.
|
// Therefore, we have to set the ETag directly as map entry.
|
||||||
@@ -374,7 +379,6 @@ func setPutObjHeaders(w http.ResponseWriter, objInfo ObjectInfo, del bool, h htt
|
|||||||
lc.SetPredictionHeaders(w, objInfo.ToLifecycleOpts())
|
lc.SetPredictionHeaders(w, objInfo.ToLifecycleOpts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cs, _ := objInfo.decryptChecksums(0, h)
|
|
||||||
hash.AddChecksumHeader(w, cs)
|
hash.AddChecksumHeader(w, cs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -1107,6 +1107,14 @@ func cloneRequestWithoutCopyReplicationHeaders(r *http.Request) *http.Request {
|
|||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyDestinationSSEHeaders(h http.Header) http.Header {
|
||||||
|
dst := h.Clone()
|
||||||
|
dst.Del(xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm)
|
||||||
|
dst.Del(xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
||||||
|
dst.Del(xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
// getRemoteInstanceTransport contains a roundtripper for external (not peers) servers
|
// getRemoteInstanceTransport contains a roundtripper for external (not peers) servers
|
||||||
var remoteInstanceTransport atomic.Value
|
var remoteInstanceTransport atomic.Value
|
||||||
|
|
||||||
@@ -1816,14 +1824,16 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
|
|
||||||
origETag := objInfo.ETag
|
origETag := objInfo.ETag
|
||||||
objInfo.ETag = getDecryptedETag(r.Header, objInfo, false)
|
objInfo.ETag = getDecryptedETag(r.Header, objInfo, false)
|
||||||
response := generateCopyObjectResponse(objInfo, r.Header)
|
dstHeaders := copyDestinationSSEHeaders(r.Header)
|
||||||
|
checksums, _ := objInfo.decryptChecksums(0, dstHeaders)
|
||||||
|
response := generateCopyObjectResponse(objInfo, checksums)
|
||||||
encodedSuccessResponse := encodeResponse(response)
|
encodedSuccessResponse := encodeResponse(response)
|
||||||
|
|
||||||
if dsc := mustReplicate(ctx, dstBucket, dstObject, objInfo.getMustReplicateOptions(replication.ObjectReplicationType, dstOpts)); dsc.ReplicateAny() {
|
if dsc := mustReplicate(ctx, dstBucket, dstObject, objInfo.getMustReplicateOptions(replication.ObjectReplicationType, dstOpts)); dsc.ReplicateAny() {
|
||||||
scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType)
|
scheduleReplication(ctx, objInfo, objectAPI, dsc, replication.ObjectReplicationType)
|
||||||
}
|
}
|
||||||
|
|
||||||
setPutObjHeaders(w, objInfo, false, r.Header)
|
setPutObjHeadersWithChecksum(w, objInfo, false, checksums)
|
||||||
// We must not use the http.Header().Set method here because some (broken)
|
// We must not use the http.Header().Set method here because some (broken)
|
||||||
// clients expect the x-amz-copy-source-version-id header key to be literally
|
// clients expect the x-amz-copy-source-version-id header key to be literally
|
||||||
// "x-amz-copy-source-version-id"- not in canonicalized form, preserve it.
|
// "x-amz-copy-source-version-id"- not in canonicalized form, preserve it.
|
||||||
|
|||||||
Reference in New Issue
Block a user