Skip to content

Commit

Permalink
Extend package API with features usefule for existing apps (#479)
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov authored Aug 4, 2023
2 parents d38aa41 + 577b2d6 commit cfc9fac
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 99 deletions.
10 changes: 3 additions & 7 deletions client/container_statistic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/container"
"github.com/nspcc-dev/neofs-sdk-go/container/acl"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
Expand Down Expand Up @@ -479,10 +480,7 @@ func TestClientStatistic_ContainerEndpointInfo(t *testing.T) {
var ver refs.Version
var nodeInfo netmapv2.NodeInfo

b := make([]byte, signer.Public().MaxEncodedSize())
signer.Public().Encode(b)

nodeInfo.SetPublicKey(b)
nodeInfo.SetPublicKey(neofscrypto.PublicKeyBytes(signer.Public()))
nodeInfo.SetAddresses("https://some-endpont.com")

body := netmapv2.LocalNodeInfoResponseBody{}
Expand Down Expand Up @@ -553,9 +551,7 @@ func TestClientStatistic_CreateSession(t *testing.T) {
body := session.CreateResponseBody{}
body.SetID(randBytes(10))

b := make([]byte, signer.Public().MaxEncodedSize())
signer.Public().Encode(b)
body.SetSessionKey(b)
body.SetSessionKey(neofscrypto.PublicKeyBytes(signer.Public()))

resp.SetBody(&body)
resp.SetMetaHeader(&meta)
Expand Down
2 changes: 1 addition & 1 deletion client/example_container_put_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func ExampleClient_ContainerPut() {
panic(err)
}

signer := user.NewSignerRFC6979(key.PrivateKey)
signer := user.NewAutoIDSignerRFC6979(key.PrivateKey)
// take account from user's signer
accountID = signer.UserID()

Expand Down
94 changes: 70 additions & 24 deletions crypto/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ type Signature refs.Signature
//
// See also WriteToV2.
func (x *Signature) ReadFromV2(m refs.Signature) error {
if len(m.GetKey()) == 0 {
bPubKey := m.GetKey()
if len(bPubKey) == 0 {
return errors.New("missing public key")
} else if len(m.GetSign()) == 0 {
}

sig := m.GetSign()
if len(sig) == 0 {
return errors.New("missing signature")
}

switch m.GetScheme() {
default:
return fmt.Errorf("unsupported scheme %v", m.GetSign())
case
refs.ECDSA_SHA512,
refs.ECDSA_RFC6979_SHA256,
refs.ECDSA_RFC6979_SHA256_WALLET_CONNECT:
_, err := decodePublicKey(m)
if err != nil {
return err
}

*x = Signature(m)
Expand All @@ -53,7 +53,7 @@ func (x *Signature) ReadFromV2(m refs.Signature) error {
//
// See also ReadFromV2.
func (x Signature) WriteToV2(m *refs.Signature) {
*m = (refs.Signature)(x)
*m = refs.Signature(x)
}

// Calculate signs data using Signer and encodes public key for subsequent
Expand Down Expand Up @@ -100,21 +100,11 @@ func (x *Signature) CalculateMarshalled(signer Signer, obj StablyMarshallable) e
//
// See also Calculate.
func (x Signature) Verify(data []byte) bool {
m := (*refs.Signature)(&x)
m := refs.Signature(x)

f, ok := publicKeys[Scheme(m.GetScheme())]
if !ok {
return false
}

key := f()

err := key.Decode(m.GetKey())
if err != nil {
return false
}
key, err := decodePublicKey(m)

return key.Verify(data, m.GetSign())
return err == nil && key.Verify(data, m.GetSign())
}

func (x *Signature) fillSignature(signer Signer, signature []byte) {
Expand All @@ -126,5 +116,61 @@ func (x *Signature) fillSignature(signer Signer, signature []byte) {
m := (*refs.Signature)(x)
m.SetScheme(refs.SignatureScheme(signer.Scheme()))
m.SetSign(signature)
m.SetKey(key)
m.SetKey(PublicKeyBytes(signer.Public()))
}

// Scheme returns signature scheme used by signer to calculate the signature.
//
// Scheme MUST NOT be called before [Signature.ReadFromV2] or
// [Signature.Calculate] methods.
func (x Signature) Scheme() Scheme {
return Scheme((*refs.Signature)(&x).GetScheme())
}

// PublicKey returns public key of the signer which calculated the signature.
//
// PublicKey MUST NOT be called before [Signature.ReadFromV2] or
// [Signature.Calculate] methods.
//
// See also [Signature.PublicKeyBytes].
func (x Signature) PublicKey() PublicKey {
key, _ := decodePublicKey(refs.Signature(x))
return key
}

// PublicKeyBytes returns binary-encoded public key of the signer which
// calculated the signature.
//
// PublicKeyBytes MUST NOT be called before [Signature.ReadFromV2] or
// [Signature.Calculate] methods.
//
// See also [Signature.PublicKey].
func (x Signature) PublicKeyBytes() []byte {
return (*refs.Signature)(&x).GetKey()
}

// Value returns calculated digital signature.
//
// Value MUST NOT be called before [Signature.ReadFromV2] or
// [Signature.Calculate] methods.
func (x Signature) Value() []byte {
return (*refs.Signature)(&x).GetSign()
}

func decodePublicKey(m refs.Signature) (PublicKey, error) {
scheme := Scheme(m.GetScheme())

newPubKey, ok := publicKeys[scheme]
if !ok {
return nil, fmt.Errorf("unsupported scheme %d", scheme)
}

pubKey := newPubKey()

err := pubKey.Decode(m.GetKey())
if err != nil {
return nil, fmt.Errorf("decode public key from binary: %w", err)
}

return pubKey, nil
}
68 changes: 68 additions & 0 deletions crypto/signature_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package neofscrypto_test

import (
"testing"

"github.com/nspcc-dev/neofs-api-go/v2/refs"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/crypto/test"
"github.com/stretchr/testify/require"
)

const anyUnsupportedScheme = neofscrypto.ECDSA_WALLETCONNECT + 1

func TestSignatureLifecycle(t *testing.T) {
data := []byte("Hello, world!")
signer := test.RandomSigner(t)
scheme := signer.Scheme()
pubKey := signer.Public()
bPubKey := neofscrypto.PublicKeyBytes(pubKey)

var clientSig neofscrypto.Signature

err := clientSig.Calculate(signer, data)
require.NoError(t, err)

testSig := func(sig neofscrypto.Signature) {
require.Equal(t, signer.Scheme(), sig.Scheme())
require.Equal(t, signer.Public(), sig.PublicKey())
require.Equal(t, bPubKey, sig.PublicKeyBytes())
require.NotEmpty(t, sig.Value())
require.True(t, sig.Verify(data))
}

testSig(clientSig)

var sigV2 refs.Signature
clientSig.WriteToV2(&sigV2)

require.Equal(t, refs.SignatureScheme(scheme), sigV2.GetScheme())
require.Equal(t, bPubKey, sigV2.GetKey())
require.Equal(t, clientSig.Value(), sigV2.GetSign())

// sigV2 transmitted to server over the network

var serverSig neofscrypto.Signature

err = serverSig.ReadFromV2(sigV2)
require.NoError(t, err)

testSig(serverSig)

// break the message in different ways
for i, breakSig := range []func(*refs.Signature){
func(sigV2 *refs.Signature) { sigV2.SetScheme(refs.SignatureScheme(anyUnsupportedScheme)) },
func(sigV2 *refs.Signature) {
key := sigV2.GetKey()
sigV2.SetKey(key[:len(key)-1])
},
func(sigV2 *refs.Signature) { sigV2.SetKey(append(sigV2.GetKey(), 1)) },
func(sigV2 *refs.Signature) { sigV2.SetSign(nil) },
} {
sigV2Cp := sigV2
breakSig(&sigV2Cp)

err = serverSig.ReadFromV2(sigV2Cp)
require.Errorf(t, err, "break func #%d", i)
}
}
2 changes: 2 additions & 0 deletions crypto/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ type PublicKey interface {
// on any failure except (*).
//
// Encode is a reverse operation to Decode.
//
// [PublicKeyBytes] may be used to skip explicit buffer allocation.
Encode(buf []byte) int

// Decode decodes binary public key.
Expand Down
2 changes: 1 addition & 1 deletion crypto/test/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ func RandomSignerRFC6979(tb testing.TB) user.Signer {
p, err := keys.NewPrivateKey()
require.NoError(tb, err)

return user.NewSignerRFC6979(p.PrivateKey)
return user.NewAutoIDSignerRFC6979(p.PrivateKey)
}
7 changes: 7 additions & 0 deletions crypto/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ import "encoding/hex"
func StringifyKeyBinary(src []byte) string {
return hex.EncodeToString(src)
}

// PublicKeyBytes returns binary-encoded PublicKey. Use [PublicKey.Encode] to
// avoid new slice allocation.
func PublicKeyBytes(pubKey PublicKey) []byte {
b := make([]byte, pubKey.MaxEncodedSize())
return b[:pubKey.Encode(b)]
}
26 changes: 26 additions & 0 deletions crypto/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package neofscrypto_test

import (
"testing"

"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/stretchr/testify/require"
)

func TestPublicKeyBytes(t *testing.T) {
k, err := keys.NewPrivateKey()
require.NoError(t, err)

pubKey := neofsecdsa.PublicKey(k.PrivateKey.PublicKey)

bPubKey := neofscrypto.PublicKeyBytes(&pubKey)

var restoredPubKey neofsecdsa.PublicKey

err = restoredPubKey.Decode(bPubKey)
require.NoError(t, err)

require.Equal(t, pubKey, restoredPubKey)
}
2 changes: 1 addition & 1 deletion object/slicer/slicer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func randomInput(tb testing.TB, size, sizeLimit uint64) (input, slicer.Options)
}

var in input
in.signer = user.NewSignerRFC6979(*key)
in.signer = user.NewAutoIDSigner(*key)
in.container = cidtest.ID()
in.currentEpoch = rand.Uint64()
if sizeLimit > 0 {
Expand Down
4 changes: 2 additions & 2 deletions pool/example_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
func ExampleNew_easiestWay() {
// Signer generation, like example.
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
signer := user.NewSignerRFC6979(*pk)
signer := user.NewAutoIDSignerRFC6979(*pk)

pool, _ := New(NewFlatNodeParams([]string{"grpc://localhost:8080", "grpcs://localhost:8081"}), signer, DefaultOptions())
_ = pool
Expand All @@ -24,7 +24,7 @@ func ExampleNew_easiestWay() {
func ExampleNew_adjustingParameters() {
// Signer generation, like example.
pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
signer := user.NewSignerRFC6979(*pk)
signer := user.NewAutoIDSignerRFC6979(*pk)

opts := DefaultOptions()
opts.SetErrorThreshold(10)
Expand Down
10 changes: 2 additions & 8 deletions pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1663,19 +1663,13 @@ func (p *innerPool) connection() (internalClient, error) {
}

func formCacheKey(address string, signer neofscrypto.Signer) string {
b := make([]byte, signer.Public().MaxEncodedSize())
signer.Public().Encode(b)

return address + string(b)
return address + string(neofscrypto.PublicKeyBytes(signer.Public()))
}

// cacheKeyForSession generates cache key for a signed session token.
// It is used with pool methods compatible with [sdkClient.Client].
func cacheKeyForSession(address string, signer neofscrypto.Signer, verb session.ObjectVerb, cnr cid.ID) string {
b := make([]byte, signer.Public().MaxEncodedSize())
signer.Public().Encode(b)

return fmt.Sprintf("%s%s%d%s", address, b, verb, cnr)
return fmt.Sprintf("%s%s%d%s", address, neofscrypto.PublicKeyBytes(signer.Public()), verb, cnr)
}

func (p *Pool) checkSessionTokenErr(err error, address string, cl internalClient) bool {
Expand Down
19 changes: 13 additions & 6 deletions session/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,7 @@ func (x commonData) ID() uuid.UUID {
//
// See also AssertAuthKey.
func (x *commonData) SetAuthKey(key neofscrypto.PublicKey) {
x.authKey = make([]byte, key.MaxEncodedSize())
x.authKey = x.authKey[:key.Encode(x.authKey)]
x.authKey = neofscrypto.PublicKeyBytes(key)
}

// SetIssuer allows to set issuer before Sign call.
Expand All @@ -321,10 +320,7 @@ func (x *commonData) SetIssuer(id user.ID) {
//
// See also SetAuthKey.
func (x commonData) AssertAuthKey(key neofscrypto.PublicKey) bool {
bKey := make([]byte, key.MaxEncodedSize())
bKey = bKey[:key.Encode(bKey)]

return bytes.Equal(bKey, x.authKey)
return bytes.Equal(neofscrypto.PublicKeyBytes(key), x.authKey)
}

// Issuer returns user ID of the session issuer.
Expand All @@ -340,3 +336,14 @@ func (x commonData) Issuer() user.ID {

return user.ID{}
}

// IssuerPublicKeyBytes returns binary-encoded public key of the session issuer.
//
// IssuerPublicKeyBytes MUST NOT be called before ReadFromV2 or Sign methods.
func (x *commonData) IssuerPublicKeyBytes() []byte {
if x.sigSet {
return x.sig.GetKey()
}

return nil
}
Loading

0 comments on commit cfc9fac

Please sign in to comment.