frostfs-node/pkg/services/util/sign.go

77 lines
2 KiB
Go
Raw Normal View History

package util
import (
"crypto/ecdsa"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/session"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/signature"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
)
type RequestMessage interface {
GetMetaHeader() *session.RequestMetaHeader
}
// ResponseMessage is an interface of FrostFS response message.
type ResponseMessage interface {
GetMetaHeader() *session.ResponseMetaHeader
SetMetaHeader(*session.ResponseMetaHeader)
}
type SignService struct {
key *ecdsa.PrivateKey
}
var ErrAbortStream = errors.New("abort message stream")
func NewUnarySignService(key *ecdsa.PrivateKey) *SignService {
return &SignService{
key: key,
}
}
// SignResponse response with private key via signature.SignServiceMessage.
// The signature error affects the result depending on the protocol version:
// - if status return is supported, panics since we cannot return the failed status, because it will not be signed.
// - otherwise, returns error in order to transport it directly.
func (s *SignService) SignResponse(resp ResponseMessage, err error) error {
if err != nil {
setStatusV2(resp, err)
}
err = signature.SignServiceMessage(s.key, resp)
if err != nil {
return fmt.Errorf("could not sign response: %w", err)
}
return nil
}
func (s *SignService) VerifyRequest(req RequestMessage) error {
if err := signature.VerifyServiceMessage(req); err != nil {
sigErr := new(apistatus.SignatureVerification)
sigErr.SetMessage(err.Error())
return sigErr
}
return nil
}
// EnsureNonNilResponse creates an appropriate response struct if it is nil.
func EnsureNonNilResponse[T any](resp *T, err error) (*T, error) {
if resp != nil {
return resp, err
}
return new(T), err
}
func setStatusV2(resp ResponseMessage, err error) {
// unwrap error
for e := errors.Unwrap(err); e != nil; e = errors.Unwrap(err) {
err = e
}
session.SetStatus(resp, apistatus.ToStatusV2(apistatus.ErrToStatus(err)))
}