diff --git a/client/container.go b/client/container.go index 4377b39..7ccb4f8 100644 --- a/client/container.go +++ b/client/container.go @@ -82,13 +82,7 @@ func (c *Client) PutContainer(ctx context.Context, cnr *container.Container, opt // if container owner is not set, then use client key as owner if cnr.OwnerID() == nil { - w, err := owner.NEO3WalletFromPublicKey(&callOptions.key.PublicKey) - if err != nil { - return nil, err - } - - ownerID := new(owner.ID) - ownerID.SetNeo3Wallet(w) + ownerID := owner.NewIDFromPublicKey(&callOptions.key.PublicKey) cnr.SetOwnerID(ownerID) } @@ -270,13 +264,7 @@ func (c *Client) ListContainers(ctx context.Context, ownerID *owner.ID, opts ... } if ownerID == nil { - w, err := owner.NEO3WalletFromPublicKey(&callOptions.key.PublicKey) - if err != nil { - return nil, err - } - - ownerID = new(owner.ID) - ownerID.SetNeo3Wallet(w) + ownerID = owner.NewIDFromPublicKey(&callOptions.key.PublicKey) } reqBody := new(v2container.ListRequestBody) diff --git a/client/session.go b/client/session.go index fe11009..c7d7131 100644 --- a/client/session.go +++ b/client/session.go @@ -50,13 +50,7 @@ func (c *Client) CreateSession(ctx context.Context, expiration uint64, opts ...C opts[i](callOptions) } - w, err := owner.NEO3WalletFromPublicKey(&callOptions.key.PublicKey) - if err != nil { - return nil, err - } - - ownerID := new(owner.ID) - ownerID.SetNeo3Wallet(w) + ownerID := owner.NewIDFromPublicKey(&callOptions.key.PublicKey) reqBody := new(v2session.CreateRequestBody) reqBody.SetOwnerID(ownerID.ToV2()) @@ -66,7 +60,7 @@ func (c *Client) CreateSession(ctx context.Context, expiration uint64, opts ...C req.SetBody(reqBody) req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) - err = v2signature.SignServiceMessage(callOptions.key, req) + err := v2signature.SignServiceMessage(callOptions.key, req) if err != nil { return nil, err } diff --git a/container/opts.go b/container/opts.go index 91a8439..af456fa 100644 --- a/container/opts.go +++ b/container/opts.go @@ -1,6 +1,8 @@ package container import ( + "crypto/ecdsa" + "github.com/google/uuid" "github.com/nspcc-dev/neofs-sdk-go/acl" "github.com/nspcc-dev/neofs-sdk-go/netmap" @@ -61,13 +63,13 @@ func WithOwnerID(id *owner.ID) Option { } } -func WithNEO3Wallet(w *owner.NEO3Wallet) Option { +func WithOwnerPublicKey(pub *ecdsa.PublicKey) Option { return func(option *containerOptions) { if option.owner == nil { option.owner = new(owner.ID) } - option.owner.SetNeo3Wallet(w) + option.owner.SetPublicKey(pub) } } diff --git a/owner/convert_test.go b/owner/convert_test.go new file mode 100644 index 0000000..6c20507 --- /dev/null +++ b/owner/convert_test.go @@ -0,0 +1,34 @@ +package owner + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "encoding/hex" + "testing" + + "github.com/mr-tron/base58" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/stretchr/testify/require" +) + +func TestNEO3WalletFromPublicKey(t *testing.T) { + rawPub, _ := hex.DecodeString("0369b7b6c49fb937f3de52af189b91069767679c2739798d85f2ed69c079940680") + x, y := elliptic.UnmarshalCompressed(elliptic.P256(), rawPub) + require.True(t, x != nil && y != nil) + + expected := "35ee628f21922d7308f1bd71f03a0d8ba89c4e7372fca1442c" + actual := PublicKeyToIDBytes(&ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}) + require.Equal(t, expected, hex.EncodeToString(actual)) +} + +func TestPublicKeyToBytes(t *testing.T) { + p, err := keys.NewPrivateKey() + require.NoError(t, err) + + expected, err := base58.Decode(p.PublicKey().Address()) + require.NoError(t, err) + + actual := PublicKeyToIDBytes((*ecdsa.PublicKey)(p.PublicKey())) + require.Equal(t, expected, actual) + require.Equal(t, NEO3WalletSize, len(actual)) +} diff --git a/owner/id.go b/owner/id.go index 6aed52e..0c7bebf 100644 --- a/owner/id.go +++ b/owner/id.go @@ -2,12 +2,15 @@ package owner import ( "bytes" + "crypto/ecdsa" "errors" "fmt" "github.com/mr-tron/base58" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neofs-api-go/v2/refs" ) @@ -16,6 +19,12 @@ type ID refs.OwnerID var errInvalidIDString = errors.New("incorrect format of the string owner ID") +// ErrEmptyPublicKey when public key passed to Verify method is nil. +var ErrEmptyPublicKey = errors.New("empty public key") + +// NEO3WalletSize contains size of neo3 wallet. +const NEO3WalletSize = 25 + // NewIDFromV2 wraps v2 OwnerID message to ID. // // Nil refs.OwnerID converts to nil. @@ -33,9 +42,9 @@ func NewID() *ID { return NewIDFromV2(new(refs.OwnerID)) } -// SetNeo3Wallet sets owner identifier value to NEO3 wallet address. -func (id *ID) SetNeo3Wallet(v *NEO3Wallet) { - (*refs.OwnerID)(id).SetValue(v.Bytes()) +// SetPublicKey sets owner identifier value to the provided NEO3 public key. +func (id *ID) SetPublicKey(pub *ecdsa.PublicKey) { + (*refs.OwnerID)(id).SetValue(PublicKeyToIDBytes(pub)) } // ToV2 returns the v2 owner ID message. @@ -60,14 +69,20 @@ func (id *ID) Equal(id2 *ID) bool { ) } -// NewIDFromNeo3Wallet creates new owner identity from 25-byte neo wallet. -func NewIDFromNeo3Wallet(v *NEO3Wallet) *ID { +// NewIDFromPublicKey creates new owner identity from ECDSA public key. +func NewIDFromPublicKey(pub *ecdsa.PublicKey) *ID { id := NewID() - id.SetNeo3Wallet(v) + id.SetPublicKey(pub) return id } +// NewIDFromPublicKey creates new owner identity from N3 wallet account. +func NewIDFromN3Account(acc *wallet.Account) *ID { + return NewIDFromPublicKey( + (*ecdsa.PublicKey)(acc.PrivateKey().PublicKey())) +} + // Parse converts base58 string representation into ID. func (id *ID) Parse(s string) error { data, err := base58.Decode(s) @@ -123,3 +138,15 @@ func (id *ID) MarshalJSON() ([]byte, error) { func (id *ID) UnmarshalJSON(data []byte) error { return (*refs.OwnerID)(id).UnmarshalJSON(data) } + +// PublicKeyToIDBytes converts public key to a byte slice of NEO3WalletSize length. +// It is similar to decoding a NEO3 address but is inlined to skip base58 encoding-decoding step +// make it clear that no errors can occur. +func PublicKeyToIDBytes(pub *ecdsa.PublicKey) []byte { + sh := (*keys.PublicKey)(pub).GetScriptHash() + b := make([]byte, NEO3WalletSize) + b[0] = address.Prefix + copy(b[1:], sh.BytesBE()) + copy(b[21:], hash.Checksum(b[:21])) + return b +} diff --git a/owner/id_test.go b/owner/id_test.go index ade70e5..c77d200 100644 --- a/owner/id_test.go +++ b/owner/id_test.go @@ -7,6 +7,7 @@ import ( "github.com/mr-tron/base58" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util/slice" + "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neofs-api-go/v2/refs" . "github.com/nspcc-dev/neofs-sdk-go/owner" ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test" @@ -49,26 +50,28 @@ func TestID_Valid(t *testing.T) { }) } -func TestNewIDFromNeo3Wallet(t *testing.T) { +func TestNewIDFromN3Account(t *testing.T) { + acc, err := wallet.NewAccount() + require.NoError(t, err) + + id := NewIDFromN3Account(acc) + require.Equal(t, id.String(), acc.Address) +} + +func TestNewIDFromPublicKey(t *testing.T) { p, err := keys.NewPrivateKey() require.NoError(t, err) - wallet, err := NEO3WalletFromPublicKey((*ecdsa.PublicKey)(p.PublicKey())) - require.NoError(t, err) - - id := NewIDFromNeo3Wallet(wallet) - require.Equal(t, id.ToV2().GetValue(), wallet.Bytes()) + id := NewIDFromPublicKey((*ecdsa.PublicKey)(p.PublicKey())) + require.Equal(t, id.String(), p.Address()) } func TestID_Parse(t *testing.T) { t.Run("should parse successful", func(t *testing.T) { - p, err := keys.NewPrivateKey() + acc, err := wallet.NewAccount() require.NoError(t, err) - wallet, err := NEO3WalletFromPublicKey((*ecdsa.PublicKey)(p.PublicKey())) - require.NoError(t, err) - - eid := NewIDFromNeo3Wallet(wallet) + eid := NewIDFromN3Account(acc) aid := NewID() require.NoError(t, aid.Parse(eid.String())) diff --git a/owner/wallet.go b/owner/wallet.go deleted file mode 100644 index 830bf7a..0000000 --- a/owner/wallet.go +++ /dev/null @@ -1,56 +0,0 @@ -package owner - -import ( - "crypto/ecdsa" - "errors" - "fmt" - - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" -) - -// NEO3Wallet represents NEO3 wallet address. -type NEO3Wallet [NEO3WalletSize]byte - -// NEO3WalletSize contains size of neo3 wallet. -const NEO3WalletSize = 25 - -// ErrEmptyPublicKey when PK passed to Verify method is nil. -var ErrEmptyPublicKey = errors.New("empty public key") - -// NEO3WalletFromPublicKey converts public key to NEO3 wallet address. -func NEO3WalletFromPublicKey(key *ecdsa.PublicKey) (*NEO3Wallet, error) { - if key == nil { - return nil, ErrEmptyPublicKey - } - - neoPublicKey := (*keys.PublicKey)(key) - - d, err := base58.Decode(neoPublicKey.Address()) - if err != nil { - return nil, fmt.Errorf("can't decode neo3 address from key: %w", err) - } - - w := new(NEO3Wallet) - copy(w.Bytes(), d) - - return w, nil -} - -// String implements fmt.Stringer. -func (w *NEO3Wallet) String() string { - if w != nil { - return base58.Encode(w[:]) - } - - return "" -} - -// Bytes returns slice of NEO3 wallet address bytes. -func (w *NEO3Wallet) Bytes() []byte { - if w != nil { - return w[:] - } - - return nil -} diff --git a/owner/wallet_test.go b/owner/wallet_test.go deleted file mode 100644 index bba293f..0000000 --- a/owner/wallet_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package owner - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "encoding/hex" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestNEO3WalletFromPublicKey(t *testing.T) { - rawPub, _ := hex.DecodeString("0369b7b6c49fb937f3de52af189b91069767679c2739798d85f2ed69c079940680") - x, y := elliptic.UnmarshalCompressed(elliptic.P256(), rawPub) - require.True(t, x != nil && y != nil) - - expected := "35ee628f21922d7308f1bd71f03a0d8ba89c4e7372fca1442c" - w, err := NEO3WalletFromPublicKey(&ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}) - require.NoError(t, err) - require.Equal(t, expected, hex.EncodeToString(w[:])) -} diff --git a/pool/pool.go b/pool/pool.go index 4f2904d..52de9b3 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -254,17 +254,12 @@ type innerPool struct { } func newPool(ctx context.Context, options *BuilderOptions) (Pool, error) { - wallet, err := owner.NEO3WalletFromPublicKey(&options.Key.PublicKey) - if err != nil { - return nil, err - } - cache, err := NewCache() if err != nil { return nil, fmt.Errorf("couldn't create cache: %w", err) } - ownerID := owner.NewIDFromNeo3Wallet(wallet) + ownerID := owner.NewIDFromPublicKey(&options.Key.PublicKey) inner := make([]*innerPool, len(options.nodesParams)) var atLeastOneHealthy bool @@ -498,11 +493,7 @@ func (p *pool) conn(ctx context.Context, cfg *callConfig) (*clientPack, []client return nil, nil, err } - wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey) - if err != nil { - return nil, nil, err - } - ownerID := owner.NewIDFromNeo3Wallet(wallet) + ownerID := owner.NewIDFromPublicKey(&key.PublicKey) sessionToken = sessionTokenForOwner(ownerID, cliRes) _ = p.cache.Put(cacheKey, sessionToken) diff --git a/pool/pool_test.go b/pool/pool_test.go index c23d6b5..e6202e9 100644 --- a/pool/pool_test.go +++ b/pool/pool_test.go @@ -511,9 +511,7 @@ func TestSessionTokenOwner(t *testing.T) { require.True(t, ok) anonKey := newPrivateKey(t) - wallet, err := owner.NEO3WalletFromPublicKey(&anonKey.PublicKey) - require.NoError(t, err) - anonOwner := owner.NewIDFromNeo3Wallet(wallet) + anonOwner := owner.NewIDFromPublicKey(&anonKey.PublicKey) cfg := cfgFromOpts(WithKey(anonKey), useDefaultSession()) cp, _, err := p.conn(ctx, cfg) diff --git a/session/test/token.go b/session/test/token.go index bf11059..95c8ca4 100644 --- a/session/test/token.go +++ b/session/test/token.go @@ -1,7 +1,9 @@ package sessiontest import ( - "math/rand" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "github.com/google/uuid" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -31,11 +33,13 @@ func Token() *session.Token { panic(err) } - w := new(owner.NEO3Wallet) - rand.Read(w.Bytes()) + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } ownerID := owner.NewID() - ownerID.SetNeo3Wallet(w) + ownerID.SetPublicKey(&priv.PublicKey) keyBin := p.PublicKey().Bytes() diff --git a/token/bearer.go b/token/bearer.go index 3cec8b4..27f6739 100644 --- a/token/bearer.go +++ b/token/bearer.go @@ -91,12 +91,10 @@ func (b *BearerToken) SignToken(key *ecdsa.PrivateKey) error { // nil if token is not signed. func (b *BearerToken) Issuer() *owner.ID { pub, _ := keys.NewPublicKeyFromBytes(b.token.GetSignature().GetKey(), elliptic.P256()) - wallet, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(pub)) - if err != nil { + if pub == nil { return nil } - - return owner.NewIDFromNeo3Wallet(wallet) + return owner.NewIDFromPublicKey((*ecdsa.PublicKey)(pub)) } // NewBearerToken creates and initializes blank BearerToken. diff --git a/token/bearer_test.go b/token/bearer_test.go index cad01bd..792a572 100644 --- a/token/bearer_test.go +++ b/token/bearer_test.go @@ -23,10 +23,7 @@ func TestBearerToken_Issuer(t *testing.T) { p, err := keys.NewPrivateKey() require.NoError(t, err) - wallet, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(p.PublicKey())) - require.NoError(t, err) - - ownerID := owner.NewIDFromNeo3Wallet(wallet) + ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(p.PublicKey())) bearerToken.SetEACLTable(eacl.NewTable()) require.NoError(t, bearerToken.SignToken(&p.PrivateKey))