package session import ( "crypto/ecdsa" "encoding/binary" "sync" "github.com/nspcc-dev/neofs-api/chain" "github.com/nspcc-dev/neofs-api/internal" "github.com/nspcc-dev/neofs-api/refs" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/pkg/errors" ) type ( // ObjectID type alias. ObjectID = refs.ObjectID // OwnerID type alias. OwnerID = refs.OwnerID // TokenID type alias. TokenID = refs.UUID // PToken is a wrapper around Token that allows to sign data // and to do thread-safe manipulations. PToken struct { Token mtx *sync.Mutex PrivateKey *ecdsa.PrivateKey } ) const ( // ErrWrongFirstEpoch is raised when passed Token contains wrong first epoch. // First epoch is an epoch since token is valid ErrWrongFirstEpoch = internal.Error("wrong first epoch") // ErrWrongLastEpoch is raised when passed Token contains wrong last epoch. // Last epoch is an epoch until token is valid ErrWrongLastEpoch = internal.Error("wrong last epoch") // ErrWrongOwner is raised when passed Token contains wrong OwnerID. ErrWrongOwner = internal.Error("wrong owner") // ErrEmptyPublicKey is raised when passed Token contains wrong public key. ErrEmptyPublicKey = internal.Error("empty public key") // ErrWrongObjectsCount is raised when passed Token contains wrong objects count. ErrWrongObjectsCount = internal.Error("wrong objects count") // ErrWrongObjects is raised when passed Token contains wrong object ids. ErrWrongObjects = internal.Error("wrong objects") // ErrInvalidSignature is raised when wrong signature is passed to VerificationHeader.VerifyData(). ErrInvalidSignature = internal.Error("invalid signature") ) // verificationData returns byte array to sign. // Note: protobuf serialization is inconsistent as // wire order is unspecified. func (m *Token) verificationData() (data []byte) { var size int if l := len(m.ObjectID); l > 0 { size = m.ObjectID[0].Size() data = make([]byte, 16+l*size) } else { data = make([]byte, 16) } binary.BigEndian.PutUint64(data, m.FirstEpoch) binary.BigEndian.PutUint64(data[8:], m.LastEpoch) for i := range m.ObjectID { copy(data[16+i*size:], m.ObjectID[i].Bytes()) } return } // IsSame checks if the passed token is valid and equal to current token func (m *Token) IsSame(t *Token) error { switch { case m.FirstEpoch != t.FirstEpoch: return ErrWrongFirstEpoch case m.LastEpoch != t.LastEpoch: return ErrWrongLastEpoch case !m.OwnerID.Equal(t.OwnerID): return ErrWrongOwner case m.Header.PublicKey == nil: return ErrEmptyPublicKey case len(m.ObjectID) != len(t.ObjectID): return ErrWrongObjectsCount default: for i := range m.ObjectID { if !m.ObjectID[i].Equal(t.ObjectID[i]) { return errors.Wrapf(ErrWrongObjects, "expect %s, actual: %s", m.ObjectID[i], t.ObjectID[i]) } } } return nil } // Sign tries to sign current Token data and stores signature inside it. func (m *Token) Sign(key *ecdsa.PrivateKey) error { if err := m.Header.Sign(key); err != nil { return err } s, err := crypto.Sign(key, m.verificationData()) if err != nil { return err } m.Signature = s return nil } // SetPublicKeys sets owner's public keys to the token func (m *Token) SetPublicKeys(keys ...*ecdsa.PublicKey) { m.PublicKeys = m.PublicKeys[:0] for i := range keys { m.PublicKeys = append(m.PublicKeys, crypto.MarshalPublicKey(keys[i])) } } // Verify checks if token is correct and signed. func (m *Token) Verify(keys ...*ecdsa.PublicKey) bool { if m.FirstEpoch > m.LastEpoch { return false } ownerFromKeys := chain.KeysToAddress(keys...) if m.OwnerID.String() != ownerFromKeys { return false } for i := range keys { if m.Header.Verify(keys[i]) && crypto.Verify(keys[i], m.verificationData(), m.Signature) == nil { return true } } return false } // AddSignatures adds token signatures. func (t *PToken) AddSignatures(signH, signT []byte) { t.mtx.Lock() t.Header.KeySignature = signH t.Signature = signT t.mtx.Unlock() } // SignData signs data with session private key. func (t *PToken) SignData(data []byte) ([]byte, error) { return crypto.Sign(t.PrivateKey, data) } // VerifyData checks if signature of data by token is equal to sign. func (m *VerificationHeader) VerifyData(data, sign []byte) error { if crypto.Verify(crypto.UnmarshalPublicKey(m.PublicKey), data, sign) != nil { return ErrInvalidSignature } return nil } // Verify checks if verification header was issued by id. func (m *VerificationHeader) Verify(keys ...*ecdsa.PublicKey) bool { for i := range keys { if crypto.Verify(keys[i], m.PublicKey, m.KeySignature) == nil { return true } } return false } // UnmarshalPublicKeys returns unmarshal public keys from the token func UnmarshalPublicKeys(t *Token) []*ecdsa.PublicKey { r := make([]*ecdsa.PublicKey, 0, len(t.PublicKeys)) for i := range t.PublicKeys { r = append(r, crypto.UnmarshalPublicKey(t.PublicKeys[i])) } return r }