diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbd6afb8..e771c197 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,23 @@
# Changelog
This is the changelog for NeoFS-API-Go
+## [1.1.0] - 2020-06-18
+
+### Added
+
+- `container.SetExtendedACL` rpc.
+- `container.GetExtendedACL` rpc.
+- Bearer token to all request messages.
+- X-headers to all request messages.
+
+### Changed
+
+- Implementation and signatures of Sign/Verify request functions.
+
+### Updated
+
+- NeoFS API v1.0.0 => 1.1.0
+
## [1.0.0] - 2020-05-26
- Bump major release
@@ -339,3 +356,4 @@ Initial public release
[0.7.5]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.4...v0.7.5
[0.7.6]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.5...v0.7.6
[1.0.0]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.6...v1.0.0
+[1.1.0]: https://github.com/nspcc-dev/neofs-api-go/compare/v1.0.0...v1.1.0
diff --git a/Makefile b/Makefile
index 3dcd690c..159c7e39 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PROTO_VERSION=v1.0.0
+PROTO_VERSION=v1.1.0
PROTO_URL=https://github.com/nspcc-dev/neofs-api/archive/$(PROTO_VERSION).tar.gz
B=\033[0;1m
diff --git a/README.md b/README.md
index fac04d07..d44e0570 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,12 @@
NeoFS API repository contains implementation of core NeoFS structures that
can be used for integration with NeoFS.
+## Сompatibility
+
+[neofs-api v1.1.0]: https://github.com/nspcc-dev/neofs-api/releases/tag/v1.1.0
+[neofs-api-go v1.1.0]: https://github.com/nspcc-dev/neofs-api-go/releases/tag/v1.1.0
+[neofs-api-go v1.1.0] supports [neofs-api v1.1.0]
+
## Description
Repository contains 13 packages that implement NeoFS core structures. These
diff --git a/accounting/sign_test.go b/accounting/sign_test.go
index dd7a819f..ebc683bf 100644
--- a/accounting/sign_test.go
+++ b/accounting/sign_test.go
@@ -13,7 +13,7 @@ func TestSignBalanceRequest(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
- service.SignedDataWithToken
+ service.RequestData
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
@@ -159,26 +159,26 @@ func TestSignBalanceRequest(t *testing.T) {
token := new(service.Token)
v.SetToken(token)
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
corruption(v)
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
}
}
diff --git a/bootstrap/sign_test.go b/bootstrap/sign_test.go
index 2c76117a..38121305 100644
--- a/bootstrap/sign_test.go
+++ b/bootstrap/sign_test.go
@@ -12,7 +12,7 @@ func TestRequestSign(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
- service.SignedDataWithToken
+ service.RequestData
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
@@ -56,26 +56,26 @@ func TestRequestSign(t *testing.T) {
token := new(service.Token)
v.SetToken(token)
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
corruption(v)
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
}
}
diff --git a/container/service.pb.go b/container/service.pb.go
index 61312544..f49f79fe 100644
Binary files a/container/service.pb.go and b/container/service.pb.go differ
diff --git a/container/service.proto b/container/service.proto
index 7df2c66d..b174052a 100644
--- a/container/service.proto
+++ b/container/service.proto
@@ -27,6 +27,12 @@ service Service {
// List returns all user's containers
rpc List(ListRequest) returns (ListResponse);
+
+ // SetExtendedACL changes extended ACL rules of the container
+ rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse);
+
+ // GetExtendedACL returns extended ACL rules of the container
+ rpc GetExtendedACL(GetExtendedACLRequest) returns (GetExtendedACLResponse);
}
message PutRequest {
@@ -99,3 +105,42 @@ message ListResponse {
// CID (container id) is list of SHA256 hashes of the container structures
repeated bytes CID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false];
}
+
+message ExtendedACLKey {
+ // ID (container id) is a SHA256 hash of the container structure
+ bytes ID = 1 [(gogoproto.customtype) = "CID", (gogoproto.nullable) = false];
+}
+
+message ExtendedACLValue {
+ // EACL carries binary representation of the table of extended ACL rules
+ bytes EACL = 1;
+ // Signature carries EACL field signature
+ bytes Signature = 2;
+}
+
+message SetExtendedACLRequest {
+ // Key carries key to extended ACL information
+ ExtendedACLKey Key = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+ // Value carries extended ACL information
+ ExtendedACLValue Value = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+ // 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)
+ service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+}
+
+message SetExtendedACLResponse {}
+
+message GetExtendedACLRequest {
+ // Key carries key to extended ACL information
+ ExtendedACLKey Key = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+ // 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)
+ service.RequestVerificationHeader Verify = 99 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+}
+
+message GetExtendedACLResponse {
+ // ACL carries extended ACL information
+ ExtendedACLValue ACL = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+}
diff --git a/container/sign.go b/container/sign.go
index eafd93c3..f538f2d7 100644
--- a/container/sign.go
+++ b/container/sign.go
@@ -135,3 +135,60 @@ func (m ListRequest) ReadSignedData(p []byte) (int, error) {
return off, nil
}
+
+// SignedData returns payload bytes of the request.
+func (m GetExtendedACLRequest) SignedData() ([]byte, error) {
+ return service.SignedDataFromReader(m)
+}
+
+// SignedDataSize returns payload size of the request.
+func (m GetExtendedACLRequest) SignedDataSize() int {
+ return m.GetID().Size()
+}
+
+// ReadSignedData copies payload bytes to passed buffer.
+//
+// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
+func (m GetExtendedACLRequest) ReadSignedData(p []byte) (int, error) {
+ if len(p) < m.SignedDataSize() {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ var off int
+
+ off += copy(p[off:], m.GetID().Bytes())
+
+ return off, nil
+}
+
+// SignedData returns payload bytes of the request.
+func (m SetExtendedACLRequest) SignedData() ([]byte, error) {
+ return service.SignedDataFromReader(m)
+}
+
+// SignedDataSize returns payload size of the request.
+func (m SetExtendedACLRequest) SignedDataSize() int {
+ return 0 +
+ m.GetID().Size() +
+ len(m.GetEACL()) +
+ len(m.GetSignature())
+}
+
+// ReadSignedData copies payload bytes to passed buffer.
+//
+// If the Request size is insufficient, io.ErrUnexpectedEOF returns.
+func (m SetExtendedACLRequest) ReadSignedData(p []byte) (int, error) {
+ if len(p) < m.SignedDataSize() {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ var off int
+
+ off += copy(p[off:], m.GetID().Bytes())
+
+ off += copy(p[off:], m.GetEACL())
+
+ off += copy(p[off:], m.GetSignature())
+
+ return off, nil
+}
diff --git a/container/sign_test.go b/container/sign_test.go
index e469399c..d04a6989 100644
--- a/container/sign_test.go
+++ b/container/sign_test.go
@@ -12,7 +12,7 @@ func TestRequestSign(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
- service.SignedDataWithToken
+ service.RequestData
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
@@ -108,6 +108,50 @@ func TestRequestSign(t *testing.T) {
},
},
},
+ { // GetExtendedACLRequest
+ constructor: func() sigType {
+ return new(GetExtendedACLRequest)
+ },
+ payloadCorrupt: []func(sigType){
+ func(s sigType) {
+ req := s.(*GetExtendedACLRequest)
+
+ id := req.GetID()
+ id[0]++
+
+ req.SetID(id)
+ },
+ },
+ },
+ { // SetExtendedACLRequest
+ constructor: func() sigType {
+ return new(SetExtendedACLRequest)
+ },
+ payloadCorrupt: []func(sigType){
+ func(s sigType) {
+ req := s.(*SetExtendedACLRequest)
+
+ id := req.GetID()
+ id[0]++
+
+ req.SetID(id)
+ },
+ func(s sigType) {
+ req := s.(*SetExtendedACLRequest)
+
+ req.SetEACL(
+ append(req.GetEACL(), 1),
+ )
+ },
+ func(s sigType) {
+ req := s.(*SetExtendedACLRequest)
+
+ req.SetSignature(
+ append(req.GetSignature(), 1),
+ )
+ },
+ },
+ },
}
for _, item := range items {
@@ -117,26 +161,26 @@ func TestRequestSign(t *testing.T) {
token := new(service.Token)
v.SetToken(token)
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
corruption(v)
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
}
}
diff --git a/container/types.go b/container/types.go
index f340aa59..38bdcffb 100644
--- a/container/types.go
+++ b/container/types.go
@@ -158,3 +158,23 @@ func (m ListRequest) GetOwnerID() OwnerID {
func (m *ListRequest) SetOwnerID(owner OwnerID) {
m.OwnerID = owner
}
+
+// GetID is an ID field getter.
+func (m ExtendedACLKey) GetID() CID {
+ return m.ID
+}
+
+// SetID is an ID field setter.
+func (m *ExtendedACLKey) SetID(v CID) {
+ m.ID = v
+}
+
+// SetEACL is an EACL field setter.
+func (m *ExtendedACLValue) SetEACL(v []byte) {
+ m.EACL = v
+}
+
+// SetSignature is a Signature field setter.
+func (m *ExtendedACLValue) SetSignature(sig []byte) {
+ m.Signature = sig
+}
diff --git a/container/types_test.go b/container/types_test.go
index cc171cbe..76bbe1ce 100644
--- a/container/types_test.go
+++ b/container/types_test.go
@@ -140,3 +140,23 @@ func TestListRequestGettersSetters(t *testing.T) {
require.Equal(t, owner, m.GetOwnerID())
})
}
+
+func TestExtendedACLKey(t *testing.T) {
+ s := new(ExtendedACLKey)
+
+ id := CID{1, 2, 3}
+ s.SetID(id)
+ require.Equal(t, id, s.GetID())
+}
+
+func TestExtendedACLValue(t *testing.T) {
+ s := new(ExtendedACLValue)
+
+ acl := []byte{1, 2, 3}
+ s.SetEACL(acl)
+ require.Equal(t, acl, s.GetEACL())
+
+ sig := []byte{4, 5, 6}
+ s.SetSignature(sig)
+ require.Equal(t, sig, s.GetSignature())
+}
diff --git a/docs/container.md b/docs/container.md
index f0188ca7..fd89acd8 100644
--- a/docs/container.md
+++ b/docs/container.md
@@ -10,12 +10,18 @@
- Messages
- [DeleteRequest](#container.DeleteRequest)
- [DeleteResponse](#container.DeleteResponse)
+ - [ExtendedACLKey](#container.ExtendedACLKey)
+ - [ExtendedACLValue](#container.ExtendedACLValue)
+ - [GetExtendedACLRequest](#container.GetExtendedACLRequest)
+ - [GetExtendedACLResponse](#container.GetExtendedACLResponse)
- [GetRequest](#container.GetRequest)
- [GetResponse](#container.GetResponse)
- [ListRequest](#container.ListRequest)
- [ListResponse](#container.ListResponse)
- [PutRequest](#container.PutRequest)
- [PutResponse](#container.PutResponse)
+ - [SetExtendedACLRequest](#container.SetExtendedACLRequest)
+ - [SetExtendedACLResponse](#container.SetExtendedACLResponse)
- [container/types.proto](#container/types.proto)
@@ -46,6 +52,8 @@ rpc Put(PutRequest) returns (PutResponse);
rpc Delete(DeleteRequest) returns (DeleteResponse);
rpc Get(GetRequest) returns (GetResponse);
rpc List(ListRequest) returns (ListResponse);
+rpc SetExtendedACL(SetExtendedACLRequest) returns (SetExtendedACLResponse);
+rpc GetExtendedACL(GetExtendedACLRequest) returns (GetExtendedACLResponse);
```
@@ -80,6 +88,20 @@ List returns all user's containers
| Name | Input | Output |
| ---- | ----- | ------ |
| List | [ListRequest](#container.ListRequest) | [ListResponse](#container.ListResponse) |
+#### Method SetExtendedACL
+
+SetExtendedACL changes extended ACL rules of the container
+
+| Name | Input | Output |
+| ---- | ----- | ------ |
+| SetExtendedACL | [SetExtendedACLRequest](#container.SetExtendedACLRequest) | [SetExtendedACLResponse](#container.SetExtendedACLResponse) |
+#### Method GetExtendedACL
+
+GetExtendedACL returns extended ACL rules of the container
+
+| Name | Input | Output |
+| ---- | ----- | ------ |
+| GetExtendedACL | [GetExtendedACLRequest](#container.GetExtendedACLRequest) | [GetExtendedACLResponse](#container.GetExtendedACLResponse) |
@@ -104,6 +126,53 @@ via consensus in inner ring nodes
+
+
+### Message ExtendedACLKey
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| ID | [bytes](#bytes) | | ID (container id) is a SHA256 hash of the container structure |
+
+
+
+
+### Message ExtendedACLValue
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| EACL | [bytes](#bytes) | | EACL carries binary representation of the table of extended ACL rules |
+| Signature | [bytes](#bytes) | | Signature carries EACL field signature |
+
+
+
+
+### Message GetExtendedACLRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| Key | [ExtendedACLKey](#container.ExtendedACLKey) | | Key carries key to extended ACL information |
+| 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) |
+
+
+
+
+### Message GetExtendedACLResponse
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| ACL | [ExtendedACLValue](#container.ExtendedACLValue) | | ACL carries extended ACL information |
+
+
### Message GetRequest
@@ -179,6 +248,27 @@ via consensus in inner ring nodes
| ----- | ---- | ----- | ----------- |
| CID | [bytes](#bytes) | | CID (container id) is a SHA256 hash of the container structure |
+
+
+
+### Message SetExtendedACLRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| Key | [ExtendedACLKey](#container.ExtendedACLKey) | | Key carries key to extended ACL information |
+| Value | [ExtendedACLValue](#container.ExtendedACLValue) | | Value carries extended ACL information |
+| 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) |
+
+
+
+
+### Message SetExtendedACLResponse
+
+
+
diff --git a/docs/service.md b/docs/service.md
index 0765f044..223ddd18 100644
--- a/docs/service.md
+++ b/docs/service.md
@@ -6,6 +6,8 @@
- [service/meta.proto](#service/meta.proto)
- Messages
+ - [RequestExtendedHeader](#service.RequestExtendedHeader)
+ - [RequestExtendedHeader.KV](#service.RequestExtendedHeader.KV)
- [RequestMetaHeader](#service.RequestMetaHeader)
- [ResponseMetaHeader](#service.ResponseMetaHeader)
@@ -13,6 +15,8 @@
- [service/verify.proto](#service/verify.proto)
- Messages
+ - [BearerTokenMsg](#service.BearerTokenMsg)
+ - [BearerTokenMsg.Info](#service.BearerTokenMsg.Info)
- [RequestVerificationHeader](#service.RequestVerificationHeader)
- [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature)
- [Token](#service.Token)
@@ -39,6 +43,29 @@
+
+
+### Message RequestExtendedHeader
+RequestExtendedHeader contains extended headers of request
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| Headers | [RequestExtendedHeader.KV](#service.RequestExtendedHeader.KV) | repeated | Headers carries list of key-value headers |
+
+
+
+
+### Message RequestExtendedHeader.KV
+KV contains string key-value pair
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| K | [string](#string) | | K carries extended header key |
+| V | [string](#string) | | V carries extended header value |
+
+
### Message RequestMetaHeader
@@ -52,6 +79,7 @@ RequestMetaHeader contains information about request meta headers
| 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 |
+| ExtendedHeader | [RequestExtendedHeader](#service.RequestExtendedHeader) | | ExtendedHeader carries extended headers of the request |
@@ -81,6 +109,32 @@ ResponseMetaHeader contains meta information based on request processing by serv
+
+
+### Message BearerTokenMsg
+BearerTokenMsg carries information about request ACL rules with limited lifetime
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| TokenInfo | [BearerTokenMsg.Info](#service.BearerTokenMsg.Info) | | TokenInfo is a grouped information about token |
+| OwnerKey | [bytes](#bytes) | | OwnerKey is a public key of the token owner |
+| Signature | [bytes](#bytes) | | Signature is a signature of token information |
+
+
+
+
+### Message BearerTokenMsg.Info
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| ACLRules | [bytes](#bytes) | | ACLRules carries a binary representation of the table of extended ACL rules |
+| OwnerID | [bytes](#bytes) | | OwnerID is an owner of token |
+| ValidUntil | [uint64](#uint64) | | ValidUntil carries a last epoch of token lifetime |
+
+
### Message RequestVerificationHeader
@@ -92,6 +146,7 @@ RequestVerificationHeader is a set of signatures of every NeoFS Node that proces
| ----- | ---- | ----- | ----------- |
| Signatures | [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) | repeated | Signatures is a set of signatures of every passed NeoFS Node |
| Token | [Token](#service.Token) | | Token is a token of the session within which the request is sent |
+| Bearer | [BearerTokenMsg](#service.BearerTokenMsg) | | Bearer is a Bearer token of the request |
diff --git a/object/sign_test.go b/object/sign_test.go
index 9480fda7..dcfbd7e3 100644
--- a/object/sign_test.go
+++ b/object/sign_test.go
@@ -13,7 +13,7 @@ func TestSignVerifyRequests(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
- service.SignedDataWithToken
+ service.RequestData
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*Token)
@@ -164,26 +164,26 @@ func TestSignVerifyRequests(t *testing.T) {
token := new(Token)
v.SetToken(token)
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
corruption(v)
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
}
}
diff --git a/service/bearer.go b/service/bearer.go
new file mode 100644
index 00000000..327f74f6
--- /dev/null
+++ b/service/bearer.go
@@ -0,0 +1,140 @@
+package service
+
+import (
+ "crypto/ecdsa"
+ "io"
+
+ "github.com/nspcc-dev/neofs-api-go/refs"
+ crypto "github.com/nspcc-dev/neofs-crypto"
+)
+
+type signedBearerToken struct {
+ BearerToken
+}
+
+type bearerMsgWrapper struct {
+ *BearerTokenMsg
+}
+
+const fixedBearerTokenDataSize = 0 +
+ refs.OwnerIDSize +
+ 8
+
+// NewSignedBearerToken wraps passed BearerToken in a component suitable for signing.
+//
+// Result can be used in AddSignatureWithKey function.
+func NewSignedBearerToken(token BearerToken) DataWithSignKeyAccumulator {
+ return &signedBearerToken{
+ BearerToken: token,
+ }
+}
+
+// NewVerifiedBearerToken wraps passed SessionToken in a component suitable for signature verification.
+//
+// Result can be used in VerifySignatureWithKey function.
+func NewVerifiedBearerToken(token BearerToken) DataWithSignature {
+ return &signedBearerToken{
+ BearerToken: token,
+ }
+}
+
+// AddSignKey calls a Signature field setter and an OwnerKey field setter with corresponding arguments.
+func (s signedBearerToken) AddSignKey(sig []byte, key *ecdsa.PublicKey) {
+ if s.BearerToken != nil {
+ s.SetSignature(sig)
+
+ s.SetOwnerKey(
+ crypto.MarshalPublicKey(key),
+ )
+ }
+}
+
+// SignedData returns token information in a binary representation.
+func (s signedBearerToken) SignedData() ([]byte, error) {
+ return SignedDataFromReader(s)
+}
+
+// SignedDataSize returns the length of signed token information slice.
+func (s signedBearerToken) SignedDataSize() int {
+ return bearerTokenInfoSize(s.BearerToken)
+}
+
+// ReadSignedData copies a binary representation of the token information to passed buffer.
+//
+// If buffer length is less than required, io.ErrUnexpectedEOF returns.
+func (s signedBearerToken) ReadSignedData(p []byte) (int, error) {
+ sz := s.SignedDataSize()
+ if len(p) < sz {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ copyBearerTokenSignedData(p, s.BearerToken)
+
+ return sz, nil
+}
+
+func bearerTokenInfoSize(v ACLRulesSource) int {
+ if v == nil {
+ return 0
+ }
+ return fixedBearerTokenDataSize + len(v.GetACLRules())
+}
+
+// Fills passed buffer with signing token information bytes.
+// Does not check buffer length, it is understood that enough space is allocated in it.
+//
+// If passed BearerTokenInfo, buffer remains unchanged.
+func copyBearerTokenSignedData(buf []byte, token BearerTokenInfo) {
+ if token == nil {
+ return
+ }
+
+ var off int
+
+ off += copy(buf[off:], token.GetACLRules())
+
+ off += copy(buf[off:], token.GetOwnerID().Bytes())
+
+ tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch())
+ off += 8
+}
+
+// SetACLRules is an ACLRules field setter.
+func (m *BearerTokenMsg_Info) SetACLRules(v []byte) {
+ m.ACLRules = v
+}
+
+// SetValidUntil is a ValidUntil field setter.
+func (m *BearerTokenMsg_Info) SetValidUntil(v uint64) {
+ m.ValidUntil = v
+}
+
+// GetOwnerID if an OwnerID field getter.
+func (m BearerTokenMsg_Info) GetOwnerID() OwnerID {
+ return m.OwnerID
+}
+
+// SetOwnerID is an OwnerID field setter.
+func (m *BearerTokenMsg_Info) SetOwnerID(v OwnerID) {
+ m.OwnerID = v
+}
+
+// ExpirationEpoch returns the result of ValidUntil field getter.
+func (m BearerTokenMsg_Info) ExpirationEpoch() uint64 {
+ return m.GetValidUntil()
+}
+
+// SetExpirationEpoch passes argument to ValidUntil field setter.
+func (m *BearerTokenMsg_Info) SetExpirationEpoch(v uint64) {
+ m.SetValidUntil(v)
+}
+
+// SetOwnerKey is an OwnerKey field setter.
+func (m *BearerTokenMsg) SetOwnerKey(v []byte) {
+ m.OwnerKey = v
+}
+
+// SetSignature is a Signature field setter.
+func (m *BearerTokenMsg) SetSignature(v []byte) {
+ m.Signature = v
+}
diff --git a/service/bearer_test.go b/service/bearer_test.go
new file mode 100644
index 00000000..d6985cbd
--- /dev/null
+++ b/service/bearer_test.go
@@ -0,0 +1,199 @@
+package service
+
+import (
+ "crypto/rand"
+ "testing"
+
+ "github.com/nspcc-dev/neofs-crypto/test"
+ "github.com/stretchr/testify/require"
+)
+
+type testBearerToken struct {
+ aclRules []byte
+ expEpoch uint64
+ owner OwnerID
+ key []byte
+ sig []byte
+}
+
+func (s testBearerToken) GetACLRules() []byte {
+ return s.aclRules
+}
+
+func (s *testBearerToken) SetACLRules(v []byte) {
+ s.aclRules = v
+}
+
+func (s testBearerToken) ExpirationEpoch() uint64 {
+ return s.expEpoch
+}
+
+func (s *testBearerToken) SetExpirationEpoch(v uint64) {
+ s.expEpoch = v
+}
+
+func (s testBearerToken) GetOwnerID() OwnerID {
+ return s.owner
+}
+
+func (s *testBearerToken) SetOwnerID(v OwnerID) {
+ s.owner = v
+}
+
+func (s testBearerToken) GetOwnerKey() []byte {
+ return s.key
+}
+
+func (s *testBearerToken) SetOwnerKey(v []byte) {
+ s.key = v
+}
+
+func (s testBearerToken) GetSignature() []byte {
+ return s.sig
+}
+
+func (s *testBearerToken) SetSignature(v []byte) {
+ s.sig = v
+}
+
+func TestBearerTokenMsgGettersSetters(t *testing.T) {
+ var tok BearerToken = new(testBearerToken)
+
+ { // ACLRules
+ rules := []byte{1, 2, 3}
+
+ tok.SetACLRules(rules)
+
+ require.Equal(t, rules, tok.GetACLRules())
+ }
+
+ { // OwnerID
+ ownerID := OwnerID{}
+ _, err := rand.Read(ownerID[:])
+ require.NoError(t, err)
+
+ tok.SetOwnerID(ownerID)
+
+ require.Equal(t, ownerID, tok.GetOwnerID())
+ }
+
+ { // ValidUntil
+ e := uint64(5)
+
+ tok.SetExpirationEpoch(e)
+
+ require.Equal(t, e, tok.ExpirationEpoch())
+ }
+
+ { // OwnerKey
+ key := make([]byte, 10)
+ _, err := rand.Read(key)
+ require.NoError(t, err)
+
+ tok.SetOwnerKey(key)
+
+ require.Equal(t, key, tok.GetOwnerKey())
+ }
+
+ { // Signature
+ sig := make([]byte, 10)
+ _, err := rand.Read(sig)
+ require.NoError(t, err)
+
+ tok.SetSignature(sig)
+
+ require.Equal(t, sig, tok.GetSignature())
+ }
+}
+
+func TestSignVerifyBearerToken(t *testing.T) {
+ var token BearerToken = new(testBearerToken)
+
+ // create private key for signing
+ sk := test.DecodeKey(0)
+ pk := &sk.PublicKey
+
+ rules := []byte{1, 2, 3}
+ token.SetACLRules(rules)
+
+ ownerID := OwnerID{}
+ _, err := rand.Read(ownerID[:])
+ require.NoError(t, err)
+ token.SetOwnerID(ownerID)
+
+ fEpoch := uint64(2)
+ token.SetExpirationEpoch(fEpoch)
+
+ signedToken := NewSignedBearerToken(token)
+ verifiedToken := NewVerifiedBearerToken(token)
+
+ // sign and verify token
+ require.NoError(t, AddSignatureWithKey(sk, signedToken))
+ require.NoError(t, VerifySignatureWithKey(pk, verifiedToken))
+
+ items := []struct {
+ corrupt func()
+ restore func()
+ }{
+ { // ACLRules
+ corrupt: func() {
+ token.SetACLRules(append(rules, 1))
+ },
+ restore: func() {
+ token.SetACLRules(rules)
+ },
+ },
+ { // Owner ID
+ corrupt: func() {
+ ownerID[0]++
+ token.SetOwnerID(ownerID)
+ },
+ restore: func() {
+ ownerID[0]--
+ token.SetOwnerID(ownerID)
+ },
+ },
+ { // Expiration epoch
+ corrupt: func() {
+ token.SetExpirationEpoch(fEpoch + 1)
+ },
+ restore: func() {
+ token.SetExpirationEpoch(fEpoch)
+ },
+ },
+ }
+
+ for _, v := range items {
+ v.corrupt()
+ require.Error(t, VerifySignatureWithKey(pk, verifiedToken))
+ v.restore()
+ require.NoError(t, VerifySignatureWithKey(pk, verifiedToken))
+ }
+}
+
+func TestBearerTokenMsg_Setters(t *testing.T) {
+ s := new(BearerTokenMsg)
+
+ aclRules := []byte{1, 2, 3}
+ s.SetACLRules(aclRules)
+ require.Equal(t, aclRules, s.GetACLRules())
+
+ validUntil := uint64(6)
+ s.SetValidUntil(validUntil)
+ require.Equal(t, validUntil, s.GetValidUntil())
+
+ s.SetExpirationEpoch(validUntil + 1)
+ require.Equal(t, validUntil+1, s.ExpirationEpoch())
+
+ ownerID := OwnerID{1, 2, 3}
+ s.SetOwnerID(ownerID)
+ require.Equal(t, ownerID, s.GetOwnerID())
+
+ ownerKey := []byte{4, 5, 6}
+ s.SetOwnerKey(ownerKey)
+ require.Equal(t, ownerKey, s.GetOwnerKey())
+
+ sig := []byte{7, 8, 9}
+ s.SetSignature(sig)
+ require.Equal(t, sig, s.GetSignature())
+}
diff --git a/service/errors.go b/service/errors.go
index f3a0dfc0..e1c49002 100644
--- a/service/errors.go
+++ b/service/errors.go
@@ -36,14 +36,18 @@ const ErrEmptyDataWithSignature = internal.Error("empty data with signature")
// negative length for slice allocation.
const ErrNegativeLength = internal.Error("negative slice length")
-// ErrNilDataWithTokenSignAccumulator is returned by functions that expect
-// a non-nil DataWithTokenSignAccumulator, but received nil.
-const ErrNilDataWithTokenSignAccumulator = internal.Error("signed data with token is nil")
+// ErrNilRequestSignedData is returned by functions that expect
+// a non-nil RequestSignedData, but received nil.
+const ErrNilRequestSignedData = internal.Error("request signed data is nil")
-// ErrNilSignatureKeySourceWithToken is returned by functions that expect
-// a non-nil SignatureKeySourceWithToken, but received nil.
-const ErrNilSignatureKeySourceWithToken = internal.Error("key-signature source with token is nil")
+// ErrNilRequestVerifyData is returned by functions that expect
+// a non-nil RequestVerifyData, but received nil.
+const ErrNilRequestVerifyData = internal.Error("request verification data is nil")
// ErrNilSignedDataReader is returned by functions that expect
// a non-nil SignedDataReader, but received nil.
const ErrNilSignedDataReader = internal.Error("signed data reader is nil")
+
+// ErrNilSignKeyPairAccumulator is returned by functions that expect
+// a non-nil SignKeyPairAccumulator, but received nil.
+const ErrNilSignKeyPairAccumulator = internal.Error("signature-key pair accumulator is nil")
diff --git a/service/meta.go b/service/meta.go
index 3f017584..557d0762 100644
--- a/service/meta.go
+++ b/service/meta.go
@@ -1,5 +1,17 @@
package service
+import (
+ "io"
+)
+
+type extHdrWrapper struct {
+ msg *RequestExtendedHeader_KV
+}
+
+type extHdrSrcWrapper struct {
+ extHdrSrc ExtendedHeadersSource
+}
+
// CutMeta returns current value and sets RequestMetaHeader to empty value.
func (m *RequestMetaHeader) CutMeta() RequestMetaHeader {
cp := *m
@@ -11,3 +23,109 @@ func (m *RequestMetaHeader) CutMeta() RequestMetaHeader {
func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) {
*m = v
}
+
+// ExtendedHeadersSignedData wraps passed ExtendedHeadersSource and returns SignedDataSource.
+func ExtendedHeadersSignedData(headers ExtendedHeadersSource) SignedDataSource {
+ return &extHdrSrcWrapper{
+ extHdrSrc: headers,
+ }
+}
+
+// SignedData returns extended headers in a binary representation.
+func (s extHdrSrcWrapper) SignedData() ([]byte, error) {
+ return SignedDataFromReader(s)
+}
+
+// SignedDataSize returns the length of extended headers slice.
+func (s extHdrSrcWrapper) SignedDataSize() (res int) {
+ if s.extHdrSrc != nil {
+ for _, h := range s.extHdrSrc.ExtendedHeaders() {
+ if h != nil {
+ res += len(h.Key()) + len(h.Value())
+ }
+ }
+ }
+
+ return
+}
+
+// ReadSignedData copies a binary representation of the extended headers to passed buffer.
+//
+// If buffer length is less than required, io.ErrUnexpectedEOF returns.
+func (s extHdrSrcWrapper) ReadSignedData(p []byte) (int, error) {
+ sz := s.SignedDataSize()
+ if len(p) < sz {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ if s.extHdrSrc != nil {
+ off := 0
+ for _, h := range s.extHdrSrc.ExtendedHeaders() {
+ if h == nil {
+ continue
+ }
+
+ off += copy(p[off:], []byte(h.Key()))
+
+ off += copy(p[off:], []byte(h.Value()))
+ }
+ }
+
+ return sz, nil
+}
+
+// SetK is a K field setter.
+func (m *RequestExtendedHeader_KV) SetK(v string) {
+ m.K = v
+}
+
+// SetV is a V field setter.
+func (m *RequestExtendedHeader_KV) SetV(v string) {
+ m.V = v
+}
+
+// SetHeaders is a Headers field setter.
+func (m *RequestExtendedHeader) SetHeaders(v []RequestExtendedHeader_KV) {
+ m.Headers = v
+}
+
+func wrapExtendedHeaderKV(msg *RequestExtendedHeader_KV) extHdrWrapper {
+ return extHdrWrapper{
+ msg: msg,
+ }
+}
+
+// Key returns the result of K field getter.
+//
+// If message is nil, empty string returns.
+func (m extHdrWrapper) Key() string {
+ if m.msg != nil {
+ return m.msg.GetK()
+ }
+
+ return ""
+}
+
+// Value returns the result of V field getter.
+//
+// If message is nil, empty string returns.
+func (m extHdrWrapper) Value() string {
+ if m.msg != nil {
+ return m.msg.GetV()
+ }
+
+ return ""
+}
+
+// ExtendedHeaders composes ExtendedHeader list from the Headers field getter result.
+func (m RequestExtendedHeader) ExtendedHeaders() []ExtendedHeader {
+ hs := m.GetHeaders()
+
+ res := make([]ExtendedHeader, 0, len(hs))
+
+ for i := range hs {
+ res = append(res, wrapExtendedHeaderKV(&hs[i]))
+ }
+
+ return res
+}
diff --git a/service/meta.pb.go b/service/meta.pb.go
index 25a54690..0d630774 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 093f1185..8171980f 100644
--- a/service/meta.proto
+++ b/service/meta.proto
@@ -19,6 +19,8 @@ message RequestMetaHeader {
uint32 Version = 3;
// Raw determines whether the request is raw or not
bool Raw = 4;
+ // ExtendedHeader carries extended headers of the request
+ RequestExtendedHeader ExtendedHeader = 5 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
}
// ResponseMetaHeader contains meta information based on request processing by server
@@ -30,3 +32,18 @@ message ResponseMetaHeader {
// TODO: not used for now, should be implemented in future
uint32 Version = 2;
}
+
+// RequestExtendedHeader contains extended headers of request
+message RequestExtendedHeader {
+ // KV contains string key-value pair
+ message KV {
+ // K carries extended header key
+ string K = 1;
+
+ // V carries extended header value
+ string V = 2;
+ }
+
+ // Headers carries list of key-value headers
+ repeated KV Headers = 1 [(gogoproto.nullable) = false];
+}
diff --git a/service/meta_test.go b/service/meta_test.go
index a0b85ef9..9e9c0129 100644
--- a/service/meta_test.go
+++ b/service/meta_test.go
@@ -23,3 +23,77 @@ func TestCutRestoreMeta(t *testing.T) {
require.Equal(t, item(), v1)
}
}
+
+func TestRequestExtendedHeader_KV_Setters(t *testing.T) {
+ s := new(RequestExtendedHeader_KV)
+
+ key := "key"
+ s.SetK(key)
+ require.Equal(t, key, s.GetK())
+
+ val := "val"
+ s.SetV(val)
+ require.Equal(t, val, s.GetV())
+}
+
+func TestRequestExtendedHeader_SetHeaders(t *testing.T) {
+ s := new(RequestExtendedHeader)
+
+ hdr := RequestExtendedHeader_KV{}
+ hdr.SetK("key")
+ hdr.SetV("val")
+
+ hdrs := []RequestExtendedHeader_KV{
+ hdr,
+ }
+
+ s.SetHeaders(hdrs)
+
+ require.Equal(t, hdrs, s.GetHeaders())
+}
+
+func TestExtHdrWrapper(t *testing.T) {
+ s := wrapExtendedHeaderKV(nil)
+ require.Empty(t, s.Key())
+ require.Empty(t, s.Value())
+
+ msg := new(RequestExtendedHeader_KV)
+ s = wrapExtendedHeaderKV(msg)
+
+ key := "key"
+ msg.SetK(key)
+ require.Equal(t, key, s.Key())
+
+ val := "val"
+ msg.SetV(val)
+ require.Equal(t, val, s.Value())
+}
+
+func TestRequestExtendedHeader_ExtendedHeaders(t *testing.T) {
+ var (
+ k1, v1 = "key1", "value1"
+ k2, v2 = "key2", "value2"
+ h1 = new(RequestExtendedHeader_KV)
+ h2 = new(RequestExtendedHeader_KV)
+ )
+
+ h1.SetK(k1)
+ h1.SetV(v1)
+
+ h2.SetK(k2)
+ h2.SetV(v2)
+
+ s := new(RequestExtendedHeader)
+ s.SetHeaders([]RequestExtendedHeader_KV{
+ *h1, *h2,
+ })
+
+ xHdrs := s.ExtendedHeaders()
+ require.Len(t, xHdrs, 2)
+
+ require.Equal(t, k1, xHdrs[0].Key())
+ require.Equal(t, v1, xHdrs[0].Value())
+
+ require.Equal(t, k2, xHdrs[1].Key())
+ require.Equal(t, v2, xHdrs[1].Value())
+}
diff --git a/service/sign.go b/service/sign.go
index 5b1548f0..50453b9f 100644
--- a/service/sign.go
+++ b/service/sign.go
@@ -2,9 +2,11 @@ package service
import (
"crypto/ecdsa"
+ "io"
"sync"
crypto "github.com/nspcc-dev/neofs-crypto"
+ "github.com/pkg/errors"
)
type keySign struct {
@@ -12,6 +14,20 @@ type keySign struct {
sign []byte
}
+type signSourceGroup struct {
+ SignKeyPairSource
+ SignKeyPairAccumulator
+
+ sources []SignedDataSource
+}
+
+type signReadersGroup struct {
+ SignKeyPairSource
+ SignKeyPairAccumulator
+
+ readers []SignedDataReader
+}
+
var bytesPool = sync.Pool{
New: func() interface{} {
return make([]byte, 5<<20)
@@ -176,54 +192,199 @@ func VerifySignatureWithKey(key *ecdsa.PublicKey, src DataWithSignature) error {
)
}
-// SignDataWithSessionToken calculates data with token signature and adds it to accumulator.
+// SignRequestData calculates request data signature and adds it to accumulator.
//
-// Any change of data or session token info provoke signature breakdown.
+// Any change of request data provoke signature breakdown.
//
// If passed private key is nil, crypto.ErrEmptyPrivateKey returns.
-// If passed DataWithTokenSignAccumulator is nil, ErrNilDataWithTokenSignAccumulator returns.
-func SignDataWithSessionToken(key *ecdsa.PrivateKey, src DataWithTokenSignAccumulator) error {
+// If passed RequestSignedData is nil, ErrNilRequestSignedData returns.
+func SignRequestData(key *ecdsa.PrivateKey, src RequestSignedData) error {
if src == nil {
- return ErrNilDataWithTokenSignAccumulator
- } else if r, ok := src.(SignedDataReader); ok {
- return AddSignatureWithKey(key, &signDataReaderWithToken{
- SignedDataSource: src,
- SignKeyPairAccumulator: src,
-
- rdr: r,
- token: src.GetSessionToken(),
- },
- )
+ return ErrNilRequestSignedData
}
- return AddSignatureWithKey(key, &signAccumWithToken{
- SignedDataSource: src,
- SignKeyPairAccumulator: src,
+ sigSrc, err := GroupSignedPayloads(
+ src,
+ src,
+ NewSignedSessionToken(
+ src.GetSessionToken(),
+ ),
+ NewSignedBearerToken(
+ src.GetBearerToken(),
+ ),
+ ExtendedHeadersSignedData(src),
+ )
+ if err != nil {
+ return err
+ }
- token: src.GetSessionToken(),
- })
+ return AddSignatureWithKey(key, sigSrc)
}
-// VerifyAccumulatedSignaturesWithToken checks if accumulated key-signature pairs of data with token are valid.
+// VerifyRequestData checks if accumulated key-signature pairs of data with token are valid.
//
-// If passed DataWithTokenSignSource is nil, ErrNilSignatureKeySourceWithToken returns.
-func VerifyAccumulatedSignaturesWithToken(src DataWithTokenSignSource) error {
+// If passed RequestVerifyData is nil, ErrNilRequestVerifyData returns.
+func VerifyRequestData(src RequestVerifyData) error {
if src == nil {
- return ErrNilSignatureKeySourceWithToken
- } else if r, ok := src.(SignedDataReader); ok {
- return VerifyAccumulatedSignatures(&signDataReaderWithToken{
- SignedDataSource: src,
- SignKeyPairSource: src,
-
- rdr: r,
- token: src.GetSessionToken(),
- })
+ return ErrNilRequestVerifyData
}
- return VerifyAccumulatedSignatures(&signAccumWithToken{
- SignedDataSource: src,
- SignKeyPairSource: src,
+ verSrc, err := GroupVerifyPayloads(
+ src,
+ src,
+ NewVerifiedSessionToken(
+ src.GetSessionToken(),
+ ),
+ NewVerifiedBearerToken(
+ src.GetBearerToken(),
+ ),
+ ExtendedHeadersSignedData(src),
+ )
+ if err != nil {
+ return err
+ }
- token: src.GetSessionToken(),
- })
+ return VerifyAccumulatedSignatures(verSrc)
+}
+
+// SignedData returns payload bytes concatenation from all sources keeping order.
+func (s signSourceGroup) SignedData() ([]byte, error) {
+ chunks := make([][]byte, 0, len(s.sources))
+ sz := 0
+
+ for i := range s.sources {
+ data, err := s.sources[i].SignedData()
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not get signed payload of element #%d", i)
+ }
+
+ chunks = append(chunks, data)
+
+ sz += len(data)
+ }
+
+ res := make([]byte, sz)
+ off := 0
+
+ for i := range chunks {
+ off += copy(res[off:], chunks[i])
+ }
+
+ return res, nil
+}
+
+// SignedData returns payload bytes concatenation from all readers.
+func (s signReadersGroup) SignedData() ([]byte, error) {
+ return SignedDataFromReader(s)
+}
+
+// SignedDataSize returns the sum of sizes of all readers.
+func (s signReadersGroup) SignedDataSize() (sz int) {
+ for i := range s.readers {
+ sz += s.readers[i].SignedDataSize()
+ }
+
+ return
+}
+
+// ReadSignedData reads data from all readers to passed buffer keeping order.
+//
+// If the buffer size is insufficient, io.ErrUnexpectedEOF returns.
+func (s signReadersGroup) ReadSignedData(p []byte) (int, error) {
+ sz := s.SignedDataSize()
+ if len(p) < sz {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ off := 0
+
+ for i := range s.readers {
+ n, err := s.readers[i].ReadSignedData(p[off:])
+ off += n
+ if err != nil {
+ return off, errors.Wrapf(err, "could not read signed payload of element #%d", i)
+ }
+ }
+
+ return off, nil
+}
+
+// GroupSignedPayloads groups SignKeyPairAccumulator and SignedDataSource list to DataWithSignKeyAccumulator.
+//
+// If passed SignKeyPairAccumulator is nil, ErrNilSignKeyPairAccumulator returns.
+//
+// Signed payload of the result is a concatenation of payloads of list elements keeping order.
+// Nil elements in list are ignored.
+//
+// If all elements implement SignedDataReader, result implements it too.
+func GroupSignedPayloads(acc SignKeyPairAccumulator, sources ...SignedDataSource) (DataWithSignKeyAccumulator, error) {
+ if acc == nil {
+ return nil, ErrNilSignKeyPairAccumulator
+ }
+
+ return groupPayloads(acc, nil, sources...), nil
+}
+
+// GroupVerifyPayloads groups SignKeyPairSource and SignedDataSource list to DataWithSignKeySource.
+//
+// If passed SignKeyPairSource is nil, ErrNilSignatureKeySource returns.
+//
+// Signed payload of the result is a concatenation of payloads of list elements keeping order.
+// Nil elements in list are ignored.
+//
+// If all elements implement SignedDataReader, result implements it too.
+func GroupVerifyPayloads(src SignKeyPairSource, sources ...SignedDataSource) (DataWithSignKeySource, error) {
+ if src == nil {
+ return nil, ErrNilSignatureKeySource
+ }
+
+ return groupPayloads(nil, src, sources...), nil
+}
+
+func groupPayloads(acc SignKeyPairAccumulator, src SignKeyPairSource, sources ...SignedDataSource) interface {
+ SignedDataSource
+ SignKeyPairSource
+ SignKeyPairAccumulator
+} {
+ var allReaders bool
+
+ for i := range sources {
+ if sources[i] == nil {
+ continue
+ } else if _, allReaders = sources[i].(SignedDataReader); !allReaders {
+ break
+ }
+ }
+
+ if !allReaders {
+ res := &signSourceGroup{
+ SignKeyPairSource: src,
+ SignKeyPairAccumulator: acc,
+
+ sources: make([]SignedDataSource, 0, len(sources)),
+ }
+
+ for i := range sources {
+ if sources[i] != nil {
+ res.sources = append(res.sources, sources[i])
+ }
+ }
+
+ return res
+ }
+
+ res := &signReadersGroup{
+ SignKeyPairSource: src,
+ SignKeyPairAccumulator: acc,
+
+ readers: make([]SignedDataReader, 0, len(sources)),
+ }
+
+ for i := range sources {
+ if sources[i] != nil {
+ res.readers = append(res.readers, sources[i].(SignedDataReader))
+ }
+ }
+
+ return res
}
diff --git a/service/sign_test.go b/service/sign_test.go
index 5cb7c409..6f1e9136 100644
--- a/service/sign_test.go
+++ b/service/sign_test.go
@@ -18,6 +18,10 @@ type testSignedDataSrc struct {
sig []byte
key *ecdsa.PublicKey
token SessionToken
+
+ bearer BearerToken
+
+ extHdrs []ExtendedHeader
}
type testSignedDataReader struct {
@@ -54,6 +58,14 @@ func (s testSignedDataSrc) GetSessionToken() SessionToken {
return s.token
}
+func (s testSignedDataSrc) GetBearerToken() BearerToken {
+ return s.bearer
+}
+
+func (s testSignedDataSrc) ExtendedHeaders() []ExtendedHeader {
+ return s.extHdrs
+}
+
func (s testSignedDataReader) SignedDataSize() int {
return len(s.data)
}
@@ -256,47 +268,63 @@ func TestVerifySignatureWithKey(t *testing.T) {
require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src))
}
-func TestSignVerifyDataWithSessionToken(t *testing.T) {
- // sign with empty DataWithTokenSignAccumulator
+func TestSignVerifyRequestData(t *testing.T) {
+ // sign with empty RequestSignedData
require.EqualError(t,
- SignDataWithSessionToken(nil, nil),
- ErrNilDataWithTokenSignAccumulator.Error(),
+ SignRequestData(nil, nil),
+ ErrNilRequestSignedData.Error(),
)
- // verify with empty DataWithTokenSignSource
+ // verify with empty RequestVerifyData
require.EqualError(t,
- VerifyAccumulatedSignaturesWithToken(nil),
- ErrNilSignatureKeySourceWithToken.Error(),
+ VerifyRequestData(nil),
+ ErrNilRequestVerifyData.Error(),
)
// create test session token
var (
token = new(Token)
initVerb = Token_Info_Verb(1)
+
+ bearer = new(BearerTokenMsg)
+ bearerEpoch = uint64(8)
+
+ extHdrKey = "key"
+ extHdr = new(RequestExtendedHeader_KV)
)
token.SetVerb(initVerb)
+ bearer.SetExpirationEpoch(bearerEpoch)
+
+ extHdr.SetK(extHdrKey)
+
// create test data with token
src := &testSignedDataSrc{
data: testData(t, 10),
token: token,
+
+ bearer: bearer,
+
+ extHdrs: []ExtendedHeader{
+ wrapExtendedHeaderKV(extHdr),
+ },
}
// create test private key
sk := test.DecodeKey(0)
// sign with private key
- require.NoError(t, SignDataWithSessionToken(sk, src))
+ require.NoError(t, SignRequestData(sk, src))
// ascertain that verification is passed
- require.NoError(t, VerifyAccumulatedSignaturesWithToken(src))
+ require.NoError(t, VerifyRequestData(src))
// break the data
src.data[0]++
// ascertain that verification is failed
- require.Error(t, VerifyAccumulatedSignaturesWithToken(src))
+ require.Error(t, VerifyRequestData(src))
// restore the data
src.data[0]--
@@ -305,13 +333,37 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) {
token.SetVerb(initVerb + 1)
// ascertain that verification is failed
- require.Error(t, VerifyAccumulatedSignaturesWithToken(src))
+ require.Error(t, VerifyRequestData(src))
// restore the token
token.SetVerb(initVerb)
// ascertain that verification is passed
- require.NoError(t, VerifyAccumulatedSignaturesWithToken(src))
+ require.NoError(t, VerifyRequestData(src))
+
+ // break the Bearer token
+ bearer.SetExpirationEpoch(bearerEpoch + 1)
+
+ // ascertain that verification is failed
+ require.Error(t, VerifyRequestData(src))
+
+ // restore the Bearer token
+ bearer.SetExpirationEpoch(bearerEpoch)
+
+ // ascertain that verification is passed
+ require.NoError(t, VerifyRequestData(src))
+
+ // break the extended header
+ extHdr.SetK(extHdrKey + "1")
+
+ // ascertain that verification is failed
+ require.Error(t, VerifyRequestData(src))
+
+ // restore the extended header
+ extHdr.SetK(extHdrKey)
+
+ // ascertain that verification is passed
+ require.NoError(t, VerifyRequestData(src))
// wrap to data reader
rdr := &testSignedDataReader{
@@ -319,8 +371,8 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) {
}
// sign with private key
- require.NoError(t, SignDataWithSessionToken(sk, rdr))
+ require.NoError(t, SignRequestData(sk, rdr))
// ascertain that verification is passed
- require.NoError(t, VerifyAccumulatedSignaturesWithToken(rdr))
+ require.NoError(t, VerifyRequestData(rdr))
}
diff --git a/service/types.go b/service/types.go
index 66582f56..75a5a0a8 100644
--- a/service/types.go
+++ b/service/types.go
@@ -250,20 +250,67 @@ type DataWithSignKeySource interface {
SignKeyPairSource
}
-// SignedDataWithToken is an interface of data-token pair with read access.
-type SignedDataWithToken interface {
+// RequestData is an interface of the request information with read access.
+type RequestData interface {
SignedDataSource
SessionTokenSource
+ BearerTokenSource
+ ExtendedHeadersSource
}
-// DataWithTokenSignAccumulator is an interface of data-token pair with signature write access.
-type DataWithTokenSignAccumulator interface {
- SignedDataWithToken
+// RequestSignedData is an interface of request information with signature write access.
+type RequestSignedData interface {
+ RequestData
SignKeyPairAccumulator
}
-// DataWithTokenSignSource is an interface of data-token pair with signature read access.
-type DataWithTokenSignSource interface {
- SignedDataWithToken
+// RequestVerifyData is an interface of request information with signature read access.
+type RequestVerifyData interface {
+ RequestData
SignKeyPairSource
}
+
+// ACLRulesSource is an interface of the container of binary extended ACL rules with read access.
+type ACLRulesSource interface {
+ GetACLRules() []byte
+}
+
+// ACLRulesContainer is an interface of the container of binary extended ACL rules.
+type ACLRulesContainer interface {
+ ACLRulesSource
+ SetACLRules([]byte)
+}
+
+// BearerTokenInfo is an interface of a fixed set of Bearer token information value containers.
+// Contains:
+// - binary extended ACL rules;
+// - expiration epoch number;
+// - ID of the token's owner.
+type BearerTokenInfo interface {
+ ACLRulesContainer
+ ExpirationEpochContainer
+ OwnerIDContainer
+}
+
+// BearerToken is an interface of Bearer token information and key-signature pair.
+type BearerToken interface {
+ BearerTokenInfo
+ OwnerKeyContainer
+ SignatureContainer
+}
+
+// BearerTokenSource is an interface of the container of a BearerToken with read access.
+type BearerTokenSource interface {
+ GetBearerToken() BearerToken
+}
+
+// ExtendedHeader is an interface of string key-value pair with read access.
+type ExtendedHeader interface {
+ Key() string
+ Value() string
+}
+
+// ExtendedHeadersSource is an interface of ExtendedHeader list with read access.
+type ExtendedHeadersSource interface {
+ ExtendedHeaders() []ExtendedHeader
+}
diff --git a/service/verify.go b/service/verify.go
index 62db2f53..e1caa068 100644
--- a/service/verify.go
+++ b/service/verify.go
@@ -67,6 +67,11 @@ func (m *RequestVerificationHeader) SetToken(token *Token) {
m.Token = token
}
+// SetBearer is a Bearer field setter.
+func (m *RequestVerificationHeader) SetBearer(v *BearerTokenMsg) {
+ m.Bearer = v
+}
+
// testCustomField for test usage only.
type testCustomField [8]uint32
@@ -98,3 +103,14 @@ func (t testCustomField) MarshalTo(data []byte) (int, error) { return 0, nil }
// Marshal skip, it's for test usage only.
func (t testCustomField) Marshal() ([]byte, error) { return nil, nil }
+
+// GetBearerToken wraps Bearer field and return BearerToken interface.
+//
+// If Bearer field value is nil, nil returns.
+func (m RequestVerificationHeader) GetBearerToken() BearerToken {
+ if t := m.GetBearer(); t != nil {
+ return t
+ }
+
+ return nil
+}
diff --git a/service/verify.pb.go b/service/verify.pb.go
index d198302b..02f3f39c 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 a6619a6c..a7e694f6 100644
--- a/service/verify.proto
+++ b/service/verify.proto
@@ -23,6 +23,9 @@ message RequestVerificationHeader {
// Token is a token of the session within which the request is sent
Token Token = 2;
+
+ // Bearer is a Bearer token of the request
+ BearerTokenMsg Bearer = 3;
}
// User token granting rights for object manipulation
@@ -91,3 +94,26 @@ message TokenLifetime {
// uint32 Version = 2;
// bytes Data = 3;
// }
+
+// BearerTokenMsg carries information about request ACL rules with limited lifetime
+message BearerTokenMsg {
+ message Info {
+ // ACLRules carries a binary representation of the table of extended ACL rules
+ bytes ACLRules = 1;
+
+ // OwnerID is an owner of token
+ bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false];
+
+ // ValidUntil carries a last epoch of token lifetime
+ uint64 ValidUntil = 3;
+ }
+
+ // TokenInfo is a grouped information about token
+ Info TokenInfo = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
+
+ // OwnerKey is a public key of the token owner
+ bytes OwnerKey = 2;
+
+ // Signature is a signature of token information
+ bytes Signature = 3;
+}
diff --git a/service/verify_test.go b/service/verify_test.go
index c6e4d619..5ab87534 100644
--- a/service/verify_test.go
+++ b/service/verify_test.go
@@ -69,7 +69,7 @@ func BenchmarkSignDataWithSessionToken(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- require.NoError(b, SignDataWithSessionToken(key, req))
+ require.NoError(b, SignRequestData(key, req))
}
}
@@ -91,14 +91,14 @@ func BenchmarkVerifyAccumulatedSignaturesWithToken(b *testing.B) {
for i := 0; i < 10; i++ {
key := test.DecodeKey(i)
- require.NoError(b, SignDataWithSessionToken(key, req))
+ require.NoError(b, SignRequestData(key, req))
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- require.NoError(b, VerifyAccumulatedSignaturesWithToken(req))
+ require.NoError(b, VerifyRequestData(req))
}
}
@@ -115,3 +115,26 @@ func TestRequestVerificationHeader_SetToken(t *testing.T) {
require.Equal(t, token, h.GetToken())
}
+
+func TestRequestVerificationHeader_SetBearer(t *testing.T) {
+ aclRules := []byte{1, 2, 3}
+
+ token := new(BearerTokenMsg)
+ token.SetACLRules(aclRules)
+
+ h := new(RequestVerificationHeader)
+
+ h.SetBearer(token)
+
+ require.Equal(t, token, h.GetBearer())
+}
+
+func TestRequestVerificationHeader_GetBearerToken(t *testing.T) {
+ s := new(RequestVerificationHeader)
+
+ require.Nil(t, s.GetBearerToken())
+
+ bearer := new(BearerTokenMsg)
+ s.SetBearer(bearer)
+ require.Equal(t, bearer, s.GetBearerToken())
+}
diff --git a/session/create.go b/session/create.go
index 35d05400..412d1fd3 100644
--- a/session/create.go
+++ b/session/create.go
@@ -53,7 +53,7 @@ func (s gRPCCreator) Create(ctx context.Context, p CreateParamsSource) (CreateRe
req.SetExpirationEpoch(p.ExpirationEpoch())
// sign with private key
- if err := service.SignDataWithSessionToken(s.key, req); err != nil {
+ if err := service.SignRequestData(s.key, req); err != nil {
return nil, err
}
diff --git a/session/create_test.go b/session/create_test.go
index 732d4fd6..943c5daa 100644
--- a/session/create_test.go
+++ b/session/create_test.go
@@ -84,7 +84,7 @@ func TestGRPCCreator_Create(t *testing.T) {
require.Equal(t, ownerID, req.GetOwnerID())
require.Equal(t, created, req.CreationEpoch())
require.Equal(t, expired, req.ExpirationEpoch())
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(req))
+ require.NoError(t, service.VerifyRequestData(req))
},
resp: &CreateResponse{
ID: TokenID{1, 2, 3},
diff --git a/state/sign_test.go b/state/sign_test.go
index 9b2bca90..05af6548 100644
--- a/state/sign_test.go
+++ b/state/sign_test.go
@@ -12,7 +12,7 @@ func TestRequestSign(t *testing.T) {
sk := test.DecodeKey(0)
type sigType interface {
- service.SignedDataWithToken
+ service.RequestData
service.SignKeyPairAccumulator
service.SignKeyPairSource
SetToken(*service.Token)
@@ -68,26 +68,26 @@ func TestRequestSign(t *testing.T) {
token := new(service.Token)
v.SetToken(token)
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
token.SetSessionKey(append(token.GetSessionKey(), 1))
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
{ // payload corruptions
for _, corruption := range item.payloadCorrupt {
v := item.constructor()
- require.NoError(t, service.SignDataWithSessionToken(sk, v))
+ require.NoError(t, service.SignRequestData(sk, v))
- require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.NoError(t, service.VerifyRequestData(v))
corruption(v)
- require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v))
+ require.Error(t, service.VerifyRequestData(v))
}
}
}