forked from TrueCloudLab/frostfs-api-go
service: Add updated Sign/Verify mechanism
This commit is contained in:
parent
ea7c6a22da
commit
297212886d
4 changed files with 206 additions and 0 deletions
85
service/signature/data.go
Normal file
85
service/signature/data.go
Normal file
|
@ -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...)
|
||||||
|
}
|
26
service/signature/options.go
Normal file
26
service/signature/options.go
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
64
service/signature/request.go
Normal file
64
service/signature/request.go
Normal file
|
@ -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
|
||||||
|
}
|
31
service/signature/util.go
Normal file
31
service/signature/util.go
Normal file
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in a new issue