From d005bf0393c6f282fc3f070e736c0f693203d97d Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Tue, 28 Feb 2023 13:01:18 +0300 Subject: [PATCH] [#3] signature: Refactor sign and verify Split methods to separate files, drop redundant intefaces Signed-off-by: Dmitrii Stepanov --- go.mod | 3 +- go.sum | 8 +- signature/body.go | 115 +++++++++++++ signature/marshaller.go | 26 +++ signature/sign.go | 357 ++++----------------------------------- signature/verify.go | 111 ++++++++++++ util/collection/slice.go | 17 ++ 7 files changed, 312 insertions(+), 325 deletions(-) create mode 100644 signature/body.go create mode 100644 signature/marshaller.go create mode 100644 signature/verify.go create mode 100644 util/collection/slice.go diff --git a/go.mod b/go.mod index a30ccb8..1c60a27 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 github.com/stretchr/testify v1.7.0 + golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 google.golang.org/grpc v1.48.0 google.golang.org/protobuf v1.28.0 ) @@ -16,7 +17,7 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect - golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect + golang.org/x/sys v0.1.0 // indirect golang.org/x/text v0.3.3 // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect diff --git a/go.sum b/go.sum index 34f4d44..c33a4c1 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -64,6 +64,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -87,8 +89,9 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -98,7 +101,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/signature/body.go b/signature/body.go new file mode 100644 index 0000000..bf3ab6c --- /dev/null +++ b/signature/body.go @@ -0,0 +1,115 @@ +package signature + +import ( + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" +) + +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() + } +} diff --git a/signature/marshaller.go b/signature/marshaller.go new file mode 100644 index 0000000..ff9beb3 --- /dev/null +++ b/signature/marshaller.go @@ -0,0 +1,26 @@ +package signature + +type stableMarshaler interface { + StableMarshal([]byte) []byte + StableSize() int +} + +type StableMarshalerWrapper struct { + SM stableMarshaler +} + +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 +} diff --git a/signature/sign.go b/signature/sign.go index f4dad90..bd53d94 100644 --- a/signature/sign.go +++ b/signature/sign.go @@ -2,15 +2,9 @@ package signature import ( "crypto/ecdsa" - "errors" "fmt" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/accounting" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/netmap" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" - "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature" ) @@ -27,169 +21,69 @@ type serviceResponse interface { 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 +type signatureReceiver interface { 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 } +// SignServiceMessage signes service message with key. 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 - } + return signServiceRequest(key, v) 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 - } + return signServiceResponse(key, v) default: panic(fmt.Sprintf("unsupported session message %T", v)) } +} - if verifyOrigin == nil { +func signServiceRequest(key *ecdsa.PrivateKey, v serviceRequest) error { + result := &session.RequestVerificationHeader{} + body := serviceMessageBody(v) + meta := v.GetMetaHeader() + header := v.GetVerificationHeader() + if err := signMessageParts(key, body, meta, header, header != nil, result); err != nil { + return err + } + result.SetOrigin(header) + v.SetVerificationHeader(result) + return nil +} + +func signServiceResponse(key *ecdsa.PrivateKey, v serviceResponse) error { + result := &session.ResponseVerificationHeader{} + body := serviceMessageBody(v) + meta := v.GetMetaHeader() + header := v.GetVerificationHeader() + if err := signMessageParts(key, body, meta, header, header != nil, result); err != nil { + return err + } + result.SetOrigin(header) + v.SetVerificationHeader(result) + return nil +} + +func signMessageParts(key *ecdsa.PrivateKey, body, meta, header stableMarshaler, hasHeader bool, result signatureReceiver) error { + if !hasHeader { // sign session message body - if err := signServiceMessagePart(key, body, verifyHdr.SetBodySignature); err != nil { + if err := signServiceMessagePart(key, body, result.SetBodySignature); err != nil { return fmt.Errorf("could not sign body: %w", err) } } // sign meta header - if err := signServiceMessagePart(key, meta, verifyHdr.SetMetaSignature); err != nil { + if err := signServiceMessagePart(key, meta, result.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 { + if err := signServiceMessagePart(key, header, result.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 } @@ -212,182 +106,3 @@ func signServiceMessagePart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrit 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() - } -} diff --git a/signature/verify.go b/signature/verify.go new file mode 100644 index 0000000..fe76844 --- /dev/null +++ b/signature/verify.go @@ -0,0 +1,111 @@ +package signature + +import ( + "errors" + "fmt" + + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/collection" + "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/util/signature" +) + +type signatureProvider interface { + GetBodySignature() *refs.Signature + GetMetaSignature() *refs.Signature + GetOriginSignature() *refs.Signature +} + +// VerifyServiceMessage verifies service message. +func VerifyServiceMessage(msg interface{}) error { + switch v := msg.(type) { + case nil: + return nil + case serviceRequest: + return verifyServiceRequest(v) + case serviceResponse: + return verifyServiceResponse(v) + default: + panic(fmt.Sprintf("unsupported session message %T", v)) + } +} + +func verifyServiceRequest(v serviceRequest) error { + meta := v.GetMetaHeader() + verificationHeader := v.GetVerificationHeader() + body := serviceMessageBody(v) + size := collection.Max(body.StableSize(), meta.StableSize(), verificationHeader.StableSize()) + buf := make([]byte, 0, size) + return verifyServiceRequestRecursive(body, meta, verificationHeader, buf) +} + +func verifyServiceRequestRecursive(body stableMarshaler, meta *session.RequestMetaHeader, verify *session.RequestVerificationHeader, buf []byte) error { + verificationHeaderOrigin := verify.GetOrigin() + metaOrigin := meta.GetOrigin() + + stop, err := verifyMessageParts(body, meta, verificationHeaderOrigin, verificationHeaderOrigin != nil, verify, buf) + if err != nil { + return err + } + if stop { + return nil + } + + return verifyServiceRequestRecursive(body, metaOrigin, verificationHeaderOrigin, buf) +} + +func verifyMessageParts(body, meta, originHeader stableMarshaler, hasOriginHeader bool, sigProvider signatureProvider, buf []byte) (stop bool, err error) { + if err := verifyServiceMessagePart(meta, sigProvider.GetMetaSignature, buf); err != nil { + return false, fmt.Errorf("could not verify meta header: %w", err) + } + + if err := verifyServiceMessagePart(originHeader, sigProvider.GetOriginSignature, buf); err != nil { + return false, fmt.Errorf("could not verify origin of verification header: %w", err) + } + + if !hasOriginHeader { + if err := verifyServiceMessagePart(body, sigProvider.GetBodySignature, buf); err != nil { + return false, fmt.Errorf("could not verify body: %w", err) + } + + return true, nil + } + + if sigProvider.GetBodySignature() != nil { + return false, errors.New("body signature misses at the matryoshka upper level") + } + + return false, nil +} + +func verifyServiceResponse(v serviceResponse) error { + meta := v.GetMetaHeader() + verificationHeader := v.GetVerificationHeader() + body := serviceMessageBody(v) + size := collection.Max(body.StableSize(), meta.StableSize(), verificationHeader.StableSize()) + buf := make([]byte, 0, size) + return verifyServiceResponseRecursive(body, meta, verificationHeader, buf) +} + +func verifyServiceResponseRecursive(body stableMarshaler, meta *session.ResponseMetaHeader, verify *session.ResponseVerificationHeader, buf []byte) error { + verificationHeaderOrigin := verify.GetOrigin() + metaOrigin := meta.GetOrigin() + + stop, err := verifyMessageParts(body, meta, verificationHeaderOrigin, verificationHeaderOrigin != nil, verify, buf) + if err != nil { + return err + } + if stop { + return nil + } + + return verifyServiceResponseRecursive(body, metaOrigin, verificationHeaderOrigin, buf) +} + +func verifyServiceMessagePart(part stableMarshaler, sigRdr func() *refs.Signature, buf []byte) error { + return signature.VerifyDataWithSource( + &StableMarshalerWrapper{part}, + sigRdr, + signature.WithBuffer(buf), + ) +} diff --git a/util/collection/slice.go b/util/collection/slice.go new file mode 100644 index 0000000..436f28c --- /dev/null +++ b/util/collection/slice.go @@ -0,0 +1,17 @@ +package collection + +import "golang.org/x/exp/constraints" + +// Max finds maximum value of items. +func Max[T constraints.Ordered](items ...T) T { + if len(items) == 0 { + panic("failed to get max value: empty slice") + } + result := items[0] + for i := 1; i < len(items); i++ { + if items[i] > result { + result = items[i] + } + } + return result +}