From 22241275bc39f0899f52836341f99f220e71d9c0 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Sat, 29 Jul 2023 07:33:15 +0400 Subject: [PATCH 1/4] crypto: Extend Signature type Provide getters of signature scheme, public key and value. Make `WriteToV2` method to decode public key from binary to assert scheme format. New API will allow to avoid `WriteToV2` calls in user space. New unit test checks and shows client-to-server transport over NeoFS API V2 protocol. Signed-off-by: Leonard Lyubich --- crypto/signature.go | 92 ++++++++++++++++++++++++++++++---------- crypto/signature_test.go | 69 ++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 crypto/signature_test.go diff --git a/crypto/signature.go b/crypto/signature.go index a0229c63..6a082fce 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -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) @@ -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 @@ -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) { @@ -128,3 +118,59 @@ func (x *Signature) fillSignature(signer Signer, signature []byte) { m.SetSign(signature) m.SetKey(key) } + +// 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 +} diff --git a/crypto/signature_test.go b/crypto/signature_test.go new file mode 100644 index 00000000..bd29582f --- /dev/null +++ b/crypto/signature_test.go @@ -0,0 +1,69 @@ +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 := make([]byte, pubKey.MaxEncodedSize()) + bPubKey = bPubKey[:pubKey.Encode(bPubKey)] + + 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) + } +} From d65d8d7846beb1fe22ea446151d39acb030ba2d8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Sat, 29 Jul 2023 07:49:44 +0400 Subject: [PATCH 2/4] crypto: Add helper to encode PublicKey Encoding methods of `PublicKey` interface require buffer pre-allocation. In cases where the user does not have a buffer prepared in advance, he has to explicitly create it and encode the key. Helper function `PublicKeyBytes` is added to make code easier. Signed-off-by: Leonard Lyubich --- client/container_statistic_test.go | 10 +++------- crypto/signature.go | 2 +- crypto/signature_test.go | 3 +-- crypto/signer.go | 2 ++ crypto/util.go | 7 +++++++ crypto/util_test.go | 26 ++++++++++++++++++++++++++ pool/pool.go | 10 ++-------- session/common.go | 8 ++------ session/container_test.go | 3 +-- session/object_test.go | 4 ++-- 10 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 crypto/util_test.go diff --git a/client/container_statistic_test.go b/client/container_statistic_test.go index 8c8c51bf..a3dda510 100644 --- a/client/container_statistic_test.go +++ b/client/container_statistic_test.go @@ -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" @@ -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{} @@ -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) diff --git a/crypto/signature.go b/crypto/signature.go index 6a082fce..0467de4d 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -116,7 +116,7 @@ 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. diff --git a/crypto/signature_test.go b/crypto/signature_test.go index bd29582f..87953aab 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -16,8 +16,7 @@ func TestSignatureLifecycle(t *testing.T) { signer := test.RandomSigner(t) scheme := signer.Scheme() pubKey := signer.Public() - bPubKey := make([]byte, pubKey.MaxEncodedSize()) - bPubKey = bPubKey[:pubKey.Encode(bPubKey)] + bPubKey := neofscrypto.PublicKeyBytes(pubKey) var clientSig neofscrypto.Signature diff --git a/crypto/signer.go b/crypto/signer.go index 530cbe84..a1034b92 100644 --- a/crypto/signer.go +++ b/crypto/signer.go @@ -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. diff --git a/crypto/util.go b/crypto/util.go index e6f87a31..f0a3bd62 100644 --- a/crypto/util.go +++ b/crypto/util.go @@ -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)] +} diff --git a/crypto/util_test.go b/crypto/util_test.go new file mode 100644 index 00000000..feb7a8e1 --- /dev/null +++ b/crypto/util_test.go @@ -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) +} diff --git a/pool/pool.go b/pool/pool.go index a7084674..f03d9d2b 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -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 { diff --git a/session/common.go b/session/common.go index f5271253..75c90b6d 100644 --- a/session/common.go +++ b/session/common.go @@ -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. @@ -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. diff --git a/session/container_test.go b/session/container_test.go index f24bfbf7..3ded50a7 100644 --- a/session/container_test.go +++ b/session/container_test.go @@ -57,8 +57,7 @@ func TestContainerProtocolV2(t *testing.T) { // Session key signer := test.RandomSignerRFC6979(t) authKey := signer.Public() - binAuthKey := make([]byte, authKey.MaxEncodedSize()) - binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)] + binAuthKey := neofscrypto.PublicKeyBytes(authKey) restoreAuthKey := func() { body.SetSessionKey(binAuthKey) } diff --git a/session/object_test.go b/session/object_test.go index f0fdca7c..c4400456 100644 --- a/session/object_test.go +++ b/session/object_test.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/v2/refs" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/crypto/test" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" "github.com/nspcc-dev/neofs-sdk-go/session" @@ -56,8 +57,7 @@ func TestObjectProtocolV2(t *testing.T) { // Session key signer := test.RandomSignerRFC6979(t) authKey := signer.Public() - binAuthKey := make([]byte, authKey.MaxEncodedSize()) - binAuthKey = binAuthKey[:authKey.Encode(binAuthKey)] + binAuthKey := neofscrypto.PublicKeyBytes(authKey) restoreAuthKey := func() { body.SetSessionKey(binAuthKey) } From da9b0b3af637828728ca5455ed3e74d31c98f5c4 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Sat, 29 Jul 2023 08:46:04 +0400 Subject: [PATCH 3/4] user: Refactor constructors of Signer instances Previously, package provided constructors for `Signer` instances with RFC 6979 signature scheme only. User may need to construct any `Signer` instance (other scheme or even custom implementation). Add generic constructor `NewSigner` accepting split `neofscrypto.Signer` and `user.ID`. Add two constructors from standard `ecdsa.PrivateKey` with an explanation that the user ID is resolved automatically. Signed-off-by: Leonard Lyubich --- client/example_container_put_test.go | 2 +- crypto/test/tests.go | 2 +- object/slicer/slicer_test.go | 2 +- pool/example_pool_test.go | 4 +- user/signer.go | 72 ++++++++++++---------------- waiter/example_waiter_test.go | 2 +- 6 files changed, 36 insertions(+), 48 deletions(-) diff --git a/client/example_container_put_test.go b/client/example_container_put_test.go index 08c2e454..3f8f6a39 100644 --- a/client/example_container_put_test.go +++ b/client/example_container_put_test.go @@ -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() diff --git a/crypto/test/tests.go b/crypto/test/tests.go index cb321ddd..f9135073 100644 --- a/crypto/test/tests.go +++ b/crypto/test/tests.go @@ -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) } diff --git a/object/slicer/slicer_test.go b/object/slicer/slicer_test.go index 6d2e949e..b4e520cb 100644 --- a/object/slicer/slicer_test.go +++ b/object/slicer/slicer_test.go @@ -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 { diff --git a/pool/example_pool_test.go b/pool/example_pool_test.go index d47bc32c..22246ca8 100644 --- a/pool/example_pool_test.go +++ b/pool/example_pool_test.go @@ -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 @@ -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) diff --git a/user/signer.go b/user/signer.go index 582a39d7..27b934bd 100644 --- a/user/signer.go +++ b/user/signer.go @@ -8,65 +8,53 @@ import ( neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" ) -// Signer is an interface of entities that can be used for signing operations -// in NeoFS. It is the same as [neofscrypto.Signer], but has an extra method to retrieve [ID]. +// Signer represents a NeoFS user authorized by a digital signature. type Signer interface { + // Signer signs data on behalf of the user. neofscrypto.Signer - + // UserID returns ID of the associated user. UserID() ID } -// SignerRFC6979 wraps [ecdsa.PrivateKey] and represents signer based on deterministic -// ECDSA with SHA-256 hashing (RFC 6979). Provides [Signer] interface. -// -// Instances SHOULD be initialized with [NewSignerRFC6979] or [NewSignerRFC6979WithID]. -type SignerRFC6979 struct { - neofsecdsa.SignerRFC6979 - userID ID +type signer struct { + neofscrypto.Signer + usr ID } -// NewSignerRFC6979 is a constructor for [SignerRFC6979]. -func NewSignerRFC6979(pk ecdsa.PrivateKey) *SignerRFC6979 { - var id ID - id.SetScriptHash((*keys.PublicKey)(&pk.PublicKey).GetScriptHash()) - - return &SignerRFC6979{ - userID: id, - SignerRFC6979: neofsecdsa.SignerRFC6979(pk), - } +func (s signer) UserID() ID { + return s.usr } -// NewSignerRFC6979WithID is a constructor for [SignerRFC6979] where you may specify [ID] associated with this signer. -func NewSignerRFC6979WithID(pk ecdsa.PrivateKey, id ID) *SignerRFC6979 { - return &SignerRFC6979{ - SignerRFC6979: neofsecdsa.SignerRFC6979(pk), - userID: id, +// NewSigner combines provided [neofscrypto.Signer] and [ID] into [Signer]. +// +// See also [NewAutoIDSigner]. +func NewSigner(s neofscrypto.Signer, usr ID) Signer { + return signer{ + Signer: s, + usr: usr, } } -// UserID returns the [ID] using script hash calculated for the given key. -func (s SignerRFC6979) UserID() ID { - return s.userID -} +func newAutoResolvedSigner(s neofscrypto.Signer, pubKey ecdsa.PublicKey) Signer { + var id ID + id.SetScriptHash((*keys.PublicKey)(&pubKey).GetScriptHash()) -// StaticSigner emulates real sign and contains already precalculated hash. -// Provides [Signer] interface. -type StaticSigner struct { - neofscrypto.StaticSigner - id ID + return NewSigner(s, id) } -// NewStaticSignerWithID creates new StaticSigner with specified [ID]. -func NewStaticSignerWithID(scheme neofscrypto.Scheme, sig []byte, pubKey neofscrypto.PublicKey, id ID) *StaticSigner { - return &StaticSigner{ - StaticSigner: *neofscrypto.NewStaticSigner(scheme, sig, pubKey), - id: id, - } +// NewAutoIDSigner returns [Signer] with neofscrypto.ECDSA_SHA512 +// signature scheme and user [ID] automatically resolved from the ECDSA public +// key. +// +// See also [NewAutoIDSignerRFC6979]. +func NewAutoIDSigner(key ecdsa.PrivateKey) Signer { + return newAutoResolvedSigner(neofsecdsa.Signer(key), key.PublicKey) } -// UserID returns underlying [ID]. -func (s *StaticSigner) UserID() ID { - return s.id +// NewAutoIDSignerRFC6979 is an analogue of [NewAutoIDSigner] but with +// [neofscrypto.ECDSA_DETERMINISTIC_SHA256] signature scheme. +func NewAutoIDSignerRFC6979(key ecdsa.PrivateKey) Signer { + return newAutoResolvedSigner(neofsecdsa.SignerRFC6979(key), key.PublicKey) } // ResolveFromECDSAPublicKey resolves [ID] from the given [ecdsa.PublicKey]. diff --git a/waiter/example_waiter_test.go b/waiter/example_waiter_test.go index ec865c6e..a6e58ae0 100644 --- a/waiter/example_waiter_test.go +++ b/waiter/example_waiter_test.go @@ -24,7 +24,7 @@ func ExampleNewWaiter() { panic(err) } - signer := user.NewSignerRFC6979(key.PrivateKey) + signer := user.NewAutoIDSignerRFC6979(key.PrivateKey) account := signer.UserID() From 577b2d684c623b353fc04a773f556b5b15558ab5 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Sat, 29 Jul 2023 14:03:08 +0400 Subject: [PATCH 4/4] session: Add getter of binary-encoded public key from signature Signature is used for user authentication so access to public key is required. Digital signature bytes may still be unexported because they are only useful for `VerifySignature` method. Add `IssuerPublicKeyBytes` method returning binary-encoded public key of the session issuer. Signed-off-by: Leonard Lyubich --- session/common.go | 11 +++++++++++ session/container_test.go | 17 +++++++++++++++-- session/object_test.go | 2 ++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/session/common.go b/session/common.go index 75c90b6d..244d81bd 100644 --- a/session/common.go +++ b/session/common.go @@ -336,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 +} diff --git a/session/container_test.go b/session/container_test.go index 3ded50a7..444cdc5d 100644 --- a/session/container_test.go +++ b/session/container_test.go @@ -523,9 +523,9 @@ func TestIssuedBy(t *testing.T) { } func TestContainer_Issuer(t *testing.T) { - var token session.Container - t.Run("signer", func(t *testing.T) { + var token session.Container + signer := test.RandomSignerRFC6979(t) require.Zero(t, token.Issuer()) @@ -536,12 +536,25 @@ func TestContainer_Issuer(t *testing.T) { }) t.Run("external", func(t *testing.T) { + var token session.Container + signer := test.RandomSignerRFC6979(t) issuer := signer.UserID() token.SetIssuer(issuer) require.True(t, token.Issuer().Equals(issuer)) }) + + t.Run("public key", func(t *testing.T) { + var token session.Container + + signer := test.RandomSignerRFC6979(t) + + require.Nil(t, token.IssuerPublicKeyBytes()) + require.NoError(t, token.Sign(signer)) + + require.Equal(t, neofscrypto.PublicKeyBytes(signer.Public()), token.IssuerPublicKeyBytes()) + }) } func TestContainer_Sign(t *testing.T) { diff --git a/session/object_test.go b/session/object_test.go index c4400456..21524c4b 100644 --- a/session/object_test.go +++ b/session/object_test.go @@ -610,12 +610,14 @@ func TestObject_Issuer(t *testing.T) { signer := test.RandomSignerRFC6979(t) require.Zero(t, token.Issuer()) + require.Nil(t, token.IssuerPublicKeyBytes()) require.NoError(t, token.Sign(signer)) issuer := signer.UserID() require.True(t, token.Issuer().Equals(issuer)) + require.Equal(t, neofscrypto.PublicKeyBytes(signer.Public()), token.IssuerPublicKeyBytes()) } func TestObject_Sign(t *testing.T) {