:* Replace signature package from neofs-api-go

close #219, #155

Signed-off-by: Evgenii Baidakov <evgenii@nspcc.io>
This commit is contained in:
Evgenii Baidakov 2023-04-25 12:21:48 +04:00
parent 77c2e227b9
commit 570a628462
No known key found for this signature in database
GPG key ID: 8733EE3D72CDB4DE
11 changed files with 477 additions and 19 deletions

View file

@ -187,7 +187,7 @@ func (x *contextCall) writeRequest() bool {
x.req.SetVerificationHeader(nil)
// sign the request
x.err = signature.SignServiceMessage(&x.key, x.req)
x.err = signServiceMessage(&x.key, x.req)
if x.err != nil {
x.err = fmt.Errorf("sign request: %w", x.err)
return false

View file

@ -8,7 +8,6 @@ import (
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/signature"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/version"
@ -248,7 +247,7 @@ func (c *Client) NetMapSnapshot(ctx context.Context, _ PrmNetMapSnapshot) (*ResN
req.SetBody(&body)
c.prepareRequest(&req, &meta)
err := signature.SignServiceMessage(&c.prm.key, &req)
err := signServiceMessage(&c.prm.key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -52,7 +52,7 @@ func (x *serverNetMap) netMapSnapshot(ctx context.Context, req v2netmap.Snapshot
resp.SetMetaHeader(&meta)
if x.signResponse {
err = signature.SignServiceMessage(key, &resp)
err = signServiceMessage(key, &resp)
if err != nil {
panic(fmt.Sprintf("sign response: %v", err))
}

View file

@ -11,7 +11,6 @@ import (
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
@ -146,7 +145,7 @@ func (c *Client) ObjectDelete(ctx context.Context, prm PrmObjectDelete) (*ResObj
key = prm.key
}
err := signature.SignServiceMessage(&key, &req)
err := signServiceMessage(&key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -13,7 +13,6 @@ import (
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
@ -326,7 +325,7 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe
key = &c.prm.key
}
err := signature.SignServiceMessage(key, &req)
err := signServiceMessage(key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -438,7 +437,7 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
}
// sign the request
err := signature.SignServiceMessage(&key, &req)
err := signServiceMessage(&key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}
@ -696,7 +695,7 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje
key = &c.prm.key
}
err := signature.SignServiceMessage(key, &req)
err := signServiceMessage(key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -11,7 +11,6 @@ import (
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
@ -192,7 +191,7 @@ func (c *Client) ObjectHash(ctx context.Context, prm PrmObjectHash) (*ResObjectH
key = prm.key
}
err := signature.SignServiceMessage(&key, &req)
err := signServiceMessage(&key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -12,7 +12,6 @@ import (
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
"github.com/nspcc-dev/neofs-sdk-go/object"
@ -117,7 +116,7 @@ func (x *ObjectWriter) WriteHeader(hdr object.Object) bool {
x.req.GetBody().SetObjectPart(&x.partInit)
x.req.SetVerificationHeader(nil)
x.err = signature.SignServiceMessage(x.key, &x.req)
x.err = signServiceMessage(x.key, &x.req)
if x.err != nil {
x.err = fmt.Errorf("sign message: %w", x.err)
return false
@ -159,7 +158,7 @@ func (x *ObjectWriter) WritePayloadChunk(chunk []byte) bool {
x.partChunk.SetChunk(chunk[:ln])
x.req.SetVerificationHeader(nil)
x.err = signature.SignServiceMessage(x.key, &x.req)
x.err = signServiceMessage(x.key, &x.req)
if x.err != nil {
x.err = fmt.Errorf("sign message: %w", x.err)
return false

View file

@ -13,7 +13,6 @@ import (
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
@ -247,7 +246,7 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob
key = &c.prm.key
}
err := signature.SignServiceMessage(key, &req)
err := signServiceMessage(key, &req)
if err != nil {
return nil, fmt.Errorf("sign request: %w", err)
}

View file

@ -10,7 +10,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
signatureV2 "github.com/nspcc-dev/neofs-api-go/v2/signature"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
"github.com/stretchr/testify/require"
@ -154,7 +153,7 @@ func (s *singleStreamResponder) Read(resp *v2object.SearchResponse) error {
}
resp.SetBody(&body)
err := signatureV2.SignServiceMessage(s.key, resp)
err := signServiceMessage(s.key, resp)
if err != nil {
panic(fmt.Errorf("error: %w", err))
}

393
client/sign.go Normal file
View file

@ -0,0 +1,393 @@
package client
import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
"github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/reputation"
"github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-api-go/v2/util/signature"
)
type serviceRequest interface {
GetMetaHeader() *session.RequestMetaHeader
GetVerificationHeader() *session.RequestVerificationHeader
SetVerificationHeader(*session.RequestVerificationHeader)
}
type serviceResponse interface {
GetMetaHeader() *session.ResponseMetaHeader
GetVerificationHeader() *session.ResponseVerificationHeader
SetVerificationHeader(*session.ResponseVerificationHeader)
}
type stableMarshaler interface {
StableMarshal([]byte) []byte
StableSize() int
}
type StableMarshalerWrapper struct {
SM stableMarshaler
}
type metaHeader interface {
stableMarshaler
getOrigin() metaHeader
}
type verificationHeader interface {
stableMarshaler
GetBodySignature() *refs.Signature
SetBodySignature(*refs.Signature)
GetMetaSignature() *refs.Signature
SetMetaSignature(*refs.Signature)
GetOriginSignature() *refs.Signature
SetOriginSignature(*refs.Signature)
setOrigin(stableMarshaler)
getOrigin() verificationHeader
}
type requestMetaHeader struct {
*session.RequestMetaHeader
}
type responseMetaHeader struct {
*session.ResponseMetaHeader
}
type requestVerificationHeader struct {
*session.RequestVerificationHeader
}
type responseVerificationHeader struct {
*session.ResponseVerificationHeader
}
func (h *requestMetaHeader) getOrigin() metaHeader {
return &requestMetaHeader{
RequestMetaHeader: h.GetOrigin(),
}
}
func (h *responseMetaHeader) getOrigin() metaHeader {
return &responseMetaHeader{
ResponseMetaHeader: h.GetOrigin(),
}
}
func (h *requestVerificationHeader) getOrigin() verificationHeader {
if origin := h.GetOrigin(); origin != nil {
return &requestVerificationHeader{
RequestVerificationHeader: origin,
}
}
return nil
}
func (h *requestVerificationHeader) setOrigin(m stableMarshaler) {
if m != nil {
h.SetOrigin(m.(*session.RequestVerificationHeader))
}
}
func (r *responseVerificationHeader) getOrigin() verificationHeader {
if origin := r.GetOrigin(); origin != nil {
return &responseVerificationHeader{
ResponseVerificationHeader: origin,
}
}
return nil
}
func (r *responseVerificationHeader) setOrigin(m stableMarshaler) {
if m != nil {
r.SetOrigin(m.(*session.ResponseVerificationHeader))
}
}
func (s StableMarshalerWrapper) ReadSignedData(buf []byte) ([]byte, error) {
if s.SM != nil {
return s.SM.StableMarshal(buf), nil
}
return nil, nil
}
func (s StableMarshalerWrapper) SignedDataSize() int {
if s.SM != nil {
return s.SM.StableSize()
}
return 0
}
func signServiceMessage(key *ecdsa.PrivateKey, msg interface{}) error {
var (
body, meta, verifyOrigin stableMarshaler
verifyHdr verificationHeader
verifyHdrSetter func(verificationHeader)
)
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
body = serviceMessageBody(v)
meta = v.GetMetaHeader()
verifyHdr = &requestVerificationHeader{new(session.RequestVerificationHeader)}
verifyHdrSetter = func(h verificationHeader) {
v.SetVerificationHeader(h.(*requestVerificationHeader).RequestVerificationHeader)
}
if h := v.GetVerificationHeader(); h != nil {
verifyOrigin = h
}
case serviceResponse:
body = serviceMessageBody(v)
meta = v.GetMetaHeader()
verifyHdr = &responseVerificationHeader{new(session.ResponseVerificationHeader)}
verifyHdrSetter = func(h verificationHeader) {
v.SetVerificationHeader(h.(*responseVerificationHeader).ResponseVerificationHeader)
}
if h := v.GetVerificationHeader(); h != nil {
verifyOrigin = h
}
default:
panic(fmt.Sprintf("unsupported session message %T", v))
}
if verifyOrigin == nil {
// sign session message body
if err := signServiceMessagePart(key, body, verifyHdr.SetBodySignature); err != nil {
return fmt.Errorf("could not sign body: %w", err)
}
}
// sign meta header
if err := signServiceMessagePart(key, meta, verifyHdr.SetMetaSignature); err != nil {
return fmt.Errorf("could not sign meta header: %w", err)
}
// sign verification header origin
if err := signServiceMessagePart(key, verifyOrigin, verifyHdr.SetOriginSignature); err != nil {
return fmt.Errorf("could not sign origin of verification header: %w", err)
}
// wrap origin verification header
verifyHdr.setOrigin(verifyOrigin)
// update matryoshka verification header
verifyHdrSetter(verifyHdr)
return nil
}
func signServiceMessagePart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrite func(*refs.Signature)) error {
var sig *refs.Signature
// sign part
if err := signature.SignDataWithHandler(
key,
&StableMarshalerWrapper{part},
func(s *refs.Signature) {
sig = s
},
); err != nil {
return err
}
// write part signature
sigWrite(sig)
return nil
}
func verifyServiceMessage(msg interface{}) error {
var (
meta metaHeader
verify verificationHeader
)
switch v := msg.(type) {
case nil:
return nil
case serviceRequest:
meta = &requestMetaHeader{
RequestMetaHeader: v.GetMetaHeader(),
}
verify = &requestVerificationHeader{
RequestVerificationHeader: v.GetVerificationHeader(),
}
case serviceResponse:
meta = &responseMetaHeader{
ResponseMetaHeader: v.GetMetaHeader(),
}
verify = &responseVerificationHeader{
ResponseVerificationHeader: v.GetVerificationHeader(),
}
default:
panic(fmt.Sprintf("unsupported session message %T", v))
}
body := serviceMessageBody(msg)
size := body.StableSize()
if sz := meta.StableSize(); sz > size {
size = sz
}
if sz := verify.StableSize(); sz > size {
size = sz
}
buf := make([]byte, 0, size)
return verifyMatryoshkaLevel(body, meta, verify, buf)
}
func verifyMatryoshkaLevel(body stableMarshaler, meta metaHeader, verify verificationHeader, buf []byte) error {
if err := verifyServiceMessagePart(meta, verify.GetMetaSignature, buf); err != nil {
return fmt.Errorf("could not verify meta header: %w", err)
}
origin := verify.getOrigin()
if err := verifyServiceMessagePart(origin, verify.GetOriginSignature, buf); err != nil {
return fmt.Errorf("could not verify origin of verification header: %w", err)
}
if origin == nil {
if err := verifyServiceMessagePart(body, verify.GetBodySignature, buf); err != nil {
return fmt.Errorf("could not verify body: %w", err)
}
return nil
}
if verify.GetBodySignature() != nil {
return errors.New("body signature at the matryoshka upper level")
}
return verifyMatryoshkaLevel(body, meta.getOrigin(), origin, buf)
}
func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature, buf []byte) error {
return signature.VerifyDataWithSource(
&StableMarshalerWrapper{part},
sigRdr,
signature.WithBuffer(buf),
)
}
func serviceMessageBody(req interface{}) stableMarshaler {
switch v := req.(type) {
default:
panic(fmt.Sprintf("unsupported session message %T", req))
/* Accounting */
case *accounting.BalanceRequest:
return v.GetBody()
case *accounting.BalanceResponse:
return v.GetBody()
/* Session */
case *session.CreateRequest:
return v.GetBody()
case *session.CreateResponse:
return v.GetBody()
/* Container */
case *container.PutRequest:
return v.GetBody()
case *container.PutResponse:
return v.GetBody()
case *container.DeleteRequest:
return v.GetBody()
case *container.DeleteResponse:
return v.GetBody()
case *container.GetRequest:
return v.GetBody()
case *container.GetResponse:
return v.GetBody()
case *container.ListRequest:
return v.GetBody()
case *container.ListResponse:
return v.GetBody()
case *container.SetExtendedACLRequest:
return v.GetBody()
case *container.SetExtendedACLResponse:
return v.GetBody()
case *container.GetExtendedACLRequest:
return v.GetBody()
case *container.GetExtendedACLResponse:
return v.GetBody()
case *container.AnnounceUsedSpaceRequest:
return v.GetBody()
case *container.AnnounceUsedSpaceResponse:
return v.GetBody()
/* Object */
case *object.PutRequest:
return v.GetBody()
case *object.PutResponse:
return v.GetBody()
case *object.GetRequest:
return v.GetBody()
case *object.GetResponse:
return v.GetBody()
case *object.HeadRequest:
return v.GetBody()
case *object.HeadResponse:
return v.GetBody()
case *object.SearchRequest:
return v.GetBody()
case *object.SearchResponse:
return v.GetBody()
case *object.DeleteRequest:
return v.GetBody()
case *object.DeleteResponse:
return v.GetBody()
case *object.GetRangeRequest:
return v.GetBody()
case *object.GetRangeResponse:
return v.GetBody()
case *object.GetRangeHashRequest:
return v.GetBody()
case *object.GetRangeHashResponse:
return v.GetBody()
/* Netmap */
case *netmap.LocalNodeInfoRequest:
return v.GetBody()
case *netmap.LocalNodeInfoResponse:
return v.GetBody()
case *netmap.NetworkInfoRequest:
return v.GetBody()
case *netmap.NetworkInfoResponse:
return v.GetBody()
case *netmap.SnapshotRequest:
return v.GetBody()
case *netmap.SnapshotResponse:
return v.GetBody()
/* Reputation */
case *reputation.AnnounceLocalTrustRequest:
return v.GetBody()
case *reputation.AnnounceLocalTrustResponse:
return v.GetBody()
case *reputation.AnnounceIntermediateResultRequest:
return v.GetBody()
case *reputation.AnnounceIntermediateResultResponse:
return v.GetBody()
}
}

72
client/sign_test.go Normal file
View file

@ -0,0 +1,72 @@
package client
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/session"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/stretchr/testify/require"
)
func TestBalanceResponse(t *testing.T) {
dec := new(accounting.Decimal)
dec.SetValue(100)
body := new(accounting.BalanceResponseBody)
body.SetBalance(dec)
meta := new(session.ResponseMetaHeader)
meta.SetTTL(1)
req := new(accounting.BalanceResponse)
req.SetBody(body)
req.SetMetaHeader(meta)
// verify unsigned request
require.Error(t, verifyServiceMessage(req))
key, err := crypto.LoadPrivateKey("Kwk6k2eC3L3QuPvD8aiaNyoSXgQ2YL1bwS5CP1oKoA9waeAze97s")
require.NoError(t, err)
// sign request
require.NoError(t, signServiceMessage(key, req))
// verification must pass
require.NoError(t, verifyServiceMessage(req))
// add level to meta header matryoshka
meta = new(session.ResponseMetaHeader)
meta.SetOrigin(req.GetMetaHeader())
req.SetMetaHeader(meta)
// sign request
require.NoError(t, signServiceMessage(key, req))
// verification must pass
require.NoError(t, verifyServiceMessage(req))
// corrupt body
dec.SetValue(dec.GetValue() + 1)
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore body
dec.SetValue(dec.GetValue() - 1)
// corrupt meta header
meta.SetTTL(meta.GetTTL() + 1)
// verification must fail
require.Error(t, verifyServiceMessage(req))
// restore meta header
meta.SetTTL(meta.GetTTL() - 1)
// corrupt origin verification header
req.GetVerificationHeader().SetOrigin(nil)
// verification must fail
require.Error(t, verifyServiceMessage(req))
}