forked from TrueCloudLab/frostfs-s3-gw
[#104] Support NEP-6 for authmate
Drop neofs-crypto. Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
39a43c21a1
commit
52c63d4c44
11 changed files with 227 additions and 172 deletions
27
README.md
27
README.md
|
@ -109,7 +109,7 @@ default. To enable them use `--pprof` and `--metrics` flags or
|
||||||
|
|
||||||
## NeoFS AuthMate
|
## 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
|
are authenticated with access key IDs and secrets, while NeoFS users are
|
||||||
authenticated with key pairs. To complicate things further we have S3 gateway
|
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
|
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
|
"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
|
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
|
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
|
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
|
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
|
to read and it needs to be encrypted with this gateway's key (among others
|
||||||
potentially).
|
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
|
#### Generation of wallet
|
||||||
|
|
||||||
To generate wallets for gateways, run the following command:
|
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:
|
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 \
|
--peer 192.168.130.71:8080 \
|
||||||
--bearer-rules '{"records":[{"operation":"PUT","action":"ALLOW","filters":[],"targets":[{"role":"OTHERS","keys":[]}]}]}' \
|
--bearer-rules '{"records":[{"operation":"PUT","action":"ALLOW","filters":[],"targets":[{"role":"OTHERS","keys":[]}]}]}' \
|
||||||
--gate-public-key dd34f6dce9a4ce0990869ec6bd33a40e102a5798881cfe61d03a5659ceee1a64 \
|
--gate-public-key dd34f6dce9a4ce0990869ec6bd33a40e102a5798881cfe61d03a5659ceee1a64 \
|
||||||
|
@ -242,6 +253,7 @@ $ ./neofs-authmate issue-secret --neofs-key user.key \
|
||||||
--create-session-token \
|
--create-session-token \
|
||||||
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'
|
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'
|
||||||
|
|
||||||
|
Enter password for wallet.json >
|
||||||
{
|
{
|
||||||
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
|
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
|
||||||
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c",
|
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c",
|
||||||
|
@ -255,14 +267,17 @@ any S3 client.
|
||||||
#### Obtainment of a secret access key
|
#### Obtainment of a secret access key
|
||||||
|
|
||||||
You can get a secret access key associated with access key ID by obtaining a
|
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 \
|
--peer 192.168.130.71:8080 \
|
||||||
--gate-private-key b8ba980eb70b959be99915d2e0ad377809984ccd1dac0a6551907f81c2b33d21 \
|
--gate-wallet gate-wallet.json \
|
||||||
--access-key-id 5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM
|
--access-key-id 5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM
|
||||||
|
|
||||||
|
Enter password for gate-wallet.json >
|
||||||
{
|
{
|
||||||
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c"
|
"secret_access_key": "438bbd8243060e1e1c9dd4821756914a6e872ce29bf203b68f81b140ac91231c"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -13,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
|
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-api-go/pkg/object"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
"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)
|
var _ io.ReadSeeker = prs(0)
|
||||||
|
|
||||||
// New creates an instance of AuthCenter.
|
// 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 ¢er{
|
return ¢er{
|
||||||
cli: tokens.New(conns, key),
|
cli: tokens.New(conns, key),
|
||||||
reg: ®expSubmatcher{re: authorizationFieldRegexp},
|
reg: ®expSubmatcher{re: authorizationFieldRegexp},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"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/acl/eacl"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
||||||
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
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/owner"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/session"
|
"github.com/nspcc-dev/neofs-api-go/pkg/session"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
"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-node/pkg/policy"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
||||||
|
@ -48,8 +48,8 @@ type (
|
||||||
IssueSecretOptions struct {
|
IssueSecretOptions struct {
|
||||||
ContainerID *cid.ID
|
ContainerID *cid.ID
|
||||||
ContainerFriendlyName string
|
ContainerFriendlyName string
|
||||||
NeoFSKey *ecdsa.PrivateKey
|
NeoFSKey *keys.PrivateKey
|
||||||
GatesPublicKeys []*ecdsa.PublicKey
|
GatesPublicKeys []*keys.PublicKey
|
||||||
EACLRules []byte
|
EACLRules []byte
|
||||||
ContextRules []byte
|
ContextRules []byte
|
||||||
SessionTkn bool
|
SessionTkn bool
|
||||||
|
@ -58,7 +58,7 @@ type (
|
||||||
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
||||||
ObtainSecretOptions struct {
|
ObtainSecretOptions struct {
|
||||||
SecretAddress string
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
|
oid, err := ownerIDFromNeoFSKey(options.NeoFSKey.PublicKey())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
ir := &issuingResult{
|
ir := &issuingResult{
|
||||||
AccessKeyID: accessKeyID,
|
AccessKeyID: accessKeyID,
|
||||||
SecretAccessKey: secrets.AccessKey,
|
SecretAccessKey: secrets.AccessKey,
|
||||||
OwnerPrivateKey: hex.EncodeToString(crypto.MarshalPrivateKey(secrets.EphemeralKey)),
|
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
||||||
}
|
}
|
||||||
|
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
|
@ -257,7 +257,7 @@ func buildContext(rules []byte) (*session.ContainerContext, error) {
|
||||||
return sessionCtx, nil
|
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)
|
oid, err := ownerIDFromNeoFSKey(gateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -268,10 +268,10 @@ func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table, gateKey *ecdsa.P
|
||||||
bearerToken.SetOwner(oid)
|
bearerToken.SetOwner(oid)
|
||||||
bearerToken.SetLifetime(math.MaxUint64, 0, 0)
|
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))
|
bearerTokens := make([]*token.BearerToken, 0, len(gatesKeys))
|
||||||
for _, gateKey := range gatesKeys {
|
for _, gateKey := range gatesKeys {
|
||||||
tkn, err := buildBearerToken(key, table, gateKey)
|
tkn, err := buildBearerToken(key, table, gateKey)
|
||||||
|
@ -283,7 +283,7 @@ func buildBearerTokens(key *ecdsa.PrivateKey, table *eacl.Table, gatesKeys []*ec
|
||||||
return bearerTokens, nil
|
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 := session.NewToken()
|
||||||
tok.SetContext(ctx)
|
tok.SetContext(ctx)
|
||||||
uid, err := uuid.New().MarshalBinary()
|
uid, err := uuid.New().MarshalBinary()
|
||||||
|
@ -292,12 +292,12 @@ func buildSessionToken(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.Contai
|
||||||
}
|
}
|
||||||
tok.SetID(uid)
|
tok.SetID(uid)
|
||||||
tok.SetOwnerID(oid)
|
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))
|
sessionTokens := make([]*session.Token, 0, len(gatesKeys))
|
||||||
for _, gateKey := range gatesKeys {
|
for _, gateKey := range gatesKeys {
|
||||||
tkn, err := buildSessionToken(key, oid, ctx, gateKey)
|
tkn, err := buildSessionToken(key, oid, ctx, gateKey)
|
||||||
|
@ -329,7 +329,7 @@ func createTokens(options *IssueSecretOptions, cid *cid.ID) ([]*accessbox.GateDa
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to build context for session token: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -346,23 +346,10 @@ func createTokens(options *IssueSecretOptions, cid *cid.ID) ([]*accessbox.GateDa
|
||||||
return gates, nil
|
return gates, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ownerIDFromNeoFSKey(key *ecdsa.PublicKey) (*owner.ID, error) {
|
func ownerIDFromNeoFSKey(key *keys.PublicKey) (*owner.ID, error) {
|
||||||
wallet, err := owner.NEO3WalletFromPublicKey(key)
|
wallet, err := owner.NEO3WalletFromPublicKey((*ecdsa.PublicKey)(key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return owner.NewIDFromNeo3Wallet(wallet), nil
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,11 +10,13 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
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/authmate"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
|
"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/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
@ -26,18 +28,25 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
neoFSKeyPathFlag string
|
walletPathFlag string
|
||||||
peerAddressFlag string
|
accountAddressFlag string
|
||||||
eaclRulesFlag string
|
peerAddressFlag string
|
||||||
contextRulesFlag string
|
eaclRulesFlag string
|
||||||
gatePrivateKeyFlag string
|
contextRulesFlag string
|
||||||
accessKeyIDFlag string
|
gateWalletPathFlag string
|
||||||
containerIDFlag string
|
gateAccountAddressFlag string
|
||||||
containerFriendlyName string
|
accessKeyIDFlag string
|
||||||
gatesPublicKeysFlag cli.StringSlice
|
containerIDFlag string
|
||||||
logEnabledFlag bool
|
containerFriendlyName string
|
||||||
logDebugEnabledFlag bool
|
gatesPublicKeysFlag cli.StringSlice
|
||||||
sessionTokenFlag bool
|
logEnabledFlag bool
|
||||||
|
logDebugEnabledFlag bool
|
||||||
|
sessionTokenFlag bool
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
envWalletPassphrase = "wallet.passphrase"
|
||||||
|
envWalletGatePassphrase = "wallet.gate.passphrase"
|
||||||
)
|
)
|
||||||
|
|
||||||
var zapConfig = zap.Config{
|
var zapConfig = zap.Config{
|
||||||
|
@ -85,6 +94,11 @@ func main() {
|
||||||
Commands: appCommands(),
|
Commands: appCommands(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viper.AutomaticEnv()
|
||||||
|
viper.SetEnvPrefix("AUTHMATE")
|
||||||
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
viper.AllowEmptyEnv(true)
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err)
|
_, _ = fmt.Fprintf(os.Stderr, "error: %s\n", err)
|
||||||
os.Exit(100)
|
os.Exit(100)
|
||||||
|
@ -119,11 +133,18 @@ func issueSecret() *cli.Command {
|
||||||
Usage: "Issue a secret in NeoFS network",
|
Usage: "Issue a secret in NeoFS network",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "neofs-key",
|
Name: "wallet",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "path to owner's neofs private ecdsa key",
|
Usage: "path to the wallet",
|
||||||
Required: true,
|
Required: true,
|
||||||
Destination: &neoFSKeyPathFlag,
|
Destination: &walletPathFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "address",
|
||||||
|
Value: "",
|
||||||
|
Usage: "address of wallet account",
|
||||||
|
Required: false,
|
||||||
|
Destination: &accountAddressFlag,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "peer",
|
Name: "peer",
|
||||||
|
@ -174,7 +195,8 @@ func issueSecret() *cli.Command {
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
ctx, log := prepare()
|
ctx, log := prepare()
|
||||||
|
|
||||||
key, err := crypto.LoadPrivateKey(neoFSKeyPathFlag)
|
password := wallet.GetPassword(viper.GetViper(), envWalletPassphrase)
|
||||||
|
key, err := wallet.GetKeyFromPath(walletPathFlag, accountAddressFlag, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to load neofs private key: %s", err), 1)
|
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)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
client, err := createSDKClient(ctx, log, key, peerAddressFlag)
|
client, err := createSDKClient(ctx, log, &key.PrivateKey, peerAddressFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
|
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() {
|
for _, key := range gatesPublicKeysFlag.Value() {
|
||||||
gpk, err := authmate.LoadPublicKey(key)
|
gpk, err := keys.NewPublicKeyFromString(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to load gate's public key: %s", err), 5)
|
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",
|
Usage: "Obtain a secret from NeoFS network",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "neofs-key",
|
Name: "wallet",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "path to owner's neofs private ecdsa key",
|
Usage: "path to the wallet",
|
||||||
Required: true,
|
Required: true,
|
||||||
Destination: &neoFSKeyPathFlag,
|
Destination: &walletPathFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "address",
|
||||||
|
Value: "",
|
||||||
|
Usage: "address of wallet account",
|
||||||
|
Required: false,
|
||||||
|
Destination: &accountAddressFlag,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "peer",
|
Name: "peer",
|
||||||
|
@ -244,10 +273,18 @@ func obtainSecret() *cli.Command {
|
||||||
Destination: &peerAddressFlag,
|
Destination: &peerAddressFlag,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "gate-private-key",
|
Name: "gate-wallet",
|
||||||
Usage: "gate's private x25519 key",
|
Value: "",
|
||||||
|
Usage: "path to the wallet",
|
||||||
Required: true,
|
Required: true,
|
||||||
Destination: &gatePrivateKeyFlag,
|
Destination: &gateWalletPathFlag,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "gate-address",
|
||||||
|
Value: "",
|
||||||
|
Usage: "address of wallet account",
|
||||||
|
Required: false,
|
||||||
|
Destination: &gateAccountAddressFlag,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "access-key-id",
|
Name: "access-key-id",
|
||||||
|
@ -259,7 +296,8 @@ func obtainSecret() *cli.Command {
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
ctx, log := prepare()
|
ctx, log := prepare()
|
||||||
|
|
||||||
key, err := crypto.LoadPrivateKey(neoFSKeyPathFlag)
|
password := wallet.GetPassword(viper.GetViper(), envWalletPassphrase)
|
||||||
|
key, err := wallet.GetKeyFromPath(walletPathFlag, accountAddressFlag, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to load neofs private key: %s", err), 1)
|
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)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
client, err := createSDKClient(ctx, log, key, peerAddressFlag)
|
client, err := createSDKClient(ctx, log, &key.PrivateKey, peerAddressFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
|
return cli.Exit(fmt.Sprintf("failed to create sdk client: %s", err), 2)
|
||||||
}
|
}
|
||||||
|
@ -276,7 +314,8 @@ func obtainSecret() *cli.Command {
|
||||||
|
|
||||||
var _ = agent
|
var _ = agent
|
||||||
|
|
||||||
gateCreds, err := crypto.LoadPrivateKey(gatePrivateKeyFlag)
|
password = wallet.GetPassword(viper.GetViper(), envWalletGatePassphrase)
|
||||||
|
gateCreds, err := wallet.GetKeyFromPath(gateWalletPathFlag, gateAccountAddressFlag, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to create owner's private key: %s", err), 4)
|
return cli.Exit(fmt.Sprintf("failed to create owner's private key: %s", err), 4)
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,6 +151,7 @@ func newSettings() *viper.Viper {
|
||||||
v.SetEnvPrefix(envPrefix)
|
v.SetEnvPrefix(envPrefix)
|
||||||
v.SetConfigType("yaml")
|
v.SetConfigType("yaml")
|
||||||
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
v.AllowEmptyEnv(true)
|
||||||
|
|
||||||
// flags setup:
|
// flags setup:
|
||||||
flags := pflag.NewFlagSet("commandline", pflag.ExitOnError)
|
flags := pflag.NewFlagSet("commandline", pflag.ExitOnError)
|
||||||
|
|
|
@ -3,20 +3,16 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"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/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"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/auth"
|
"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/handler"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"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/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -85,12 +81,8 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
reBalance = v
|
reBalance = v
|
||||||
}
|
}
|
||||||
|
|
||||||
var password *string
|
password := wallet.GetPassword(v, cfgWalletPassphrase)
|
||||||
if v.IsSet(cfgWalletPassphrase) {
|
if key, err = wallet.GetKeyFromPath(v.GetString(cfgWallet), v.GetString(cfgAddress), password); err != nil {
|
||||||
pwd := v.GetString(cfgWalletPassphrase)
|
|
||||||
password = &pwd
|
|
||||||
}
|
|
||||||
if key, err = getKeyFromWallet(v.GetString(cfgWallet), v.GetString(cfgAddress), password); err != nil {
|
|
||||||
l.Fatal("could not load NeoFS private key", zap.Error(err))
|
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)
|
obj = layer.NewLayer(l, conns)
|
||||||
|
|
||||||
// prepare auth center
|
// prepare auth center
|
||||||
ctr = auth.New(conns, &key.PrivateKey)
|
ctr = auth.New(conns, key)
|
||||||
|
|
||||||
if caller, err = handler.New(l, obj); err != nil {
|
if caller, err = handler.New(l, obj); err != nil {
|
||||||
l.Fatal("could not initialize API handler", zap.Error(err))
|
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.
|
// Wait waits for application to finish.
|
||||||
func (a *App) Wait() {
|
func (a *App) Wait() {
|
||||||
a.log.Info("application started")
|
a.log.Info("application started")
|
||||||
|
|
|
@ -3,7 +3,6 @@ package accessbox
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
@ -11,9 +10,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"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/session"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
"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/chacha20poly1305"
|
||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
@ -24,18 +23,18 @@ type GateData struct {
|
||||||
AccessKey string
|
AccessKey string
|
||||||
BearerToken *token.BearerToken
|
BearerToken *token.BearerToken
|
||||||
SessionToken *session.Token
|
SessionToken *session.Token
|
||||||
GateKey *ecdsa.PublicKey
|
GateKey *keys.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGateData returns GateData from provided bearer token and public gate key.
|
// 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}
|
return &GateData{GateKey: gateKey, BearerToken: bearerTkn}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secrets represents AccessKey and key to encrypt gate tokens.
|
// Secrets represents AccessKey and key to encrypt gate tokens.
|
||||||
type Secrets struct {
|
type Secrets struct {
|
||||||
AccessKey string
|
AccessKey string
|
||||||
EphemeralKey *ecdsa.PrivateKey
|
EphemeralKey *keys.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal returns the wire-format of AccessBox.
|
// Marshal returns the wire-format of AccessBox.
|
||||||
|
@ -52,11 +51,11 @@ func (x *AccessBox) Unmarshal(data []byte) error {
|
||||||
// Session token can be nil.
|
// Session token can be nil.
|
||||||
func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
|
func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
|
||||||
box := &AccessBox{}
|
box := &AccessBox{}
|
||||||
ephemeralKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
ephemeralKey, err := keys.NewPrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
box.OwnerPublicKey = crypto.MarshalPublicKey(&ephemeralKey.PublicKey)
|
box.OwnerPublicKey = ephemeralKey.PublicKey().Bytes()
|
||||||
|
|
||||||
secret, err := generateSecret()
|
secret, err := generateSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,9 +70,12 @@ func PackTokens(gatesData []*GateData) (*AccessBox, *Secrets, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTokens returns gate tokens from AccessBox.
|
// GetTokens returns gate tokens from AccessBox.
|
||||||
func (x *AccessBox) GetTokens(owner *ecdsa.PrivateKey) (*GateData, error) {
|
func (x *AccessBox) GetTokens(owner *keys.PrivateKey) (*GateData, error) {
|
||||||
sender := crypto.UnmarshalPublicKey(x.OwnerPublicKey)
|
sender, err := keys.NewPublicKeyFromBytes(x.OwnerPublicKey, elliptic.P256())
|
||||||
ownerKey := crypto.MarshalPublicKey(&owner.PublicKey)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't unmarshal OwnerPublicKey: %w", err)
|
||||||
|
}
|
||||||
|
ownerKey := owner.PublicKey().Bytes()
|
||||||
for _, gate := range x.Gates {
|
for _, gate := range x.Gates {
|
||||||
if !bytes.Equal(gate.GatePublicKey, ownerKey) {
|
if !bytes.Equal(gate.GatePublicKey, ownerKey) {
|
||||||
continue
|
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)
|
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 {
|
for i, gate := range gatesData {
|
||||||
encBearer, err := gate.BearerToken.Marshal()
|
encBearer, err := gate.BearerToken.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,7 +119,7 @@ func (x *AccessBox) addTokens(gatesData []*GateData, ephemeralKey *ecdsa.Private
|
||||||
return nil
|
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)
|
data, err := proto.Marshal(tokens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -129,12 +131,12 @@ func encodeGate(ephemeralKey *ecdsa.PrivateKey, ownerKey *ecdsa.PublicKey, token
|
||||||
}
|
}
|
||||||
|
|
||||||
gate := new(AccessBox_Gate)
|
gate := new(AccessBox_Gate)
|
||||||
gate.GatePublicKey = crypto.MarshalPublicKey(ownerKey)
|
gate.GatePublicKey = ownerKey.Bytes()
|
||||||
gate.Tokens = encrypted
|
gate.Tokens = encrypted
|
||||||
return gate, nil
|
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)
|
data, err := decrypt(owner, sender, gate.Tokens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -153,14 +155,14 @@ func decodeGate(gate *AccessBox_Gate, owner *ecdsa.PrivateKey, sender *ecdsa.Pub
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
gateData := NewGateData(&owner.PublicKey, bearerTkn)
|
gateData := NewGateData(owner.PublicKey(), bearerTkn)
|
||||||
gateData.SessionToken = sessionTkn
|
gateData.SessionToken = sessionTkn
|
||||||
gateData.AccessKey = hex.EncodeToString(tokens.AccessKey)
|
gateData.AccessKey = hex.EncodeToString(tokens.AccessKey)
|
||||||
return gateData, nil
|
return gateData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateShared256(prv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) (sk []byte, err error) {
|
func generateShared256(prv *keys.PrivateKey, pub *keys.PublicKey) (sk []byte, err error) {
|
||||||
if prv.PublicKey.Curve != pub.Curve {
|
if prv.PublicKey().Curve != pub.Curve {
|
||||||
return nil, fmt.Errorf("not equal curves")
|
return nil, fmt.Errorf("not equal curves")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ func deriveKey(secret []byte) ([]byte, error) {
|
||||||
return key, err
|
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)
|
enc, err := getCipher(owner, sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
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)
|
dec, err := getCipher(owner, sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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)
|
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)
|
secret, err := generateShared256(owner, sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
package accessbox
|
package accessbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"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/acl/eacl"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/session"
|
"github.com/nspcc-dev/neofs-api-go/pkg/session"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,22 +16,22 @@ func Test_tokens_encrypt_decrypt(t *testing.T) {
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
tkn2 = token.NewBearerToken()
|
tkn2 = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
cred, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
tkn.SetEACLTable(eacl.NewTable())
|
||||||
require.NoError(t, tkn.SignToken(sec))
|
require.NoError(t, tkn.SignToken(&sec.PrivateKey))
|
||||||
|
|
||||||
rawTkn, err := tkn.Marshal()
|
rawTkn, err := tkn.Marshal()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
data, err := encrypt(cred, &cred.PublicKey, rawTkn)
|
data, err := encrypt(cred, cred.PublicKey(), rawTkn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rawTkn2, err := decrypt(cred, &cred.PublicKey, data)
|
rawTkn2, err := decrypt(cred, cred.PublicKey(), data)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = tkn2.Unmarshal(rawTkn2)
|
err = tkn2.Unmarshal(rawTkn2)
|
||||||
|
@ -50,16 +47,16 @@ func Test_bearer_token_in_access_box(t *testing.T) {
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
cred, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
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})
|
box, _, err = PackTokens([]*GateData{gate})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -82,10 +79,10 @@ func Test_session_token_in_access_box(t *testing.T) {
|
||||||
tkn = session.NewToken()
|
tkn = session.NewToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
cred, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tok := session.NewToken()
|
tok := session.NewToken()
|
||||||
|
@ -93,10 +90,10 @@ func Test_session_token_in_access_box(t *testing.T) {
|
||||||
uid, err := uuid.New().MarshalBinary()
|
uid, err := uuid.New().MarshalBinary()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tok.SetID(uid)
|
tok.SetID(uid)
|
||||||
tok.SetSessionKey(crypto.MarshalPublicKey(&sec.PublicKey))
|
tok.SetSessionKey(sec.PublicKey().Bytes())
|
||||||
require.NoError(t, tkn.Sign(sec))
|
require.NoError(t, tkn.Sign(&sec.PrivateKey))
|
||||||
|
|
||||||
gate := NewGateData(&cred.PublicKey, token.NewBearerToken())
|
gate := NewGateData(cred.PublicKey(), token.NewBearerToken())
|
||||||
gate.SessionToken = tkn
|
gate.SessionToken = tkn
|
||||||
box, _, err = PackTokens([]*GateData{gate})
|
box, _, err = PackTokens([]*GateData{gate})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -119,29 +116,29 @@ func Test_accessbox_multiple_keys(t *testing.T) {
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
tkn.SetEACLTable(eacl.NewTable())
|
||||||
require.NoError(t, tkn.SignToken(sec))
|
require.NoError(t, tkn.SignToken(&sec.PrivateKey))
|
||||||
|
|
||||||
count := 10
|
count := 10
|
||||||
gates := make([]*GateData, 0, count)
|
gates := make([]*GateData, 0, count)
|
||||||
keys := make([]*ecdsa.PrivateKey, 0, count)
|
privateKeys := make([]*keys.PrivateKey, 0, count)
|
||||||
{ // generate keys
|
{ // generate keys
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
cred, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
gates = append(gates, NewGateData(&cred.PublicKey, tkn))
|
gates = append(gates, NewGateData(cred.PublicKey(), tkn))
|
||||||
keys = append(keys, cred)
|
privateKeys = append(privateKeys, cred)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box, _, err = PackTokens(gates)
|
box, _, err = PackTokens(gates)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i, k := range keys {
|
for i, k := range privateKeys {
|
||||||
tkns, err := box.GetTokens(k)
|
tkns, err := box.GetTokens(k)
|
||||||
require.NoError(t, err, "key #%d: %s failed", i, k)
|
require.NoError(t, err, "key #%d: %s failed", i, k)
|
||||||
require.Equal(t, tkns.BearerToken, tkn)
|
require.Equal(t, tkns.BearerToken, tkn)
|
||||||
|
@ -154,19 +151,19 @@ func Test_unknown_key(t *testing.T) {
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
cred, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wrongCred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
wrongCred, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
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})
|
box, _, err = PackTokens([]*GateData{gate})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ package tokens
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/client"
|
"github.com/nspcc-dev/neofs-api-go/pkg/client"
|
||||||
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
||||||
|
@ -21,11 +21,11 @@ type (
|
||||||
// Credentials is a bearer token get/put interface.
|
// Credentials is a bearer token get/put interface.
|
||||||
Credentials interface {
|
Credentials interface {
|
||||||
GetTokens(context.Context, *object.Address) (*accessbox.GateData, error)
|
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 {
|
cred struct {
|
||||||
key *ecdsa.PrivateKey
|
key *keys.PrivateKey
|
||||||
pool pool.Pool
|
pool pool.Pool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -46,7 +46,7 @@ var bufferPool = sync.Pool{
|
||||||
var _ = New
|
var _ = New
|
||||||
|
|
||||||
// New creates new Credentials instance using given cli and key.
|
// 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}
|
return &cred{pool: conns, key: key}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*acce
|
||||||
return &box, nil
|
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 (
|
var (
|
||||||
err error
|
err error
|
||||||
created = strconv.FormatInt(time.Now().Unix(), 10)
|
created = strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -9,7 +9,6 @@ require (
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/nspcc-dev/neo-go v0.95.3
|
github.com/nspcc-dev/neo-go v0.95.3
|
||||||
github.com/nspcc-dev/neofs-api-go v1.27.1
|
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-node v1.22.0
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210624072335-0348eb331c92
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210624072335-0348eb331c92
|
||||||
github.com/prometheus/client_golang v1.9.0
|
github.com/prometheus/client_golang v1.9.0
|
||||||
|
|
61
internal/wallet/wallet.go
Normal file
61
internal/wallet/wallet.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue