forked from TrueCloudLab/frostfs-api-go
Merge pull request #72 from nspcc-dev/session-refactor
Session package refactoring
This commit is contained in:
commit
8cbdb9183f
7 changed files with 181 additions and 151 deletions
15
session/alias.go
Normal file
15
session/alias.go
Normal 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
38
session/private.go
Normal 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
33
session/private_test.go
Normal 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,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -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}}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue