diff --git a/object/extensions.go b/object/extensions.go index 6e577bd..be755c6 100644 --- a/object/extensions.go +++ b/object/extensions.go @@ -19,21 +19,6 @@ func (m Object) IsLinking() bool { return false } -// VerificationHeader returns verification header if it is presented in extended headers. -func (m Object) VerificationHeader() (*VerificationHeader, error) { - _, vh := m.LastHeader(HeaderType(VerifyHdr)) - if vh == nil { - return nil, ErrHeaderNotFound - } - return vh.Value.(*Header_Verify).Verify, nil -} - -// SetVerificationHeader sets verification header in the object. -// It will replace existing verification header or add a new one. -func (m *Object) SetVerificationHeader(header *VerificationHeader) { - m.SetHeader(&Header{Value: &Header_Verify{Verify: header}}) -} - // Links returns slice of ids of specified link type func (m *Object) Links(t Link_Type) []ID { var res []ID diff --git a/object/service_test.go b/object/service_test.go index 4b02b37..5b7a358 100644 --- a/object/service_test.go +++ b/object/service_test.go @@ -16,8 +16,8 @@ func TestRequest(t *testing.T) { &DeleteRequest{}, &GetRangeRequest{}, &GetRangeHashRequest{}, - MakePutRequestHeader(nil, nil), - MakePutRequestHeader(&Object{}, nil), + MakePutRequestHeader(nil), + MakePutRequestHeader(&Object{}), } types := []RequestType{ diff --git a/object/types.go b/object/types.go index aebb2fc..83b03c7 100644 --- a/object/types.go +++ b/object/types.go @@ -7,7 +7,6 @@ import ( "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/session" ) type ( @@ -19,9 +18,6 @@ type ( // Address is a type alias of object Address. Address = refs.Address - // VerificationHeader is a type alias of session's verification header. - VerificationHeader = session.VerificationHeader - // PositionReader defines object reader that returns slice of bytes // for specified object and data range. PositionReader interface { @@ -60,8 +56,8 @@ const ( TransformHdr // TombstoneHdr is a tombstone header type. TombstoneHdr - // VerifyHdr is a verification header type. - VerifyHdr + // TokenHdr is a token header type. + TokenHdr // HomoHashHdr is a homomorphic hash header type. HomoHashHdr // PayloadChecksumHdr is a payload checksum header type. @@ -175,8 +171,8 @@ func (m Header) typeOf(t isHeader_Value) (ok bool) { _, ok = m.Value.(*Header_Transform) case *Header_Tombstone: _, ok = m.Value.(*Header_Tombstone) - case *Header_Verify: - _, ok = m.Value.(*Header_Verify) + case *Header_Token: + _, ok = m.Value.(*Header_Token) case *Header_HomoHash: _, ok = m.Value.(*Header_HomoHash) case *Header_PayloadChecksum: @@ -205,8 +201,8 @@ func HeaderType(t headerType) Pred { return func(h *Header) bool { _, ok := h.Value.(*Header_Transform); return ok } case TombstoneHdr: return func(h *Header) bool { _, ok := h.Value.(*Header_Tombstone); return ok } - case VerifyHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Verify); return ok } + case TokenHdr: + return func(h *Header) bool { _, ok := h.Value.(*Header_Token); return ok } case HomoHashHdr: return func(h *Header) bool { _, ok := h.Value.(*Header_HomoHash); return ok } case PayloadChecksumHdr: diff --git a/object/utils.go b/object/utils.go index 07f0984..33423aa 100644 --- a/object/utils.go +++ b/object/utils.go @@ -4,7 +4,6 @@ import ( "io" "strconv" - "github.com/nspcc-dev/neofs-api-go/session" "github.com/pkg/errors" ) @@ -46,11 +45,10 @@ func (b ByteSize) String() string { // MakePutRequestHeader combines object and session token value // into header of object put request. -func MakePutRequestHeader(obj *Object, token *session.Token) *PutRequest { +func MakePutRequestHeader(obj *Object) *PutRequest { return &PutRequest{ R: &PutRequest_Header{Header: &PutRequest_PutHeader{ Object: obj, - Token: token, }}, } } diff --git a/object/verification.go b/object/verification.go index a00b30a..5694316 100644 --- a/object/verification.go +++ b/object/verification.go @@ -77,7 +77,7 @@ func (m Object) Verify() error { integrity := ih.Value.(*Header_Integrity).Integrity // Prepare structures - _, vh := m.LastHeader(HeaderType(VerifyHdr)) + _, vh := m.LastHeader(HeaderType(TokenHdr)) if vh == nil { _, pkh := m.LastHeader(HeaderType(PublicKeyHdr)) if pkh == nil { @@ -85,7 +85,7 @@ func (m Object) Verify() error { } pubkey = pkh.Value.(*Header_PublicKey).PublicKey.Value } else { - pubkey = vh.Value.(*Header_Verify).Verify.PublicKey + pubkey = vh.Value.(*Header_Token).Token.SessionKey } // Verify signature diff --git a/object/verification_test.go b/object/verification_test.go index b37ec70..004f969 100644 --- a/object/verification_test.go +++ b/object/verification_test.go @@ -6,7 +6,7 @@ import ( "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/session" + "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" @@ -77,11 +77,13 @@ func TestObject_Verify(t *testing.T) { dataPK := crypto.MarshalPublicKey(&sessionkey.PublicKey) signature, err = crypto.Sign(key, dataPK) - vh := &session.VerificationHeader{ - PublicKey: dataPK, - KeySignature: signature, + tok := &service.Token{ + Token_Info: service.Token_Info{ + SessionKey: dataPK, + }, + Signature: signature, } - obj.SetVerificationHeader(vh) + obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) // validation header is not last t.Run("error validation header is not last", func(t *testing.T) { @@ -90,7 +92,7 @@ func TestObject_Verify(t *testing.T) { }) obj.Headers = obj.Headers[:len(obj.Headers)-2] - obj.SetVerificationHeader(vh) + obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) obj.SetHeader(&Header{Value: &Header_Integrity{ih}}) t.Run("error invalid header checksum", func(t *testing.T) { @@ -115,7 +117,7 @@ func TestObject_Verify(t *testing.T) { require.NoError(t, err) obj.SetHeader(genIH) - t.Run("correct with vh", func(t *testing.T) { + t.Run("correct with tok", func(t *testing.T) { err = obj.Verify() require.NoError(t, err) }) @@ -123,7 +125,7 @@ func TestObject_Verify(t *testing.T) { pkh := Header{Value: &Header_PublicKey{&PublicKey{ Value: crypto.MarshalPublicKey(&key.PublicKey), }}} - // replace vh with pkh + // replace tok with pkh obj.Headers[len(obj.Headers)-2] = pkh // re-sign object obj.Sign(sessionkey) diff --git a/service/alias.go b/service/alias.go new file mode 100644 index 0000000..6c22ece --- /dev/null +++ b/service/alias.go @@ -0,0 +1,14 @@ +package service + +import ( + "github.com/nspcc-dev/neofs-api-go/refs" +) + +// TokenID is type alias of UUID ref. +type TokenID = refs.UUID + +// OwnerID is type alias of OwnerID ref. +type OwnerID = refs.OwnerID + +// Address is type alias of Address ref. +type Address = refs.Address diff --git a/service/verify.go b/service/verify.go index 9687032..ade13ef 100644 --- a/service/verify.go +++ b/service/verify.go @@ -53,18 +53,6 @@ func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_ m.Signatures = append(m.Signatures, sig) } -// SetOwner adds origin (sign and public key) of owner (client) into first signature. -func (m *RequestVerificationHeader) SetOwner(pub *ecdsa.PublicKey, sign []byte) { - if len(m.Signatures) == 0 || pub == nil { - return - } - - m.Signatures[0].Origin = &RequestVerificationHeader_Sign{ - Sign: sign, - Peer: crypto.MarshalPublicKey(pub), - } -} - // CheckOwner validates, that passed OwnerID is equal to present PublicKey of owner. func (m *RequestVerificationHeader) CheckOwner(owner refs.OwnerID) error { if key, err := m.GetOwner(); err != nil { @@ -83,18 +71,6 @@ func (m *RequestVerificationHeader) CheckOwner(owner refs.OwnerID) error { func (m *RequestVerificationHeader) GetOwner() (*ecdsa.PublicKey, error) { if len(m.Signatures) == 0 { return nil, ErrCannotFindOwner - } - - // if first signature contains origin, we should try to validate session key - if m.Signatures[0].Origin != nil { - owner := crypto.UnmarshalPublicKey(m.Signatures[0].Origin.Peer) - if owner == nil { - return nil, ErrCannotLoadPublicKey - } else if err := crypto.Verify(owner, m.Signatures[0].Peer, m.Signatures[0].Origin.Sign); err != nil { - return nil, errors.Wrap(err, "could not verify session token") - } - - return owner, nil } else if key := crypto.UnmarshalPublicKey(m.Signatures[0].Peer); key != nil { return key, nil } @@ -128,10 +104,8 @@ func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeade } return &RequestVerificationHeader_Signature{ - RequestVerificationHeader_Sign: RequestVerificationHeader_Sign{ - Sign: sign, - Peer: crypto.MarshalPublicKey(&key.PublicKey), - }, + Sign: sign, + Peer: crypto.MarshalPublicKey(&key.PublicKey), }, nil } diff --git a/service/verify_test.go b/service/verify_test.go index 27491da..ce333aa 100644 --- a/service/verify_test.go +++ b/service/verify_test.go @@ -119,15 +119,13 @@ func TestMaintainableRequest(t *testing.T) { req.TTL-- key := test.DecodeKey(i) - require.NoError(t, SignRequestHeader(key, req)) // sign first key (session key) by owner key if i == 0 { - sign, err := crypto.Sign(owner, crypto.MarshalPublicKey(&key.PublicKey)) - require.NoError(t, err) - - req.SetOwner(&owner.PublicKey, sign) + key = owner } + + require.NoError(t, SignRequestHeader(key, req)) } { // Validate owner @@ -150,17 +148,8 @@ func TestMaintainableRequest(t *testing.T) { require.Equal(t, &owner.PublicKey, pub) } - { // wrong owner: - req.Signatures[0].Origin = nil - - pub, err := req.GetOwner() - require.NoError(t, err) - - require.NotEqual(t, &owner.PublicKey, pub) - } - { // Wrong signatures: - copy(req.Signatures[count-1].Sign, req.Signatures[count-1].Peer) + copy(req.Signatures[count-1].Sign, req.Signatures[count-2].Sign) err := VerifyRequestHeader(req) require.EqualError(t, errors.Cause(err), crypto.ErrInvalidSignature.Error()) } diff --git a/session/service.go b/session/service.go index 182ff7d..367aeb1 100644 --- a/session/service.go +++ b/session/service.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" ) type ( @@ -31,9 +30,9 @@ type ( TokenParams struct { FirstEpoch uint64 LastEpoch uint64 - ObjectID []ObjectID + Address Address OwnerID OwnerID - PublicKeys [][]byte + Verb Verb } ) @@ -46,13 +45,3 @@ func NewInitRequest(t *Token) *CreateRequest { func NewSignedRequest(t *Token) *CreateRequest { return &CreateRequest{Message: &CreateRequest_Signed{Signed: t}} } - -// Sign signs contents of the header with the private key. -func (m *VerificationHeader) Sign(key *ecdsa.PrivateKey) error { - s, err := crypto.Sign(key, m.PublicKey) - if err != nil { - return err - } - m.KeySignature = s - return nil -} diff --git a/session/store.go b/session/store.go index f6a6655..e46afde 100644 --- a/session/store.go +++ b/session/store.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -48,13 +49,15 @@ func (s *simpleStore) New(p TokenParams) *PToken { t := &PToken{ mtx: new(sync.Mutex), Token: Token{ - ID: tid, - Header: VerificationHeader{PublicKey: crypto.MarshalPublicKey(&key.PublicKey)}, - FirstEpoch: p.FirstEpoch, - LastEpoch: p.LastEpoch, - ObjectID: p.ObjectID, - OwnerID: p.OwnerID, - PublicKeys: p.PublicKeys, + Token_Info: service.Token_Info{ + ID: tid, + OwnerID: p.OwnerID, + Verb: p.Verb, + Address: p.Address, + Created: p.FirstEpoch, + ValidUntil: p.LastEpoch, + SessionKey: crypto.MarshalPublicKey(&key.PublicKey), + }, }, PrivateKey: key, } diff --git a/session/store_test.go b/session/store_test.go index 9ad0e1d..f51fb18 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -1,96 +1,3 @@ package session -import ( - "crypto/ecdsa" - "crypto/rand" - "testing" - - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/stretchr/testify/require" -) - -type testClient struct { - *ecdsa.PrivateKey - OwnerID OwnerID -} - -func (c *testClient) Sign(data []byte) ([]byte, error) { - return crypto.Sign(c.PrivateKey, data) -} - -func newTestClient(t *testing.T) *testClient { - key, err := ecdsa.GenerateKey(defaultCurve(), rand.Reader) - require.NoError(t, err) - - owner, err := refs.NewOwnerID(&key.PublicKey) - require.NoError(t, err) - - return &testClient{PrivateKey: key, OwnerID: owner} -} - -func signToken(t *testing.T, token *PToken, c *testClient) { - require.NotNil(t, token) - token.SetPublicKeys(&c.PublicKey) - - signH, err := c.Sign(token.Header.PublicKey) - require.NoError(t, err) - require.NotNil(t, signH) - - // data is not yet signed - keys := UnmarshalPublicKeys(&token.Token) - require.False(t, token.Verify(keys...)) - - signT, err := c.Sign(token.verificationData()) - require.NoError(t, err) - require.NotNil(t, signT) - - token.AddSignatures(signH, signT) - require.True(t, token.Verify(keys...)) -} - -func TestTokenStore(t *testing.T) { - s := NewSimpleStore() - - oid, err := refs.NewObjectID() - require.NoError(t, err) - - c := newTestClient(t) - require.NotNil(t, c) - pk := [][]byte{crypto.MarshalPublicKey(&c.PublicKey)} - - // create new token - token := s.New(TokenParams{ - ObjectID: []ObjectID{oid}, - OwnerID: c.OwnerID, - PublicKeys: pk, - }) - signToken(t, token, c) - - // check that it can be fetched - t1 := s.Fetch(token.ID) - require.NotNil(t, t1) - require.Equal(t, token, t1) - - // create and sign another token by the same client - t1 = s.New(TokenParams{ - ObjectID: []ObjectID{oid}, - OwnerID: c.OwnerID, - PublicKeys: pk, - }) - - signToken(t, t1, c) - - data := []byte{1, 2, 3} - sign, err := t1.SignData(data) - require.NoError(t, err) - require.Error(t, token.Header.VerifyData(data, sign)) - - sign, err = token.SignData(data) - require.NoError(t, err) - require.NoError(t, token.Header.VerifyData(data, sign)) - - s.Remove(token.ID) - require.Nil(t, s.Fetch(token.ID)) - require.NotNil(t, s.Fetch(t1.ID)) -} +// TODO: write unit tests diff --git a/session/types.go b/session/types.go index 4165291..e56373c 100644 --- a/session/types.go +++ b/session/types.go @@ -2,14 +2,12 @@ package session import ( "crypto/ecdsa" - "encoding/binary" "sync" - "github.com/nspcc-dev/neofs-api-go/chain" "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" - "github.com/pkg/errors" ) type ( @@ -19,6 +17,12 @@ type ( 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 // and to do thread-safe manipulations. @@ -55,127 +59,7 @@ const ( 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 -}