diff --git a/Makefile b/Makefile index 29f41bc..9773c9b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PROTO_VERSION=v0.7.1 +PROTO_VERSION=v0.7.2 PROTO_URL=https://github.com/nspcc-dev/neofs-api/archive/$(PROTO_VERSION).tar.gz B=\033[0;1m diff --git a/docs/object.md b/docs/object.md index 4ec32fc..27e4bcf 100644 --- a/docs/object.md +++ b/docs/object.md @@ -149,7 +149,6 @@ calculated for XORed data. | ----- | ---- | ----- | ----------- | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| Token | [session.Token](#session.Token) | | Token with session public key and user's signature | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -228,7 +227,6 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | -| Raw | [bool](#bool) | | Raw is the request flag of a physically stored representation of an object | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -256,7 +254,6 @@ in distributed system. | ----- | ---- | ----- | ----------- | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | FullHeaders | [bool](#bool) | | FullHeaders can be set true for extended headers in the object | -| Raw | [bool](#bool) | | Raw is the request flag of a physically stored representation of an object | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -296,7 +293,6 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | Object | [Object](#object.Object) | | Object with at least container id and owner id fields | -| Token | [session.Token](#session.Token) | | Token with session public key and user's signature | | CopiesNumber | [uint32](#uint32) | | Number of the object copies to store within the RPC call (zero is processed according to the placement rules) | @@ -378,7 +374,7 @@ in distributed system. | UserHeader | [UserHeader](#object.UserHeader) | | UserHeader is a set of KV headers defined by user | | Transform | [Transform](#object.Transform) | | Transform defines transform operation (e.g. payload split) | | Tombstone | [Tombstone](#object.Tombstone) | | Tombstone header that set up in deleted objects | -| Verify | [session.VerificationHeader](#session.VerificationHeader) | | Verify header that contains session public key and user's signature | +| Token | [service.Token](#service.Token) | | Token header contains token of the session within which the object was created | | HomoHash | [bytes](#bytes) | | HomoHash is a homomorphic hash of original object payload | | PayloadChecksum | [bytes](#bytes) | | PayloadChecksum of actual object's payload | | Integrity | [IntegrityHeader](#object.IntegrityHeader) | | Integrity header with checksum of all above headers in the object | diff --git a/docs/service.md b/docs/service.md index 90e1bd2..eef1e49 100644 --- a/docs/service.md +++ b/docs/service.md @@ -14,8 +14,9 @@ - Messages - [RequestVerificationHeader](#service.RequestVerificationHeader) - - [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) - [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) + - [Token](#service.Token) + - [Token.Info](#service.Token.Info) - [service/verify_test.proto](#service/verify_test.proto) @@ -49,6 +50,7 @@ RequestMetaHeader contains information about request meta headers | TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every NeoFS Node | | Epoch | [uint64](#uint64) | | Epoch for user can be empty, because node sets epoch to the actual value | | Version | [uint32](#uint32) | | Version defines protocol version TODO: not used for now, should be implemented in future | +| Raw | [bool](#bool) | | Raw determines whether the request is raw or not | @@ -88,18 +90,7 @@ RequestVerificationHeader is a set of signatures of every NeoFS Node that proces | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | Signatures | [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) | repeated | Signatures is a set of signatures of every passed NeoFS Node | - - - - -### Message RequestVerificationHeader.Sign - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| Sign | [bytes](#bytes) | | Sign is signature of the request or session key. | -| Peer | [bytes](#bytes) | | Peer is compressed public key used for signature. | +| Token | [Token](#service.Token) | | Token is a token of the session within which the request is sent | @@ -110,11 +101,57 @@ RequestVerificationHeader is a set of signatures of every NeoFS Node that proces | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Sign | [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) | | Sign is a signature and public key of the request. | -| Origin | [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) | | Origin used for requests, when trusted node changes it and re-sign with session key. If session key used for signature request, then Origin should contain public key of user and signed session key. | +| Sign | [bytes](#bytes) | | Sign is signature of the request or session key. | +| Peer | [bytes](#bytes) | | Peer is compressed public key used for signature. | + + + + +### Message Token +User token granting rights for object manipulation + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| TokenInfo | [Token.Info](#service.Token.Info) | | TokenInfo is a grouped information about token | +| Signature | [bytes](#bytes) | | Signature is a signature of session token information | + + + + +### Message Token.Info + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| ID | [bytes](#bytes) | | ID is a token identifier. valid UUIDv4 represented in bytes | +| OwnerID | [bytes](#bytes) | | OwnerID is an owner of manipulation object | +| verb | [Token.Info.Verb](#service.Token.Info.Verb) | | Verb is a type of request for which the token is issued | +| Address | [refs.Address](#refs.Address) | | Address is an object address for which token is issued | +| Created | [uint64](#uint64) | | Created is an initial epoch of token lifetime | +| ValidUntil | [uint64](#uint64) | | ValidUntil is a last epoch of token lifetime | +| SessionKey | [bytes](#bytes) | | SessionKey is a public key of session key | + + + +### Token.Info.Verb +Verb is an enumeration of session request types + +| Name | Number | Description | +| ---- | ------ | ----------- | +| Put | 0 | Put refers to object.Put RPC call | +| Get | 1 | Get refers to object.Get RPC call | +| Head | 2 | Head refers to object.Head RPC call | +| Search | 3 | Search refers to object.Search RPC call | +| Delete | 4 | Delete refers to object.Delete RPC call | +| Range | 5 | Range refers to object.GetRange RPC call | +| RangeHash | 6 | RangeHash refers to object.GetRangeHash RPC call | + + diff --git a/docs/session.md b/docs/session.md index ba615c3..4a537e6 100644 --- a/docs/session.md +++ b/docs/session.md @@ -12,13 +12,6 @@ - [CreateResponse](#session.CreateResponse) -- [session/types.proto](#session/types.proto) - - - Messages - - [Token](#session.Token) - - [VerificationHeader](#session.VerificationHeader) - - - [Scalar Value Types](#scalar-value-types) @@ -68,8 +61,8 @@ session key. Session is established during 4-step handshake in one gRPC stream | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Init | [Token](#session.Token) | | Init is a message to initialize session opening. Carry: owner of manipulation object; ID of manipulation object; token lifetime bounds. | -| Signed | [Token](#session.Token) | | Signed Init message response (Unsigned) from server with user private key | +| Init | [service.Token](#service.Token) | | Init is a message to initialize session opening. Carry: owner of manipulation object; ID of manipulation object; token lifetime bounds. | +| Signed | [service.Token](#service.Token) | | Signed Init message response (Unsigned) from server with user private key | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -82,52 +75,8 @@ session key. Session is established during 4-step handshake in one gRPC stream | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Unsigned | [Token](#session.Token) | | Unsigned token with token ID and session public key generated on server side | -| Result | [Token](#session.Token) | | Result is a resulting token which can be used for object placing through an trusted intermediary | - - - - - - - - -

Top

- -## session/types.proto - - - - - - - -### Message Token -User token granting rights for object manipulation - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| Header | [VerificationHeader](#session.VerificationHeader) | | Header carries verification data of session key | -| OwnerID | [bytes](#bytes) | | OwnerID is an owner of manipulation object | -| FirstEpoch | [uint64](#uint64) | | FirstEpoch is an initial epoch of token lifetime | -| LastEpoch | [uint64](#uint64) | | LastEpoch is a last epoch of token lifetime | -| ObjectID | [bytes](#bytes) | repeated | ObjectID is an object identifier of manipulation object | -| Signature | [bytes](#bytes) | | Signature is a token signature, signed by owner of manipulation object | -| ID | [bytes](#bytes) | | ID is a token identifier. valid UUIDv4 represented in bytes | -| PublicKeys | [bytes](#bytes) | repeated | PublicKeys associated with owner | - - - - -### Message VerificationHeader - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| PublicKey | [bytes](#bytes) | | PublicKey is a session public key | -| KeySignature | [bytes](#bytes) | | KeySignature is a session public key signature. Signed by trusted side | +| Unsigned | [service.Token](#service.Token) | | Unsigned token with token ID and session public key generated on server side | +| Result | [service.Token](#service.Token) | | Result is a resulting token which can be used for object placing through an trusted intermediary | 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.pb.go b/object/service.pb.go index 4ac61bc..f882f5e 100644 Binary files a/object/service.pb.go and b/object/service.pb.go differ diff --git a/object/service.proto b/object/service.proto index b5042e2..91d0b99 100644 --- a/object/service.proto +++ b/object/service.proto @@ -5,7 +5,6 @@ option csharp_namespace = "NeoFS.API.Object"; import "refs/types.proto"; import "object/types.proto"; -import "session/types.proto"; import "service/meta.proto"; import "service/verify.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -58,8 +57,6 @@ service Service { message GetRequest { // Address of object (container id + object id) refs.Address Address = 1 [(gogoproto.nullable) = false]; - // Raw is the request flag of a physically stored representation of an object - bool Raw = 2; // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) @@ -82,10 +79,8 @@ message PutRequest { message PutHeader { // Object with at least container id and owner id fields Object Object = 1; - // Token with session public key and user's signature - session.Token Token = 2; // Number of the object copies to store within the RPC call (zero is processed according to the placement rules) - uint32 CopiesNumber = 3; + uint32 CopiesNumber = 2; } oneof R { @@ -112,8 +107,6 @@ message DeleteRequest { refs.Address Address = 1 [(gogoproto.nullable) = false]; // OwnerID is a wallet address bytes OwnerID = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "OwnerID"]; - // Token with session public key and user's signature - session.Token Token = 3; // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) @@ -132,8 +125,6 @@ message HeadRequest { refs.Address Address = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; // FullHeaders can be set true for extended headers in the object bool FullHeaders = 2; - // Raw is the request flag of a physically stored representation of an object - bool Raw = 3; // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) 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/types.pb.go b/object/types.pb.go index fe47459..a24a2f1 100644 Binary files a/object/types.pb.go and b/object/types.pb.go differ diff --git a/object/types.proto b/object/types.proto index f21bf74..21ab981 100644 --- a/object/types.proto +++ b/object/types.proto @@ -4,7 +4,7 @@ option go_package = "github.com/nspcc-dev/neofs-api-go/object"; option csharp_namespace = "NeoFS.API.Object"; import "refs/types.proto"; -import "session/types.proto"; +import "service/verify.proto"; import "storagegroup/types.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -36,8 +36,8 @@ message Header { Transform Transform = 4; // Tombstone header that set up in deleted objects Tombstone Tombstone = 5; - // Verify header that contains session public key and user's signature - session.VerificationHeader Verify = 6; + // Token header contains token of the session within which the object was created + service.Token Token = 6; // HomoHash is a homomorphic hash of original object payload bytes HomoHash = 7 [(gogoproto.customtype) = "Hash"]; // PayloadChecksum of actual object's payload 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/meta.pb.go b/service/meta.pb.go index 8039990..25a5469 100644 Binary files a/service/meta.pb.go and b/service/meta.pb.go differ diff --git a/service/meta.proto b/service/meta.proto index 99b37d3..093f118 100644 --- a/service/meta.proto +++ b/service/meta.proto @@ -17,6 +17,8 @@ message RequestMetaHeader { // Version defines protocol version // TODO: not used for now, should be implemented in future uint32 Version = 3; + // Raw determines whether the request is raw or not + bool Raw = 4; } // ResponseMetaHeader contains meta information based on request processing by server 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.pb.go b/service/verify.pb.go index 9dca855..023e639 100644 Binary files a/service/verify.pb.go and b/service/verify.pb.go differ diff --git a/service/verify.proto b/service/verify.proto index de0a69a..b25cd47 100644 --- a/service/verify.proto +++ b/service/verify.proto @@ -3,6 +3,7 @@ package service; option go_package = "github.com/nspcc-dev/neofs-api-go/service"; option csharp_namespace = "NeoFS.API.Service"; +import "refs/types.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; option (gogoproto.stable_marshaler_all) = true; @@ -10,22 +11,74 @@ option (gogoproto.stable_marshaler_all) = true; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request // (should be embedded into message). message RequestVerificationHeader { - message Sign { + message Signature { // Sign is signature of the request or session key. bytes Sign = 1; // Peer is compressed public key used for signature. bytes Peer = 2; } - message Signature { - // Sign is a signature and public key of the request. - Sign Sign = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // Origin used for requests, when trusted node changes it and re-sign with session key. - // If session key used for signature request, then Origin should contain - // public key of user and signed session key. - Sign Origin = 2; - } - // Signatures is a set of signatures of every passed NeoFS Node repeated Signature Signatures = 1; + + // Token is a token of the session within which the request is sent + Token Token = 2; } + +// User token granting rights for object manipulation +message Token { + message Info { + // ID is a token identifier. valid UUIDv4 represented in bytes + bytes ID = 1 [(gogoproto.customtype) = "TokenID", (gogoproto.nullable) = false]; + + // OwnerID is an owner of manipulation object + bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; + + // Verb is an enumeration of session request types + enum Verb { + // Put refers to object.Put RPC call + Put = 0; + // Get refers to object.Get RPC call + Get = 1; + // Head refers to object.Head RPC call + Head = 2; + // Search refers to object.Search RPC call + Search = 3; + // Delete refers to object.Delete RPC call + Delete = 4; + // Range refers to object.GetRange RPC call + Range = 5; + // RangeHash refers to object.GetRangeHash RPC call + RangeHash = 6; + } + + // Verb is a type of request for which the token is issued + Verb verb = 3 [(gogoproto.customname) = "Verb"]; + + // Address is an object address for which token is issued + refs.Address Address = 4 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; + + // Created is an initial epoch of token lifetime + uint64 Created = 5; + + // ValidUntil is a last epoch of token lifetime + uint64 ValidUntil = 6; + + // SessionKey is a public key of session key + bytes SessionKey = 7; + } + + // TokenInfo is a grouped information about token + Info TokenInfo = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + + // Signature is a signature of session token information + bytes Signature = 8; +} + +// TODO: for variable token types and version redefine message +// Example: +// message Token { +// TokenType TokenType = 1; +// uint32 Version = 2; +// bytes Data = 3; +// } 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/service.pb.go b/session/service.pb.go index abd1618..1088308 100644 Binary files a/session/service.pb.go and b/session/service.pb.go differ diff --git a/session/service.proto b/session/service.proto index 524213a..5c22fc3 100644 --- a/session/service.proto +++ b/session/service.proto @@ -3,7 +3,6 @@ package session; option go_package = "github.com/nspcc-dev/neofs-api-go/session"; option csharp_namespace = "NeoFS.API.Session"; -import "session/types.proto"; import "service/meta.proto"; import "service/verify.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -33,9 +32,9 @@ message CreateRequest { // owner of manipulation object; // ID of manipulation object; // token lifetime bounds. - session.Token Init = 1; + service.Token Init = 1; // Signed Init message response (Unsigned) from server with user private key - session.Token Signed = 2; + service.Token Signed = 2; } // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; @@ -46,8 +45,8 @@ message CreateRequest { message CreateResponse { oneof Message { // Unsigned token with token ID and session public key generated on server side - session.Token Unsigned = 1; + service.Token Unsigned = 1; // Result is a resulting token which can be used for object placing through an trusted intermediary - session.Token Result = 2; + service.Token Result = 2; } } 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 -} diff --git a/session/types.pb.go b/session/types.pb.go deleted file mode 100644 index 01458dd..0000000 Binary files a/session/types.pb.go and /dev/null differ diff --git a/session/types.proto b/session/types.proto deleted file mode 100644 index 3ae49a3..0000000 --- a/session/types.proto +++ /dev/null @@ -1,35 +0,0 @@ -syntax = "proto3"; -package session; -option go_package = "github.com/nspcc-dev/neofs-api-go/session"; -option csharp_namespace = "NeoFS.API.Session"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -message VerificationHeader { - // PublicKey is a session public key - bytes PublicKey = 1; - // KeySignature is a session public key signature. Signed by trusted side - bytes KeySignature = 2; -} - -// User token granting rights for object manipulation -message Token { - // Header carries verification data of session key - VerificationHeader Header = 1 [(gogoproto.nullable) = false]; - // OwnerID is an owner of manipulation object - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // FirstEpoch is an initial epoch of token lifetime - uint64 FirstEpoch = 3; - // LastEpoch is a last epoch of token lifetime - uint64 LastEpoch = 4; - // ObjectID is an object identifier of manipulation object - repeated bytes ObjectID = 5 [(gogoproto.customtype) = "ObjectID", (gogoproto.nullable) = false]; - // Signature is a token signature, signed by owner of manipulation object - bytes Signature = 6; - // ID is a token identifier. valid UUIDv4 represented in bytes - bytes ID = 7 [(gogoproto.customtype) = "TokenID", (gogoproto.nullable) = false]; - // PublicKeys associated with owner - repeated bytes PublicKeys = 8; -}