diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d998f8a82..52322288da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Changelog for NeoFS Node ### Changed - SNs exchange TLS certificates on inter-node connections (#4097) +- SN no longer adds origin signature to EC requests sent to remote nodes with API >= v2.25 (#4118) ### Removed diff --git a/pkg/services/object/get.go b/pkg/services/object/get.go index 2e1c49a3e0..cc018b2efc 100644 --- a/pkg/services/object/get.go +++ b/pkg/services/object/get.go @@ -24,6 +24,7 @@ import ( protoobject "github.com/nspcc-dev/neofs-sdk-go/proto/object" iprotobuf "github.com/nspcc-dev/neofs-sdk-go/proto/protobuf" "github.com/nspcc-dev/neofs-sdk-go/proto/protobuf/protoscan" + protorefs "github.com/nspcc-dev/neofs-sdk-go/proto/refs" protostatus "github.com/nspcc-dev/neofs-sdk-go/proto/status" "go.uber.org/zap" "google.golang.org/grpc" @@ -478,13 +479,13 @@ func (x *getECTransport) CopyLocalECPartRange(ctx context.Context, storage *engi return ln, nil } -func (x *getECTransport) initGetPartRequest(partInfo iec.PartInfo) error { +func (x *getECTransport) initGetPartRequest(remoteServerAPIVersion *protorefs.Version, partInfo iec.PartInfo) error { if x.getPartRequestInfo == partInfo && x.getPartRequest != nil { return nil } var err error - x.getPartRequest, err = x.server.makeGetECPartRequest(x.requestContainer, x.requestObject, partInfo) + x.getPartRequest, err = x.server.makeGetECPartRequest(remoteServerAPIVersion, x.requestContainer, x.requestObject, partInfo) if err != nil { return fmt.Errorf("make GET request: %w", err) } @@ -501,9 +502,11 @@ func (x *getECTransport) CopyRemoteECPartParentHeaderAndPayload(ctx context.Cont var partPldLen uint64 var copiedPartPld uint64 + connAPIVersion := conn.APIVersion() + err := conn.ForAnyGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { if !copiedHdr { - if err := x.initGetPartRequest(partInfo); err != nil { + if err := x.initGetPartRequest(connAPIVersion, partInfo); err != nil { return err } @@ -520,7 +523,7 @@ func (x *getECTransport) CopyRemoteECPartParentHeaderAndPayload(ctx context.Cont return clientcore.ErrSkipConnection } - copiedFromNode, err := x.copyRemotePartRange(ctx, conn, partInfo, copiedPartPld, partPldLen-copiedPartPld, nil) + copiedFromNode, err := x.copyRemotePartRange(ctx, conn, connAPIVersion, partInfo, copiedPartPld, partPldLen-copiedPartPld, nil) if err != nil { return err } @@ -647,8 +650,8 @@ func (x *getECTransport) copyRemotePart(ctx context.Context, conn *grpc.ClientCo return copiedHdr, parentPldLen, partPldLen, copiedPartPldLen, nil } -func (x *getECTransport) copyRemotePartRange(ctx context.Context, conn *grpc.ClientConn, partInfo iec.PartInfo, off, ln uint64, controlCh <-chan bool) (uint64, error) { - request, err := x.makeGetECPartRangeRequest(partInfo, off, ln) +func (x *getECTransport) copyRemotePartRange(ctx context.Context, conn *grpc.ClientConn, connAPIVersion *protorefs.Version, partInfo iec.PartInfo, off, ln uint64, controlCh <-chan bool) (uint64, error) { + request, err := x.makeGetECPartRangeRequest(connAPIVersion, partInfo, off, ln) if err != nil { return 0, fmt.Errorf("make request: %w", err) } @@ -831,13 +834,15 @@ func handleGetECPartResponseInit(buffers iprotobuf.BuffersSlice) (iprotobuf.Buff func (x *getECTransport) CopyRemoteECPartRange(ctx context.Context, conn clientcore.MultiAddressClient, partInfo iec.PartInfo, off uint64, ln uint64, full bool, controlCh <-chan bool) (uint64, error) { var copiedPld uint64 + connAPIVersion := conn.APIVersion() + err := conn.ForAnyGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error { var reqLen uint64 if !full || off > 0 || copiedPld > 0 { reqLen = ln - copiedPld } - copiedFromNode, err := x.copyRemotePartRange(ctx, conn, partInfo, off+copiedPld, reqLen, controlCh) + copiedFromNode, err := x.copyRemotePartRange(ctx, conn, connAPIVersion, partInfo, off+copiedPld, reqLen, controlCh) if err != nil { return err } @@ -860,7 +865,7 @@ func (x *getECTransport) CopyRemoteECPartRange(ctx context.Context, conn clientc return copiedPld, nil } -func (s *Server) makeGetECPartRequest(cnr cid.ID, parent oid.ID, partInfo iec.PartInfo) (mem.Buffer, error) { +func (s *Server) makeGetECPartRequest(remoteServerAPIVersion *protorefs.Version, cnr cid.ID, parent oid.ID, partInfo iec.PartInfo) (mem.Buffer, error) { ruleIdxStr := strconv.Itoa(partInfo.RuleIndex) partIdxStr := strconv.Itoa(partInfo.Index) @@ -869,14 +874,16 @@ func (s *Server) makeGetECPartRequest(cnr cid.ID, parent oid.ID, partInfo iec.Pa metaHdrLen := calculateGetECPartRequestMetaHeaderLength(ruleIdxHdrLen, partIdxHdrLen) + verifHdrSigCount := getRequestVerificationSignaturesCount(remoteServerAPIVersion) + reqLen := 1 + 1 + getByAddressRequestBodyLen + // first 1 for iprotobuf.TagBytes1 1 + protowire.SizeBytes(metaHdrLen) + // 1 for iprotobuf.TagBytes2 - 1 + 2 + requestVerificationHeaderECDSAWIthSHA512Len // 1 for iprotobuf.TagBytes3 + 1 + 2 + calculateRequestVerificationHeaderLen(verifHdrSigCount) // 1 for iprotobuf.TagBytes3 // TODO: try with sync.Pool buf := make([]byte, reqLen) - n, err := s.writeGetECPartRequest(buf, cnr, parent, metaHdrLen, ruleIdxHdrLen, ruleIdxStr, partIdxHdrLen, partIdxStr) + n, err := s.writeGetECPartRequest(buf, cnr, parent, metaHdrLen, ruleIdxHdrLen, ruleIdxStr, partIdxHdrLen, partIdxStr, verifHdrSigCount) if err != nil { return nil, err } @@ -887,7 +894,7 @@ func (s *Server) makeGetECPartRequest(cnr cid.ID, parent oid.ID, partInfo iec.Pa return mem.SliceBuffer(buf), nil } -func (x *getECTransport) makeGetECPartRangeRequest(partInfo iec.PartInfo, off, ln uint64) (mem.Buffer, error) { +func (x *getECTransport) makeGetECPartRangeRequest(remoteServerAPIVersion *protorefs.Version, partInfo iec.PartInfo, off, ln uint64) (mem.Buffer, error) { x.getPartRangeRequestsMtx.RLock() req := x.getPartRangeRequests[partInfo] x.getPartRangeRequestsMtx.RUnlock() @@ -911,7 +918,7 @@ func (x *getECTransport) makeGetECPartRangeRequest(partInfo iec.PartInfo, off, l return req.buffer, nil } - reqBuf, err := x.server.makeGetECPartRangeRequest(x.requestContainer, x.requestObject, partInfo, off, ln) + reqBuf, err := x.server.makeGetECPartRangeRequest(remoteServerAPIVersion, x.requestContainer, x.requestObject, partInfo, off, ln) if err != nil { // stream is closed by context cancellation return nil, err @@ -924,7 +931,7 @@ func (x *getECTransport) makeGetECPartRangeRequest(partInfo iec.PartInfo, off, l return reqBuf, nil } -func (s *Server) makeGetECPartRangeRequest(cnr cid.ID, parent oid.ID, partInfo iec.PartInfo, off, ln uint64) (mem.Buffer, error) { +func (s *Server) makeGetECPartRangeRequest(remoteServerAPIVersion *protorefs.Version, cnr cid.ID, parent oid.ID, partInfo iec.PartInfo, off, ln uint64) (mem.Buffer, error) { ruleIdxStr := strconv.Itoa(partInfo.RuleIndex) partIdxStr := strconv.Itoa(partInfo.Index) @@ -950,15 +957,17 @@ func (s *Server) makeGetECPartRangeRequest(cnr cid.ID, parent oid.ID, partInfo i // payload_only flag bodyLen += 1 + 1 // 1 for iprotobuf.TagVarint4, 1 for true + verifHdrSigCount := getRequestVerificationSignaturesCount(remoteServerAPIVersion) + reqLen := 1 + protowire.SizeBytes(bodyLen) + // 1 for iprotobuf.TagBytes1 1 + protowire.SizeBytes(metaHdrLen) + // 1 for iprotobuf.TagBytes2 - 1 + 2 + requestVerificationHeaderECDSAWIthSHA512Len // 1 for iprotobuf.TagBytes3 + 1 + 2 + calculateRequestVerificationHeaderLen(verifHdrSigCount) // 1 for iprotobuf.TagBytes3 // TODO: try with sync.Pool buf := make([]byte, reqLen) n, err := s.writeGetECPartRangeRequest(buf, bodyLen, cnr, parent, rngLen, off, ln, - metaHdrLen, ruleIdxHdrLen, ruleIdxStr, partIdxHdrLen, partIdxStr) + metaHdrLen, ruleIdxHdrLen, ruleIdxStr, partIdxHdrLen, partIdxStr, verifHdrSigCount) if err != nil { return nil, err } @@ -969,11 +978,14 @@ func (s *Server) makeGetECPartRangeRequest(cnr cid.ID, parent oid.ID, partInfo i return mem.SliceBuffer(buf), nil } -func (s *Server) writeGetECPartRequest(buf []byte, cnr cid.ID, parent oid.ID, metaHdrLen int, ruleIdxHdrLen int, ruleIdxHdr string, partIdxHdrLen int, partIdxHdr string) (int, error) { - // TODO: can be calculated once and reused - originSig, err := neofsecdsa.Signer(s.signer).Sign(nil) - if err != nil { - return 0, fmt.Errorf("sign empty data: %w", err) +func (s *Server) writeGetECPartRequest(buf []byte, cnr cid.ID, parent oid.ID, metaHdrLen int, ruleIdxHdrLen int, ruleIdxHdr string, partIdxHdrLen int, partIdxHdr string, verifHdrSigCount int) (int, error) { + var originSig []byte + var err error + if verifHdrSigCount == 3 { + originSig, err = neofsecdsa.Signer(s.signer).Sign(nil) + if err != nil { + return 0, fmt.Errorf("sign empty data: %w", err) + } } // body @@ -1013,17 +1025,20 @@ func (s *Server) writeGetECPartRequest(buf []byte, cnr cid.ID, parent oid.ID, me } // verification header - off += writeRequestVerificationHeader(buf[off:], s.pubKeyBytes, bodySig, metaHdrSig, originSig) + off += writeRequestVerificationHeader(buf[off:], verifHdrSigCount, s.pubKeyBytes, bodySig, metaHdrSig, originSig) return off, nil } func (s *Server) writeGetECPartRangeRequest(buf []byte, bodyLen int, cnr cid.ID, parent oid.ID, rngLen int, off uint64, ln uint64, - metaHdrLen int, ruleIdxHdrLen int, ruleIdxHdr string, partIdxHdrLen int, partIdxHdr string) (int, error) { - // TODO: can be calculated once and reused - originSig, err := neofsecdsa.Signer(s.signer).Sign(nil) - if err != nil { - return 0, fmt.Errorf("sign empty data: %w", err) + metaHdrLen int, ruleIdxHdrLen int, ruleIdxHdr string, partIdxHdrLen int, partIdxHdr string, verifHdrSigCount int) (int, error) { + var originSig []byte + var err error + if verifHdrSigCount == 3 { + originSig, err = neofsecdsa.Signer(s.signer).Sign(nil) + if err != nil { + return 0, fmt.Errorf("sign empty data: %w", err) + } } // body @@ -1094,7 +1109,7 @@ func (s *Server) writeGetECPartRangeRequest(buf []byte, bodyLen int, cnr cid.ID, } // verification header - n += writeRequestVerificationHeader(buf[n:], s.pubKeyBytes, bodySig, metaHdrSig, originSig) + n += writeRequestVerificationHeader(buf[n:], verifHdrSigCount, s.pubKeyBytes, bodySig, metaHdrSig, originSig) return n, nil } @@ -1111,7 +1126,7 @@ func (s *Server) writeInitGetResponseBuffers(respStream grpc.ServerStream, id, s respLen := 1 + protowire.SizeBytes(bodyLen) // 1 for iprotobuf.TagBytes1 if signResponse { - respLen += 1 + protowire.SizeBytes(requestVerificationHeaderECDSAWIthSHA512Len) // 1 for iprotobuf.TagBytes3 + respLen += 1 + protowire.SizeBytes(responseVerificationHeaderECDSAWIthSHA512Len) // 1 for iprotobuf.TagBytes3 } var respBuf mem.Buffer diff --git a/pkg/services/object/proto.go b/pkg/services/object/proto.go index 0a5b58ff6d..4952def79b 100644 --- a/pkg/services/object/proto.go +++ b/pkg/services/object/proto.go @@ -12,6 +12,7 @@ import ( neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" iprotobuf "github.com/nspcc-dev/neofs-sdk-go/proto/protobuf" + protorefs "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/nspcc-dev/neofs-sdk-go/version" "google.golang.org/protobuf/encoding/protowire" ) @@ -37,7 +38,8 @@ const ( ecdsaWithSHA256SignatureValueLen = 1 + keys.SignatureLen ecdsaWithSHA512SignatureLen = 1 + 1 + compressedECDSAPublicKeyLen + 1 + 1 + ecdsaWithSHA256SignatureValueLen // scheme is 0 - requestVerificationHeaderECDSAWIthSHA512Len = (1 + 1 + ecdsaWithSHA512SignatureLen) * 3 + verificationHeaderECDSAWithSHA512SignatureLen = 1 + 1 + ecdsaWithSHA512SignatureLen + responseVerificationHeaderECDSAWIthSHA512Len = verificationHeaderECDSAWithSHA512SignatureLen * 3 ) var currentVersionResponseMetaHeader []byte @@ -305,9 +307,21 @@ func signECDSAWithSHA512(privKey ecdsa.PrivateKey, data []byte) ([]byte, error) return sig, nil } -func writeRequestVerificationHeader(buf []byte, pubKey, bodySIg, metaSig, originSig []byte) int { +func getRequestVerificationSignaturesCount(remoteServerAPIVersion *protorefs.Version) int { + v := version.New(remoteServerAPIVersion.GetMajor(), remoteServerAPIVersion.GetMinor()) + if v.Compare(version.New(2, 25)) >= 0 { + return 2 + } + return 3 +} + +func calculateRequestVerificationHeaderLen(sigCount int) int { + return verificationHeaderECDSAWithSHA512SignatureLen * sigCount +} + +func writeRequestVerificationHeader(buf []byte, sigCount int, pubKey, bodySIg, metaSig, originSig []byte) int { buf[0] = iprotobuf.TagBytes3 - off := 1 + binary.PutUvarint(buf[1:], requestVerificationHeaderECDSAWIthSHA512Len) + off := 1 + binary.PutUvarint(buf[1:], uint64(calculateRequestVerificationHeaderLen(sigCount))) off += writeRequestVerificationSignature(buf[off:], iprotobuf.TagBytes1, pubKey, bodySIg) off += writeRequestVerificationSignature(buf[off:], iprotobuf.TagBytes2, pubKey, metaSig) off += writeRequestVerificationSignature(buf[off:], iprotobuf.TagBytes3, pubKey, originSig) @@ -315,6 +329,9 @@ func writeRequestVerificationHeader(buf []byte, pubKey, bodySIg, metaSig, origin } func writeRequestVerificationSignature(buf []byte, tag byte, pubKey, sig []byte) int { + if len(sig) == 0 { + return 0 + } buf[0] = tag buf[1] = ecdsaWithSHA512SignatureLen return 2 + writeECDSAWithSHA512Signature(buf[2:], pubKey, sig) diff --git a/pkg/services/object/proto_internal_test.go b/pkg/services/object/proto_internal_test.go new file mode 100644 index 0000000000..8d75594f1c --- /dev/null +++ b/pkg/services/object/proto_internal_test.go @@ -0,0 +1,33 @@ +package object + +import ( + "testing" + + protorefs "github.com/nspcc-dev/neofs-sdk-go/proto/refs" + "github.com/stretchr/testify/require" +) + +func TestGetRequestVerificationSignaturesCount(t *testing.T) { + assert := func(t *testing.T, exp int, vers []*protorefs.Version) { + for _, v := range vers { + require.EqualValues(t, exp, getRequestVerificationSignaturesCount(v), v) + } + } + + t.Run("all", func(t *testing.T) { + assert(t, 3, []*protorefs.Version{ + nil, + new(protorefs.Version), + {Major: 1, Minor: 26}, + {Major: 2, Minor: 24}, + }) + }) + + t.Run("no origin", func(t *testing.T) { + assert(t, 2, []*protorefs.Version{ + {Major: 2, Minor: 25}, + {Major: 2, Minor: 26}, + {Major: 3, Minor: 0}, + }) + }) +}