From 619190332687f6579569286b78d1ff07b54c6803 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 14 Aug 2020 15:51:10 +0300 Subject: [PATCH] Implement new request signing mechanism Signed-off-by: Leonard Lyubich --- pkg/.gitkeep | 0 pkg/signature/request.go | 64 ------ {pkg => util}/signature/data.go | 0 {pkg => util}/signature/options.go | 0 {pkg => util}/signature/util.go | 0 v2/accounting.go | 85 +++++++ v2/convert.go | 351 +++++++++++++++++++++++++++++ v2/service.go | 331 +++++++++++++++++++++++++++ v2/signature/sign.go | 137 +++++++++++ 9 files changed, 904 insertions(+), 64 deletions(-) create mode 100644 pkg/.gitkeep delete mode 100644 pkg/signature/request.go rename {pkg => util}/signature/data.go (100%) rename {pkg => util}/signature/options.go (100%) rename {pkg => util}/signature/util.go (100%) create mode 100644 v2/accounting.go create mode 100644 v2/convert.go create mode 100644 v2/service.go create mode 100644 v2/signature/sign.go diff --git a/pkg/.gitkeep b/pkg/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pkg/signature/request.go b/pkg/signature/request.go deleted file mode 100644 index fc72be7..0000000 --- a/pkg/signature/request.go +++ /dev/null @@ -1,64 +0,0 @@ -package signature - -import ( - "crypto/ecdsa" - - "github.com/pkg/errors" -) - -type SignedRequest interface { - RequestBody() DataSource - RequestMetaHeader() DataSource - OriginVerificationHeader() DataSource - - SetBodySignatureWithKey(key, sig []byte) - BodySignatureWithKey() (key, sig []byte) - - SetMetaSignatureWithKey(key, sig []byte) - MetaSignatureWithKey() (key, sig []byte) - - SetOriginSignatureWithKey(key, sig []byte) - OriginSignatureWithKey() (key, sig []byte) -} - -func SignRequest(key *ecdsa.PrivateKey, src SignedRequest) error { - if src == nil { - return errors.New("nil source") - } - - // sign body - if err := SignDataWithHandler(key, src.RequestBody(), src.SetBodySignatureWithKey); err != nil { - return errors.Wrap(err, "could not sign body") - } - - // sign meta - if err := SignDataWithHandler(key, src.RequestMetaHeader(), src.SetMetaSignatureWithKey); err != nil { - return errors.Wrap(err, "could not sign meta header") - } - - // sign verify origin - if err := SignDataWithHandler(key, src.OriginVerificationHeader(), src.SetOriginSignatureWithKey); err != nil { - return errors.Wrap(err, "could not sign verification header origin") - } - - return nil -} - -func VerifyRequest(src SignedRequest) error { - // verify body signature - if err := VerifyDataWithSource(src.RequestBody(), src.BodySignatureWithKey); err != nil { - return errors.Wrap(err, "could not verify body") - } - - // verify meta header - if err := VerifyDataWithSource(src.RequestMetaHeader(), src.MetaSignatureWithKey); err != nil { - return errors.Wrap(err, "could not verify meta header") - } - - // verify verification header origin - if err := VerifyDataWithSource(src.OriginVerificationHeader(), src.OriginSignatureWithKey); err != nil { - return errors.Wrap(err, "could not verify verification header origin") - } - - return nil -} diff --git a/pkg/signature/data.go b/util/signature/data.go similarity index 100% rename from pkg/signature/data.go rename to util/signature/data.go diff --git a/pkg/signature/options.go b/util/signature/options.go similarity index 100% rename from pkg/signature/options.go rename to util/signature/options.go diff --git a/pkg/signature/util.go b/util/signature/util.go similarity index 100% rename from pkg/signature/util.go rename to util/signature/util.go diff --git a/v2/accounting.go b/v2/accounting.go new file mode 100644 index 0000000..a14e5a1 --- /dev/null +++ b/v2/accounting.go @@ -0,0 +1,85 @@ +package v2 + +type BalanceRequestBody struct { + ownerID *OwnerID +} + +type BalanceRequest struct { + body *BalanceRequestBody + + metaHeader *RequestMetaHeader + + verifyHeader *RequestVerificationHeader +} + +func (b *BalanceRequestBody) GetOwnerID() *OwnerID { + if b != nil { + return b.ownerID + } + + return nil +} + +func (b *BalanceRequestBody) SetOwnerID(v *OwnerID) { + if b != nil { + b.ownerID = v + } +} + +func (r *BalanceRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return nil, nil + } + + // TODO: do not use hack + _, err := BalanceRequestBodyToGRPCMessage(r).MarshalTo(buf) + + return buf, err +} + +func (r *BalanceRequestBody) StableSize() int { + // TODO: do not use hack + return BalanceRequestBodyToGRPCMessage(r).Size() +} + +func (b *BalanceRequest) GetBody() *BalanceRequestBody { + if b != nil { + return b.body + } + + return nil +} + +func (b *BalanceRequest) SetBody(v *BalanceRequestBody) { + if b != nil { + b.body = v + } +} + +func (b *BalanceRequest) GetRequestMetaHeader() *RequestMetaHeader { + if b != nil { + return b.metaHeader + } + + return nil +} + +func (b *BalanceRequest) SetRequestMetaHeader(v *RequestMetaHeader) { + if b != nil { + b.metaHeader = v + } +} + +func (b *BalanceRequest) GetRequestVerificationHeader() *RequestVerificationHeader { + if b != nil { + return b.verifyHeader + } + + return nil +} + +func (b *BalanceRequest) SetRequestVerificationHeader(v *RequestVerificationHeader) { + if b != nil { + b.verifyHeader = v + } +} diff --git a/v2/convert.go b/v2/convert.go new file mode 100644 index 0000000..8cc95a6 --- /dev/null +++ b/v2/convert.go @@ -0,0 +1,351 @@ +package v2 + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/accounting" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-api-go/v2/service" +) + +func SignatureToGRPCMessage(s *Signature) *service.Signature { + if s == nil { + return nil + } + + m := new(service.Signature) + + m.SetKey(s.GetKey()) + m.SetSign(s.GetSign()) + + return m +} + +func SignatureFromGRPCMessage(m *service.Signature) *Signature { + if m == nil { + return nil + } + + s := new(Signature) + + s.SetKey(m.GetKey()) + s.SetSign(m.GetSign()) + + return s +} + +func VersionToGRPCMessage(v *Version) *service.Version { + if v == nil { + return nil + } + + msg := new(service.Version) + + msg.SetMajor(v.GetMajor()) + msg.SetMinor(v.GetMinor()) + + return msg +} + +func VersionFromGRPCMessage(m *service.Version) *Version { + if m == nil { + return nil + } + + v := new(Version) + + v.SetMajor(m.GetMajor()) + v.SetMinor(m.GetMinor()) + + return v +} + +func XHeaderToGRPCMessage(x *XHeader) *service.XHeader { + if x == nil { + return nil + } + + m := new(service.XHeader) + + m.SetKey(x.GetKey()) + m.SetValue(x.GetValue()) + + return m +} + +func XHeaderFromGRPCMessage(m *service.XHeader) *XHeader { + if m == nil { + return nil + } + + x := new(XHeader) + + x.SetKey(m.GetKey()) + x.SetValue(m.GetValue()) + + return x +} + +func SessionTokenToGRPCMessage(t *SessionToken) *service.SessionToken { + // TODO: fill me + return nil +} + +func SessionTokenFromGRPCMessage(m *service.SessionToken) *SessionToken { + // TODO: fill me + return nil +} + +func BearerTokenToGRPCMessage(t *BearerToken) *service.BearerToken { + // TODO: fill me + return nil +} + +func BearerTokenFromGRPCMessage(m *service.BearerToken) *BearerToken { + // TODO: fill me + return nil +} + +func RequestVerificationHeaderToGRPCMessage(r *RequestVerificationHeader) *service.RequestVerificationHeader { + if r == nil { + return nil + } + + m := new(service.RequestVerificationHeader) + + m.SetBodySignature( + SignatureToGRPCMessage(r.GetBodySignature()), + ) + + m.SetMetaSignature( + SignatureToGRPCMessage(r.GetMetaSignature()), + ) + + m.SetOriginSignature( + SignatureToGRPCMessage(r.GetOriginSignature()), + ) + + m.SetOrigin( + RequestVerificationHeaderToGRPCMessage(r.GetOrigin()), + ) + + return m +} + +func RequestVerificationHeaderFromGRPCMessage(m *service.RequestVerificationHeader) *RequestVerificationHeader { + if m == nil { + return nil + } + + r := new(RequestVerificationHeader) + + r.SetBodySignature( + SignatureFromGRPCMessage(m.GetBodySignature()), + ) + + r.SetMetaSignature( + SignatureFromGRPCMessage(m.GetMetaSignature()), + ) + + r.SetOriginSignature( + SignatureFromGRPCMessage(m.GetOriginSignature()), + ) + + r.SetOrigin( + RequestVerificationHeaderFromGRPCMessage(m.GetOrigin()), + ) + + return r +} + +func RequestMetaHeaderToGRPCMessage(r *RequestMetaHeader) *service.RequestMetaHeader { + if r == nil { + return nil + } + + m := new(service.RequestMetaHeader) + + m.SetTtl(r.GetTTL()) + m.SetEpoch(r.GetEpoch()) + + m.SetVersion( + VersionToGRPCMessage(r.GetVersion()), + ) + + m.SetSessionToken( + SessionTokenToGRPCMessage(r.GetSessionToken()), + ) + + m.SetBearerToken( + BearerTokenToGRPCMessage(r.GetBearerToken()), + ) + + m.SetOrigin( + RequestMetaHeaderToGRPCMessage(r.GetOrigin()), + ) + + xHeaders := r.GetXHeaders() + xHdrMsg := make([]*service.XHeader, 0, len(xHeaders)) + + for i := range xHeaders { + xHdrMsg = append(xHdrMsg, XHeaderToGRPCMessage(xHeaders[i])) + } + + return m +} + +func RequestMetaHeaderFromGRPCMessage(m *service.RequestMetaHeader) *RequestMetaHeader { + if m == nil { + return nil + } + + r := new(RequestMetaHeader) + + r.SetTTL(m.GetTtl()) + r.SetEpoch(m.GetEpoch()) + + r.SetVersion( + VersionFromGRPCMessage(m.GetVersion()), + ) + + r.SetSessionToken( + SessionTokenFromGRPCMessage(m.GetSessionToken()), + ) + + r.SetBearerToken( + BearerTokenFromGRPCMessage(m.GetBearerToken()), + ) + + r.SetOrigin( + RequestMetaHeaderFromGRPCMessage(m.GetOrigin()), + ) + + xHdrMsg := m.GetXHeaders() + xHeaders := make([]*XHeader, 0, len(xHdrMsg)) + + for i := range xHdrMsg { + xHeaders = append(xHeaders, XHeaderFromGRPCMessage(xHdrMsg[i])) + } + + return r +} + +func OwnerIDToGRPCMessage(o *OwnerID) *refs.OwnerID { + if o == nil { + return nil + } + + m := new(refs.OwnerID) + + m.SetValue(o.GetValue()) + + return m +} + +func OwnerIDFromGRPCMessage(m *refs.OwnerID) *OwnerID { + if m == nil { + return nil + } + + o := new(OwnerID) + + o.SetValue(m.GetValue()) + + return o +} + +func BalanceRequestBodyToGRPCMessage(b *BalanceRequestBody) *accounting.BalanceRequest_Body { + if b == nil { + return nil + } + + m := new(accounting.BalanceRequest_Body) + + m.SetOwnerId( + OwnerIDToGRPCMessage(b.GetOwnerID()), + ) + + return m +} + +func BalanceRequestBodyFromGRPCMessage(m *accounting.BalanceRequest_Body) *BalanceRequestBody { + if m == nil { + return nil + } + + b := new(BalanceRequestBody) + + b.SetOwnerID( + OwnerIDFromGRPCMessage(m.GetOwnerId()), + ) + + return b +} + +func headersToGRPC( + src interface { + GetRequestMetaHeader() *RequestMetaHeader + GetRequestVerificationHeader() *RequestVerificationHeader + }, + dst interface { + SetMetaHeader(*service.RequestMetaHeader) + SetVerifyHeader(*service.RequestVerificationHeader) + }, +) { + dst.SetMetaHeader( + RequestMetaHeaderToGRPCMessage(src.GetRequestMetaHeader()), + ) + + dst.SetVerifyHeader( + RequestVerificationHeaderToGRPCMessage(src.GetRequestVerificationHeader()), + ) +} + +func headersFromGRPC( + src interface { + GetMetaHeader() *service.RequestMetaHeader + GetVerifyHeader() *service.RequestVerificationHeader + }, + dst interface { + SetRequestMetaHeader(*RequestMetaHeader) + SetRequestVerificationHeader(*RequestVerificationHeader) + }, +) { + dst.SetRequestMetaHeader( + RequestMetaHeaderFromGRPCMessage(src.GetMetaHeader()), + ) + + dst.SetRequestVerificationHeader( + RequestVerificationHeaderFromGRPCMessage(src.GetVerifyHeader()), + ) +} + +func BalanceRequestToGRPCMessage(b *BalanceRequest) *accounting.BalanceRequest { + if b == nil { + return nil + } + + m := new(accounting.BalanceRequest) + + m.SetBody( + BalanceRequestBodyToGRPCMessage(b.GetBody()), + ) + + headersToGRPC(b, m) + + return m +} + +func BalanceRequestFromGRPCMessage(m *accounting.BalanceRequest) *BalanceRequest { + if m == nil { + return nil + } + + b := new(BalanceRequest) + + b.SetBody( + BalanceRequestBodyFromGRPCMessage(m.GetBody()), + ) + + headersFromGRPC(m, b) + + return b +} diff --git a/v2/service.go b/v2/service.go new file mode 100644 index 0000000..55a3947 --- /dev/null +++ b/v2/service.go @@ -0,0 +1,331 @@ +package v2 + +type Signature struct { + key, sign []byte +} + +type Version struct { + major, minor uint32 +} + +type XHeader struct { + key, val string +} + +type SessionToken struct { + // TODO: fill me +} + +type BearerToken struct { + // TODO: fill me +} + +type RequestVerificationHeader struct { + bodySig, metaSig, originSig *Signature + + origin *RequestVerificationHeader +} + +type RequestMetaHeader struct { + version *Version + + ttl uint32 + + epoch uint64 + + xHeaders []*XHeader + + sessionToken *SessionToken + + bearerToken *BearerToken + + origin *RequestMetaHeader +} + +type OwnerID struct { + val []byte +} + +func (s *Signature) GetKey() []byte { + if s != nil { + return s.key + } + + return nil +} + +func (s *Signature) SetKey(v []byte) { + if s != nil { + s.key = v + } +} + +func (s *Signature) GetSign() []byte { + if s != nil { + return s.sign + } + + return nil +} + +func (s *Signature) SetSign(v []byte) { + if s != nil { + s.sign = v + } +} + +func (v *Version) GetMajor() uint32 { + if v != nil { + return v.major + } + + return 0 +} + +func (v *Version) SetMajor(val uint32) { + if v != nil { + v.major = val + } +} + +func (v *Version) GetMinor() uint32 { + if v != nil { + return v.minor + } + + return 0 +} + +func (v *Version) SetMinor(val uint32) { + if v != nil { + v.minor = val + } +} + +func (x *XHeader) GetKey() string { + if x != nil { + return x.key + } + + return "" +} + +func (x *XHeader) SetKey(v string) { + if x != nil { + x.key = v + } +} + +func (x *XHeader) GetValue() string { + if x != nil { + return x.val + } + + return "" +} + +func (x *XHeader) SetValue(v string) { + if x != nil { + x.val = v + } +} + +func (r *RequestVerificationHeader) GetBodySignature() *Signature { + if r != nil { + return r.bodySig + } + + return nil +} + +func (r *RequestVerificationHeader) SetBodySignature(v *Signature) { + if r != nil { + r.bodySig = v + } +} + +func (r *RequestVerificationHeader) GetMetaSignature() *Signature { + if r != nil { + return r.metaSig + } + + return nil +} + +func (r *RequestVerificationHeader) SetMetaSignature(v *Signature) { + if r != nil { + r.metaSig = v + } +} + +func (r *RequestVerificationHeader) GetOriginSignature() *Signature { + if r != nil { + return r.originSig + } + + return nil +} + +func (r *RequestVerificationHeader) SetOriginSignature(v *Signature) { + if r != nil { + r.originSig = v + } +} + +func (r *RequestVerificationHeader) GetOrigin() *RequestVerificationHeader { + if r != nil { + return r.origin + } + + return nil +} + +func (r *RequestVerificationHeader) SetOrigin(v *RequestVerificationHeader) { + if r != nil { + r.origin = v + } +} + +func (r *RequestVerificationHeader) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return nil, nil + } + + // TODO: do not use hack + _, err := RequestVerificationHeaderToGRPCMessage(r).MarshalTo(buf) + + return buf, err +} + +func (r *RequestVerificationHeader) StableSize() int { + // TODO: do not use hack + return RequestVerificationHeaderToGRPCMessage(r).Size() +} + +func (r *RequestMetaHeader) GetVersion() *Version { + if r != nil { + return r.version + } + + return nil +} + +func (r *RequestMetaHeader) SetVersion(v *Version) { + if r != nil { + r.version = v + } +} + +func (r *RequestMetaHeader) GetTTL() uint32 { + if r != nil { + return r.ttl + } + + return 0 +} + +func (r *RequestMetaHeader) SetTTL(v uint32) { + if r != nil { + r.ttl = v + } +} + +func (r *RequestMetaHeader) GetEpoch() uint64 { + if r != nil { + return r.epoch + } + + return 0 +} + +func (r *RequestMetaHeader) SetEpoch(v uint64) { + if r != nil { + r.epoch = v + } +} + +func (r *RequestMetaHeader) GetXHeaders() []*XHeader { + if r != nil { + return r.xHeaders + } + + return nil +} + +func (r *RequestMetaHeader) SetXHeaders(v []*XHeader) { + if r != nil { + r.xHeaders = v + } +} + +func (r *RequestMetaHeader) GetSessionToken() *SessionToken { + if r != nil { + return r.sessionToken + } + + return nil +} + +func (r *RequestMetaHeader) SetSessionToken(v *SessionToken) { + if r != nil { + r.sessionToken = v + } +} + +func (r *RequestMetaHeader) GetBearerToken() *BearerToken { + if r != nil { + return r.bearerToken + } + + return nil +} + +func (r *RequestMetaHeader) SetBearerToken(v *BearerToken) { + if r != nil { + r.bearerToken = v + } +} + +func (r *RequestMetaHeader) GetOrigin() *RequestMetaHeader { + if r != nil { + return r.origin + } + + return nil +} + +func (r *RequestMetaHeader) SetOrigin(v *RequestMetaHeader) { + if r != nil { + r.origin = v + } +} + +func (r *RequestMetaHeader) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return nil, nil + } + + // TODO: do not use hack + _, err := RequestMetaHeaderToGRPCMessage(r).MarshalTo(buf) + + return buf, err +} + +func (r *RequestMetaHeader) StableSize() int { + // TODO: do not use hack + return RequestMetaHeaderToGRPCMessage(r).Size() +} + +func (o *OwnerID) GetValue() []byte { + if o != nil { + return o.val + } + + return nil +} + +func (o *OwnerID) SetValue(v []byte) { + if o != nil { + o.val = v + } +} diff --git a/v2/signature/sign.go b/v2/signature/sign.go new file mode 100644 index 0000000..79fef30 --- /dev/null +++ b/v2/signature/sign.go @@ -0,0 +1,137 @@ +package signature + +import ( + "crypto/ecdsa" + "fmt" + + "github.com/nspcc-dev/neofs-api-go/util/signature" + v2 "github.com/nspcc-dev/neofs-api-go/v2" + "github.com/pkg/errors" +) + +type SignedRequest interface { + GetRequestMetaHeader() *v2.RequestMetaHeader + GetRequestVerificationHeader() *v2.RequestVerificationHeader + SetRequestVerificationHeader(*v2.RequestVerificationHeader) +} + +type stableMarshaler interface { + StableMarshal([]byte) ([]byte, error) + StableSize() int +} + +type stableMarshalerWrapper struct { + sm stableMarshaler +} + +func (s stableMarshalerWrapper) ReadSignedData(buf []byte) ([]byte, error) { + return s.sm.StableMarshal(buf) +} + +func (s stableMarshalerWrapper) SignedDataSize() int { + return s.sm.StableSize() +} + +func keySignatureHandler(s *v2.Signature) signature.KeySignatureHandler { + return func(key []byte, sig []byte) { + s.SetKey(key) + s.SetSign(sig) + } +} + +func keySignatureSource(s *v2.Signature) signature.KeySignatureSource { + return func() ([]byte, []byte) { + return s.GetKey(), s.GetSign() + } +} + +func requestBody(req SignedRequest) stableMarshaler { + switch v := req.(type) { + case *v2.BalanceRequest: + return v.GetBody() + default: + panic(fmt.Sprintf("unknown request %T", req)) + } +} + +func SignRequest(key *ecdsa.PrivateKey, req SignedRequest) error { + if req == nil { + return nil + } + + // create new level of matryoshka + verifyHdr := new(v2.RequestVerificationHeader) + + // attach the previous matryoshka + verifyHdr.SetOrigin(req.GetRequestVerificationHeader()) + + // sign request body + if err := signRequestPart(key, requestBody(req), verifyHdr.SetBodySignature); err != nil { + return errors.Wrap(err, "could not sign request body") + } + + // sign meta header + if err := signRequestPart(key, req.GetRequestMetaHeader(), verifyHdr.SetMetaSignature); err != nil { + return errors.Wrap(err, "could not sign request meta header") + } + + // sign verification header origin + if err := signRequestPart(key, verifyHdr.GetOrigin(), verifyHdr.SetOriginSignature); err != nil { + return errors.Wrap(err, "could not sign origin of request verification header") + } + + // make a new top of the matryoshka + req.SetRequestVerificationHeader(verifyHdr) + + return nil +} + +func signRequestPart(key *ecdsa.PrivateKey, part stableMarshaler, sigWrite func(*v2.Signature)) error { + sig := new(v2.Signature) + + // sign part + if err := signature.SignDataWithHandler( + key, + &stableMarshalerWrapper{part}, + keySignatureHandler(sig), + ); err != nil { + return err + } + + // write part signature + sigWrite(sig) + + return nil +} + +func VerifyRequest(req SignedRequest) error { + verifyHdr := req.GetRequestVerificationHeader() + + // verify body signature + if err := verifyRequestPart(requestBody(req), verifyHdr.GetBodySignature); err != nil { + return errors.Wrap(err, "could not verify request body") + } + + // verify meta header + if err := verifyRequestPart(req.GetRequestMetaHeader(), verifyHdr.GetMetaSignature); err != nil { + return errors.Wrap(err, "could not verify request meta header") + } + + // verify verification header origin + if err := verifyRequestPart(verifyHdr.GetOrigin(), verifyHdr.GetOriginSignature); err != nil { + return errors.Wrap(err, "could not verify origin of request verification header") + } + + return nil +} + +func verifyRequestPart(part stableMarshaler, sigRdr func() *v2.Signature) error { + if err := signature.VerifyDataWithSource( + &stableMarshalerWrapper{part}, + keySignatureSource(sigRdr()), + ); err != nil { + return err + } + + return nil +}