From 22af538c9895896bc324f51493e6d9d46b3b83fc Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Sat, 16 May 2020 15:28:35 +0300 Subject: [PATCH 1/8] Revert "service: add owner key to a signed payload of SessionToken" This reverts commit 1896264f --- service/token.go | 8 +++----- service/token_test.go | 27 --------------------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/service/token.go b/service/token.go index a5dd5da3..2497718a 100644 --- a/service/token.go +++ b/service/token.go @@ -174,11 +174,11 @@ func NewVerifiedSessionToken(token SessionToken) DataWithSignature { } } -func tokenInfoSize(v SessionTokenInfo) int { +func tokenInfoSize(v SessionKeySource) int { if v == nil { return 0 } - return fixedTokenDataSize + len(v.GetSessionKey()) + len(v.GetOwnerKey()) + return fixedTokenDataSize + len(v.GetSessionKey()) } // Fills passed buffer with signing token information bytes. @@ -208,9 +208,7 @@ func copyTokenSignedData(buf []byte, token SessionTokenInfo) { tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch()) off += 8 - off += copy(buf[off:], token.GetSessionKey()) - - copy(buf[off:], token.GetOwnerKey()) + copy(buf[off:], token.GetSessionKey()) } // SignedData concatenates signed data with session token information. Returns concatenation result. diff --git a/service/token_test.go b/service/token_test.go index 43e380d7..e8599e76 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -77,16 +77,6 @@ func TestTokenGettersSetters(t *testing.T) { require.Equal(t, key, tok.GetSessionKey()) } - { - 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) @@ -136,11 +126,6 @@ func TestSignToken(t *testing.T) { require.NoError(t, err) token.SetSessionKey(sessionKey) - ownerKey := make([]byte, 10) - _, err = rand.Read(ownerKey[:]) - require.NoError(t, err) - token.SetOwnerKey(ownerKey) - signedToken := NewSignedSessionToken(token) verifiedToken := NewVerifiedSessionToken(token) @@ -226,18 +211,6 @@ func TestSignToken(t *testing.T) { token.SetSessionKey(sessionKey) }, }, - { // Owner key - corrupt: func() { - ownerKey := token.GetOwnerKey() - ownerKey[0]++ - token.SetOwnerKey(ownerKey) - }, - restore: func() { - ownerKey := token.GetOwnerKey() - ownerKey[0]-- - token.SetOwnerKey(ownerKey) - }, - }, } for _, v := range items { From bd261cf56659247d41b5a437a6c2d35e96f84c66 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Sat, 16 May 2020 15:29:44 +0300 Subject: [PATCH 2/8] service: call OwnerKey setter in AddSignKey method implementation --- service/token.go | 9 +++++++-- service/token_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/service/token.go b/service/token.go index 2497718a..638539f6 100644 --- a/service/token.go +++ b/service/token.go @@ -6,6 +6,7 @@ import ( "io" "github.com/nspcc-dev/neofs-api-go/refs" + crypto "github.com/nspcc-dev/neofs-crypto" ) type signAccumWithToken struct { @@ -125,10 +126,14 @@ func (x Token_Info_Verb) Bytes() []byte { return data } -// AddSignKey calls a Signature field setter of token with passed signature. -func (s signedSessionToken) AddSignKey(sig []byte, _ *ecdsa.PublicKey) { +// AddSignKey calls a Signature field setter and an OwnerKey field setter with corresponding arguments. +func (s signedSessionToken) AddSignKey(sig []byte, key *ecdsa.PublicKey) { if s.SessionToken != nil { s.SessionToken.SetSignature(sig) + + s.SessionToken.SetOwnerKey( + crypto.MarshalPublicKey(key), + ) } } diff --git a/service/token_test.go b/service/token_test.go index e8599e76..4fb430a9 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/refs" + crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" ) @@ -220,3 +221,28 @@ func TestSignToken(t *testing.T) { require.NoError(t, VerifySignatureWithKey(pk, verifiedToken)) } } + +func TestSignedSessionToken_AddSignKey(t *testing.T) { + // nil SessionToken + s := new(signedSessionToken) + + require.NotPanics(t, func() { + s.AddSignKey(nil, nil) + }) + + // create test public key and signature + pk := &test.DecodeKey(0).PublicKey + sig := []byte{1, 2, 3} + + s.SessionToken = new(Token) + + // add key-signature pair to SessionToken + s.AddSignKey(sig, pk) + + require.Equal(t, sig, s.GetSignature()) + + require.Equal(t, + crypto.MarshalPublicKey(pk), + s.GetOwnerKey(), + ) +} From 2c571718d06d635f1cb5ca7f8bcb134a222f5357 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 18 May 2020 12:47:13 +0300 Subject: [PATCH 3/8] object: add unit test of HeadRequest.ReadSignedData method --- object/sign_test.go | 26 ++++++++++++++++++++++++++ object/types.go | 5 +++++ 2 files changed, 31 insertions(+) diff --git a/object/sign_test.go b/object/sign_test.go index 4df1c2b1..fb57baaf 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -187,3 +187,29 @@ func TestSignVerifyRequests(t *testing.T) { } } } + +func TestHeadRequest_ReadSignedData(t *testing.T) { + t.Run("full headers", func(t *testing.T) { + req := new(HeadRequest) + + // allocate buffer for reading + buf := make([]byte, req.SignedDataSize()) + buf[0] = 2 + + // set FullHeaders option + req.SetFullHeaders(true) + + _, err := req.ReadSignedData(buf) + require.NoError(t, err) + + require.Equal(t, byte(1), buf[0]) + + // unset FullHeaders option + req.SetFullHeaders(false) + + _, err = req.ReadSignedData(buf) + require.NoError(t, err) + + require.Equal(t, byte(0), buf[0]) + }) +} diff --git a/object/types.go b/object/types.go index c8d3f250..4edf5f40 100644 --- a/object/types.go +++ b/object/types.go @@ -386,3 +386,8 @@ func Stringify(dst io.Writer, obj *Object) error { return nil } + +// SetFullHeaders is a FullHeaders field setter. +func (m *HeadRequest) SetFullHeaders(v bool) { + m.FullHeaders = v +} From 1931bd590dbcee33e904a99d822ee3e494b777d5 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 18 May 2020 12:48:04 +0300 Subject: [PATCH 4/8] object: fix signed payload calculation of HeadRequest message In previous implementation first byte of buffer for HeadRequest signed payload was set to 1 if FullHeaders flag was set. Otherwise, this byte remained unchanged. For correct recording of a signed payload, it is necessary to explicitly set the first byte with the unset flag. --- object/sign.go | 2 ++ object/sign_test.go | 36 +++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/object/sign.go b/object/sign.go index 1ed3efa2..def7bc35 100644 --- a/object/sign.go +++ b/object/sign.go @@ -82,6 +82,8 @@ func (m HeadRequest) ReadSignedData(p []byte) (int, error) { if m.GetFullHeaders() { p[0] = 1 + } else { + p[0] = 0 } off := 1 diff --git a/object/sign_test.go b/object/sign_test.go index fb57baaf..abf4549f 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -1,6 +1,7 @@ package object import ( + "crypto/rand" "testing" "github.com/nspcc-dev/neofs-api-go/service" @@ -192,24 +193,29 @@ func TestHeadRequest_ReadSignedData(t *testing.T) { t.Run("full headers", func(t *testing.T) { req := new(HeadRequest) - // allocate buffer for reading - buf := make([]byte, req.SignedDataSize()) - buf[0] = 2 - - // set FullHeaders option - req.SetFullHeaders(true) - - _, err := req.ReadSignedData(buf) - require.NoError(t, err) - - require.Equal(t, byte(1), buf[0]) - - // unset FullHeaders option + // unset FullHeaders flag req.SetFullHeaders(false) - _, err = req.ReadSignedData(buf) + // allocate two different buffers for reading + buf1 := testData(t, req.SignedDataSize()) + buf2 := testData(t, req.SignedDataSize()) + + // read to both buffers + n1, err := req.ReadSignedData(buf1) require.NoError(t, err) - require.Equal(t, byte(0), buf[0]) + n2, err := req.ReadSignedData(buf2) + require.NoError(t, err) + + require.Equal(t, buf1[:n1], buf2[:n2]) }) } + +func testData(t *testing.T, sz int) []byte { + data := make([]byte, sz) + + _, err := rand.Read(data) + require.NoError(t, err) + + return data +} From af28735ca6f1eb95d6964a77fe0e07bb5fe63ce8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 18 May 2020 13:11:39 +0300 Subject: [PATCH 5/8] session: change PrivateToken interface methods This commit replaces PublicKey() and SignData() methods of PrivateToken with PrivateKey() in order to have the ability to sign data with session key using service package functions. --- session/private.go | 13 +++---------- session/private_test.go | 23 ++--------------------- session/types.go | 10 ++-------- 3 files changed, 7 insertions(+), 39 deletions(-) diff --git a/session/private.go b/session/private.go index 42bb2058..6c9c68da 100644 --- a/session/private.go +++ b/session/private.go @@ -4,8 +4,6 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" - - crypto "github.com/nspcc-dev/neofs-crypto" ) type pToken struct { @@ -30,14 +28,9 @@ func NewPrivateToken(validUntil uint64) (PrivateToken, error) { }, nil } -// Sign signs data with session private key. -func (t *pToken) Sign(data []byte) ([]byte, error) { - return crypto.Sign(t.sessionKey, data) -} - -// PublicKey returns a binary representation of the session public key. -func (t *pToken) PublicKey() []byte { - return crypto.MarshalPublicKey(&t.sessionKey.PublicKey) +// PrivateKey returns a binary representation of the session public key. +func (t *pToken) PrivateKey() *ecdsa.PrivateKey { + return t.sessionKey } func (t *pToken) Expired(epoch uint64) bool { diff --git a/session/private_test.go b/session/private_test.go index 8097b97a..9dedc29b 100644 --- a/session/private_test.go +++ b/session/private_test.go @@ -1,35 +1,16 @@ package session import ( - "crypto/rand" "testing" - crypto "github.com/nspcc-dev/neofs-crypto" "github.com/stretchr/testify/require" ) -func TestPrivateToken(t *testing.T) { +func TestPToken_PrivateKey(t *testing.T) { // create new private token pToken, err := NewPrivateToken(0) require.NoError(t, err) - - // generate data to sign - data := make([]byte, 10) - _, err = rand.Read(data) - require.NoError(t, err) - - // sign data via private token - sig, err := pToken.Sign(data) - require.NoError(t, err) - - // check signature - require.NoError(t, - crypto.Verify( - crypto.UnmarshalPublicKey(pToken.PublicKey()), - data, - sig, - ), - ) + require.NotNil(t, pToken.PrivateKey()) } func TestPToken_Expired(t *testing.T) { diff --git a/session/types.go b/session/types.go index ee13b927..95a00656 100644 --- a/session/types.go +++ b/session/types.go @@ -10,14 +10,8 @@ import ( // PrivateToken is an interface of session private part. type PrivateToken interface { - // PublicKey must return a binary representation of session public key. - PublicKey() []byte - - // Sign must return the signature of passed data. - // - // Resulting signature must be verified by crypto.Verify function - // with the session public key. - Sign([]byte) ([]byte, error) + // PrivateKey must return session private key. + PrivateKey() *ecdsa.PrivateKey // Expired must return true if and only if private token is expired in the given epoch number. Expired(uint64) bool From 291d5128400266fc5fc4192d2b6f30338ebfbe8a Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 18 May 2020 13:14:18 +0300 Subject: [PATCH 6/8] session: implement function for receiving session public key bytes After recent changes PrivateToken cannot directly return public key bytes. In order to provide this ability, this commit implements a function over PrivateToken interface. --- session/errors.go | 4 ++++ session/private.go | 21 ++++++++++++++++++++- session/private_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/session/errors.go b/session/errors.go index 3a9c1290..d35bed40 100644 --- a/session/errors.go +++ b/session/errors.go @@ -13,3 +13,7 @@ const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil") // ErrPrivateTokenNotFound is returned when addressed private token was // not found in storage. const ErrPrivateTokenNotFound = internal.Error("private token not found") + +// ErrNilPrivateToken is returned by functions that expect a non-nil +// PrivateToken, but received nil. +const ErrNilPrivateToken = internal.Error("private token is nil") diff --git a/session/private.go b/session/private.go index 6c9c68da..bb9242f4 100644 --- a/session/private.go +++ b/session/private.go @@ -4,6 +4,8 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + + crypto "github.com/nspcc-dev/neofs-crypto" ) type pToken struct { @@ -28,7 +30,24 @@ func NewPrivateToken(validUntil uint64) (PrivateToken, error) { }, nil } -// PrivateKey returns a binary representation of the session public key. +// PublicSessionToken returns a binary representation of session public key. +// +// If passed PrivateToken is nil, ErrNilPrivateToken returns. +// If passed PrivateToken carries nil private key, crypto.ErrEmptyPrivateKey returns. +func PublicSessionToken(pToken PrivateToken) ([]byte, error) { + if pToken == nil { + return nil, ErrNilPrivateToken + } + + sk := pToken.PrivateKey() + if sk == nil { + return nil, crypto.ErrEmptyPrivateKey + } + + return crypto.MarshalPublicKey(&sk.PublicKey), nil +} + +// PrivateKey is a session private key getter. func (t *pToken) PrivateKey() *ecdsa.PrivateKey { return t.sessionKey } diff --git a/session/private_test.go b/session/private_test.go index 9dedc29b..c6f81254 100644 --- a/session/private_test.go +++ b/session/private_test.go @@ -3,6 +3,7 @@ package session import ( "testing" + crypto "github.com/nspcc-dev/neofs-crypto" "github.com/stretchr/testify/require" ) @@ -49,3 +50,27 @@ func TestPrivateTokenKey_SetTokenID(t *testing.T) { require.Equal(t, tokenID, s.token) } + +func TestPublicSessionToken(t *testing.T) { + var err error + + // nil PrivateToken + _, err = PublicSessionToken(nil) + require.EqualError(t, err, ErrNilPrivateToken.Error()) + + // empty private key + var pToken PrivateToken = new(pToken) + _, err = PublicSessionToken(pToken) + require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) + + // correct PrivateToken + pToken, err = NewPrivateToken(0) + require.NoError(t, err) + + key := pToken.PrivateKey() + require.NotNil(t, key) + + res, err := PublicSessionToken(pToken) + require.NoError(t, err) + require.Equal(t, res, crypto.MarshalPublicKey(&key.PublicKey)) +} From 96a6bb484207ea65b0184a99a626781d2edba104 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 18 May 2020 13:44:32 +0300 Subject: [PATCH 7/8] object: implement SignedData() and AddSignKey() methods on IntegrityHeader --- object/sign.go | 11 +++++++++++ object/sign_test.go | 18 ++++++++++++++++++ object/types.go | 15 +++++++++++++++ object/types_test.go | 20 ++++++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/object/sign.go b/object/sign.go index def7bc35..3cdf94f7 100644 --- a/object/sign.go +++ b/object/sign.go @@ -1,6 +1,7 @@ package object import ( + "crypto/ecdsa" "encoding/binary" "io" @@ -259,3 +260,13 @@ func addressSize(addr Address) int { func addressBytes(addr Address) []byte { return append(addr.CID.Bytes(), addr.ObjectID.Bytes()...) } + +// SignedData returns the result of the ChecksumSignature field getter. +func (m IntegrityHeader) SignedData() ([]byte, error) { + return m.GetHeadersChecksum(), nil +} + +// AddSignKey calls the ChecksumSignature field setter with signature argument. +func (m *IntegrityHeader) AddSignKey(sign []byte, _ *ecdsa.PublicKey) { + m.SetSignature(sign) +} diff --git a/object/sign_test.go b/object/sign_test.go index abf4549f..9480fda7 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -219,3 +219,21 @@ func testData(t *testing.T, sz int) []byte { return data } + +func TestIntegrityHeaderSignMethods(t *testing.T) { + // create new IntegrityHeader + s := new(IntegrityHeader) + + // set test headers checksum + s.SetHeadersChecksum([]byte{1, 2, 3}) + + data, err := s.SignedData() + require.NoError(t, err) + require.Equal(t, data, s.GetHeadersChecksum()) + + // add signature + sig := []byte{4, 5, 6} + s.AddSignKey(sig, nil) + + require.Equal(t, sig, s.GetSignature()) +} diff --git a/object/types.go b/object/types.go index 4edf5f40..058cd701 100644 --- a/object/types.go +++ b/object/types.go @@ -391,3 +391,18 @@ func Stringify(dst io.Writer, obj *Object) error { func (m *HeadRequest) SetFullHeaders(v bool) { m.FullHeaders = v } + +// GetSignature is a ChecksumSignature field getter. +func (m IntegrityHeader) GetSignature() []byte { + return m.ChecksumSignature +} + +// SetSignature is a ChecksumSignature field setter. +func (m *IntegrityHeader) SetSignature(v []byte) { + m.ChecksumSignature = v +} + +// SetHeadersChecksum is a HeadersChecksum field setter. +func (m *IntegrityHeader) SetHeadersChecksum(v []byte) { + m.HeadersChecksum = v +} diff --git a/object/types_test.go b/object/types_test.go index 3f9292d6..0a7085f9 100644 --- a/object/types_test.go +++ b/object/types_test.go @@ -199,3 +199,23 @@ func TestObject_Copy(t *testing.T) { require.Equal(t, token, h.GetValue().(*Header_Token).Token) }) } + +func TestIntegrityHeaderGettersSetters(t *testing.T) { + t.Run("headers checksum", func(t *testing.T) { + data := []byte{1, 2, 3} + + v := new(IntegrityHeader) + + v.SetHeadersChecksum(data) + require.Equal(t, data, v.GetHeadersChecksum()) + }) + + t.Run("headers checksum", func(t *testing.T) { + data := []byte{1, 2, 3} + + v := new(IntegrityHeader) + + v.SetSignature(data) + require.Equal(t, data, v.GetSignature()) + }) +} From 114144646baed7fe68ad265fd0aa1df8358c522a Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 19 May 2020 13:29:27 +0300 Subject: [PATCH 8/8] Update changelog for v0.7.6 --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdb994e1..498e0287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,26 @@ # Changelog This is the changelog for NeoFS-API-Go +## [0.7.6] - 2020-05-19 + +### Added + +- `session.PublicSessionToken` function for session public key bytes receiving. +- The implementation of `service.DataWithSignKeyAccumulator` methods on `object.IntegrityHeader`. + +### Changed + +- The implementation of `AddSignKey` method on `service.signedSessionToken` structure. +- `session.PrivateTOken` interface methods group. + +### Removed + +- `OwnerKey` from `service.SessionToken` signed payload. + +### Fixed + +- Incorrect `object.HeadRequest.ReadSignedData` method implementation. + ## [0.7.5] - 2020-05-16 ### Added @@ -305,3 +325,4 @@ Initial public release [0.7.1]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.0...v0.7.1 [0.7.4]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.1...v0.7.4 [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