From 297212886db6e5381b3144576bba5d9deadc7de8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 12 Aug 2020 17:03:11 +0300 Subject: [PATCH] service: Add updated Sign/Verify mechanism --- service/signature/data.go | 85 ++++++++++++++++++++++++++++++++++++ service/signature/options.go | 26 +++++++++++ service/signature/request.go | 64 +++++++++++++++++++++++++++ service/signature/util.go | 31 +++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 service/signature/data.go create mode 100644 service/signature/options.go create mode 100644 service/signature/request.go create mode 100644 service/signature/util.go diff --git a/service/signature/data.go b/service/signature/data.go new file mode 100644 index 00000000..f42b06f2 --- /dev/null +++ b/service/signature/data.go @@ -0,0 +1,85 @@ +package signature + +import ( + "crypto/ecdsa" + + crypto "github.com/nspcc-dev/neofs-crypto" +) + +type DataSource interface { + ReadSignedData([]byte) ([]byte, error) + SignedDataLength() int +} + +type DataWithSignature interface { + DataSource + GetSignatureWithKey() (key, sig []byte) + SetSignatureWithKey(key, sig []byte) +} + +type SignOption func(*cfg) + +type KeySignatureHandler func(key []byte, sig []byte) + +type KeySignatureSource func() (key, sig []byte) + +func DataSignature(key *ecdsa.PrivateKey, src DataSource, opts ...SignOption) ([]byte, error) { + if key == nil { + return nil, crypto.ErrEmptyPrivateKey + } + + data, err := dataForSignature(src) + if err != nil { + return nil, err + } + defer bytesPool.Put(data) + + cfg := defaultCfg() + + for i := range opts { + opts[i](cfg) + } + + return cfg.signFunc(key, data) +} + +func SignDataWithHandler(key *ecdsa.PrivateKey, src DataSource, handler KeySignatureHandler, opts ...SignOption) error { + sig, err := DataSignature(key, src, opts...) + if err != nil { + return err + } + + handler(crypto.MarshalPublicKey(&key.PublicKey), sig) + + return nil +} + +func VerifyDataWithSource(dataSrc DataSource, sigSrc KeySignatureSource, opts ...SignOption) error { + data, err := dataForSignature(dataSrc) + if err != nil { + return err + } + defer bytesPool.Put(data) + + cfg := defaultCfg() + + for i := range opts { + opts[i](cfg) + } + + key, sig := sigSrc() + + return cfg.verifyFunc( + crypto.UnmarshalPublicKey(key), + data, + sig, + ) +} + +func SignData(key *ecdsa.PrivateKey, v DataWithSignature, opts ...SignOption) error { + return SignDataWithHandler(key, v, v.SetSignatureWithKey, opts...) +} + +func VerifyData(src DataWithSignature, opts ...SignOption) error { + return VerifyDataWithSource(src, src.GetSignatureWithKey, opts...) +} diff --git a/service/signature/options.go b/service/signature/options.go new file mode 100644 index 00000000..16d63304 --- /dev/null +++ b/service/signature/options.go @@ -0,0 +1,26 @@ +package signature + +import ( + "crypto/ecdsa" + + crypto "github.com/nspcc-dev/neofs-crypto" +) + +type cfg struct { + signFunc func(key *ecdsa.PrivateKey, msg []byte) ([]byte, error) + verifyFunc func(key *ecdsa.PublicKey, msg []byte, sig []byte) error +} + +func defaultCfg() *cfg { + return &cfg{ + signFunc: crypto.Sign, + verifyFunc: crypto.Verify, + } +} + +func SignWithRFC6979() SignOption { + return func(c *cfg) { + c.signFunc = crypto.SignRFC6979 + c.verifyFunc = crypto.VerifyRFC6979 + } +} diff --git a/service/signature/request.go b/service/signature/request.go new file mode 100644 index 00000000..9dd34a96 --- /dev/null +++ b/service/signature/request.go @@ -0,0 +1,64 @@ +package signature + +import ( + "crypto/ecdsa" + + "github.com/pkg/errors" +) + +type SignedRequest interface { + Body() DataSource + MetaHeader() 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.Body(), src.SetBodySignatureWithKey); err != nil { + return errors.Wrap(err, "could not sign body") + } + + // sign meta + if err := SignDataWithHandler(key, src.Body(), 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.Body(), src.BodySignatureWithKey); err != nil { + return errors.Wrap(err, "could not verify body") + } + + // verify meta header + if err := VerifyDataWithSource(src.MetaHeader(), 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/service/signature/util.go b/service/signature/util.go new file mode 100644 index 00000000..c86fd00a --- /dev/null +++ b/service/signature/util.go @@ -0,0 +1,31 @@ +package signature + +import ( + "sync" + + "github.com/pkg/errors" +) + +var bytesPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 5<<20) + }, +} + +func dataForSignature(src DataSource) ([]byte, error) { + if src == nil { + return nil, errors.New("nil source") + } + + buf := bytesPool.Get().([]byte) + + if size := src.SignedDataLength(); size < 0 { + return nil, errors.New("negative length") + } else if size <= cap(buf) { + buf = buf[:size] + } else { + buf = make([]byte, size) + } + + return src.ReadSignedData(buf) +}