2021-05-19 16:14:00 +00:00
|
|
|
package container
|
|
|
|
|
|
|
|
import (
|
2021-05-27 12:07:39 +00:00
|
|
|
"bytes"
|
2021-05-25 09:09:44 +00:00
|
|
|
"crypto/ecdsa"
|
2021-05-27 12:07:39 +00:00
|
|
|
"errors"
|
2021-05-19 16:14:00 +00:00
|
|
|
"fmt"
|
|
|
|
|
2022-01-31 11:04:59 +00:00
|
|
|
"github.com/nspcc-dev/neofs-node/pkg/morph/client/neofsid"
|
2021-11-10 07:08:33 +00:00
|
|
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
2022-05-18 15:20:08 +00:00
|
|
|
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
|
|
|
|
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
|
2021-11-10 07:08:33 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
2022-05-17 13:59:46 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
2021-05-19 16:14:00 +00:00
|
|
|
)
|
|
|
|
|
2021-05-28 12:39:27 +00:00
|
|
|
var (
|
2022-05-18 15:20:08 +00:00
|
|
|
errWrongSessionVerb = errors.New("wrong token verb")
|
|
|
|
errWrongCID = errors.New("wrong container ID")
|
2021-05-28 12:39:27 +00:00
|
|
|
)
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
type signatureVerificationData struct {
|
|
|
|
ownerContainer user.ID
|
2021-05-19 16:14:00 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
verb session.ContainerVerb
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
idContainerSet bool
|
|
|
|
idContainer cid.ID
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
binTokenSession []byte
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
binPublicKey []byte
|
|
|
|
|
|
|
|
signature []byte
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
signedData []byte
|
2021-05-27 12:07:39 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
// verifySignature is a common method of Container service authentication. Asserts that:
|
|
|
|
// - for trusted parties: session is valid (*) and issued by container owner
|
|
|
|
// - operation data is signed by container owner or trusted party
|
|
|
|
// - operation data signature is correct
|
|
|
|
//
|
|
|
|
// (*) includes:
|
|
|
|
// - session token decodes correctly
|
|
|
|
// - signature is valid
|
|
|
|
// - session issued by the container owner
|
|
|
|
// - v.binPublicKey is a public session key
|
|
|
|
// - session context corresponds to the container and verb in v
|
|
|
|
// - session is "alive"
|
|
|
|
func (cp *Processor) verifySignature(v signatureVerificationData) error {
|
|
|
|
var err error
|
|
|
|
var key neofsecdsa.PublicKeyRFC6979
|
|
|
|
keyProvided := v.binPublicKey != nil
|
|
|
|
withSession := len(v.binTokenSession) > 0
|
|
|
|
|
|
|
|
if keyProvided {
|
|
|
|
err = key.Decode(v.binPublicKey)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("decode public key: %w", err)
|
2021-05-27 12:07:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if withSession {
|
|
|
|
var tok session.Container
|
2021-05-19 16:14:00 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
err = tok.Unmarshal(v.binTokenSession)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("decode session token: %w", err)
|
2021-05-19 16:14:00 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if !tok.VerifySignature() {
|
|
|
|
return errors.New("invalid session token signature")
|
|
|
|
}
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
// FIXME(@cthulhu-rider): #1387 check token is signed by container owner, see neofs-sdk-go#233
|
|
|
|
// We'll get container owner's keys which is needed below, so it's worth to cache them
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if keyProvided && !tok.AssertAuthKey(&key) {
|
|
|
|
return errors.New("signed with a non-session key")
|
|
|
|
}
|
2022-05-17 13:59:46 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if !tok.AssertVerb(v.verb) {
|
|
|
|
return errWrongSessionVerb
|
|
|
|
}
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if v.idContainerSet && !tok.AppliedTo(v.idContainer) {
|
|
|
|
return errWrongCID
|
|
|
|
}
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if !session.IssuedBy(tok, v.ownerContainer) {
|
|
|
|
return errors.New("owner differs with token owner")
|
|
|
|
}
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
err = cp.checkTokenLifetime(tok)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("check session lifetime: %w", err)
|
|
|
|
}
|
2021-05-27 12:07:39 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
var verificationKeys []neofscrypto.PublicKey
|
2021-06-04 15:02:25 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if keyProvided {
|
|
|
|
if withSession {
|
|
|
|
verificationKeys = []neofscrypto.PublicKey{&key}
|
|
|
|
} else {
|
|
|
|
var idFromKey user.ID
|
|
|
|
user.IDFromKey(&idFromKey, (ecdsa.PublicKey)(key))
|
2021-05-27 12:07:39 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if v.ownerContainer.Equals(idFromKey) {
|
|
|
|
verificationKeys = []neofscrypto.PublicKey{&key}
|
|
|
|
}
|
|
|
|
}
|
2021-05-27 12:07:39 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if verificationKeys == nil {
|
|
|
|
var prm neofsid.AccountKeysPrm
|
|
|
|
prm.SetID(&v.ownerContainer)
|
2021-05-28 12:39:27 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
ownerKeys, err := cp.idClient.AccountKeys(prm)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("receive owner keys %s: %w", v.ownerContainer, err)
|
|
|
|
}
|
2021-05-28 12:39:27 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if !keyProvided {
|
|
|
|
verificationKeys = make([]neofscrypto.PublicKey, 0, len(ownerKeys))
|
|
|
|
}
|
2021-05-28 12:39:27 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
for i := range ownerKeys {
|
|
|
|
if keyProvided {
|
|
|
|
// TODO(@cthulhu-rider): keys have been decoded in order to encode only, should be optimized by #1387
|
|
|
|
if bytes.Equal(ownerKeys[i].Bytes(), v.binPublicKey) {
|
|
|
|
verificationKeys = []neofscrypto.PublicKey{(*neofsecdsa.PublicKeyRFC6979)(ownerKeys[i])}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
verificationKeys = append(verificationKeys, (*neofsecdsa.PublicKeyRFC6979)(ownerKeys[i]))
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 12:39:27 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if len(verificationKeys) == 0 {
|
|
|
|
return errors.New("key is not a container owner's key")
|
2021-05-28 12:39:27 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
for i := range verificationKeys {
|
|
|
|
if verificationKeys[i].Verify(v.signedData, v.signature) {
|
|
|
|
return nil
|
|
|
|
}
|
2021-05-28 12:39:27 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
return errors.New("invalid signature")
|
2021-05-28 12:39:27 +00:00
|
|
|
}
|
2021-06-04 15:02:25 +00:00
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
func (cp *Processor) checkTokenLifetime(token session.Container) error {
|
2021-06-04 15:02:25 +00:00
|
|
|
curEpoch, err := cp.netState.Epoch()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not read current epoch: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if token.ExpiredAt(curEpoch) {
|
|
|
|
return fmt.Errorf("token is expired at %d", curEpoch)
|
2021-06-04 15:02:25 +00:00
|
|
|
}
|
|
|
|
|
2022-05-18 15:20:08 +00:00
|
|
|
if token.InvalidAt(curEpoch) {
|
|
|
|
return fmt.Errorf("token is not valid at %d", curEpoch)
|
2021-06-04 15:02:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|