[#75] Using secp256r1 instead of curve25519

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-06-16 17:07:31 +03:00
parent aa950b8af6
commit b86d20265d
12 changed files with 178 additions and 405 deletions

View file

@ -34,29 +34,22 @@ Minimalistic S3 gateway setup needs:
NeoFS nodes with weighted load balancing).
* a key used to communicate with NeoFS nodes
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
backend node, some keys and otherwise default settings:
```
$ neofs-s3-gw -p 192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
--auth-key a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab
$ neofs-s3-gw -p 192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr
$ S3_GW_PEERS_0_ADDRESS=192.168.130.72:8080 \
S3_GW_NEOFS-KEY=KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
S3_GW_AUTH-KEY=a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab \
neofs-s3-gw
```
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 \
--auth-key a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab
$ neofs-s3-gw -p grpc://192.168.130.72:8080 --neofs-key KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr
$ S3_GW_PEERS_0_ADDRESS=grpcs://192.168.130.72:8080 \
S3_GW_NEOFS-KEY=KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr \
S3_GW_AUTH-KEY=a04edd5b3c497eed83be25fb136bafd056928c17986440745775223615f2cbab \
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
192.168.130.71 for remaining 10%.
### Keys
### Key
NeoFS (`--neofs-key`) and authentication (`--auth-key`) keys are mandatory
parameters. NeoFS key can be a path to private key file (as raw bytes), a hex
string or (unencrypted) WIF string. Authentication key is either a path to
raw private key file or a hex string.
NeoFS (`--neofs-key`) is mandatory parameter. NeoFS key can be a path to private key file (as raw bytes),
a hex string or (unencrypted) WIF string.
### Binding and TLS
@ -208,7 +199,7 @@ potentially).
#### 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):
```

View file

@ -2,6 +2,7 @@ package auth
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"io"
@ -15,7 +16,6 @@ import (
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
"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-sdk-go/pkg/pool"
"go.uber.org/zap"
@ -38,7 +38,6 @@ type (
Params struct {
Pool pool.Pool
Logger *zap.Logger
Credential hcs.Credentials
}
prs int
@ -58,7 +57,7 @@ func (p prs) Seek(_ int64, _ int) (int64, error) {
var _ io.ReadSeeker = prs(0)
// New creates an instance of AuthCenter.
func New(conns pool.Pool, key hcs.PrivateKey) Center {
func New(conns pool.Pool, key *ecdsa.PrivateKey) Center {
return &center{
cli: tokens.New(conns, key),
reg: &regexpSubmatcher{re: authorizationFieldRegexp},

View file

@ -21,9 +21,9 @@ import (
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-api-go/pkg/session"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/nspcc-dev/neofs-node/pkg/policy"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
"go.uber.org/zap"
@ -52,8 +52,7 @@ type (
ContainerID *cid.ID
ContainerFriendlyName string
NeoFSKey *ecdsa.PrivateKey
OwnerPrivateKey hcs.PrivateKey
GatesPublicKeys []hcs.PublicKey
GatesPublicKeys []*ecdsa.PublicKey
EACLRules []byte
ContextRules []byte
SessionTkn bool
@ -62,7 +61,7 @@ type (
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
ObtainSecretOptions struct {
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 (
err error
cid *cid.ID
box accessbox.AccessBox
box *accessbox.AccessBox
)
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
}
box.SetOwnerPublicKey(options.OwnerPrivateKey.PublicKey())
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
if err != nil {
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)
}
bearerTkn, err := buildBearerToken(options.NeoFSKey, oid, bearerRules)
bearerTkn, err := buildBearerToken(options.NeoFSKey, bearerRules, options.GatesPublicKeys[0])
if err != nil {
return fmt.Errorf("failed to build bearer token: %w", err)
}
err = box.AddBearerToken(bearerTkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
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)
sessionTkn, err := createSessionToken(options, oid)
if err != nil {
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 {
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 {
_, err := w.Write([]byte("Warning: rules for session token were set but --create-session flag wasn't, " +
"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.
New(a.pool, options.OwnerPrivateKey).
Put(ctx, cid, oid, &box, options.GatesPublicKeys...)
New(a.pool, ownerKey).
Put(ctx, cid, oid, box, options.GatesPublicKeys...)
if err != nil {
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{
AccessKeyID: accessKeyID,
SecretAccessKey: secret,
OwnerPrivateKey: options.OwnerPrivateKey.String(),
OwnerPrivateKey: hex.EncodeToString(crypto.MarshalPrivateKey(ownerKey)),
}
enc := json.NewEncoder(w)
@ -316,7 +302,12 @@ func buildContext(rules []byte) (*session.ContainerContext, error) {
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.SetEACLTable(table)
bearerToken.SetOwner(oid)
@ -338,6 +329,17 @@ func buildSessionToken(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.Contai
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.
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
data, err := tkn.Marshal()
@ -356,3 +358,16 @@ func ownerIDFromNeoFSKey(key *ecdsa.PublicKey) (*owner.ID, error) {
}
return owner.NewIDFromNeo3Wallet(wallet), nil
}
// LoadPublicKey returns ecdsa.PublicKey from hex string.
func LoadPublicKey(val string) (*ecdsa.PublicKey, error) {
data, err := hex.DecodeString(val)
if err != nil {
return nil, fmt.Errorf("unknown key format (%q), expect: hex-string", val)
}
if key := crypto.UnmarshalPublicKey(data); key != nil {
return key, nil
}
return nil, fmt.Errorf("couldn't unmarshal public key (%q)", val)
}

View file

@ -3,7 +3,9 @@ package main
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"os"
@ -15,7 +17,6 @@ import (
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/creds/hcs"
"github.com/nspcc-dev/neofs-s3-gw/internal/version"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
"github.com/urfave/cli/v2"
@ -40,7 +41,6 @@ var (
contextRulesFlag string
gatePrivateKeyFlag string
accessKeyIDFlag string
ownerPrivateKeyFlag string
containerIDFlag string
containerFriendlyName string
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 (
err error
res = make([]hcs.Credentials, count)
res = make([]*ecdsa.PrivateKey, count)
)
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
}
}
@ -146,7 +146,7 @@ func generateKeys() *cli.Command {
Flags: []cli.Flag{
&cli.IntFlag{
Name: "count",
Usage: "number of x25519 key pairs to generate",
Usage: "number of 256r1 key pairs to generate",
Value: 1,
Destination: &gatesKeysCountFlag,
},
@ -154,18 +154,18 @@ func generateKeys() *cli.Command {
Action: func(c *cli.Context) error {
_, log := prepare()
log.Info("start generating x25519 keys")
log.Info("start generating P-256 keys")
csl, err := generateGatesKeys(gatesKeysCountFlag)
if err != nil {
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))
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}
}
@ -213,16 +213,10 @@ func issueSecret() *cli.Command {
},
&cli.StringSliceFlag{
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,
Destination: &gatesPublicKeysFlag,
},
&cli.StringFlag{
Name: "owner-private-key",
Usage: "owner's private x25519 key",
Required: false,
Destination: &ownerPrivateKeyFlag,
},
&cli.StringFlag{
Name: "container-id",
Usage: "auth container id to put the secret into",
@ -269,14 +263,9 @@ func issueSecret() *cli.Command {
}
}
var owner hcs.Credentials
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
var gatesPublicKeys []*ecdsa.PublicKey
for _, key := range gatesPublicKeysFlag.Value() {
gpk, err := hcs.LoadPublicKey(key)
gpk, err := authmate.LoadPublicKey(key)
if err != nil {
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,
ContainerFriendlyName: containerFriendlyName,
NeoFSKey: key,
OwnerPrivateKey: owner.PrivateKey(),
GatesPublicKeys: gatesPublicKeys,
EACLRules: []byte(eaclRulesFlag),
ContextRules: []byte(contextRulesFlag),
@ -355,7 +343,7 @@ func obtainSecret() *cli.Command {
var _ = agent
gateCreds, err := hcs.NewCredentials(gatePrivateKeyFlag)
gateCreds, err := crypto.LoadPrivateKey(gatePrivateKeyFlag)
if err != nil {
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{
SecretAddress: secretAddress,
GatePrivateKey: gateCreds.PrivateKey(),
GatePrivateKey: gateCreds,
}
if err = agent.ObtainSecret(ctx, os.Stdout, obtainSecretOptions); err != nil {
@ -377,14 +365,6 @@ func obtainSecret() *cli.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) {
log.Debug("prepare connection pool")

View file

@ -44,7 +44,6 @@ const ( // Settings.
// Keys.
cfgNeoFSPrivateKey = "neofs-key"
cfgGateAuthPrivateKey = "auth-key"
// HTTPS/TLS.
cfgTLSKeyFile = "tls.key_file"
@ -163,7 +162,6 @@ func newSettings() *viper.Viper {
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(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.Duration(cfgRequestTimeout, defaultRequestTimeout, "set gRPC request timeout")

View file

@ -12,7 +12,6 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/api/auth"
"github.com/nspcc-dev/neofs-s3-gw/api/handler"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
"github.com/spf13/viper"
"go.uber.org/zap"
@ -51,8 +50,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
ctr auth.Center
obj layer.Client
hcsCred hcs.Credentials
poolPeers = fetchPeers(l, v)
reBalance = defaultRebalanceTimer
@ -62,7 +59,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
maxClientsCount = defaultMaxClientsCount
maxClientsDeadline = defaultMaxClientsDeadline
hcsCredential = v.GetString(cfgGateAuthPrivateKey)
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")
}
if hcsCred, err = hcs.NewCredentials(hcsCredential); err != nil {
l.Fatal("could not load gate auth key")
}
if v.IsSet(cfgTLSKeyFile) && v.IsSet(cfgTLSCertFile) {
tls = &tlsConfig{
KeyFile: v.GetString(cfgTLSKeyFile),
@ -102,7 +94,6 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
}
l.Info("using credentials",
zap.String("HCS", hcsCredential),
zap.String("NeoFS", nfsCredential))
opts := &pool.BuilderOptions{
@ -121,7 +112,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
obj = layer.NewLayer(l, conns)
// prepare auth center
ctr = auth.New(conns, hcsCred.PrivateKey())
ctr = auth.New(conns, key)
if caller, err = handler.New(l, obj); err != nil {
l.Fatal("could not initialize API handler", zap.Error(err))

View file

@ -2,14 +2,19 @@ package accessbox
import (
"bytes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"github.com/nspcc-dev/neofs-api-go/pkg/session"
"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/curve25519"
"golang.org/x/crypto/hkdf"
"google.golang.org/protobuf/proto"
)
@ -28,43 +33,34 @@ func (x *AccessBox) Unmarshal(data []byte) error {
return proto.Unmarshal(data, x)
}
// AddBearerToken adds a bearer token to BearerTokens list.
func (x *AccessBox) AddBearerToken(tkn *token.BearerToken, owner hcs.PrivateKey, keys ...hcs.PublicKey) error {
if x.OwnerPublicKey == nil {
return fmt.Errorf("owner's public key is nil")
// PackTokens adds a bearer and session tokens to BearerTokens and SessionToken lists respectively.
// Session token can be nil.
func PackTokens(bearer *token.BearerToken, sess *session.Token, keys ...*ecdsa.PublicKey) (*AccessBox, *ecdsa.PrivateKey, error) {
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
if len(x.BearerTokens) > 0 {
return fmt.Errorf("bearer token is already set")
box.OwnerPublicKey = crypto.MarshalPublicKey(&ephemeralKey.PublicKey)
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.
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()
return box, ephemeralKey, err
}
// GetBearerToken returns bearer token from AccessBox.
func (x *AccessBox) GetBearerToken(owner hcs.PrivateKey) (*token.BearerToken, error) {
sender, err := hcs.PublicKeyFromBytes(x.OwnerPublicKey)
if err != nil {
return nil, fmt.Errorf("failed to load owner public key from AccessBox: %w", err)
}
func (x *AccessBox) GetBearerToken(owner *ecdsa.PrivateKey) (*token.BearerToken, error) {
sender := crypto.UnmarshalPublicKey(x.OwnerPublicKey)
ownerKey := crypto.MarshalPublicKey(&owner.PublicKey)
for _, data := range x.BearerTokens {
if !bytes.Equal(data.GatePublicKey, owner.PublicKey().Bytes()) {
if !bytes.Equal(data.GatePublicKey, ownerKey) {
continue
}
tkn := token.NewBearerToken()
@ -74,17 +70,15 @@ func (x *AccessBox) GetBearerToken(owner hcs.PrivateKey) (*token.BearerToken, er
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.
func (x *AccessBox) GetSessionToken(owner hcs.PrivateKey) (*session.Token, error) {
sender, err := hcs.PublicKeyFromBytes(x.OwnerPublicKey)
if err != nil {
return nil, fmt.Errorf("failed to load owner public key from AccessBox: %w", err)
}
func (x *AccessBox) GetSessionToken(owner *ecdsa.PrivateKey) (*session.Token, error) {
sender := crypto.UnmarshalPublicKey(x.OwnerPublicKey)
ownerKey := crypto.MarshalPublicKey(&owner.PublicKey)
for _, data := range x.SessionTokens {
if !bytes.Equal(data.GatePublicKey, owner.PublicKey().Bytes()) {
if !bytes.Equal(data.GatePublicKey, ownerKey) {
continue
}
tkn := session.NewToken()
@ -95,16 +89,16 @@ func (x *AccessBox) GetSessionToken(owner hcs.PrivateKey) (*session.Token, error
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 {
data, err := encodeToken(tkn, owner, sender)
if err != nil {
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
}
@ -116,7 +110,7 @@ func newToken(data []byte, key []byte) *AccessBox_Token {
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()
if err != nil {
return nil, err
@ -129,7 +123,7 @@ func encodeToken(tkn tokenInterface, owner hcs.PrivateKey, sender hcs.PublicKey)
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)
if err != nil {
return err
@ -143,13 +137,32 @@ func decodeToken(data []byte, tkn tokenInterface, owner hcs.PrivateKey, sender h
return nil
}
func encrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, error) {
key, err := curve25519.X25519(owner.Bytes(), sender.Bytes())
if err != nil {
return nil, err
func generateShared256(prv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) (sk []byte, err error) {
if prv.PublicKey.Curve != pub.Curve {
return nil, fmt.Errorf("not equal curves")
}
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 {
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
}
func decrypt(owner hcs.PrivateKey, sender hcs.PublicKey, data []byte) ([]byte, error) {
sb := sender.Bytes()
key, err := curve25519.X25519(owner.Bytes(), sb)
if err != nil {
return nil, err
}
dec, err := chacha20poly1305.NewX(key)
func decrypt(owner *ecdsa.PrivateKey, sender *ecdsa.PublicKey, data []byte) ([]byte, error) {
dec, err := getCipher(owner, sender)
if err != nil {
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():]
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
}

View file

@ -8,7 +8,6 @@ import (
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
"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)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
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)
err = decodeToken(data, tkn2, cred.PrivateKey(), cred.PublicKey())
err = decodeToken(data, tkn2, cred, &cred.PublicKey)
require.NoError(t, err)
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) {
var (
box, box2 AccessBox
box *AccessBox
box2 AccessBox
tkn = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
box.SetOwnerPublicKey(cred.PublicKey())
err = box.AddBearerToken(tkn, cred.PrivateKey(), cred.PublicKey())
box, _, err = PackTokens(tkn, nil, &cred.PublicKey)
require.NoError(t, err)
data, err := box.Marshal()
@ -61,7 +59,7 @@ func Test_bearer_token_in_access_box(t *testing.T) {
err = box2.Unmarshal(data)
require.NoError(t, err)
tkn2, err := box2.GetBearerToken(cred.PrivateKey())
tkn2, err := box2.GetBearerToken(cred)
require.NoError(t, err)
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) {
var (
box AccessBox
box *AccessBox
tkn = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
count := 10
pubs := make([]hcs.PublicKey, 0, count)
keys := make([]hcs.PrivateKey, 0, count)
pubs := make([]*ecdsa.PublicKey, 0, count)
keys := make([]*ecdsa.PrivateKey, 0, count)
{ // generate keys
for i := 0; i < count; i++ {
cred, err := hcs.Generate(rand.Reader)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
pubs = append(pubs, cred.PublicKey())
keys = append(keys, cred.PrivateKey())
pubs = append(pubs, &cred.PublicKey)
keys = append(keys, cred)
}
}
box.SetOwnerPublicKey(cred.PublicKey())
err = box.AddBearerToken(tkn, cred.PrivateKey(), pubs...)
box, _, err = PackTokens(tkn, nil, pubs...)
require.NoError(t, err)
for i, k := range keys {
@ -109,27 +102,25 @@ func Test_accessbox_multiple_keys(t *testing.T) {
func Test_unknown_key(t *testing.T) {
var (
box AccessBox
box *AccessBox
tkn = token.NewBearerToken()
)
sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
cred, err := hcs.Generate(rand.Reader)
cred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
wrongCred, err := hcs.Generate(rand.Reader)
wrongCred, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
tkn.SetEACLTable(eacl.NewTable())
require.NoError(t, tkn.SignToken(sec))
box.SetOwnerPublicKey(cred.PublicKey())
err = box.AddBearerToken(tkn, cred.PrivateKey(), cred.PublicKey())
box, _, err = PackTokens(tkn, nil, &cred.PublicKey)
require.NoError(t, err)
_, err = box.GetBearerToken(wrongCred.PrivateKey())
_, err = box.GetBearerToken(wrongCred)
require.Error(t, err)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -3,6 +3,7 @@ package tokens
import (
"bytes"
"context"
"crypto/ecdsa"
"errors"
"strconv"
"sync"
@ -15,7 +16,6 @@ import (
"github.com/nspcc-dev/neofs-api-go/pkg/session"
"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/hcs"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
)
@ -24,11 +24,11 @@ type (
Credentials interface {
GetBearerToken(context.Context, *object.Address) (*token.BearerToken, 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 {
key hcs.PrivateKey
key *ecdsa.PrivateKey
pool pool.Pool
}
)
@ -49,7 +49,7 @@ var bufferPool = sync.Pool{
var _ = New
// 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}
}
@ -112,7 +112,7 @@ func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*acce
return &box, nil
}
func (c *cred) Put(ctx context.Context, cid *cid.ID, issuer *owner.ID, box *accessbox.AccessBox, keys ...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 (
err error
created = strconv.FormatInt(time.Now().Unix(), 10)