[#75] Using secp256r1 instead of curve25519
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
aa950b8af6
commit
b86d20265d
12 changed files with 178 additions and 405 deletions
21
README.md
21
README.md
|
@ -34,29 +34,22 @@ Minimalistic S3 gateway setup needs:
|
||||||
NeoFS nodes with weighted load balancing).
|
NeoFS nodes with weighted load balancing).
|
||||||
* a key used to communicate with NeoFS nodes
|
* a key used to communicate with NeoFS nodes
|
||||||
Passed via `--neofs-key` parameter or `S3_GW_NEOFS-KEY` environment variable.
|
Passed via `--neofs-key` parameter or `S3_GW_NEOFS-KEY` environment variable.
|
||||||
* a key used for client authentication
|
|
||||||
Passed via `--auth-key` parameter or `S3_GW_AUTH-KEY` environment variable.
|
|
||||||
To generate it use `neofs-authmate generate-keys` command.
|
|
||||||
|
|
||||||
These two commands are functionally equivalent, they run the gate with one
|
These two commands are functionally equivalent, they run the gate with one
|
||||||
backend node, some keys and otherwise default settings:
|
backend node, some keys and otherwise default settings:
|
||||||
```
|
```
|
||||||
$ neofs-s3-gw -p 192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
|
$ neofs-s3-gw -p 192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr
|
||||||
--auth-key a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab
|
|
||||||
|
|
||||||
$ S3_GW_PEERS_0_ADDRESS=192.168.130.72:8080 \
|
$ S3_GW_PEERS_0_ADDRESS=192.168.130.72:8080 \
|
||||||
S3_GW_NEOFS-KEY=KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
|
S3_GW_NEOFS-KEY=KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
|
||||||
S3_GW_AUTH-KEY=a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab \
|
|
||||||
neofs-s3-gw
|
neofs-s3-gw
|
||||||
```
|
```
|
||||||
It's also possible to specify uri scheme (grpc or grpcs) when using `-p` or environment variables:
|
It's also possible to specify uri scheme (grpc or grpcs) when using `-p` or environment variables:
|
||||||
```
|
```
|
||||||
$ neofs-s3-gw -p grpc://192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
|
$ neofs-s3-gw -p grpc://192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr
|
||||||
--auth-key a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab
|
|
||||||
|
|
||||||
$ S3_GW_PEERS_0_ADDRESS=grpcs://192.168.130.72:8080 \
|
$ S3_GW_PEERS_0_ADDRESS=grpcs://192.168.130.72:8080 \
|
||||||
S3_GW_NEOFS-KEY=KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
|
S3_GW_NEOFS-KEY=KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
|
||||||
S3_GW_AUTH-KEY=a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab \
|
|
||||||
neofs-s3-gw
|
neofs-s3-gw
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -84,12 +77,10 @@ $ HTTP_GW_PEERS_0_ADDRESS=192.168.130.72:8080 HTTP_GW_PEERS_0_WEIGHT=9 \
|
||||||
This command will make gateway use 192.168.130.72 for 90% of requests and
|
This command will make gateway use 192.168.130.72 for 90% of requests and
|
||||||
192.168.130.71 for remaining 10%.
|
192.168.130.71 for remaining 10%.
|
||||||
|
|
||||||
### Keys
|
### Key
|
||||||
|
|
||||||
NeoFS (`--neofs-key`) and authentication (`--auth-key`) keys are mandatory
|
NeoFS (`--neofs-key`) is mandatory parameter. NeoFS key can be a path to private key file (as raw bytes),
|
||||||
parameters. NeoFS key can be a path to private key file (as raw bytes), a hex
|
a hex string or (unencrypted) WIF string.
|
||||||
string or (unencrypted) WIF string. Authentication key is either a path to
|
|
||||||
raw private key file or a hex string.
|
|
||||||
|
|
||||||
### Binding and TLS
|
### Binding and TLS
|
||||||
|
|
||||||
|
@ -208,7 +199,7 @@ potentially).
|
||||||
|
|
||||||
#### Generation of key pairs
|
#### Generation of key pairs
|
||||||
|
|
||||||
To generate key pairs for gateways, run the following command (`--count` is 1
|
To generate neofs key pairs for gateways, run the following command (`--count` is 1
|
||||||
by default):
|
by default):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,6 +2,7 @@ package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -15,7 +16,6 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/authmate"
|
"github.com/nspcc-dev/neofs-s3-gw/authmate"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -38,7 +38,6 @@ type (
|
||||||
Params struct {
|
Params struct {
|
||||||
Pool pool.Pool
|
Pool pool.Pool
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
Credential hcs.Credentials
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prs int
|
prs int
|
||||||
|
@ -58,7 +57,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 hcs.PrivateKey) Center {
|
func New(conns pool.Pool, key *ecdsa.PrivateKey) Center {
|
||||||
return ¢er{
|
return ¢er{
|
||||||
cli: tokens.New(conns, key),
|
cli: tokens.New(conns, key),
|
||||||
reg: ®expSubmatcher{re: authorizationFieldRegexp},
|
reg: ®expSubmatcher{re: authorizationFieldRegexp},
|
||||||
|
|
|
@ -21,9 +21,9 @@ 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/hcs"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -52,8 +52,7 @@ type (
|
||||||
ContainerID *cid.ID
|
ContainerID *cid.ID
|
||||||
ContainerFriendlyName string
|
ContainerFriendlyName string
|
||||||
NeoFSKey *ecdsa.PrivateKey
|
NeoFSKey *ecdsa.PrivateKey
|
||||||
OwnerPrivateKey hcs.PrivateKey
|
GatesPublicKeys []*ecdsa.PublicKey
|
||||||
GatesPublicKeys []hcs.PublicKey
|
|
||||||
EACLRules []byte
|
EACLRules []byte
|
||||||
ContextRules []byte
|
ContextRules []byte
|
||||||
SessionTkn bool
|
SessionTkn bool
|
||||||
|
@ -62,7 +61,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 hcs.PrivateKey
|
GatePrivateKey *ecdsa.PrivateKey
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -134,7 +133,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
cid *cid.ID
|
cid *cid.ID
|
||||||
box accessbox.AccessBox
|
box *accessbox.AccessBox
|
||||||
)
|
)
|
||||||
|
|
||||||
a.log.Info("check container", zap.Stringer("cid", options.ContainerID))
|
a.log.Info("check container", zap.Stringer("cid", options.ContainerID))
|
||||||
|
@ -142,8 +141,6 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
box.SetOwnerPublicKey(options.OwnerPrivateKey.PublicKey())
|
|
||||||
|
|
||||||
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
|
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,35 +152,24 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return fmt.Errorf("failed to build eacl table: %w", err)
|
return fmt.Errorf("failed to build eacl table: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bearerTkn, err := buildBearerToken(options.NeoFSKey, oid, bearerRules)
|
bearerTkn, err := buildBearerToken(options.NeoFSKey, bearerRules, options.GatesPublicKeys[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build bearer token: %w", err)
|
return fmt.Errorf("failed to build bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = box.AddBearerToken(bearerTkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
|
sessionTkn, err := createSessionToken(options, oid)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to add bearer token to accessbox: %w", err)
|
|
||||||
}
|
|
||||||
a.log.Info("store bearer token into NeoFS",
|
|
||||||
zap.Stringer("owner_tkn", bearerTkn.Issuer()))
|
|
||||||
|
|
||||||
if options.SessionTkn {
|
|
||||||
sessionRules, err := buildContext(options.ContextRules)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to build context for session token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionTkn, err := buildSessionToken(options.NeoFSKey, oid, sessionRules)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create session token: %w", err)
|
return fmt.Errorf("failed to create session token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = box.AddSessionToken(sessionTkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
|
box, ownerKey, err := accessbox.PackTokens(bearerTkn, sessionTkn, options.GatesPublicKeys...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to add session token to accessbox: %w", err)
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.log.Info("store bearer token into NeoFS",
|
||||||
|
zap.Stringer("owner_tkn", bearerTkn.Issuer()))
|
||||||
|
|
||||||
if !options.SessionTkn && len(options.ContextRules) > 0 {
|
if !options.SessionTkn && len(options.ContextRules) > 0 {
|
||||||
_, err := w.Write([]byte("Warning: rules for session token were set but --create-session flag wasn't, " +
|
_, err := w.Write([]byte("Warning: rules for session token were set but --create-session flag wasn't, " +
|
||||||
"so session token was not created\n"))
|
"so session token was not created\n"))
|
||||||
|
@ -193,8 +179,8 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
}
|
}
|
||||||
|
|
||||||
address, err := tokens.
|
address, err := tokens.
|
||||||
New(a.pool, options.OwnerPrivateKey).
|
New(a.pool, ownerKey).
|
||||||
Put(ctx, cid, oid, &box, options.GatesPublicKeys...)
|
Put(ctx, cid, oid, box, options.GatesPublicKeys...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to put bearer token: %w", err)
|
return fmt.Errorf("failed to put bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -209,7 +195,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
ir := &issuingResult{
|
ir := &issuingResult{
|
||||||
AccessKeyID: accessKeyID,
|
AccessKeyID: accessKeyID,
|
||||||
SecretAccessKey: secret,
|
SecretAccessKey: secret,
|
||||||
OwnerPrivateKey: options.OwnerPrivateKey.String(),
|
OwnerPrivateKey: hex.EncodeToString(crypto.MarshalPrivateKey(ownerKey)),
|
||||||
}
|
}
|
||||||
|
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
|
@ -316,7 +302,12 @@ func buildContext(rules []byte) (*session.ContainerContext, error) {
|
||||||
return sessionCtx, nil
|
return sessionCtx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildBearerToken(key *ecdsa.PrivateKey, oid *owner.ID, table *eacl.Table) (*token.BearerToken, error) {
|
func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table, ownerKey *ecdsa.PublicKey) (*token.BearerToken, error) {
|
||||||
|
oid, err := ownerIDFromNeoFSKey(ownerKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
bearerToken := token.NewBearerToken()
|
bearerToken := token.NewBearerToken()
|
||||||
bearerToken.SetEACLTable(table)
|
bearerToken.SetEACLTable(table)
|
||||||
bearerToken.SetOwner(oid)
|
bearerToken.SetOwner(oid)
|
||||||
|
@ -338,6 +329,17 @@ func buildSessionToken(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.Contai
|
||||||
return tok, tok.Sign(key)
|
return tok, tok.Sign(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createSessionToken(options *IssueSecretOptions, oid *owner.ID) (*session.Token, error) {
|
||||||
|
if options.SessionTkn {
|
||||||
|
sessionRules, err := buildContext(options.ContextRules)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to build context for session token: %w", err)
|
||||||
|
}
|
||||||
|
return buildSessionToken(options.NeoFSKey, oid, sessionRules)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// BearerToAccessKey returns secret access key generated from given BearerToken.
|
// BearerToAccessKey returns secret access key generated from given BearerToken.
|
||||||
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
|
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
|
||||||
data, err := tkn.Marshal()
|
data, err := tkn.Marshal()
|
||||||
|
@ -356,3 +358,16 @@ func ownerIDFromNeoFSKey(key *ecdsa.PublicKey) (*owner.ID, error) {
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,7 +17,6 @@ import (
|
||||||
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"
|
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/creds/hcs"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
|
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
@ -40,7 +41,6 @@ var (
|
||||||
contextRulesFlag string
|
contextRulesFlag string
|
||||||
gatePrivateKeyFlag string
|
gatePrivateKeyFlag string
|
||||||
accessKeyIDFlag string
|
accessKeyIDFlag string
|
||||||
ownerPrivateKeyFlag string
|
|
||||||
containerIDFlag string
|
containerIDFlag string
|
||||||
containerFriendlyName string
|
containerFriendlyName string
|
||||||
gatesPublicKeysFlag cli.StringSlice
|
gatesPublicKeysFlag cli.StringSlice
|
||||||
|
@ -124,14 +124,14 @@ func appCommands() []*cli.Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateGatesKeys(count int) ([]hcs.Credentials, error) {
|
func generateGatesKeys(count int) ([]*ecdsa.PrivateKey, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
res = make([]hcs.Credentials, count)
|
res = make([]*ecdsa.PrivateKey, count)
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
if res[i], err = hcs.Generate(rand.Reader); err != nil {
|
if res[i], err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ func generateKeys() *cli.Command {
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Name: "count",
|
Name: "count",
|
||||||
Usage: "number of x25519 key pairs to generate",
|
Usage: "number of 256r1 key pairs to generate",
|
||||||
Value: 1,
|
Value: 1,
|
||||||
Destination: &gatesKeysCountFlag,
|
Destination: &gatesKeysCountFlag,
|
||||||
},
|
},
|
||||||
|
@ -154,18 +154,18 @@ func generateKeys() *cli.Command {
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
_, log := prepare()
|
_, log := prepare()
|
||||||
|
|
||||||
log.Info("start generating x25519 keys")
|
log.Info("start generating P-256 keys")
|
||||||
|
|
||||||
csl, err := generateGatesKeys(gatesKeysCountFlag)
|
csl, err := generateGatesKeys(gatesKeysCountFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.Exit(fmt.Sprintf("failed to create key pairs of gates: %s", err), 1)
|
return cli.Exit(fmt.Sprintf("failed to create key pairs of gates: %s", err), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("generated x25519 keys")
|
log.Info("generated P-256 keys")
|
||||||
|
|
||||||
gatesKeys := make([]gateKey, len(csl))
|
gatesKeys := make([]gateKey, len(csl))
|
||||||
for i, cs := range csl {
|
for i, cs := range csl {
|
||||||
privateKey, publicKey := cs.PrivateKey().String(), cs.PublicKey().String()
|
privateKey, publicKey := hex.EncodeToString(cs.D.Bytes()), hex.EncodeToString(crypto.MarshalPublicKey(&cs.PublicKey))
|
||||||
gatesKeys[i] = gateKey{PrivateKey: privateKey, PublicKey: publicKey}
|
gatesKeys[i] = gateKey{PrivateKey: privateKey, PublicKey: publicKey}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,16 +213,10 @@ func issueSecret() *cli.Command {
|
||||||
},
|
},
|
||||||
&cli.StringSliceFlag{
|
&cli.StringSliceFlag{
|
||||||
Name: "gate-public-key",
|
Name: "gate-public-key",
|
||||||
Usage: "public x25519 key of a gate (use flags repeatedly for multiple gates)",
|
Usage: "public 256r1 key of a gate (use flags repeatedly for multiple gates)",
|
||||||
Required: true,
|
Required: true,
|
||||||
Destination: &gatesPublicKeysFlag,
|
Destination: &gatesPublicKeysFlag,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "owner-private-key",
|
|
||||||
Usage: "owner's private x25519 key",
|
|
||||||
Required: false,
|
|
||||||
Destination: &ownerPrivateKeyFlag,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "container-id",
|
Name: "container-id",
|
||||||
Usage: "auth container id to put the secret into",
|
Usage: "auth container id to put the secret into",
|
||||||
|
@ -269,14 +263,9 @@ func issueSecret() *cli.Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var owner hcs.Credentials
|
var gatesPublicKeys []*ecdsa.PublicKey
|
||||||
if owner, err = fetchHCSCredentials(ownerPrivateKeyFlag); err != nil {
|
|
||||||
return cli.Exit(fmt.Sprintf("failed to create owner's private key: %s", err), 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
var gatesPublicKeys []hcs.PublicKey
|
|
||||||
for _, key := range gatesPublicKeysFlag.Value() {
|
for _, key := range gatesPublicKeysFlag.Value() {
|
||||||
gpk, err := hcs.LoadPublicKey(key)
|
gpk, err := authmate.LoadPublicKey(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)
|
||||||
}
|
}
|
||||||
|
@ -287,7 +276,6 @@ func issueSecret() *cli.Command {
|
||||||
ContainerID: containerID,
|
ContainerID: containerID,
|
||||||
ContainerFriendlyName: containerFriendlyName,
|
ContainerFriendlyName: containerFriendlyName,
|
||||||
NeoFSKey: key,
|
NeoFSKey: key,
|
||||||
OwnerPrivateKey: owner.PrivateKey(),
|
|
||||||
GatesPublicKeys: gatesPublicKeys,
|
GatesPublicKeys: gatesPublicKeys,
|
||||||
EACLRules: []byte(eaclRulesFlag),
|
EACLRules: []byte(eaclRulesFlag),
|
||||||
ContextRules: []byte(contextRulesFlag),
|
ContextRules: []byte(contextRulesFlag),
|
||||||
|
@ -355,7 +343,7 @@ func obtainSecret() *cli.Command {
|
||||||
|
|
||||||
var _ = agent
|
var _ = agent
|
||||||
|
|
||||||
gateCreds, err := hcs.NewCredentials(gatePrivateKeyFlag)
|
gateCreds, err := crypto.LoadPrivateKey(gatePrivateKeyFlag)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -364,7 +352,7 @@ func obtainSecret() *cli.Command {
|
||||||
|
|
||||||
obtainSecretOptions := &authmate.ObtainSecretOptions{
|
obtainSecretOptions := &authmate.ObtainSecretOptions{
|
||||||
SecretAddress: secretAddress,
|
SecretAddress: secretAddress,
|
||||||
GatePrivateKey: gateCreds.PrivateKey(),
|
GatePrivateKey: gateCreds,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = agent.ObtainSecret(ctx, os.Stdout, obtainSecretOptions); err != nil {
|
if err = agent.ObtainSecret(ctx, os.Stdout, obtainSecretOptions); err != nil {
|
||||||
|
@ -377,14 +365,6 @@ func obtainSecret() *cli.Command {
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchHCSCredentials(val string) (hcs.Credentials, error) {
|
|
||||||
if val == "" {
|
|
||||||
return hcs.Generate(rand.Reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hcs.NewCredentials(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSDKClient(ctx context.Context, log *zap.Logger, key *ecdsa.PrivateKey, peerAddress string) (pool.Pool, error) {
|
func createSDKClient(ctx context.Context, log *zap.Logger, key *ecdsa.PrivateKey, peerAddress string) (pool.Pool, error) {
|
||||||
log.Debug("prepare connection pool")
|
log.Debug("prepare connection pool")
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ const ( // Settings.
|
||||||
|
|
||||||
// Keys.
|
// Keys.
|
||||||
cfgNeoFSPrivateKey = "neofs-key"
|
cfgNeoFSPrivateKey = "neofs-key"
|
||||||
cfgGateAuthPrivateKey = "auth-key"
|
|
||||||
|
|
||||||
// HTTPS/TLS.
|
// HTTPS/TLS.
|
||||||
cfgTLSKeyFile = "tls.key_file"
|
cfgTLSKeyFile = "tls.key_file"
|
||||||
|
@ -163,7 +162,6 @@ func newSettings() *viper.Viper {
|
||||||
versionFlag := flags.BoolP(cmdVersion, "v", false, "show version")
|
versionFlag := flags.BoolP(cmdVersion, "v", false, "show version")
|
||||||
|
|
||||||
flags.String(cfgNeoFSPrivateKey, "", "set value to hex string, WIF string, or path to NeoFS private key file")
|
flags.String(cfgNeoFSPrivateKey, "", "set value to hex string, WIF string, or path to NeoFS private key file")
|
||||||
flags.String(cfgGateAuthPrivateKey, "", "set path to file with auth (curve25519) private key to use in auth scheme")
|
|
||||||
|
|
||||||
flags.Bool(cfgGRPCVerbose, false, "set debug mode of gRPC connections")
|
flags.Bool(cfgGRPCVerbose, false, "set debug mode of gRPC connections")
|
||||||
flags.Duration(cfgRequestTimeout, defaultRequestTimeout, "set gRPC request timeout")
|
flags.Duration(cfgRequestTimeout, defaultRequestTimeout, "set gRPC request timeout")
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"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/creds/hcs"
|
|
||||||
"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"
|
||||||
|
@ -51,8 +50,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
ctr auth.Center
|
ctr auth.Center
|
||||||
obj layer.Client
|
obj layer.Client
|
||||||
|
|
||||||
hcsCred hcs.Credentials
|
|
||||||
|
|
||||||
poolPeers = fetchPeers(l, v)
|
poolPeers = fetchPeers(l, v)
|
||||||
|
|
||||||
reBalance = defaultRebalanceTimer
|
reBalance = defaultRebalanceTimer
|
||||||
|
@ -62,7 +59,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
maxClientsCount = defaultMaxClientsCount
|
maxClientsCount = defaultMaxClientsCount
|
||||||
maxClientsDeadline = defaultMaxClientsDeadline
|
maxClientsDeadline = defaultMaxClientsDeadline
|
||||||
|
|
||||||
hcsCredential = v.GetString(cfgGateAuthPrivateKey)
|
|
||||||
nfsCredential = v.GetString(cfgNeoFSPrivateKey)
|
nfsCredential = v.GetString(cfgNeoFSPrivateKey)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -90,10 +86,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
l.Fatal("could not load NeoFS private key")
|
l.Fatal("could not load NeoFS private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if hcsCred, err = hcs.NewCredentials(hcsCredential); err != nil {
|
|
||||||
l.Fatal("could not load gate auth key")
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile) {
|
if v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile) {
|
||||||
tls = &tlsConfig{
|
tls = &tlsConfig{
|
||||||
KeyFile: v.GetString(cfgTLSKeyFile),
|
KeyFile: v.GetString(cfgTLSKeyFile),
|
||||||
|
@ -102,7 +94,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Info("using credentials",
|
l.Info("using credentials",
|
||||||
zap.String("HCS", hcsCredential),
|
|
||||||
zap.String("NeoFS", nfsCredential))
|
zap.String("NeoFS", nfsCredential))
|
||||||
|
|
||||||
opts := &pool.BuilderOptions{
|
opts := &pool.BuilderOptions{
|
||||||
|
@ -121,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, hcsCred.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))
|
||||||
|
|
|
@ -2,14 +2,19 @@ package accessbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/hkdf"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,43 +33,34 @@ func (x *AccessBox) Unmarshal(data []byte) error {
|
||||||
return proto.Unmarshal(data, x)
|
return proto.Unmarshal(data, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBearerToken adds a bearer token to BearerTokens list.
|
// PackTokens adds a bearer and session tokens to BearerTokens and SessionToken lists respectively.
|
||||||
func (x *AccessBox) AddBearerToken(tkn *token.BearerToken, owner hcs.PrivateKey, keys ...hcs.PublicKey) error {
|
// Session token can be nil.
|
||||||
if x.OwnerPublicKey == nil {
|
func PackTokens(bearer *token.BearerToken, sess *session.Token, keys ...*ecdsa.PublicKey) (*AccessBox, *ecdsa.PrivateKey, error) {
|
||||||
return fmt.Errorf("owner's public key is nil")
|
box := &AccessBox{}
|
||||||
|
ephemeralKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// restriction to rewrite token for the second time
|
box.OwnerPublicKey = crypto.MarshalPublicKey(&ephemeralKey.PublicKey)
|
||||||
if len(x.BearerTokens) > 0 {
|
|
||||||
return fmt.Errorf("bearer token is already set")
|
if err := box.addToken(bearer, &box.BearerTokens, ephemeralKey, keys...); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to add bearer token to accessbox: %w", err)
|
||||||
|
}
|
||||||
|
if sess != nil {
|
||||||
|
if err := box.addToken(sess, &box.SessionTokens, ephemeralKey, keys...); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to add session token to accessbox: %w", err)
|
||||||
}
|
}
|
||||||
return x.addToken(tkn, &x.BearerTokens, owner, keys...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSessionToken adds a session token to SessionTokens list.
|
return box, ephemeralKey, err
|
||||||
func (x *AccessBox) AddSessionToken(tkn *session.Token, owner hcs.PrivateKey, keys ...hcs.PublicKey) error {
|
|
||||||
if x.OwnerPublicKey == nil {
|
|
||||||
return fmt.Errorf("owner's public key is nil")
|
|
||||||
}
|
|
||||||
//restriction to rewrite token for the second time
|
|
||||||
if len(x.SessionTokens) > 0 {
|
|
||||||
return fmt.Errorf("bearer token is already set")
|
|
||||||
}
|
|
||||||
return x.addToken(tkn, &x.SessionTokens, owner, keys...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOwnerPublicKey sets a public key of an issuer.
|
|
||||||
func (x *AccessBox) SetOwnerPublicKey(key hcs.PublicKey) {
|
|
||||||
x.OwnerPublicKey = key.Bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBearerToken returns bearer token from AccessBox.
|
// GetBearerToken returns bearer token from AccessBox.
|
||||||
func (x *AccessBox) GetBearerToken(owner hcs.PrivateKey) (*token.BearerToken, error) {
|
func (x *AccessBox) GetBearerToken(owner *ecdsa.PrivateKey) (*token.BearerToken, error) {
|
||||||
sender, err := hcs.PublicKeyFromBytes(x.OwnerPublicKey)
|
sender := crypto.UnmarshalPublicKey(x.OwnerPublicKey)
|
||||||
if err != nil {
|
ownerKey := crypto.MarshalPublicKey(&owner.PublicKey)
|
||||||
return nil, fmt.Errorf("failed to load owner public key from AccessBox: %w", err)
|
|
||||||
}
|
|
||||||
for _, data := range x.BearerTokens {
|
for _, data := range x.BearerTokens {
|
||||||
if !bytes.Equal(data.GatePublicKey, owner.PublicKey().Bytes()) {
|
if !bytes.Equal(data.GatePublicKey, ownerKey) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tkn := token.NewBearerToken()
|
tkn := token.NewBearerToken()
|
||||||
|
@ -74,17 +70,15 @@ func (x *AccessBox) GetBearerToken(owner hcs.PrivateKey) (*token.BearerToken, er
|
||||||
return tkn, nil
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("no bearer token for key %s was found", owner.String())
|
return nil, fmt.Errorf("no bearer token for key %x was found", ownerKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSessionToken returns session token from AccessBox.
|
// GetSessionToken returns session token from AccessBox.
|
||||||
func (x *AccessBox) GetSessionToken(owner hcs.PrivateKey) (*session.Token, error) {
|
func (x *AccessBox) GetSessionToken(owner *ecdsa.PrivateKey) (*session.Token, error) {
|
||||||
sender, err := hcs.PublicKeyFromBytes(x.OwnerPublicKey)
|
sender := crypto.UnmarshalPublicKey(x.OwnerPublicKey)
|
||||||
if err != nil {
|
ownerKey := crypto.MarshalPublicKey(&owner.PublicKey)
|
||||||
return nil, fmt.Errorf("failed to load owner public key from AccessBox: %w", err)
|
|
||||||
}
|
|
||||||
for _, data := range x.SessionTokens {
|
for _, data := range x.SessionTokens {
|
||||||
if !bytes.Equal(data.GatePublicKey, owner.PublicKey().Bytes()) {
|
if !bytes.Equal(data.GatePublicKey, ownerKey) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tkn := session.NewToken()
|
tkn := session.NewToken()
|
||||||
|
@ -95,16 +89,16 @@ func (x *AccessBox) GetSessionToken(owner hcs.PrivateKey) (*session.Token, error
|
||||||
return tkn, nil
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("no session token for key %s was found", owner.String())
|
return nil, fmt.Errorf("no session token for key %x was found", ownerKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AccessBox) addToken(tkn tokenInterface, list *[]*AccessBox_Token, owner hcs.PrivateKey, keys ...hcs.PublicKey) error {
|
func (x *AccessBox) addToken(tkn tokenInterface, list *[]*AccessBox_Token, owner *ecdsa.PrivateKey, keys ...*ecdsa.PublicKey) error {
|
||||||
for i, sender := range keys {
|
for i, sender := range keys {
|
||||||
data, err := encodeToken(tkn, owner, sender)
|
data, err := encodeToken(tkn, owner, sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%w, sender = %d", err, i)
|
return fmt.Errorf("%w, sender = %d", err, i)
|
||||||
}
|
}
|
||||||
*list = append(*list, newToken(data, sender.Bytes()))
|
*list = append(*list, newToken(data, crypto.MarshalPublicKey(sender)))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -116,7 +110,7 @@ func newToken(data []byte, key []byte) *AccessBox_Token {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeToken(tkn tokenInterface, owner hcs.PrivateKey, sender hcs.PublicKey) ([]byte, error) {
|
func encodeToken(tkn tokenInterface, owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey) ([]byte, error) {
|
||||||
data, err := tkn.Marshal()
|
data, err := tkn.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -129,7 +123,7 @@ func encodeToken(tkn tokenInterface, owner hcs.PrivateKey, sender hcs.PublicKey)
|
||||||
return encrypted, nil
|
return encrypted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeToken(data []byte, tkn tokenInterface, owner hcs.PrivateKey, sender hcs.PublicKey) error {
|
func decodeToken(data []byte, tkn tokenInterface, owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey) error {
|
||||||
decoded, err := decrypt(owner, sender, data)
|
decoded, err := decrypt(owner, sender, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -143,13 +137,32 @@ func decodeToken(data []byte, tkn tokenInterface, owner hcs.PrivateKey, sender h
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, error) {
|
func generateShared256(prv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) (sk []byte, err error) {
|
||||||
key, err := curve25519.X25519(owner.Bytes(), sender.Bytes())
|
if prv.PublicKey.Curve != pub.Curve {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("not equal curves")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enc, err := chacha20poly1305.NewX(key)
|
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
|
||||||
|
if x == nil {
|
||||||
|
return nil, fmt.Errorf("shared key is point at infinity")
|
||||||
|
}
|
||||||
|
|
||||||
|
sk = make([]byte, 32)
|
||||||
|
skBytes := x.Bytes()
|
||||||
|
copy(sk[len(sk)-len(skBytes):], skBytes)
|
||||||
|
return sk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deriveKey(secret []byte) ([]byte, error) {
|
||||||
|
hash := sha256.New
|
||||||
|
kdf := hkdf.New(hash, secret, nil, nil)
|
||||||
|
key := make([]byte, 32)
|
||||||
|
_, err := io.ReadFull(kdf, key)
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]byte, error) {
|
||||||
|
enc, err := getCipher(owner, sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -162,15 +175,8 @@ func encrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, e
|
||||||
return enc.Seal(nonce, nonce, data, nil), nil
|
return enc.Seal(nonce, nonce, data, nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, error) {
|
func decrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]byte, error) {
|
||||||
sb := sender.Bytes()
|
dec, err := getCipher(owner, sender)
|
||||||
|
|
||||||
key, err := curve25519.X25519(owner.Bytes(), sb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dec, err := chacha20poly1305.NewX(key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -182,3 +188,21 @@ func decrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, e
|
||||||
nonce, cypher := data[:dec.NonceSize()], data[dec.NonceSize():]
|
nonce, cypher := data[:dec.NonceSize()], data[dec.NonceSize():]
|
||||||
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) {
|
||||||
|
secret, err := generateShared256(owner, sender)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := deriveKey(secret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
aead, err := chacha20poly1305.NewX(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return aead, nil
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"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/token"
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,16 +19,16 @@ func Test_tokens_encode_decode(t *testing.T) {
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := hcs.Generate(rand.Reader)
|
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
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))
|
||||||
|
|
||||||
data, err := encodeToken(tkn, cred.PrivateKey(), cred.PublicKey())
|
data, err := encodeToken(tkn, cred, &cred.PublicKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = decodeToken(data, tkn2, cred.PrivateKey(), cred.PublicKey())
|
err = decodeToken(data, tkn2, cred, &cred.PublicKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, tkn, tkn2)
|
require.Equal(t, tkn, tkn2)
|
||||||
|
@ -37,22 +36,21 @@ func Test_tokens_encode_decode(t *testing.T) {
|
||||||
|
|
||||||
func Test_bearer_token_in_access_box(t *testing.T) {
|
func Test_bearer_token_in_access_box(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
box, box2 AccessBox
|
box *AccessBox
|
||||||
|
box2 AccessBox
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := hcs.Generate(rand.Reader)
|
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
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))
|
||||||
|
|
||||||
box.SetOwnerPublicKey(cred.PublicKey())
|
box, _, err = PackTokens(tkn, nil, &cred.PublicKey)
|
||||||
|
|
||||||
err = box.AddBearerToken(tkn, cred.PrivateKey(), cred.PublicKey())
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
data, err := box.Marshal()
|
data, err := box.Marshal()
|
||||||
|
@ -61,7 +59,7 @@ func Test_bearer_token_in_access_box(t *testing.T) {
|
||||||
err = box2.Unmarshal(data)
|
err = box2.Unmarshal(data)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tkn2, err := box2.GetBearerToken(cred.PrivateKey())
|
tkn2, err := box2.GetBearerToken(cred)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, tkn, tkn2)
|
require.Equal(t, tkn, tkn2)
|
||||||
|
@ -69,35 +67,30 @@ func Test_bearer_token_in_access_box(t *testing.T) {
|
||||||
|
|
||||||
func Test_accessbox_multiple_keys(t *testing.T) {
|
func Test_accessbox_multiple_keys(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
box AccessBox
|
box *AccessBox
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := hcs.Generate(rand.Reader)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
tkn.SetEACLTable(eacl.NewTable())
|
||||||
require.NoError(t, tkn.SignToken(sec))
|
require.NoError(t, tkn.SignToken(sec))
|
||||||
|
|
||||||
count := 10
|
count := 10
|
||||||
pubs := make([]hcs.PublicKey, 0, count)
|
pubs := make([]*ecdsa.PublicKey, 0, count)
|
||||||
keys := make([]hcs.PrivateKey, 0, count)
|
keys := make([]*ecdsa.PrivateKey, 0, count)
|
||||||
{ // generate keys
|
{ // generate keys
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
cred, err := hcs.Generate(rand.Reader)
|
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pubs = append(pubs, cred.PublicKey())
|
pubs = append(pubs, &cred.PublicKey)
|
||||||
keys = append(keys, cred.PrivateKey())
|
keys = append(keys, cred)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box.SetOwnerPublicKey(cred.PublicKey())
|
box, _, err = PackTokens(tkn, nil, pubs...)
|
||||||
|
|
||||||
err = box.AddBearerToken(tkn, cred.PrivateKey(), pubs...)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i, k := range keys {
|
for i, k := range keys {
|
||||||
|
@ -109,27 +102,25 @@ func Test_accessbox_multiple_keys(t *testing.T) {
|
||||||
|
|
||||||
func Test_unknown_key(t *testing.T) {
|
func Test_unknown_key(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
box AccessBox
|
box *AccessBox
|
||||||
tkn = token.NewBearerToken()
|
tkn = token.NewBearerToken()
|
||||||
)
|
)
|
||||||
|
|
||||||
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cred, err := hcs.Generate(rand.Reader)
|
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wrongCred, err := hcs.Generate(rand.Reader)
|
wrongCred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
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))
|
||||||
|
|
||||||
box.SetOwnerPublicKey(cred.PublicKey())
|
box, _, err = PackTokens(tkn, nil, &cred.PublicKey)
|
||||||
|
|
||||||
err = box.AddBearerToken(tkn, cred.PrivateKey(), cred.PublicKey())
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = box.GetBearerToken(wrongCred.PrivateKey())
|
_, err = box.GetBearerToken(wrongCred)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
package hcs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// Credentials is an HCS interface (private/public key).
|
|
||||||
Credentials interface {
|
|
||||||
PublicKey() PublicKey
|
|
||||||
PrivateKey() PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
keyer interface {
|
|
||||||
io.WriterTo
|
|
||||||
|
|
||||||
Bytes() []byte
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey is a public key wrapper providing useful methods.
|
|
||||||
PublicKey interface {
|
|
||||||
keyer
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateKey is private key wrapper providing useful methods.
|
|
||||||
PrivateKey interface {
|
|
||||||
keyer
|
|
||||||
|
|
||||||
PublicKey() PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
credentials struct {
|
|
||||||
public PublicKey
|
|
||||||
secret PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
public []byte
|
|
||||||
secret []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrEmptyCredentials is returned when no credentials are provided.
|
|
||||||
var ErrEmptyCredentials = errors.New("empty credentials")
|
|
||||||
|
|
||||||
var _ = NewCredentials
|
|
||||||
|
|
||||||
// Generate generates new key pair using given source of randomness.
|
|
||||||
func Generate(r io.Reader) (Credentials, error) {
|
|
||||||
buf := make([]byte, curve25519.ScalarSize)
|
|
||||||
|
|
||||||
if _, err := r.Read(buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sk := secret(buf)
|
|
||||||
return &credentials{
|
|
||||||
secret: &sk,
|
|
||||||
public: sk.PublicKey(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCredentials loads private key from the string given and returns Credentials wrapper.
|
|
||||||
func NewCredentials(val string) (Credentials, error) {
|
|
||||||
if val == "" {
|
|
||||||
return nil, ErrEmptyCredentials
|
|
||||||
}
|
|
||||||
|
|
||||||
sk, err := loadPrivateKey(val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &credentials{
|
|
||||||
secret: sk,
|
|
||||||
public: sk.PublicKey(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKey returns public key.
|
|
||||||
func (c *credentials) PublicKey() PublicKey {
|
|
||||||
return c.public
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrivateKey returns private key.
|
|
||||||
func (c *credentials) PrivateKey() PrivateKey {
|
|
||||||
return c.secret
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package hcs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *public) Bytes() []byte {
|
|
||||||
buf := make([]byte, curve25519.PointSize)
|
|
||||||
copy(buf, *p)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *public) String() string {
|
|
||||||
buf := p.Bytes()
|
|
||||||
return hex.EncodeToString(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *public) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
pb := p.Bytes()
|
|
||||||
pl, err := w.Write(pb)
|
|
||||||
return int64(pl), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PublicKeyFromBytes reads a public key from given bytes.
|
|
||||||
func PublicKeyFromBytes(v []byte) (PublicKey, error) {
|
|
||||||
pub := public(v)
|
|
||||||
return &pub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func publicKeyFromString(val string) (PublicKey, error) {
|
|
||||||
v, err := hex.DecodeString(val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return PublicKeyFromBytes(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPublicKeyFromReader reads new public key from given reader.
|
|
||||||
func NewPublicKeyFromReader(r io.Reader) (PublicKey, error) {
|
|
||||||
data := make([]byte, curve25519.PointSize)
|
|
||||||
if _, err := r.Read(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return PublicKeyFromBytes(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadPublicKey loads public key from given file or (serialized) string.
|
|
||||||
func LoadPublicKey(val string) (PublicKey, error) {
|
|
||||||
data, err := ioutil.ReadFile(val)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return publicKeyFromString(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return PublicKeyFromBytes(data)
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package hcs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *secret) Bytes() []byte {
|
|
||||||
buf := make([]byte, curve25519.ScalarSize)
|
|
||||||
copy(buf, *s)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *secret) String() string {
|
|
||||||
buf := s.Bytes()
|
|
||||||
return hex.EncodeToString(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *secret) PublicKey() PublicKey {
|
|
||||||
sk := s.Bytes()
|
|
||||||
|
|
||||||
pb, _ := curve25519.X25519(sk, curve25519.Basepoint)
|
|
||||||
pk := public(pb)
|
|
||||||
return &pk
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *secret) WriteTo(w io.Writer) (int64, error) {
|
|
||||||
sb := s.Bytes()
|
|
||||||
sl, err := w.Write(sb)
|
|
||||||
return int64(sl), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func privateKeyFromBytes(val []byte) (PrivateKey, error) {
|
|
||||||
sk := secret(val)
|
|
||||||
return &sk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func privateKeyFromString(val string) (PrivateKey, error) {
|
|
||||||
data, err := hex.DecodeString(val)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return privateKeyFromBytes(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPrivateKey(val string) (PrivateKey, error) {
|
|
||||||
data, err := ioutil.ReadFile(val)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return privateKeyFromString(val)
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return privateKeyFromBytes(data)
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@ package tokens
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -15,7 +16,6 @@ import (
|
||||||
"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"
|
||||||
"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/hcs"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,11 +24,11 @@ type (
|
||||||
Credentials interface {
|
Credentials interface {
|
||||||
GetBearerToken(context.Context, *object.Address) (*token.BearerToken, error)
|
GetBearerToken(context.Context, *object.Address) (*token.BearerToken, error)
|
||||||
GetSessionToken(context.Context, *object.Address) (*session.Token, error)
|
GetSessionToken(context.Context, *object.Address) (*session.Token, error)
|
||||||
Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, ...hcs.PublicKey) (*object.Address, error)
|
Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, ...*ecdsa.PublicKey) (*object.Address, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
cred struct {
|
cred struct {
|
||||||
key hcs.PrivateKey
|
key *ecdsa.PrivateKey
|
||||||
pool pool.Pool
|
pool pool.Pool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -49,7 +49,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 hcs.PrivateKey) Credentials {
|
func New(conns pool.Pool, key *ecdsa.PrivateKey) Credentials {
|
||||||
return &cred{pool: conns, key: key}
|
return &cred{pool: conns, key: key}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,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 ...hcs.PublicKey) (*object.Address, error) {
|
func (c *cred) Put(ctx context.Context, cid *cid.ID, issuer *owner.ID, box *accessbox.AccessBox, keys ...*ecdsa.PublicKey) (*object.Address, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
created = strconv.FormatInt(time.Now().Unix(), 10)
|
created = strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
|
Loading…
Reference in a new issue