Merge pull request #72 from nspcc-dev/session-refactor

Session package refactoring
This commit is contained in:
Leonard Lyubich 2020-04-29 13:14:17 +03:00 committed by GitHub
commit 8cbdb9183f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 181 additions and 151 deletions

15
session/alias.go Normal file
View file

@ -0,0 +1,15 @@
package session
import (
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-api-go/service"
)
// OwnerID is a type alias of OwnerID ref.
type OwnerID = refs.OwnerID
// TokenID is a type alias of TokenID ref.
type TokenID = service.TokenID
// Token is a type alias of Token.
type Token = service.Token

38
session/private.go Normal file
View file

@ -0,0 +1,38 @@
package session
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
crypto "github.com/nspcc-dev/neofs-crypto"
)
type pToken struct {
// private session token
sessionKey *ecdsa.PrivateKey
}
// NewPrivateToken creates PrivateToken instance.
//
// Returns non-nil error on key generation error.
func NewPrivateToken() (PrivateToken, error) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
return &pToken{
sessionKey: sk,
}, nil
}
// Sign signs data with session private key.
func (t *pToken) Sign(data []byte) ([]byte, error) {
return crypto.Sign(t.sessionKey, data)
}
// PublicKey returns a binary representation of the session public key.
func (t *pToken) PublicKey() []byte {
return crypto.MarshalPublicKey(&t.sessionKey.PublicKey)
}

33
session/private_test.go Normal file
View file

@ -0,0 +1,33 @@
package session
import (
"crypto/rand"
"testing"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/stretchr/testify/require"
)
func TestPrivateToken(t *testing.T) {
// create new private token
pToken, err := NewPrivateToken()
require.NoError(t, err)
// generate data to sign
data := make([]byte, 10)
_, err = rand.Read(data)
require.NoError(t, err)
// sign data via private token
sig, err := pToken.Sign(data)
require.NoError(t, err)
// check signature
require.NoError(t,
crypto.Verify(
crypto.UnmarshalPublicKey(pToken.PublicKey()),
data,
sig,
),
)
}

View file

@ -1,41 +1,5 @@
package session package session
import (
"context"
"crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/refs"
)
type (
// KeyStore is an interface that describes storage,
// that allows to fetch public keys by OwnerID.
KeyStore interface {
Get(ctx context.Context, id refs.OwnerID) ([]*ecdsa.PublicKey, error)
}
// TokenStore is a PToken storage manipulation interface.
TokenStore interface {
// New returns new token with specified parameters.
New(p TokenParams) *PToken
// Fetch tries to fetch a token with specified id.
Fetch(id TokenID) *PToken
// Remove removes token with id from store.
Remove(id TokenID)
}
// TokenParams contains params to create new PToken.
TokenParams struct {
FirstEpoch uint64
LastEpoch uint64
Address Address
OwnerID OwnerID
Verb Verb
}
)
// NewInitRequest returns new initialization CreateRequest from passed Token. // NewInitRequest returns new initialization CreateRequest from passed Token.
func NewInitRequest(t *Token) *CreateRequest { func NewInitRequest(t *Token) *CreateRequest {
return &CreateRequest{Message: &CreateRequest_Init{Init: t}} return &CreateRequest{Message: &CreateRequest_Init{Init: t}}

View file

@ -1,83 +1,47 @@
package session package session
import ( import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"sync" "sync"
"github.com/nspcc-dev/neofs-api-go/refs"
crypto "github.com/nspcc-dev/neofs-crypto"
) )
type simpleStore struct { type mapTokenStore struct {
*sync.RWMutex *sync.RWMutex
tokens map[TokenID]*PToken tokens map[TokenID]PrivateToken
} }
// TODO get curve from neofs-crypto // NewMapTokenStore creates new PrivateTokenStore instance.
func defaultCurve() elliptic.Curve { //
return elliptic.P256() // The elements of the instance are stored in the map.
} func NewMapTokenStore() PrivateTokenStore {
return &mapTokenStore{
// NewSimpleStore creates simple token storage
func NewSimpleStore() TokenStore {
return &simpleStore{
RWMutex: new(sync.RWMutex), RWMutex: new(sync.RWMutex),
tokens: make(map[TokenID]*PToken), tokens: make(map[TokenID]PrivateToken),
} }
} }
// New returns new token with specified parameters. // Store adds passed token to the map.
func (s *simpleStore) New(p TokenParams) *PToken { //
tid, err := refs.NewUUID() // Resulting error is always nil.
if err != nil { func (s *mapTokenStore) Store(id TokenID, token PrivateToken) error {
return nil
}
key, err := ecdsa.GenerateKey(defaultCurve(), rand.Reader)
if err != nil {
return nil
}
if p.FirstEpoch > p.LastEpoch || p.OwnerID.Empty() {
return nil
}
token := new(Token)
token.SetID(tid)
token.SetOwnerID(p.OwnerID)
token.SetVerb(p.Verb)
token.SetAddress(p.Address)
token.SetCreationEpoch(p.FirstEpoch)
token.SetExpirationEpoch(p.LastEpoch)
token.SetSessionKey(crypto.MarshalPublicKey(&key.PublicKey))
t := &PToken{
mtx: new(sync.Mutex),
Token: *token,
PrivateKey: key,
}
s.Lock() s.Lock()
s.tokens[tid] = t s.tokens[id] = token
s.Unlock() s.Unlock()
return t return nil
} }
// Fetch tries to fetch a token with specified id. // Fetch returns the map element corresponding to the given key.
func (s *simpleStore) Fetch(id TokenID) *PToken { //
// Returns ErrPrivateTokenNotFound is there is no element in map.
func (s *mapTokenStore) Fetch(id TokenID) (PrivateToken, error) {
s.RLock() s.RLock()
defer s.RUnlock() defer s.RUnlock()
return s.tokens[id] t, ok := s.tokens[id]
if !ok {
return nil, ErrPrivateTokenNotFound
} }
// Remove removes token with id from store. return t, nil
func (s *simpleStore) Remove(id TokenID) {
s.Lock()
delete(s.tokens, id)
s.Unlock()
} }

View file

@ -1,3 +1,35 @@
package session package session
// TODO: write unit tests import (
"testing"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/stretchr/testify/require"
)
func TestMapTokenStore(t *testing.T) {
// create new private token
pToken, err := NewPrivateToken()
require.NoError(t, err)
// create map token store
s := NewMapTokenStore()
// create new storage key
id, err := refs.NewUUID()
require.NoError(t, err)
// ascertain that there is no record for the key
_, err = s.Fetch(id)
require.EqualError(t, err, ErrPrivateTokenNotFound.Error())
// save private token record
require.NoError(t, s.Store(id, pToken))
// fetch private token by the key
res, err := s.Fetch(id)
require.NoError(t, err)
// ascertain that returned token equals to initial
require.Equal(t, pToken, res)
}

View file

@ -1,65 +1,49 @@
package session package session
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
"sync"
"github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/internal"
"github.com/nspcc-dev/neofs-api-go/refs"
"github.com/nspcc-dev/neofs-api-go/service"
crypto "github.com/nspcc-dev/neofs-crypto"
) )
type ( // PrivateToken is an interface of session private part.
// ObjectID type alias. type PrivateToken interface {
ObjectID = refs.ObjectID // PublicKey must return a binary representation of session public key.
// OwnerID type alias. PublicKey() []byte
OwnerID = refs.OwnerID
// TokenID type alias.
TokenID = refs.UUID
// Token type alias
Token = service.Token
// Address type alias
Address = refs.Address
// Verb is Token_Info_Verb type alias
Verb = service.Token_Info_Verb
// PToken is a wrapper around Token that allows to sign data // Sign must return the signature of passed data.
// and to do thread-safe manipulations. //
PToken struct { // Resulting signature must be verified by crypto.Verify function
Token // with the session public key.
Sign([]byte) ([]byte, error)
mtx *sync.Mutex
PrivateKey *ecdsa.PrivateKey
} }
)
const ( // PrivateTokenSource is an interface of private token storage with read access.
// ErrWrongFirstEpoch is raised when passed Token contains wrong first epoch. type PrivateTokenSource interface {
// First epoch is an epoch since token is valid // Fetch must return the storage record corresponding to the passed key.
ErrWrongFirstEpoch = internal.Error("wrong first epoch") //
// Resulting error must be ErrPrivateTokenNotFound if there is no corresponding record.
// ErrWrongLastEpoch is raised when passed Token contains wrong last epoch. Fetch(TokenID) (PrivateToken, error)
// 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")
)
// SignData signs data with session private key.
func (t *PToken) SignData(data []byte) ([]byte, error) {
return crypto.Sign(t.PrivateKey, data)
} }
// PrivateTokenStore is an interface of the storage of private tokens addressable by TokenID.
type PrivateTokenStore interface {
PrivateTokenSource
// Store must save passed private token in the storage under the given key.
//
// Resulting error must be nil if private token was stored successfully.
Store(TokenID, PrivateToken) error
}
// KeyStore is an interface of the storage of public keys addressable by OwnerID,
type KeyStore interface {
// Get must return the storage record corresponding to the passed key.
//
// Resulting error must be ErrKeyNotFound if there is no corresponding record.
Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error)
}
// ErrPrivateTokenNotFound is raised when addressed private token was not found in storage.
const ErrPrivateTokenNotFound = internal.Error("private token not found")