frostfs-sdk-go/object/fmt.go
Evgenii Stratonikov 1c7dd03cf5 [#150] signature: Add scheme
Allow `SignOption` to set 2 parameters:
1. Default signature scheme, which is used in case scheme is
   unspecified.
2. Restrict scheme option which also checks that scheme is either
   unspecified or equal to the restricted scheme. This is only used
   for verification and is necessary because some of the signatures
   are used in smart-contracts.

Also provide signature struct to sign/verify functions in helpers.

The constant names differ a bit from those in API because of linter
complaints.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2022-02-25 12:42:38 +03:00

171 lines
3.9 KiB
Go

package object
import (
"crypto/ecdsa"
"crypto/sha256"
"errors"
"fmt"
signatureV2 "github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/signature"
sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature"
)
var errCheckSumMismatch = errors.New("payload checksum mismatch")
var errIncorrectID = errors.New("incorrect object identifier")
// CalculatePayloadChecksum calculates and returns checksum of
// object payload bytes.
func CalculatePayloadChecksum(payload []byte) *checksum.Checksum {
res := checksum.New()
res.SetSHA256(sha256.Sum256(payload))
return res
}
// CalculateAndSetPayloadChecksum calculates checksum of current
// object payload and writes it to the object.
func CalculateAndSetPayloadChecksum(obj *RawObject) {
obj.SetPayloadChecksum(
CalculatePayloadChecksum(obj.Payload()),
)
}
// VerifyPayloadChecksum checks if payload checksum in the object
// corresponds to its payload.
func VerifyPayloadChecksum(obj *Object) error {
actual := CalculatePayloadChecksum(obj.Payload())
if !checksum.Equal(obj.PayloadChecksum(), actual) {
return errCheckSumMismatch
}
return nil
}
// CalculateID calculates identifier for the object.
func CalculateID(obj *Object) (*oid.ID, error) {
data, err := obj.ToV2().GetHeader().StableMarshal(nil)
if err != nil {
return nil, err
}
id := oid.NewID()
id.SetSHA256(sha256.Sum256(data))
return id, nil
}
// CalculateAndSetID calculates identifier for the object
// and writes the result to it.
func CalculateAndSetID(obj *RawObject) error {
id, err := CalculateID(obj.Object())
if err != nil {
return err
}
obj.SetID(id)
return nil
}
// VerifyID checks if identifier in the object corresponds to
// its structure.
func VerifyID(obj *Object) error {
id, err := CalculateID(obj)
if err != nil {
return err
}
if !id.Equal(obj.ID()) {
return errIncorrectID
}
return nil
}
func CalculateIDSignature(key *ecdsa.PrivateKey, id *oid.ID) (*signature.Signature, error) {
sig := signature.New()
if err := sigutil.SignDataWithHandler(
key,
signatureV2.StableMarshalerWrapper{
SM: id.ToV2(),
},
func(s *signature.Signature) {
*sig = *s
},
); err != nil {
return nil, err
}
return sig, nil
}
func CalculateAndSetSignature(key *ecdsa.PrivateKey, obj *RawObject) error {
sig, err := CalculateIDSignature(key, obj.ID())
if err != nil {
return err
}
obj.SetSignature(sig)
return nil
}
func VerifyIDSignature(obj *Object) error {
return sigutil.VerifyDataWithSource(
signatureV2.StableMarshalerWrapper{
SM: obj.ID().ToV2(),
},
obj.Signature,
)
}
// SetIDWithSignature sets object identifier and signature.
func SetIDWithSignature(key *ecdsa.PrivateKey, obj *RawObject) error {
if err := CalculateAndSetID(obj); err != nil {
return fmt.Errorf("could not set identifier: %w", err)
}
if err := CalculateAndSetSignature(key, obj); err != nil {
return fmt.Errorf("could not set signature: %w", err)
}
return nil
}
// SetVerificationFields calculates and sets all verification fields of the object.
func SetVerificationFields(key *ecdsa.PrivateKey, obj *RawObject) error {
CalculateAndSetPayloadChecksum(obj)
return SetIDWithSignature(key, obj)
}
// CheckVerificationFields checks all verification fields of the object.
func CheckVerificationFields(obj *Object) error {
if err := CheckHeaderVerificationFields(obj); err != nil {
return fmt.Errorf("invalid header structure: %w", err)
}
if err := VerifyPayloadChecksum(obj); err != nil {
return fmt.Errorf("invalid payload checksum: %w", err)
}
return nil
}
// CheckHeaderVerificationFields checks all verification fields except payload.
func CheckHeaderVerificationFields(obj *Object) error {
if err := VerifyIDSignature(obj); err != nil {
return fmt.Errorf("invalid signature: %w", err)
}
if err := VerifyID(obj); err != nil {
return fmt.Errorf("invalid identifier: %w", err)
}
return nil
}