From 3f7d3f8a86d5df90e222e64f16b735b81766407c Mon Sep 17 00:00:00 2001
From: Leonard Lyubich <leonard@nspcc.ru>
Date: Thu, 18 Jun 2020 15:24:17 +0300
Subject: [PATCH 1/2] service: make RequestData to provide BearerTokenSource
 interface

---
 service/bearer.go      | 30 ++++++++++++++++++++++++++++++
 service/bearer_test.go | 15 +++++++++++++++
 service/sign_test.go   |  6 ++++++
 service/types.go       |  1 +
 service/verify.go      | 11 +++++++++++
 service/verify_test.go | 10 ++++++++++
 6 files changed, 73 insertions(+)

diff --git a/service/bearer.go b/service/bearer.go
index 6013e03..dc556ce 100644
--- a/service/bearer.go
+++ b/service/bearer.go
@@ -12,6 +12,10 @@ type signedBearerToken struct {
 	BearerToken
 }
 
+type bearerMsgWrapper struct {
+	*BearerTokenMsg
+}
+
 const fixedBearerTokenDataSize = 0 +
 	refs.OwnerIDSize +
 	8
@@ -124,3 +128,29 @@ func (m *BearerTokenMsg) SetOwnerKey(v []byte) {
 func (m *BearerTokenMsg) SetSignature(v []byte) {
 	m.Signature = v
 }
+
+func wrapBearerTokenMsg(msg *BearerTokenMsg) bearerMsgWrapper {
+	return bearerMsgWrapper{
+		BearerTokenMsg: msg,
+	}
+}
+
+// ExpirationEpoch returns the result of ValidUntil field getter.
+//
+// If message is nil, 0 returns.
+func (s bearerMsgWrapper) ExpirationEpoch() uint64 {
+	if s.BearerTokenMsg != nil {
+		return s.GetValidUntil()
+	}
+
+	return 0
+}
+
+// SetExpirationEpoch passes argument to ValidUntil field setter.
+//
+// If message is nil, nothing changes.
+func (s bearerMsgWrapper) SetExpirationEpoch(v uint64) {
+	if s.BearerTokenMsg != nil {
+		s.SetValidUntil(v)
+	}
+}
diff --git a/service/bearer_test.go b/service/bearer_test.go
index 9ece9c8..381f190 100644
--- a/service/bearer_test.go
+++ b/service/bearer_test.go
@@ -194,3 +194,18 @@ func TestBearerTokenMsg_Setters(t *testing.T) {
 	s.SetSignature(sig)
 	require.Equal(t, sig, s.GetSignature())
 }
+
+func TestBearerMsgWrapper_ExpirationEpoch(t *testing.T) {
+	s := wrapBearerTokenMsg(nil)
+	require.Zero(t, s.ExpirationEpoch())
+	require.NotPanics(t, func() {
+		s.SetExpirationEpoch(1)
+	})
+
+	msg := new(BearerTokenMsg)
+	s = wrapBearerTokenMsg(msg)
+
+	epoch := uint64(7)
+	s.SetExpirationEpoch(epoch)
+	require.Equal(t, epoch, s.ExpirationEpoch())
+}
diff --git a/service/sign_test.go b/service/sign_test.go
index ca469b8..8b67e5b 100644
--- a/service/sign_test.go
+++ b/service/sign_test.go
@@ -18,6 +18,8 @@ type testSignedDataSrc struct {
 	sig   []byte
 	key   *ecdsa.PublicKey
 	token SessionToken
+
+	bearer BearerToken
 }
 
 type testSignedDataReader struct {
@@ -54,6 +56,10 @@ func (s testSignedDataSrc) GetSessionToken() SessionToken {
 	return s.token
 }
 
+func (s testSignedDataSrc) GetBearerToken() BearerToken {
+	return s.bearer
+}
+
 func (s testSignedDataReader) SignedDataSize() int {
 	return len(s.data)
 }
diff --git a/service/types.go b/service/types.go
index 87c3a77..feba2e3 100644
--- a/service/types.go
+++ b/service/types.go
@@ -254,6 +254,7 @@ type DataWithSignKeySource interface {
 type RequestData interface {
 	SignedDataSource
 	SessionTokenSource
+	BearerTokenSource
 }
 
 // RequestSignedData is an interface of request information with signature write access.
diff --git a/service/verify.go b/service/verify.go
index 0673a01..9fbdfdf 100644
--- a/service/verify.go
+++ b/service/verify.go
@@ -103,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 returns 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 wrapBearerTokenMsg(t)
+	}
+
+	return nil
+}
diff --git a/service/verify_test.go b/service/verify_test.go
index 55ec65f..b42bb79 100644
--- a/service/verify_test.go
+++ b/service/verify_test.go
@@ -128,3 +128,13 @@ func TestRequestVerificationHeader_SetBearer(t *testing.T) {
 
 	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, wrapBearerTokenMsg(bearer), s.GetBearerToken())
+}

From a3569ad99e7f016cc59e20d9f41ef08bdd98c72c Mon Sep 17 00:00:00 2001
From: Leonard Lyubich <leonard@nspcc.ru>
Date: Thu, 18 Jun 2020 15:26:56 +0300
Subject: [PATCH 2/2] service: ad BearerToken to signed payload of the requests

---
 service/sign.go      |  6 ++++++
 service/sign_test.go | 19 +++++++++++++++++++
 service/verify.go    |  2 +-
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/service/sign.go b/service/sign.go
index eb1c16d..a0bb7e5 100644
--- a/service/sign.go
+++ b/service/sign.go
@@ -209,6 +209,9 @@ func SignRequestData(key *ecdsa.PrivateKey, src RequestSignedData) error {
 		NewSignedSessionToken(
 			src.GetSessionToken(),
 		),
+		NewSignedBearerToken(
+			src.GetBearerToken(),
+		),
 	)
 	if err != nil {
 		return err
@@ -231,6 +234,9 @@ func VerifyRequestData(src RequestVerifyData) error {
 		NewVerifiedSessionToken(
 			src.GetSessionToken(),
 		),
+		NewVerifiedBearerToken(
+			src.GetBearerToken(),
+		),
 	)
 	if err != nil {
 		return err
diff --git a/service/sign_test.go b/service/sign_test.go
index 8b67e5b..80c0d19 100644
--- a/service/sign_test.go
+++ b/service/sign_test.go
@@ -279,14 +279,21 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) {
 	var (
 		token    = new(Token)
 		initVerb = Token_Info_Verb(1)
+
+		bearer      = wrapBearerTokenMsg(new(BearerTokenMsg))
+		bearerEpoch = uint64(8)
 	)
 
 	token.SetVerb(initVerb)
 
+	bearer.SetExpirationEpoch(bearerEpoch)
+
 	// create test data with token
 	src := &testSignedDataSrc{
 		data:  testData(t, 10),
 		token: token,
+
+		bearer: bearer,
 	}
 
 	// create test private key
@@ -319,6 +326,18 @@ func TestSignVerifyDataWithSessionToken(t *testing.T) {
 	// ascertain that verification is passed
 	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))
+
 	// wrap to data reader
 	rdr := &testSignedDataReader{
 		testSignedDataSrc: src,
diff --git a/service/verify.go b/service/verify.go
index 9fbdfdf..ee2eecc 100644
--- a/service/verify.go
+++ b/service/verify.go
@@ -104,7 +104,7 @@ 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 returns wraps Bearer field and return BearerToken interface.
+// GetBearerToken wraps Bearer field and return BearerToken interface.
 //
 // If Bearer field value is nil, nil returns.
 func (m RequestVerificationHeader) GetBearerToken() BearerToken {