diff --git a/CHANGELOG.md b/CHANGELOG.md index bdb994e..498e028 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 diff --git a/object/sign.go b/object/sign.go index 1ed3efa..3cdf94f 100644 --- a/object/sign.go +++ b/object/sign.go @@ -1,6 +1,7 @@ package object import ( + "crypto/ecdsa" "encoding/binary" "io" @@ -82,6 +83,8 @@ func (m HeadRequest) ReadSignedData(p []byte) (int, error) { if m.GetFullHeaders() { p[0] = 1 + } else { + p[0] = 0 } off := 1 @@ -257,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 4df1c2b..9480fda 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" @@ -187,3 +188,52 @@ func TestSignVerifyRequests(t *testing.T) { } } } + +func TestHeadRequest_ReadSignedData(t *testing.T) { + t.Run("full headers", func(t *testing.T) { + req := new(HeadRequest) + + // unset FullHeaders flag + req.SetFullHeaders(false) + + // 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) + + 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 +} + +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 c8d3f25..058cd70 100644 --- a/object/types.go +++ b/object/types.go @@ -386,3 +386,23 @@ 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 +} + +// 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 3f9292d..0a7085f 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()) + }) +} diff --git a/service/token.go b/service/token.go index a5dd5da..638539f 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), + ) } } @@ -174,11 +179,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 +213,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 43e380d..4fb430a 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" ) @@ -77,16 +78,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 +127,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 +212,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 { @@ -247,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(), + ) +} diff --git a/session/errors.go b/session/errors.go index 3a9c129..d35bed4 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 42bb205..bb9242f 100644 --- a/session/private.go +++ b/session/private.go @@ -30,14 +30,26 @@ 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) +// 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 } -// PublicKey returns a binary representation of the session public key. -func (t *pToken) PublicKey() []byte { - return crypto.MarshalPublicKey(&t.sessionKey.PublicKey) +// PrivateKey is a session private key getter. +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 8097b97..c6f8125 100644 --- a/session/private_test.go +++ b/session/private_test.go @@ -1,35 +1,17 @@ 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) { @@ -68,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)) +} diff --git a/session/types.go b/session/types.go index ee13b92..95a0065 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