From e59d18635f1781a5fbc43f78ebf4c3c074ff486c Mon Sep 17 00:00:00 2001 From: Evgenii Baidakov Date: Tue, 10 Sep 2024 09:54:47 +0400 Subject: [PATCH] neofs: Sign multipart objects in the gate Closes #975. Signed-off-by: Evgenii Baidakov --- api/layer/multipart_upload.go | 12 ++++++++---- api/layer/neofs.go | 2 ++ api/layer/object.go | 18 ++++++++++++++++-- internal/neofs/neofs.go | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/api/layer/multipart_upload.go b/api/layer/multipart_upload.go index 946b3b99..3371975b 100644 --- a/api/layer/multipart_upload.go +++ b/api/layer/multipart_upload.go @@ -340,8 +340,10 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf if nBts > 0 { prm.Payload = bytes.NewReader((*chunk)[:nBts]) prm.PayloadSize = uint64(nBts) + prm.Multipart.PayloadHash = sha256.New() + prm.Multipart.PayloadHash.Write((*chunk)[:nBts]) - id, _, err = n.objectPutAndHash(ctx, prm, bktInfo) + id, err = n.multipartObjectPut(ctx, prm, bktInfo) if err != nil { return nil, err } @@ -496,11 +498,12 @@ func (n *layer) uploadZeroPart(ctx context.Context, multipartInfo *data.Multipar Multipart: &Multipart{ MultipartHashes: objHashes, HeaderObject: &hashlessHeaderObject, + PayloadHash: sha256.New(), }, Payload: bytes.NewBuffer(nil), } - id, _, err := n.objectPutAndHash(ctx, prm, bktInfo) + id, err := n.multipartObjectPut(ctx, prm, bktInfo) if err != nil { return nil, err } @@ -822,11 +825,12 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar SplitFirstID: &splitFirstID, SplitPreviousID: &splitPreviousID, HeaderObject: header, + PayloadHash: sha256.New(), }, Payload: bytes.NewBuffer(nil), } - lastPartObjID, _, err := n.objectPutAndHash(ctx, prm, p.Info.Bkt) + lastPartObjID, err := n.multipartObjectPut(ctx, prm, p.Info.Bkt) if err != nil { return nil, nil, err } @@ -852,7 +856,7 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar }, } - _, _, err = n.objectPutAndHash(ctx, prm, p.Info.Bkt) + _, err = n.multipartObjectPut(ctx, prm, p.Info.Bkt) if err != nil { return nil, nil, err } diff --git a/api/layer/neofs.go b/api/layer/neofs.go index f5c56e8b..516e4bf0 100644 --- a/api/layer/neofs.go +++ b/api/layer/neofs.go @@ -132,6 +132,8 @@ type Multipart struct { HeaderObject *object.Object // Link contains info for linking object. Link *object.Link + // PayloadHash contains precalculated hash for object. + PayloadHash hash.Hash } // PrmObjectDelete groups parameters of NeoFS.DeleteObject operation. diff --git a/api/layer/object.go b/api/layer/object.go index 7bb77674..5b9b452f 100644 --- a/api/layer/object.go +++ b/api/layer/object.go @@ -488,6 +488,20 @@ func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktIn hash := sha256.New() prm.Payload = wrapReader(prm.Payload, 64*1024, func(buf []byte) { hash.Write(buf) + }) + id, err := n.neoFS.CreateObject(ctx, prm) + if err != nil { + return oid.ID{}, nil, err + } + return id, hash.Sum(nil), nil +} + +// multipartObjectPut writes only Multipart hash collection. +// It doesn't calculate hash from payload, it already calculated in prm.Multipart.PayloadHash, if required. +func (n *layer) multipartObjectPut(ctx context.Context, prm PrmObjectCreate, bktInfo *data.BucketInfo) (oid.ID, error) { + n.prepareAuthParameters(ctx, &prm.PrmAuth, bktInfo.Owner) + + prm.Payload = wrapReader(prm.Payload, 64*1024, func(buf []byte) { if prm.Multipart != nil { for _, h := range prm.Multipart.MultipartHashes { h.Write(buf) @@ -496,9 +510,9 @@ func (n *layer) objectPutAndHash(ctx context.Context, prm PrmObjectCreate, bktIn }) id, err := n.neoFS.CreateObject(ctx, prm) if err != nil { - return oid.ID{}, nil, err + return oid.ID{}, err } - return id, hash.Sum(nil), nil + return id, nil } // ListObjectsV1 returns objects in a bucket for requests of Version 1. diff --git a/internal/neofs/neofs.go b/internal/neofs/neofs.go index 660ca5a6..043fc50d 100644 --- a/internal/neofs/neofs.go +++ b/internal/neofs/neofs.go @@ -26,6 +26,7 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/container" "github.com/nspcc-dev/neofs-sdk-go/container/acl" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" @@ -246,6 +247,33 @@ func (x *NeoFS) DeleteContainer(ctx context.Context, id cid.ID, token *session.C return nil } +func (x *NeoFS) signMultipartObject(obj *object.Object, signer neofscrypto.Signer, payloadHash hash.Hash) error { + var ( + sig neofscrypto.Signature + ) + + obj.SetPayloadChecksum(checksum.NewFromHash(checksum.SHA256, payloadHash)) + + if x.IsHomomorphicHashingEnabled() { + var homoChecksum = tz.New() + + homoChecksum.Write(obj.Payload()) + obj.SetPayloadHomomorphicHash(checksum.NewFromHash(checksum.TillichZemor, homoChecksum)) + } + + if err := obj.CalculateAndSetID(); err != nil { + return fmt.Errorf("calculate ID: %w", err) + } + + if err := sig.Calculate(signer, obj.GetID().Marshal()); err != nil { + return fmt.Errorf("sign object ID: %w", err) + } + + obj.SetSignature(&sig) + + return nil +} + // CreateObject implements neofs.NeoFS interface method. func (x *NeoFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (oid.ID, error) { attrNum := len(prm.Attributes) + 1 // + creation time @@ -310,9 +338,16 @@ func (x *NeoFS) CreateObject(ctx context.Context, prm layer.PrmObjectCreate) (oi prm.Payload = bytes.NewReader(obj.Payload()) obj.SetPayloadSize(uint64(len(obj.Payload()))) + prm.Multipart.PayloadHash = sha256.New() + prm.Multipart.PayloadHash.Write(obj.Payload()) + // Link object should never have a previous one. obj.ResetPreviousID() } + + if err := x.signMultipartObject(&obj, x.signer(ctx), prm.Multipart.PayloadHash); err != nil { + return oid.ID{}, errors.New("failed to sign object") + } } if len(prm.Locks) > 0 {