Merge branch 'release/0.7.6'
This commit is contained in:
commit
9b0757c6b1
11 changed files with 210 additions and 68 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,6 +1,26 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
This is the changelog for NeoFS-API-Go
|
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
|
## [0.7.5] - 2020-05-16
|
||||||
|
|
||||||
### Added
|
### 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.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.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.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,6 +1,7 @@
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
@ -82,6 +83,8 @@ func (m HeadRequest) ReadSignedData(p []byte) (int, error) {
|
||||||
|
|
||||||
if m.GetFullHeaders() {
|
if m.GetFullHeaders() {
|
||||||
p[0] = 1
|
p[0] = 1
|
||||||
|
} else {
|
||||||
|
p[0] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
off := 1
|
off := 1
|
||||||
|
@ -257,3 +260,13 @@ func addressSize(addr Address) int {
|
||||||
func addressBytes(addr Address) []byte {
|
func addressBytes(addr Address) []byte {
|
||||||
return append(addr.CID.Bytes(), addr.ObjectID.Bytes()...)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/service"
|
"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())
|
||||||
|
}
|
||||||
|
|
|
@ -386,3 +386,23 @@ func Stringify(dst io.Writer, obj *Object) error {
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -199,3 +199,23 @@ func TestObject_Copy(t *testing.T) {
|
||||||
require.Equal(t, token, h.GetValue().(*Header_Token).Token)
|
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())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||||
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type signAccumWithToken struct {
|
type signAccumWithToken struct {
|
||||||
|
@ -125,10 +126,14 @@ func (x Token_Info_Verb) Bytes() []byte {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSignKey calls a Signature field setter of token with passed signature.
|
// AddSignKey calls a Signature field setter and an OwnerKey field setter with corresponding arguments.
|
||||||
func (s signedSessionToken) AddSignKey(sig []byte, _ *ecdsa.PublicKey) {
|
func (s signedSessionToken) AddSignKey(sig []byte, key *ecdsa.PublicKey) {
|
||||||
if s.SessionToken != nil {
|
if s.SessionToken != nil {
|
||||||
s.SessionToken.SetSignature(sig)
|
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 {
|
if v == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return fixedTokenDataSize + len(v.GetSessionKey()) + len(v.GetOwnerKey())
|
return fixedTokenDataSize + len(v.GetSessionKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fills passed buffer with signing token information bytes.
|
// Fills passed buffer with signing token information bytes.
|
||||||
|
@ -208,9 +213,7 @@ func copyTokenSignedData(buf []byte, token SessionTokenInfo) {
|
||||||
tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch())
|
tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch())
|
||||||
off += 8
|
off += 8
|
||||||
|
|
||||||
off += copy(buf[off:], token.GetSessionKey())
|
copy(buf[off:], token.GetSessionKey())
|
||||||
|
|
||||||
copy(buf[off:], token.GetOwnerKey())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignedData concatenates signed data with session token information. Returns concatenation result.
|
// SignedData concatenates signed data with session token information. Returns concatenation result.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
"github.com/nspcc-dev/neofs-api-go/refs"
|
||||||
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||||
"github.com/nspcc-dev/neofs-crypto/test"
|
"github.com/nspcc-dev/neofs-crypto/test"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -77,16 +78,6 @@ func TestTokenGettersSetters(t *testing.T) {
|
||||||
require.Equal(t, key, tok.GetSessionKey())
|
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
|
{ // Signature
|
||||||
sig := make([]byte, 10)
|
sig := make([]byte, 10)
|
||||||
_, err := rand.Read(sig)
|
_, err := rand.Read(sig)
|
||||||
|
@ -136,11 +127,6 @@ func TestSignToken(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
token.SetSessionKey(sessionKey)
|
token.SetSessionKey(sessionKey)
|
||||||
|
|
||||||
ownerKey := make([]byte, 10)
|
|
||||||
_, err = rand.Read(ownerKey[:])
|
|
||||||
require.NoError(t, err)
|
|
||||||
token.SetOwnerKey(ownerKey)
|
|
||||||
|
|
||||||
signedToken := NewSignedSessionToken(token)
|
signedToken := NewSignedSessionToken(token)
|
||||||
verifiedToken := NewVerifiedSessionToken(token)
|
verifiedToken := NewVerifiedSessionToken(token)
|
||||||
|
|
||||||
|
@ -226,18 +212,6 @@ func TestSignToken(t *testing.T) {
|
||||||
token.SetSessionKey(sessionKey)
|
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 {
|
for _, v := range items {
|
||||||
|
@ -247,3 +221,28 @@ func TestSignToken(t *testing.T) {
|
||||||
require.NoError(t, VerifySignatureWithKey(pk, verifiedToken))
|
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(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -13,3 +13,7 @@ const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil")
|
||||||
// ErrPrivateTokenNotFound is returned when addressed private token was
|
// ErrPrivateTokenNotFound is returned when addressed private token was
|
||||||
// not found in storage.
|
// not found in storage.
|
||||||
const ErrPrivateTokenNotFound = internal.Error("private token not found")
|
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")
|
||||||
|
|
|
@ -30,14 +30,26 @@ func NewPrivateToken(validUntil uint64) (PrivateToken, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs data with session private key.
|
// PublicSessionToken returns a binary representation of session public key.
|
||||||
func (t *pToken) Sign(data []byte) ([]byte, error) {
|
//
|
||||||
return crypto.Sign(t.sessionKey, data)
|
// 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.
|
// PrivateKey is a session private key getter.
|
||||||
func (t *pToken) PublicKey() []byte {
|
func (t *pToken) PrivateKey() *ecdsa.PrivateKey {
|
||||||
return crypto.MarshalPublicKey(&t.sessionKey.PublicKey)
|
return t.sessionKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *pToken) Expired(epoch uint64) bool {
|
func (t *pToken) Expired(epoch uint64) bool {
|
||||||
|
|
|
@ -1,35 +1,17 @@
|
||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPrivateToken(t *testing.T) {
|
func TestPToken_PrivateKey(t *testing.T) {
|
||||||
// create new private token
|
// create new private token
|
||||||
pToken, err := NewPrivateToken(0)
|
pToken, err := NewPrivateToken(0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, pToken.PrivateKey())
|
||||||
// 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,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPToken_Expired(t *testing.T) {
|
func TestPToken_Expired(t *testing.T) {
|
||||||
|
@ -68,3 +50,27 @@ func TestPrivateTokenKey_SetTokenID(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, tokenID, s.token)
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -10,14 +10,8 @@ import (
|
||||||
|
|
||||||
// PrivateToken is an interface of session private part.
|
// PrivateToken is an interface of session private part.
|
||||||
type PrivateToken interface {
|
type PrivateToken interface {
|
||||||
// PublicKey must return a binary representation of session public key.
|
// PrivateKey must return session private key.
|
||||||
PublicKey() []byte
|
PrivateKey() *ecdsa.PrivateKey
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// Expired must return true if and only if private token is expired in the given epoch number.
|
// Expired must return true if and only if private token is expired in the given epoch number.
|
||||||
Expired(uint64) bool
|
Expired(uint64) bool
|
||||||
|
|
Loading…
Reference in a new issue