[#1423] session: Upgrade SDK package

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-05-18 18:20:08 +03:00 committed by LeL
parent dda56f1319
commit 4c8ec20e32
41 changed files with 740 additions and 663 deletions

View file

@ -3,184 +3,167 @@ package container
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/neofsid"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
var (
errWrongSessionContext = errors.New("wrong session context")
errWrongSessionVerb = errors.New("wrong token verb")
errWrongCID = errors.New("wrong container ID")
errWrongSessionVerb = errors.New("wrong token verb")
errWrongCID = errors.New("wrong container ID")
)
type ownerIDSource interface {
OwnerID() *user.ID
type signatureVerificationData struct {
ownerContainer user.ID
verb session.ContainerVerb
idContainerSet bool
idContainer cid.ID
binTokenSession []byte
binPublicKey []byte
signature []byte
signedData []byte
}
func tokenFromEvent(src interface {
SessionToken() []byte
}) (*session.Token, error) {
binToken := src.SessionToken()
// 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 len(binToken) == 0 {
return nil, nil
}
tok := session.NewToken()
err := tok.Unmarshal(binToken)
if err != nil {
return nil, fmt.Errorf("could not unmarshal session token: %w", err)
}
return tok, nil
}
func (cp *Processor) checkKeyOwnership(ownerIDSrc ownerIDSource, key *keys.PublicKey) error {
if tokenSrc, ok := ownerIDSrc.(interface {
SessionToken() *session.Token
}); ok {
if token := tokenSrc.SessionToken(); token != nil {
return cp.checkKeyOwnershipWithToken(ownerIDSrc, key, tokenSrc.SessionToken())
if keyProvided {
err = key.Decode(v.binPublicKey)
if err != nil {
return fmt.Errorf("decode public key: %w", err)
}
}
ownerSrc := ownerIDSrc.OwnerID()
if ownerSrc == nil {
return errors.New("missing owner")
if withSession {
var tok session.Container
err = tok.Unmarshal(v.binTokenSession)
if err != nil {
return fmt.Errorf("decode session token: %w", err)
}
if !tok.VerifySignature() {
return errors.New("invalid session token signature")
}
// 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
if keyProvided && !tok.AssertAuthKey(&key) {
return errors.New("signed with a non-session key")
}
if !tok.AssertVerb(v.verb) {
return errWrongSessionVerb
}
if v.idContainerSet && !tok.AppliedTo(v.idContainer) {
return errWrongCID
}
if !session.IssuedBy(tok, v.ownerContainer) {
return errors.New("owner differs with token owner")
}
err = cp.checkTokenLifetime(tok)
if err != nil {
return fmt.Errorf("check session lifetime: %w", err)
}
}
var ownerKey user.ID
user.IDFromKey(&ownerKey, (ecdsa.PublicKey)(*key))
var verificationKeys []neofscrypto.PublicKey
if ownerSrc.Equals(ownerKey) {
return nil
if keyProvided {
if withSession {
verificationKeys = []neofscrypto.PublicKey{&key}
} else {
var idFromKey user.ID
user.IDFromKey(&idFromKey, (ecdsa.PublicKey)(key))
if v.ownerContainer.Equals(idFromKey) {
verificationKeys = []neofscrypto.PublicKey{&key}
}
}
}
prm := neofsid.AccountKeysPrm{}
prm.SetID(ownerIDSrc.OwnerID())
if verificationKeys == nil {
var prm neofsid.AccountKeysPrm
prm.SetID(&v.ownerContainer)
ownerKeys, err := cp.idClient.AccountKeys(prm)
if err != nil {
return fmt.Errorf("could not received owner keys %s: %w", ownerIDSrc.OwnerID(), err)
ownerKeys, err := cp.idClient.AccountKeys(prm)
if err != nil {
return fmt.Errorf("receive owner keys %s: %w", v.ownerContainer, err)
}
if !keyProvided {
verificationKeys = make([]neofscrypto.PublicKey, 0, len(ownerKeys))
}
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]))
}
}
}
for _, ownerKey := range ownerKeys {
if ownerKey.Equal(key) {
if len(verificationKeys) == 0 {
return errors.New("key is not a container owner's key")
}
for i := range verificationKeys {
if verificationKeys[i].Verify(v.signedData, v.signature) {
return nil
}
}
return fmt.Errorf("key %s is not tied to the owner of the container", key)
return errors.New("invalid signature")
}
func (cp *Processor) checkKeyOwnershipWithToken(ownerIDSrc ownerIDSource, key *keys.PublicKey, token *session.Token) error {
// check session key
if !bytes.Equal(
key.Bytes(),
token.SessionKey(),
) {
return errors.New("signed with a non-session key")
}
ownerToken, ownerSrc := token.OwnerID(), ownerIDSrc.OwnerID()
// check owner
if ownerToken == nil || ownerSrc == nil || !ownerToken.Equals(*ownerSrc) {
return errors.New("owner differs with token owner")
}
err := cp.checkSessionToken(token)
if err != nil {
return fmt.Errorf("invalid session token: %w", err)
}
return nil
}
func (cp *Processor) checkSessionToken(token *session.Token) error {
// verify signature
if !token.VerifySignature() {
return errors.New("invalid signature")
}
// check lifetime
err := cp.checkTokenLifetime(token)
if err != nil {
return err
}
// check token owner's key ownership
// FIXME(@cthulhu-rider): #1387 see neofs-sdk-go#233
key, err := keys.NewPublicKeyFromBytes(token.ToV2().GetSignature().GetKey(), elliptic.P256())
if err != nil {
return fmt.Errorf("invalid key: %w", err)
}
return cp.checkKeyOwnership(token, key)
}
type verbAssert func(*session.ContainerContext) bool
func contextWithVerifiedVerb(tok *session.Token, verbAssert verbAssert) (*session.ContainerContext, error) {
c := session.GetContainerContext(tok)
if c == nil {
return nil, errWrongSessionContext
}
if !verbAssert(c) {
return nil, errWrongSessionVerb
}
return c, nil
}
func checkTokenContext(tok *session.Token, verbAssert verbAssert) error {
_, err := contextWithVerifiedVerb(tok, verbAssert)
return err
}
func checkTokenContextWithCID(tok *session.Token, id cid.ID, verbAssert verbAssert) error {
c, err := contextWithVerifiedVerb(tok, verbAssert)
if err != nil {
return err
}
tokCID := c.Container()
if tokCID != nil && !tokCID.Equals(id) {
return errWrongCID
}
return nil
}
func (cp *Processor) checkTokenLifetime(token *session.Token) error {
func (cp *Processor) checkTokenLifetime(token session.Container) error {
curEpoch, err := cp.netState.Epoch()
if err != nil {
return fmt.Errorf("could not read current epoch: %w", err)
}
nbf := token.Nbf()
if curEpoch < nbf {
return fmt.Errorf("token is not valid yet: nbf %d, cur %d", nbf, curEpoch)
if token.ExpiredAt(curEpoch) {
return fmt.Errorf("token is expired at %d", curEpoch)
}
iat := token.Iat()
if curEpoch < iat {
return fmt.Errorf("token is issued in future: iat %d, cur %d", iat, curEpoch)
}
exp := token.Exp()
if curEpoch >= exp {
return fmt.Errorf("token is expired: exp %d, cur %d", exp, curEpoch)
if token.InvalidAt(curEpoch) {
return fmt.Errorf("token is not valid at %d", curEpoch)
}
return nil