[#104] Support NEP-6 for authmate

Drop neofs-crypto.

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-06-24 18:21:34 +03:00
parent 39a43c21a1
commit 52c63d4c44
11 changed files with 227 additions and 172 deletions

View file

@ -109,7 +109,7 @@ default. To enable them use `--pprof` and `--metrics` flags or
## NeoFS AuthMate
Authmate is a tool to create gateway key pairs and AWS credentials. AWS users
Authmate is a tool to create gateway AWS credentials. AWS users
are authenticated with access key IDs and secrets, while NeoFS users are
authenticated with key pairs. To complicate things further we have S3 gateway
that usually acts on behalf of some user, but user doesn't necessarily want to
@ -119,7 +119,7 @@ To solve this we use NeoFS bearer tokens that are signed by the owner (NeoFS
"user") and that can implement any kind of policy for NeoFS requests allowed
using this token. But tokens can't be used directly as AWS credentials, thus
they're stored on NeoFS as regular objects and access key ID is just an
address of this object while secret is an SHA256 hash of this key.
address of this object while secret is generated randomly.
Tokens are not stored on NeoFS in plaintext, they're encrypted with a set of
gateway keys. So in order for gateway to be able to successfully extract bearer
@ -127,6 +127,17 @@ token the object needs to be stored in a container available for the gateway
to read and it needs to be encrypted with this gateway's key (among others
potentially).
### Variables
Authmate support the following variables to decrypt wallets provided by `--wallet` and `--gate-wallet`
parameters respectevely:
* `AUTHMATE_WALLET_PASSPHRASE`
* `AUTHMATE_WALLET_GATE_PASSPHRASE`
If the passphrase is not specified, you will be asked to enter the password interactively:
```
Enter password for wallet.json >
```
#### Generation of wallet
To generate wallets for gateways, run the following command:
@ -234,7 +245,7 @@ token will not be created.
Example of a command to issue a secret with custom rules for multiple gates:
```
$ ./neofs-authmate issue-secret --neofs-key user.key \
$ ./neofs-authmate issue-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \
--bearer-rules '{"records":[{"operation":"PUT","action":"ALLOW","filters":[],"targets":[{"role":"OTHERS","keys":[]}]}]}' \
--gate-public-key dd34f6dce9a4ce0990869ec6bd33a40e102a5798881cfe61d03a5659ceee1a64 \
@ -242,6 +253,7 @@ $ ./neofs-authmate issue-secret --neofs-key user.key \
--create-session-token \
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'
Enter password for wallet.json >
{
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c",
@ -255,14 +267,17 @@ any S3 client.
#### Obtainment of a secret access key
You can get a secret access key associated with access key ID by obtaining a
secret stored on the NeoFS network:
secret stored on the NeoFS network. Here example of providing one password (for `wallet.json`) via env variable
and other (for `gate-wallet.json`) interactively:
```
$ ./neofs-authmate obtain-secret --neofs-key user.key \
$ AUTHMATE_WALLET_PASSPHRASE=some-pwd \
./neofs-authmate obtain-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \
--gate-private-key b8ba980eb70b959be99915d2e0ad377809984ccd1dac0a6551907f81c2b33d21 \
--gate-wallet gate-wallet.json \
--access-key-id 5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM
Enter password for gate-wallet.json >
{
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c"
}

View file

@ -2,7 +2,6 @@ package auth
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"io"
@ -13,6 +12,7 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
@ -56,7 +56,7 @@ func (p prs) Seek(_ int64, _ int) (int64, error) {
var _ io.ReadSeeker = prs(0)
// New creates an instance of AuthCenter.
func New(conns pool.Pool, key *ecdsa.PrivateKey) Center {
func New(conns pool.Pool, key *keys.PrivateKey) Center {
return &center{
cli: tokens.New(conns, key),
reg: &regexpSubmatcher{re: authorizationFieldRegexp},

View file

@ -12,6 +12,7 @@ import (
"time"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
"github.com/nspcc-dev/neofs-api-go/pkg/container"
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
@ -20,7 +21,6 @@ import (
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-api-go/pkg/session"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-node/pkg/policy"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
@ -48,8 +48,8 @@ type (
IssueSecretOptions struct {
ContainerID *cid.ID
ContainerFriendlyName string
NeoFSKey *ecdsa.PrivateKey
GatesPublicKeys []*ecdsa.PublicKey
NeoFSKey *keys.PrivateKey
GatesPublicKeys []*keys.PublicKey
EACLRules []byte
ContextRules []byte
SessionTkn bool
@ -58,7 +58,7 @@ type (
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
ObtainSecretOptions struct {
SecretAddress string
GatePrivateKey *ecdsa.PrivateKey
GatePrivateKey *keys.PrivateKey
}
)
@ -127,7 +127,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
return err
}
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
oid, err := ownerIDFromNeoFSKey(options.NeoFSKey.PublicKey())
if err != nil {
return err
}
@ -155,7 +155,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
ir := &issuingResult{
AccessKeyID: accessKeyID,
SecretAccessKey: secrets.AccessKey,
OwnerPrivateKey: hex.EncodeToString(crypto.MarshalPrivateKey(secrets.EphemeralKey)),
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
}
enc := json.NewEncoder(w)
@ -257,7 +257,7 @@ func buildContext(rules []byte) (*session.ContainerContext, error) {
return sessionCtx, nil
}
func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table, gateKey *ecdsa.PublicKey) (*token.BearerToken, error) {
func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, gateKey *keys.PublicKey) (*token.BearerToken, error) {
oid, err := ownerIDFromNeoFSKey(gateKey)
if err != nil {
return nil, err
@ -268,10 +268,10 @@ func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table, gateKey *ecdsa.P
bearerToken.SetOwner(oid)
bearerToken.SetLifetime(math.MaxUint64, 0, 0)
return bearerToken, bearerToken.SignToken(key)
return bearerToken, bearerToken.SignToken(&key.PrivateKey)
}
func buildBearerTokens(key *ecdsa.PrivateKey, table *eacl.Table, gatesKeys []*ecdsa.PublicKey) ([]*token.BearerToken, error) {
func buildBearerTokens(key *keys.PrivateKey, table *eacl.Table, gatesKeys []*keys.PublicKey) ([]*token.BearerToken, error) {
bearerTokens := make([]*token.BearerToken, 0, len(gatesKeys))
for _, gateKey := range gatesKeys {
tkn, err := buildBearerToken(key, table, gateKey)
@ -283,7 +283,7 @@ func buildBearerTokens(key *ecdsa.PrivateKey, table *eacl.Table, gatesKeys []*ec
return bearerTokens, nil
}
func buildSessionToken(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.ContainerContext, gateKey *ecdsa.PublicKey) (*session.Token, error) {
func buildSessionToken(key *keys.PrivateKey, oid *owner.ID, ctx *session.ContainerContext, gateKey *keys.PublicKey) (*session.Token, error) {
tok := session.NewToken()
tok.SetContext(ctx)
uid, err := uuid.New().MarshalBinary()
@ -292,12 +292,12 @@ func buildSessionToken(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.Contai
}
tok.SetID(uid)
tok.SetOwnerID(oid)
tok.SetSessionKey(crypto.MarshalPublicKey(gateKey))
tok.SetSessionKey(gateKey.Bytes())
return tok, tok.Sign(key)
return tok, tok.Sign(&key.PrivateKey)
}
func buildSessionTokens(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.ContainerContext, gatesKeys []*ecdsa.PublicKey) ([]*session.Token, error) {
func buildSessionTokens(key *keys.PrivateKey, oid *owner.ID, ctx *session.ContainerContext, gatesKeys []*keys.PublicKey) ([]*session.Token, error) {
sessionTokens := make([]*session.Token, 0, len(gatesKeys))
for _, gateKey := range gatesKeys {
tkn, err := buildSessionToken(key, oid, ctx, gateKey)
@ -329,7 +329,7 @@ func createTokens(options *IssueSecretOptions, cid *cid.ID) ([]*accessbox.GateDa
if err != nil {
return nil, fmt.Errorf("failed to build context for session token: %w", err)
}
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
oid, err := ownerIDFromNeoFSKey(options.NeoFSKey.PublicKey())
if err != nil {
return nil, err
}
@ -346,23 +346,10 @@ func createTokens(options *IssueSecretOptions, cid *cid.ID) ([]*accessbox.GateDa
return gates, nil
}
func ownerIDFromNeoFSKey(key *ecdsa.PublicKey) (*owner.ID, error) {
wallet, err := owner.NEO3WalletFromPublicKey(key)
func ownerIDFromNeoFSKey(key *keys.PublicKey) (*owner.ID, error) {
wallet, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(key))
if err != nil {
return nil, err
}
return owner.NewIDFromNeo3Wallet(wallet), nil
}
// LoadPublicKey returns ecdsa.PublicKey from hex string.
func LoadPublicKey(val string) (*ecdsa.PublicKey, error) {
data, err := hex.DecodeString(val)
if err != nil {
return nil, fmt.Errorf("unknown key format (%q), expect: hex-string", val)
}
if key := crypto.UnmarshalPublicKey(data); key != nil {
return key, nil
}
return nil, fmt.Errorf("couldn't unmarshal public key (%q)", val)
}

View file

@ -10,11 +10,13 @@ import (
"syscall"
"time"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-s3-gw/authmate"
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
"github.com/nspcc-dev/neofs-s3-gw/internal/wallet"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
"github.com/spf13/viper"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -26,18 +28,25 @@ const (
)
var (
neoFSKeyPathFlag string
peerAddressFlag string
eaclRulesFlag string
contextRulesFlag string
gatePrivateKeyFlag string
accessKeyIDFlag string
containerIDFlag string
containerFriendlyName string
gatesPublicKeysFlag cli.StringSlice
logEnabledFlag bool
logDebugEnabledFlag bool
sessionTokenFlag bool
walletPathFlag string
accountAddressFlag string
peerAddressFlag string
eaclRulesFlag string
contextRulesFlag string
gateWalletPathFlag string
gateAccountAddressFlag string
accessKeyIDFlag string
containerIDFlag string
containerFriendlyName string
gatesPublicKeysFlag cli.StringSlice
logEnabledFlag bool
logDebugEnabledFlag bool
sessionTokenFlag bool
)
const (
envWalletPassphrase = "wallet.passphrase"
envWalletGatePassphrase = "wallet.gate.passphrase"
)
var zapConfig = zap.Config{
@ -85,6 +94,11 @@ func main() {
Commands: appCommands(),
}
viper.AutomaticEnv()
viper.SetEnvPrefix("AUTHMATE")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AllowEmptyEnv(true)
if err := app.Run(os.Args); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err)
os.Exit(100)
@ -119,11 +133,18 @@ func issueSecret() *cli.Command {
Usage: "Issue a secret in NeoFS network",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "neofs-key",
Name: "wallet",
Value: "",
Usage: "path to owner's neofs private ecdsa key",
Usage: "path to the wallet",
Required: true,
Destination: &neoFSKeyPathFlag,
Destination: &walletPathFlag,
},
&cli.StringFlag{
Name: "address",
Value: "",
Usage: "address of wallet account",
Required: false,
Destination: &accountAddressFlag,
},
&cli.StringFlag{
Name: "peer",
@ -174,7 +195,8 @@ func issueSecret() *cli.Command {
Action: func(c *cli.Context) error {
ctx, log := prepare()
key, err := crypto.LoadPrivateKey(neoFSKeyPathFlag)
password := wallet.GetPassword(viper.GetViper(), envWalletPassphrase)
key, err := wallet.GetKeyFromPath(walletPathFlag, accountAddressFlag, password)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to load neofs private key: %s", err), 1)
}
@ -182,7 +204,7 @@ func issueSecret() *cli.Command {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
client, err := createSDKClient(ctx, log, key, peerAddressFlag)
client, err := createSDKClient(ctx, log, &key.PrivateKey, peerAddressFlag)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
}
@ -196,9 +218,9 @@ func issueSecret() *cli.Command {
}
}
var gatesPublicKeys []*ecdsa.PublicKey
var gatesPublicKeys []*keys.PublicKey
for _, key := range gatesPublicKeysFlag.Value() {
gpk, err := authmate.LoadPublicKey(key)
gpk, err := keys.NewPublicKeyFromString(key)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to load gate's public key: %s", err), 5)
}
@ -230,11 +252,18 @@ func obtainSecret() *cli.Command {
Usage: "Obtain a secret from NeoFS network",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "neofs-key",
Name: "wallet",
Value: "",
Usage: "path to owner's neofs private ecdsa key",
Usage: "path to the wallet",
Required: true,
Destination: &neoFSKeyPathFlag,
Destination: &walletPathFlag,
},
&cli.StringFlag{
Name: "address",
Value: "",
Usage: "address of wallet account",
Required: false,
Destination: &accountAddressFlag,
},
&cli.StringFlag{
Name: "peer",
@ -244,10 +273,18 @@ func obtainSecret() *cli.Command {
Destination: &peerAddressFlag,
},
&cli.StringFlag{
Name: "gate-private-key",
Usage: "gate's private x25519 key",
Name: "gate-wallet",
Value: "",
Usage: "path to the wallet",
Required: true,
Destination: &gatePrivateKeyFlag,
Destination: &gateWalletPathFlag,
},
&cli.StringFlag{
Name: "gate-address",
Value: "",
Usage: "address of wallet account",
Required: false,
Destination: &gateAccountAddressFlag,
},
&cli.StringFlag{
Name: "access-key-id",
@ -259,7 +296,8 @@ func obtainSecret() *cli.Command {
Action: func(c *cli.Context) error {
ctx, log := prepare()
key, err := crypto.LoadPrivateKey(neoFSKeyPathFlag)
password := wallet.GetPassword(viper.GetViper(), envWalletPassphrase)
key, err := wallet.GetKeyFromPath(walletPathFlag, accountAddressFlag, password)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to load neofs private key: %s", err), 1)
}
@ -267,7 +305,7 @@ func obtainSecret() *cli.Command {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
client, err := createSDKClient(ctx, log, key, peerAddressFlag)
client, err := createSDKClient(ctx, log, &key.PrivateKey, peerAddressFlag)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
}
@ -276,7 +314,8 @@ func obtainSecret() *cli.Command {
var _ = agent
gateCreds, err := crypto.LoadPrivateKey(gatePrivateKeyFlag)
password = wallet.GetPassword(viper.GetViper(), envWalletGatePassphrase)
gateCreds, err := wallet.GetKeyFromPath(gateWalletPathFlag, gateAccountAddressFlag, password)
if err != nil {
return cli.Exit(fmt.Sprintf("failed to create owner's private key: %s", err), 4)
}

View file

@ -151,6 +151,7 @@ func newSettings() *viper.Viper {
v.SetEnvPrefix(envPrefix)
v.SetConfigType("yaml")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AllowEmptyEnv(true)
// flags setup:
flags := pflag.NewFlagSet("commandline", pflag.ExitOnError)

View file

@ -3,20 +3,16 @@ package main
import (
"context"
"encoding/hex"
"fmt"
"math"
"net"
"net/http"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/auth"
"github.com/nspcc-dev/neofs-s3-gw/api/handler"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/nspcc-dev/neofs-s3-gw/internal/wallet"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
"github.com/spf13/viper"
"go.uber.org/zap"
@ -85,12 +81,8 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
reBalance = v
}
var password *string
if v.IsSet(cfgWalletPassphrase) {
pwd := v.GetString(cfgWalletPassphrase)
password = &pwd
}
if key, err = getKeyFromWallet(v.GetString(cfgWallet), v.GetString(cfgAddress), password); err != nil {
password := wallet.GetPassword(v, cfgWalletPassphrase)
if key, err = wallet.GetKeyFromPath(v.GetString(cfgWallet), v.GetString(cfgAddress), password); err != nil {
l.Fatal("could not load NeoFS private key", zap.Error(err))
}
@ -120,7 +112,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
obj = layer.NewLayer(l, conns)
// prepare auth center
ctr = auth.New(conns, &key.PrivateKey)
ctr = auth.New(conns, key)
if caller, err = handler.New(l, obj); err != nil {
l.Fatal("could not initialize API handler", zap.Error(err))
@ -142,44 +134,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
}
}
func getKeyFromWallet(walletPath, addrStr string, password *string) (*keys.PrivateKey, error) {
if len(walletPath) == 0 {
return nil, fmt.Errorf("wallet path must not be empty")
}
w, err := wallet.NewWalletFromFile(walletPath)
if err != nil {
return nil, err
}
var addr util.Uint160
if len(addrStr) == 0 {
addr = w.GetChangeAddress()
} else {
addr, err = flags.ParseAddress(addrStr)
if err != nil {
return nil, fmt.Errorf("invalid address")
}
}
acc := w.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("couldn't find wallet account for %s", addrStr)
}
if password == nil {
pwd, err := input.ReadPassword("Enter password > ")
if err != nil {
return nil, fmt.Errorf("couldn't read password")
}
password = &pwd
}
if err := acc.Decrypt(*password, w.Scrypt); err != nil {
return nil, fmt.Errorf("couldn't decrypt account: %w", err)
}
return acc.PrivateKey(), nil
}
// Wait waits for application to finish.
func (a *App) Wait() {
a.log.Info("application started")

View file

@ -3,7 +3,6 @@ package accessbox
import (
"bytes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
@ -11,9 +10,9 @@ import (
"fmt"
"io"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/pkg/session"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
crypto "github.com/nspcc-dev/neofs-crypto"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
"google.golang.org/protobuf/proto"
@ -24,18 +23,18 @@ type GateData struct {
AccessKey string
BearerToken *token.BearerToken
SessionToken *session.Token
GateKey *ecdsa.PublicKey
GateKey *keys.PublicKey
}
// NewGateData returns GateData from provided bearer token and public gate key.
func NewGateData(gateKey *ecdsa.PublicKey, bearerTkn *token.BearerToken) *GateData {
func NewGateData(gateKey *keys.PublicKey, bearerTkn *token.BearerToken) *GateData {
return &GateData{GateKey: gateKey, BearerToken: bearerTkn}
}
// Secrets represents AccessKey and key to encrypt gate tokens.
type Secrets struct {
AccessKey string
EphemeralKey *ecdsa.PrivateKey
EphemeralKey *keys.PrivateKey
}
// Marshal returns the wire-format of AccessBox.
@ -52,11 +51,11 @@ func (x *AccessBox) Unmarshal(data []byte) error {
// Session token can be nil.
func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
box := &AccessBox{}
ephemeralKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
ephemeralKey, err := keys.NewPrivateKey()
if err != nil {
return nil, nil, err
}
box.OwnerPublicKey = crypto.MarshalPublicKey(&ephemeralKey.PublicKey)
box.OwnerPublicKey = ephemeralKey.PublicKey().Bytes()
secret, err := generateSecret()
if err != nil {
@ -71,9 +70,12 @@ func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
}
// GetTokens returns gate tokens from AccessBox.
func (x *AccessBox) GetTokens(owner *ecdsa.PrivateKey) (*GateData, error) {
sender := crypto.UnmarshalPublicKey(x.OwnerPublicKey)
ownerKey := crypto.MarshalPublicKey(&owner.PublicKey)
func (x *AccessBox) GetTokens(owner *keys.PrivateKey) (*GateData, error) {
sender, err := keys.NewPublicKeyFromBytes(x.OwnerPublicKey, elliptic.P256())
if err != nil {
return nil, fmt.Errorf("couldn't unmarshal OwnerPublicKey: %w", err)
}
ownerKey := owner.PublicKey().Bytes()
for _, gate := range x.Gates {
if !bytes.Equal(gate.GatePublicKey, ownerKey) {
continue
@ -89,7 +91,7 @@ func (x *AccessBox) GetTokens(owner *ecdsa.PrivateKey) (*GateData, error) {
return nil, fmt.Errorf("no gate data for key %x was found", ownerKey)
}
func (x *AccessBox) addTokens(gatesData []*GateData, ephemeralKey *ecdsa.PrivateKey, secret []byte) error {
func (x *AccessBox) addTokens(gatesData []*GateData, ephemeralKey *keys.PrivateKey, secret []byte) error {
for i, gate := range gatesData {
encBearer, err := gate.BearerToken.Marshal()
if err != nil {
@ -117,7 +119,7 @@ func (x *AccessBox) addTokens(gatesData []*GateData, ephemeralKey *ecdsa.Private
return nil
}
func encodeGate(ephemeralKey *ecdsa.PrivateKey, ownerKey *ecdsa.PublicKey, tokens *Tokens) (*AccessBox_Gate, error) {
func encodeGate(ephemeralKey *keys.PrivateKey, ownerKey *keys.PublicKey, tokens *Tokens) (*AccessBox_Gate, error) {
data, err := proto.Marshal(tokens)
if err != nil {
return nil, err
@ -129,12 +131,12 @@ func encodeGate(ephemeralKey *ecdsa.PrivateKey, ownerKey *ecdsa.PublicKey, token
}
gate := new(AccessBox_Gate)
gate.GatePublicKey = crypto.MarshalPublicKey(ownerKey)
gate.GatePublicKey = ownerKey.Bytes()
gate.Tokens = encrypted
return gate, nil
}
func decodeGate(gate *AccessBox_Gate, owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey) (*GateData, error) {
func decodeGate(gate *AccessBox_Gate, owner *keys.PrivateKey, sender *keys.PublicKey) (*GateData, error) {
data, err := decrypt(owner, sender, gate.Tokens)
if err != nil {
return nil, err
@ -153,14 +155,14 @@ func decodeGate(gate *AccessBox_Gate, owner *ecdsa.PrivateKey, sender *ecdsa.Pub
return nil, err
}
gateData := NewGateData(&owner.PublicKey, bearerTkn)
gateData := NewGateData(owner.PublicKey(), bearerTkn)
gateData.SessionToken = sessionTkn
gateData.AccessKey = hex.EncodeToString(tokens.AccessKey)
return gateData, nil
}
func generateShared256(prv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) (sk []byte, err error) {
if prv.PublicKey.Curve != pub.Curve {
func generateShared256(prv *keys.PrivateKey, pub *keys.PublicKey) (sk []byte, err error) {
if prv.PublicKey().Curve != pub.Curve {
return nil, fmt.Errorf("not equal curves")
}
@ -183,7 +185,7 @@ func deriveKey(secret []byte) ([]byte, error) {
return key, err
}
func encrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]byte, error) {
func encrypt(owner *keys.PrivateKey, sender *keys.PublicKey, data []byte) ([]byte, error) {
enc, err := getCipher(owner, sender)
if err != nil {
return nil, err
@ -197,7 +199,7 @@ func encrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]b
return enc.Seal(nonce, nonce, data, nil), nil
}
func decrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]byte, error) {
func decrypt(owner *keys.PrivateKey, sender *keys.PublicKey, data []byte) ([]byte, error) {
dec, err := getCipher(owner, sender)
if err != nil {
return nil, err
@ -211,7 +213,7 @@ func decrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]b
return dec.Open(nil, nonce, cypher, nil)
}
func getCipher(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey) (cipher.AEAD, error) {
func getCipher(owner *keys.PrivateKey, sender *keys.PublicKey) (cipher.AEAD, error) {
secret, err := generateShared256(owner, sender)
if err != nil {
return nil, err

View file

@ -1,16 +1,13 @@
package accessbox
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"testing"
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
"github.com/nspcc-dev/neofs-api-go/pkg/session"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/stretchr/testify/require"
)
@ -19,22 +16,22 @@ func Test_tokens_encrypt_decrypt(t *testing.T) {
tkn = token.NewBearerToken()
tkn2 = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sec, err := keys.NewPrivateKey()
require.NoError(t, err)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
cred, err := keys.NewPrivateKey()
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
require.NoError(t, tkn.SignToken(&sec.PrivateKey))
rawTkn, err := tkn.Marshal()
require.NoError(t, err)
data, err := encrypt(cred, &cred.PublicKey, rawTkn)
data, err := encrypt(cred, cred.PublicKey(), rawTkn)
require.NoError(t, err)
rawTkn2, err := decrypt(cred, &cred.PublicKey, data)
rawTkn2, err := decrypt(cred, cred.PublicKey(), data)
require.NoError(t, err)
err = tkn2.Unmarshal(rawTkn2)
@ -50,16 +47,16 @@ func Test_bearer_token_in_access_box(t *testing.T) {
tkn = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sec, err := keys.NewPrivateKey()
require.NoError(t, err)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
cred, err := keys.NewPrivateKey()
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
require.NoError(t, tkn.SignToken(&sec.PrivateKey))
gate := NewGateData(&cred.PublicKey, tkn)
gate := NewGateData(cred.PublicKey(), tkn)
box, _, err = PackTokens([]*GateData{gate})
require.NoError(t, err)
@ -82,10 +79,10 @@ func Test_session_token_in_access_box(t *testing.T) {
tkn = session.NewToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sec, err := keys.NewPrivateKey()
require.NoError(t, err)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
cred, err := keys.NewPrivateKey()
require.NoError(t, err)
tok := session.NewToken()
@ -93,10 +90,10 @@ func Test_session_token_in_access_box(t *testing.T) {
uid, err := uuid.New().MarshalBinary()
require.NoError(t, err)
tok.SetID(uid)
tok.SetSessionKey(crypto.MarshalPublicKey(&sec.PublicKey))
require.NoError(t, tkn.Sign(sec))
tok.SetSessionKey(sec.PublicKey().Bytes())
require.NoError(t, tkn.Sign(&sec.PrivateKey))
gate := NewGateData(&cred.PublicKey, token.NewBearerToken())
gate := NewGateData(cred.PublicKey(), token.NewBearerToken())
gate.SessionToken = tkn
box, _, err = PackTokens([]*GateData{gate})
require.NoError(t, err)
@ -119,29 +116,29 @@ func Test_accessbox_multiple_keys(t *testing.T) {
tkn = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sec, err := keys.NewPrivateKey()
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
require.NoError(t, tkn.SignToken(&sec.PrivateKey))
count := 10
gates := make([]*GateData, 0, count)
keys := make([]*ecdsa.PrivateKey, 0, count)
privateKeys := make([]*keys.PrivateKey, 0, count)
{ // generate keys
for i := 0; i < count; i++ {
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
cred, err := keys.NewPrivateKey()
require.NoError(t, err)
gates = append(gates, NewGateData(&cred.PublicKey, tkn))
keys = append(keys, cred)
gates = append(gates, NewGateData(cred.PublicKey(), tkn))
privateKeys = append(privateKeys, cred)
}
}
box, _, err = PackTokens(gates)
require.NoError(t, err)
for i, k := range keys {
for i, k := range privateKeys {
tkns, err := box.GetTokens(k)
require.NoError(t, err, "key #%d: %s failed", i, k)
require.Equal(t, tkns.BearerToken, tkn)
@ -154,19 +151,19 @@ func Test_unknown_key(t *testing.T) {
tkn = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sec, err := keys.NewPrivateKey()
require.NoError(t, err)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
cred, err := keys.NewPrivateKey()
require.NoError(t, err)
wrongCred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
wrongCred, err := keys.NewPrivateKey()
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
require.NoError(t, tkn.SignToken(&sec.PrivateKey))
gate := NewGateData(&cred.PublicKey, tkn)
gate := NewGateData(cred.PublicKey(), tkn)
box, _, err = PackTokens([]*GateData{gate})
require.NoError(t, err)

View file

@ -3,12 +3,12 @@ package tokens
import (
"bytes"
"context"
"crypto/ecdsa"
"errors"
"strconv"
"sync"
"time"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/pkg/client"
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
@ -21,11 +21,11 @@ type (
// Credentials is a bearer token get/put interface.
Credentials interface {
GetTokens(context.Context, *object.Address) (*accessbox.GateData, error)
Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, ...*ecdsa.PublicKey) (*object.Address, error)
Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, ...*keys.PublicKey) (*object.Address, error)
}
cred struct {
key *ecdsa.PrivateKey
key *keys.PrivateKey
pool pool.Pool
}
)
@ -46,7 +46,7 @@ var bufferPool = sync.Pool{
var _ = New
// New creates new Credentials instance using given cli and key.
func New(conns pool.Pool, key *ecdsa.PrivateKey) Credentials {
func New(conns pool.Pool, key *keys.PrivateKey) Credentials {
return &cred{pool: conns, key: key}
}
@ -91,7 +91,7 @@ func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*acce
return &box, nil
}
func (c *cred) Put(ctx context.Context, cid *cid.ID, issuer *owner.ID, box *accessbox.AccessBox, keys ...*ecdsa.PublicKey) (*object.Address, error) {
func (c *cred) Put(ctx context.Context, cid *cid.ID, issuer *owner.ID, box *accessbox.AccessBox, keys ...*keys.PublicKey) (*object.Address, error) {
var (
err error
created = strconv.FormatInt(time.Now().Unix(), 10)

1
go.mod
View file

@ -9,7 +9,6 @@ require (
github.com/gorilla/mux v1.8.0
github.com/nspcc-dev/neo-go v0.95.3
github.com/nspcc-dev/neofs-api-go v1.27.1
github.com/nspcc-dev/neofs-crypto v0.3.0
github.com/nspcc-dev/neofs-node v1.22.0
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210624072335-0348eb331c92
github.com/prometheus/client_golang v1.9.0

61
internal/wallet/wallet.go Normal file
View file

@ -0,0 +1,61 @@
package wallet
import (
"fmt"
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/viper"
)
// GetPassword gets passphrase for wallet.
func GetPassword(v *viper.Viper, variable string) *string {
var password *string
if v.IsSet(variable) {
pwd := v.GetString(variable)
password = &pwd
}
return password
}
// GetKeyFromPath reads wallet and gets private key.
func GetKeyFromPath(walletPath, addrStr string, password *string) (*keys.PrivateKey, error) {
if len(walletPath) == 0 {
return nil, fmt.Errorf("wallet path must not be empty")
}
w, err := wallet.NewWalletFromFile(walletPath)
if err != nil {
return nil, err
}
var addr util.Uint160
if len(addrStr) == 0 {
addr = w.GetChangeAddress()
} else {
addr, err = flags.ParseAddress(addrStr)
if err != nil {
return nil, fmt.Errorf("invalid address")
}
}
acc := w.GetAccount(addr)
if acc == nil {
return nil, fmt.Errorf("couldn't find wallet account for %s", addrStr)
}
if password == nil {
pwd, err := input.ReadPassword(fmt.Sprintf("Enter password for %s > ", walletPath))
if err != nil {
return nil, fmt.Errorf("couldn't read password")
}
password = &pwd
}
if err := acc.Decrypt(*password, w.Scrypt); err != nil {
return nil, fmt.Errorf("couldn't decrypt account: %w", err)
}
return acc.PrivateKey(), nil
}