forked from TrueCloudLab/frostfs-s3-gw
Merge pull request #80 from masterSplinter01/feature/48-protobuf-format-authbox
Protobuf format of accessbox
This commit is contained in:
commit
ee078eabcc
13 changed files with 604 additions and 376 deletions
9
Makefile
9
Makefile
|
@ -109,3 +109,12 @@ help:
|
||||||
# Clean up
|
# Clean up
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BINDIR)
|
rm -rf $(BINDIR)
|
||||||
|
|
||||||
|
protoc:
|
||||||
|
# Protoc generate
|
||||||
|
@for f in `find . -type f -name '*.proto' -not -path './vendor/*'`; do \
|
||||||
|
echo "⇒ Processing $$f "; \
|
||||||
|
protoc \
|
||||||
|
--go_out=paths=source_relative:. $$f; \
|
||||||
|
done
|
||||||
|
rm -rf vendor
|
||||||
|
|
|
@ -15,8 +15,8 @@ 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/bearer"
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
"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"
|
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -31,7 +31,7 @@ type (
|
||||||
|
|
||||||
center struct {
|
center struct {
|
||||||
reg *regexpSubmatcher
|
reg *regexpSubmatcher
|
||||||
cli bearer.Credentials
|
cli tokens.Credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
// Params stores node connection parameters.
|
// Params stores node connection parameters.
|
||||||
|
@ -59,7 +59,7 @@ 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 hcs.PrivateKey) Center {
|
||||||
return ¢er{
|
return ¢er{
|
||||||
cli: bearer.New(conns, key),
|
cli: tokens.New(conns, key),
|
||||||
reg: ®expSubmatcher{re: authorizationFieldRegexp},
|
reg: ®expSubmatcher{re: authorizationFieldRegexp},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ func (c *center) Authenticate(r *http.Request) (*token.BearerToken, error) {
|
||||||
return nil, fmt.Errorf("could not parse AccessBox address: %s : %w", accessKeyID, err)
|
return nil, fmt.Errorf("could not parse AccessBox address: %s : %w", accessKeyID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn, err := c.cli.Get(r.Context(), address)
|
tkn, err := c.cli.GetBearerToken(r.Context(), address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,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/token"
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/bearer"
|
"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/hcs"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
|
@ -129,6 +130,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
|
||||||
)
|
)
|
||||||
|
|
||||||
a.log.Info("check container", zap.Stringer("cid", options.ContainerID))
|
a.log.Info("check container", zap.Stringer("cid", options.ContainerID))
|
||||||
|
@ -148,12 +150,18 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return fmt.Errorf("failed to build bearer token: %w", err)
|
return fmt.Errorf("failed to build bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
box.SetOwnerPublicKey(options.OwnerPrivateKey.PublicKey())
|
||||||
|
err = box.AddBearerToken(tkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add token to accessbox: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
a.log.Info("store bearer token into NeoFS",
|
a.log.Info("store bearer token into NeoFS",
|
||||||
zap.Stringer("owner_tkn", tkn.Issuer()))
|
zap.Stringer("owner_tkn", tkn.Issuer()))
|
||||||
|
|
||||||
address, err := bearer.
|
address, err := tokens.
|
||||||
New(a.pool, options.OwnerPrivateKey).
|
New(a.pool, options.OwnerPrivateKey).
|
||||||
Put(ctx, cid, tkn, options.GatesPublicKeys...)
|
Put(ctx, cid, tkn.Issuer(), &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)
|
||||||
}
|
}
|
||||||
|
@ -179,13 +187,13 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
// ObtainSecret receives an existing secret access key from NeoFS and
|
// ObtainSecret receives an existing secret access key from NeoFS and
|
||||||
// writes to io.Writer the secret access key.
|
// writes to io.Writer the secret access key.
|
||||||
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
||||||
bearerCreds := bearer.New(a.pool, options.GatePrivateKey)
|
bearerCreds := tokens.New(a.pool, options.GatePrivateKey)
|
||||||
address := object.NewAddress()
|
address := object.NewAddress()
|
||||||
if err := address.Parse(options.SecretAddress); err != nil {
|
if err := address.Parse(options.SecretAddress); err != nil {
|
||||||
return fmt.Errorf("failed to parse secret address: %w", err)
|
return fmt.Errorf("failed to parse secret address: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn, err := bearerCreds.Get(ctx, address)
|
tkn, err := bearerCreds.GetBearerToken(ctx, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get bearer token: %w", err)
|
return fmt.Errorf("failed to get bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,184 @@
|
||||||
package accessbox
|
package accessbox
|
||||||
|
|
||||||
import "github.com/nspcc-dev/neofs-api-go/pkg/token"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
type (
|
"github.com/nspcc-dev/neofs-api-go/pkg/session"
|
||||||
// Box provides marshalling/unmarshalling for the token.
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
Box interface {
|
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
||||||
Marshal() ([]byte, error)
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tokenInterface interface {
|
||||||
|
Marshal(bs ...[]byte) ([]byte, error)
|
||||||
Unmarshal([]byte) error
|
Unmarshal([]byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encoder provides encoding method.
|
// Marshal returns the wire-format of AccessBox.
|
||||||
Encoder interface {
|
func (x *AccessBox) Marshal() ([]byte, error) {
|
||||||
Encode(Box) error
|
return proto.Marshal(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decoder provides decoding method.
|
// Unmarshal parses the wire-format message and put data to x.
|
||||||
Decoder interface {
|
func (x *AccessBox) Unmarshal(data []byte) error {
|
||||||
Decode(Box) error
|
return proto.Unmarshal(data, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BearerTokenBox is a marshalling/unmarshalling bearer token wrapper.
|
// AddBearerToken adds a bearer token to BearerTokens list.
|
||||||
BearerTokenBox interface {
|
func (x *AccessBox) AddBearerToken(tkn *token.BearerToken, owner hcs.PrivateKey, keys ...hcs.PublicKey) error {
|
||||||
Box
|
if x.OwnerPublicKey == nil {
|
||||||
|
return fmt.Errorf("owner's public key is nil")
|
||||||
Token() *token.BearerToken
|
}
|
||||||
SetToken(*token.BearerToken)
|
// restriction to rewrite token for the second time
|
||||||
|
if len(x.BearerTokens) > 0 {
|
||||||
|
return fmt.Errorf("bearer token is already set")
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
for _, data := range x.BearerTokens {
|
||||||
|
if !bytes.Equal(data.GatePublicKey, owner.PublicKey().Bytes()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tkn := token.NewBearerToken()
|
||||||
|
if err := decodeToken(data.Token, tkn, owner, sender); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode bearer token: %w", err)
|
||||||
|
}
|
||||||
|
return tkn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no bearer token for key %s was found", owner.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
for _, data := range x.SessionTokens {
|
||||||
|
if !bytes.Equal(data.GatePublicKey, owner.PublicKey().Bytes()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tkn := session.NewToken()
|
||||||
|
|
||||||
|
if err := decodeToken(data.Token, tkn, owner, sender); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode session token: %w", err)
|
||||||
|
}
|
||||||
|
return tkn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no session token for key %s was found", owner.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) addToken(tkn tokenInterface, list *[]*AccessBox_Token, owner hcs.PrivateKey, keys ...hcs.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()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newToken(data []byte, key []byte) *AccessBox_Token {
|
||||||
|
res := new(AccessBox_Token)
|
||||||
|
res.Token = data
|
||||||
|
res.GatePublicKey = key
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeToken(tkn tokenInterface, owner hcs.PrivateKey, sender hcs.PublicKey) ([]byte, error) {
|
||||||
|
data, err := tkn.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := encrypt(owner, sender, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return encrypted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeToken(data []byte, tkn tokenInterface, owner hcs.PrivateKey, sender hcs.PublicKey) error {
|
||||||
|
decoded, err := decrypt(owner, sender, data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tkn.Unmarshal(decoded)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
enc, err := chacha20poly1305.NewX(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := make([]byte, enc.NonceSize(), enc.NonceSize()+len(data)+enc.Overhead())
|
||||||
|
if _, err := rand.Read(nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ld, ns := len(data), dec.NonceSize(); ld < ns {
|
||||||
|
return nil, fmt.Errorf("wrong data size (%d), should be greater than %d", ld, ns)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, cypher := data[:dec.NonceSize()], data[dec.NonceSize():]
|
||||||
|
return dec.Open(nil, nonce, cypher, nil)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
250
creds/accessbox/accessbox.pb.go
Normal file
250
creds/accessbox/accessbox.pb.go
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.25.0
|
||||||
|
// protoc v3.15.8
|
||||||
|
// source: creds/accessbox/accessbox.proto
|
||||||
|
|
||||||
|
package accessbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
|
// of the legacy proto package is being used.
|
||||||
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
|
type AccessBox struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
OwnerPublicKey []byte `protobuf:"bytes,1,opt,name=ownerPublicKey,json=oownerPublicKey,proto3" json:"ownerPublicKey,omitempty"`
|
||||||
|
BearerTokens []*AccessBox_Token `protobuf:"bytes,2,rep,name=bearerTokens,proto3" json:"bearerTokens,omitempty"`
|
||||||
|
SessionTokens []*AccessBox_Token `protobuf:"bytes,3,rep,name=sessionTokens,proto3" json:"sessionTokens,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) Reset() {
|
||||||
|
*x = AccessBox{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_creds_accessbox_accessbox_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*AccessBox) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *AccessBox) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_creds_accessbox_accessbox_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use AccessBox.ProtoReflect.Descriptor instead.
|
||||||
|
func (*AccessBox) Descriptor() ([]byte, []int) {
|
||||||
|
return file_creds_accessbox_accessbox_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) GetOwnerPublicKey() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.OwnerPublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) GetBearerTokens() []*AccessBox_Token {
|
||||||
|
if x != nil {
|
||||||
|
return x.BearerTokens
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) GetSessionTokens() []*AccessBox_Token {
|
||||||
|
if x != nil {
|
||||||
|
return x.SessionTokens
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessBox_Token struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||||
|
GatePublicKey []byte `protobuf:"bytes,2,opt,name=gatePublicKey,proto3" json:"gatePublicKey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox_Token) Reset() {
|
||||||
|
*x = AccessBox_Token{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_creds_accessbox_accessbox_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox_Token) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*AccessBox_Token) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *AccessBox_Token) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_creds_accessbox_accessbox_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use AccessBox_Token.ProtoReflect.Descriptor instead.
|
||||||
|
func (*AccessBox_Token) Descriptor() ([]byte, []int) {
|
||||||
|
return file_creds_accessbox_accessbox_proto_rawDescGZIP(), []int{0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox_Token) GetToken() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Token
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox_Token) GetGatePublicKey() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.GatePublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_creds_accessbox_accessbox_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_creds_accessbox_accessbox_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1f, 0x63, 0x72, 0x65, 0x64, 0x73, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f,
|
||||||
|
0x78, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x12, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x22, 0xfb, 0x01, 0x0a,
|
||||||
|
0x09, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0e, 0x6f, 0x77,
|
||||||
|
0x6e, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x0c, 0x52, 0x0f, 0x6f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||||
|
0x4b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b,
|
||||||
|
0x65, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x63, 0x63, 0x65,
|
||||||
|
0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6f, 0x78, 0x2e,
|
||||||
|
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0c, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b,
|
||||||
|
0x65, 0x6e, 0x73, 0x12, 0x40, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f,
|
||||||
|
0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x63, 0x63,
|
||||||
|
0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6f, 0x78,
|
||||||
|
0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54,
|
||||||
|
0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x1a, 0x43, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14,
|
||||||
|
0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74,
|
||||||
|
0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c,
|
||||||
|
0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x67, 0x61, 0x74,
|
||||||
|
0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69,
|
||||||
|
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x73, 0x70, 0x63, 0x63, 0x2d, 0x64,
|
||||||
|
0x65, 0x76, 0x2f, 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x2d, 0x73, 0x33, 0x2d, 0x67, 0x77, 0x2f, 0x63,
|
||||||
|
0x72, 0x65, 0x64, 0x73, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x62, 0x6f, 0x78, 0x3b, 0x61, 0x63,
|
||||||
|
0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_creds_accessbox_accessbox_proto_rawDescOnce sync.Once
|
||||||
|
file_creds_accessbox_accessbox_proto_rawDescData = file_creds_accessbox_accessbox_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_creds_accessbox_accessbox_proto_rawDescGZIP() []byte {
|
||||||
|
file_creds_accessbox_accessbox_proto_rawDescOnce.Do(func() {
|
||||||
|
file_creds_accessbox_accessbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_creds_accessbox_accessbox_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_creds_accessbox_accessbox_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_creds_accessbox_accessbox_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_creds_accessbox_accessbox_proto_goTypes = []interface{}{
|
||||||
|
(*AccessBox)(nil), // 0: accessbox.AccessBox
|
||||||
|
(*AccessBox_Token)(nil), // 1: accessbox.AccessBox.Token
|
||||||
|
}
|
||||||
|
var file_creds_accessbox_accessbox_proto_depIdxs = []int32{
|
||||||
|
1, // 0: accessbox.AccessBox.bearerTokens:type_name -> accessbox.AccessBox.Token
|
||||||
|
1, // 1: accessbox.AccessBox.sessionTokens:type_name -> accessbox.AccessBox.Token
|
||||||
|
2, // [2:2] is the sub-list for method output_type
|
||||||
|
2, // [2:2] is the sub-list for method input_type
|
||||||
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_creds_accessbox_accessbox_proto_init() }
|
||||||
|
func file_creds_accessbox_accessbox_proto_init() {
|
||||||
|
if File_creds_accessbox_accessbox_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_creds_accessbox_accessbox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*AccessBox); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_creds_accessbox_accessbox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*AccessBox_Token); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_creds_accessbox_accessbox_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_creds_accessbox_accessbox_proto_goTypes,
|
||||||
|
DependencyIndexes: file_creds_accessbox_accessbox_proto_depIdxs,
|
||||||
|
MessageInfos: file_creds_accessbox_accessbox_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_creds_accessbox_accessbox_proto = out.File
|
||||||
|
file_creds_accessbox_accessbox_proto_rawDesc = nil
|
||||||
|
file_creds_accessbox_accessbox_proto_goTypes = nil
|
||||||
|
file_creds_accessbox_accessbox_proto_depIdxs = nil
|
||||||
|
}
|
19
creds/accessbox/accessbox.proto
Normal file
19
creds/accessbox/accessbox.proto
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package accessbox;
|
||||||
|
|
||||||
|
option go_package = "github.com/nspcc-dev/neofs-s3-gw/creds/tokenbox;accessbox";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
message AccessBox {
|
||||||
|
message Token {
|
||||||
|
bytes token = 1 [json_name = "token"];
|
||||||
|
bytes gatePublicKey = 2 [json_name = "gatePublicKey"];
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes ownerPublicKey = 1 [json_name = "oownerPublicKey"];
|
||||||
|
repeated Token bearerTokens = 2 [json_name = "bearerTokens"];
|
||||||
|
repeated Token sessionTokens = 3 [json_name = "sessionTokens"];
|
||||||
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
package accessbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
|
||||||
)
|
|
||||||
|
|
||||||
type bearerBox struct {
|
|
||||||
tkn *token.BearerToken
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBearerBox wraps given bearer token into BearerTokenBox.
|
|
||||||
func NewBearerBox(token *token.BearerToken) BearerTokenBox {
|
|
||||||
return &bearerBox{tkn: token}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal serializes bearer token.
|
|
||||||
func (b *bearerBox) Marshal() ([]byte, error) {
|
|
||||||
return b.tkn.Marshal(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal initializes bearer box from its serialized representation.
|
|
||||||
func (b *bearerBox) Unmarshal(data []byte) error {
|
|
||||||
tkn := token.NewBearerToken()
|
|
||||||
|
|
||||||
err := tkn.Unmarshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.SetToken(tkn)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Token unwraps bearer token from the box.
|
|
||||||
func (b *bearerBox) Token() *token.BearerToken {
|
|
||||||
return b.tkn
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetToken sets new token in the box.
|
|
||||||
func (b *bearerBox) SetToken(tkn *token.BearerToken) {
|
|
||||||
b.tkn = tkn
|
|
||||||
}
|
|
|
@ -1,12 +1,9 @@
|
||||||
package accessbox
|
package accessbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
||||||
|
@ -15,9 +12,34 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_encrypt_decrypt(t *testing.T) {
|
func Test_tokens_encode_decode(t *testing.T) {
|
||||||
tkn := token.NewBearerToken()
|
var (
|
||||||
box := NewBearerBox(tkn)
|
tkn = token.NewBearerToken()
|
||||||
|
tkn2 = 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))
|
||||||
|
|
||||||
|
data, err := encodeToken(tkn, cred.PrivateKey(), cred.PublicKey())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = decodeToken(data, tkn2, cred.PrivateKey(), cred.PublicKey())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, tkn, tkn2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_bearer_token_in_access_box(t *testing.T) {
|
||||||
|
var (
|
||||||
|
box, box2 AccessBox
|
||||||
|
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)
|
||||||
|
@ -28,21 +50,28 @@ func Test_encrypt_decrypt(t *testing.T) {
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
tkn.SetEACLTable(eacl.NewTable())
|
||||||
require.NoError(t, tkn.SignToken(sec))
|
require.NoError(t, tkn.SignToken(sec))
|
||||||
|
|
||||||
|
box.SetOwnerPublicKey(cred.PublicKey())
|
||||||
|
|
||||||
|
err = box.AddBearerToken(tkn, cred.PrivateKey(), cred.PublicKey())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
data, err := box.Marshal()
|
data, err := box.Marshal()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
encrypted, err := encrypt(cred.PrivateKey(), cred.PublicKey(), data)
|
err = box2.Unmarshal(data)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
decrypted, err := decrypt(cred.PrivateKey(), cred.PublicKey(), encrypted)
|
tkn2, err := box2.GetBearerToken(cred.PrivateKey())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, data, decrypted)
|
require.Equal(t, tkn, tkn2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_encrypt_decrypt_step_by_step(t *testing.T) {
|
func Test_accessbox_multiple_keys(t *testing.T) {
|
||||||
tkn := token.NewBearerToken()
|
var (
|
||||||
box := NewBearerBox(tkn)
|
box AccessBox
|
||||||
|
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)
|
||||||
|
@ -53,75 +82,6 @@ func Test_encrypt_decrypt_step_by_step(t *testing.T) {
|
||||||
tkn.SetEACLTable(eacl.NewTable())
|
tkn.SetEACLTable(eacl.NewTable())
|
||||||
require.NoError(t, tkn.SignToken(sec))
|
require.NoError(t, tkn.SignToken(sec))
|
||||||
|
|
||||||
data, err := box.Marshal()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
_, err = cred.PublicKey().WriteTo(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
encrypted, err := encrypt(cred.PrivateKey(), cred.PublicKey(), data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
length := len(encrypted)
|
|
||||||
temp := make([]byte, length+binary.MaxVarintLen64)
|
|
||||||
size := binary.PutVarint(temp, int64(length))
|
|
||||||
copy(temp[size:], encrypted)
|
|
||||||
buf.Write(temp[:length+size])
|
|
||||||
|
|
||||||
sender, err := hcs.NewPublicKeyFromReader(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, cred.PublicKey(), sender)
|
|
||||||
|
|
||||||
ln, err := binary.ReadVarint(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, int64(length), ln)
|
|
||||||
|
|
||||||
enc := make([]byte, ln)
|
|
||||||
n, err := buf.Read(enc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, length, n)
|
|
||||||
require.Equal(t, encrypted, enc)
|
|
||||||
|
|
||||||
decrypted, err := decrypt(cred.PrivateKey(), sender, enc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, data, decrypted)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSingleKey_AccessBox(t *testing.T) {
|
|
||||||
tkn := token.NewBearerToken()
|
|
||||||
expect := NewBearerBox(tkn)
|
|
||||||
actual := NewBearerBox(nil)
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
data, err := Encode(expect, cred.PrivateKey(), cred.PublicKey())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.NoError(t, Decode(data, actual, cred.PrivateKey()))
|
|
||||||
require.Equal(t, expect, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBearerToken_AccessBox(t *testing.T) {
|
|
||||||
tkn := token.NewBearerToken()
|
|
||||||
box := NewBearerBox(tkn)
|
|
||||||
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
|
count := 10
|
||||||
pubs := make([]hcs.PublicKey, 0, count)
|
pubs := make([]hcs.PublicKey, 0, count)
|
||||||
keys := make([]hcs.PrivateKey, 0, count)
|
keys := make([]hcs.PrivateKey, 0, count)
|
||||||
|
@ -135,27 +95,41 @@ func TestBearerToken_AccessBox(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
box.SetOwnerPublicKey(cred.PublicKey())
|
||||||
require.NoError(t, NewEncoder(buf, cred.PrivateKey(), pubs...).Encode(box))
|
|
||||||
|
|
||||||
data := buf.Bytes()
|
err = box.AddBearerToken(tkn, cred.PrivateKey(), pubs...)
|
||||||
|
|
||||||
for i := range keys {
|
|
||||||
key := keys[i]
|
|
||||||
t.Run("try with key "+strconv.Itoa(i), func(t *testing.T) {
|
|
||||||
r := bytes.NewReader(data)
|
|
||||||
nbx := NewBearerBox(nil)
|
|
||||||
require.NoError(t, NewDecoder(r, key).Decode(nbx))
|
|
||||||
require.Equal(t, tkn, nbx.Token())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("should fail for unknown key", func(t *testing.T) {
|
|
||||||
cred, err = hcs.Generate(rand.Reader)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
r := bytes.NewReader(data)
|
for i, k := range keys {
|
||||||
nbx := NewBearerBox(nil)
|
tkn2, err := box.GetBearerToken(k)
|
||||||
require.EqualError(t, NewDecoder(r, cred.PrivateKey()).Decode(nbx), "chacha20poly1305: message authentication failed")
|
require.NoError(t, err, "key #%d: %s failed", i, k)
|
||||||
})
|
require.Equal(t, tkn2, tkn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_unknown_key(t *testing.T) {
|
||||||
|
var (
|
||||||
|
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)
|
||||||
|
|
||||||
|
wrongCred, err := hcs.Generate(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())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = box.GetBearerToken(wrongCred.PrivateKey())
|
||||||
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
package accessbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
*bufio.Reader
|
|
||||||
|
|
||||||
key hcs.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDecoder returns new private key decoder.
|
|
||||||
func NewDecoder(r io.Reader, key hcs.PrivateKey) Decoder {
|
|
||||||
return &decoder{Reader: bufio.NewReader(r), key: key}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ld, ns := len(data), dec.NonceSize(); ld < ns {
|
|
||||||
return nil, fmt.Errorf("wrong data size (%d), should be greater than %d", ld, ns)
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce, cypher := data[:dec.NonceSize()], data[dec.NonceSize():]
|
|
||||||
return dec.Open(nil, nonce, cypher, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Decode(box Box) error {
|
|
||||||
sender, err := hcs.NewPublicKeyFromReader(d)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastErr error
|
|
||||||
|
|
||||||
for {
|
|
||||||
size, err := binary.ReadVarint(d)
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data := make([]byte, size)
|
|
||||||
|
|
||||||
if ln, err := d.Read(data); err != nil {
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
} else if ln != int(size) {
|
|
||||||
lastErr = fmt.Errorf("expect %d bytes, but read only %d bytes", size, ln)
|
|
||||||
continue
|
|
||||||
} else if decoded, err := decrypt(d.key, sender, data); err != nil {
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
} else if err = box.Unmarshal(decoded); err != nil {
|
|
||||||
lastErr = err
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return lastErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode unwraps serialized bearer token from data into box using owner key.
|
|
||||||
func Decode(data []byte, box Box, owner hcs.PrivateKey) error {
|
|
||||||
return NewDecoder(bytes.NewBuffer(data), owner).Decode(box)
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package accessbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/hcs"
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encoder struct {
|
|
||||||
io.Writer
|
|
||||||
|
|
||||||
owner hcs.PrivateKey
|
|
||||||
keys []hcs.PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder creates encoder.
|
|
||||||
func NewEncoder(w io.Writer, owner hcs.PrivateKey, keys ...hcs.PublicKey) Encoder {
|
|
||||||
return &encoder{
|
|
||||||
Writer: w,
|
|
||||||
owner: owner,
|
|
||||||
keys: keys,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
enc, err := chacha20poly1305.NewX(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce := make([]byte, enc.NonceSize(), enc.NonceSize()+len(data)+enc.Overhead())
|
|
||||||
if _, err := rand.Read(nonce); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return enc.Seal(nonce, nonce, data, nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode and encrypt box through owner private key and public keys.
|
|
||||||
func (e *encoder) Encode(box Box) error {
|
|
||||||
data, err := box.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// write owner public key
|
|
||||||
if _, err = e.owner.PublicKey().WriteTo(e); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, sender := range e.keys {
|
|
||||||
encrypted, err := encrypt(e.owner, sender, data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%w, sender = %d", err, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
ln := len(encrypted)
|
|
||||||
temp := make([]byte, ln+binary.MaxVarintLen64)
|
|
||||||
size := binary.PutVarint(temp, int64(ln))
|
|
||||||
copy(temp[size:], encrypted)
|
|
||||||
if _, err := e.Write(temp[:size+ln]); err != nil {
|
|
||||||
return fmt.Errorf("%w, sender = %d", err, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode and encrypt box through owner private key and public keys.
|
|
||||||
func Encode(box Box, owner hcs.PrivateKey, keys ...hcs.PublicKey) ([]byte, error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
err := NewEncoder(buf, owner, keys...).Encode(box)
|
|
||||||
return buf.Bytes(), err
|
|
||||||
}
|
|
|
@ -26,7 +26,8 @@ func (p *public) WriteTo(w io.Writer) (int64, error) {
|
||||||
return int64(pl), err
|
return int64(pl), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func publicKeyFromBytes(v []byte) (PublicKey, error) {
|
// PublicKeyFromBytes reads a public key from given bytes.
|
||||||
|
func PublicKeyFromBytes(v []byte) (PublicKey, error) {
|
||||||
pub := public(v)
|
pub := public(v)
|
||||||
return &pub, nil
|
return &pub, nil
|
||||||
}
|
}
|
||||||
|
@ -37,7 +38,7 @@ func publicKeyFromString(val string) (PublicKey, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return publicKeyFromBytes(v)
|
return PublicKeyFromBytes(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicKeyFromReader reads new public key from given reader.
|
// NewPublicKeyFromReader reads new public key from given reader.
|
||||||
|
@ -47,7 +48,7 @@ func NewPublicKeyFromReader(r io.Reader) (PublicKey, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return publicKeyFromBytes(data)
|
return PublicKeyFromBytes(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPublicKey loads public key from given file or (serialized) string.
|
// LoadPublicKey loads public key from given file or (serialized) string.
|
||||||
|
@ -61,5 +62,5 @@ func LoadPublicKey(val string) (PublicKey, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return publicKeyFromBytes(data)
|
return PublicKeyFromBytes(data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package bearer
|
package tokens
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -11,6 +11,8 @@ import (
|
||||||
"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"
|
||||||
|
"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"
|
"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-s3-gw/creds/hcs"
|
||||||
|
@ -20,8 +22,9 @@ import (
|
||||||
type (
|
type (
|
||||||
// Credentials is a bearer token get/put interface.
|
// Credentials is a bearer token get/put interface.
|
||||||
Credentials interface {
|
Credentials interface {
|
||||||
Get(context.Context, *object.Address) (*token.BearerToken, error)
|
GetBearerToken(context.Context, *object.Address) (*token.BearerToken, error)
|
||||||
Put(context.Context, *cid.ID, *token.BearerToken, ...hcs.PublicKey) (*object.Address, error)
|
GetSessionToken(context.Context, *object.Address) (*session.Token, error)
|
||||||
|
Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, ...hcs.PublicKey) (*object.Address, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
cred struct {
|
cred struct {
|
||||||
|
@ -59,11 +62,39 @@ func (c *cred) releaseBuffer(buf *bytes.Buffer) {
|
||||||
bufferPool.Put(buf)
|
bufferPool.Put(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cred) Get(ctx context.Context, address *object.Address) (*token.BearerToken, error) {
|
func (c *cred) GetBearerToken(ctx context.Context, address *object.Address) (*token.BearerToken, error) {
|
||||||
buf := c.acquireBuffer()
|
box, err := c.getAccessBox(ctx, address)
|
||||||
defer c.releaseBuffer(buf)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
box := accessbox.NewBearerBox(nil)
|
tkn, err := box.GetBearerToken(c.key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tkn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cred) GetSessionToken(ctx context.Context, address *object.Address) (*session.Token, error) {
|
||||||
|
box, err := c.getAccessBox(ctx, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tkn, err := box.GetSessionToken(c.key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tkn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*accessbox.AccessBox, error) {
|
||||||
|
var (
|
||||||
|
box accessbox.AccessBox
|
||||||
|
buf = c.acquireBuffer()
|
||||||
|
)
|
||||||
|
defer c.releaseBuffer(buf)
|
||||||
|
|
||||||
conn, tok, err := c.pool.Connection()
|
conn, tok, err := c.pool.Connection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -80,30 +111,25 @@ func (c *cred) Get(ctx context.Context, address *object.Address) (*token.BearerT
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = accessbox.NewDecoder(buf, c.key).Decode(box)
|
if err = box.Unmarshal(buf.Bytes()); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return &box, nil
|
||||||
return box.Token(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cred) Put(ctx context.Context, cid *cid.ID, tkn *token.BearerToken, keys ...hcs.PublicKey) (*object.Address, error) {
|
func (c *cred) Put(ctx context.Context, cid *cid.ID, issuer *owner.ID, box *accessbox.AccessBox, keys ...hcs.PublicKey) (*object.Address, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
buf = c.acquireBuffer()
|
|
||||||
box = accessbox.NewBearerBox(tkn)
|
|
||||||
|
|
||||||
created = strconv.FormatInt(time.Now().Unix(), 10)
|
created = strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
)
|
)
|
||||||
|
|
||||||
defer c.releaseBuffer(buf)
|
|
||||||
|
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
return nil, ErrEmptyPublicKeys
|
return nil, ErrEmptyPublicKeys
|
||||||
} else if tkn == nil {
|
} else if box == nil {
|
||||||
return nil, ErrEmptyBearerToken
|
return nil, ErrEmptyBearerToken
|
||||||
} else if err = accessbox.NewEncoder(buf, c.key, keys...).Encode(box); err != nil {
|
}
|
||||||
|
data, err := box.Marshal()
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +147,10 @@ func (c *cred) Put(ctx context.Context, cid *cid.ID, tkn *token.BearerToken, key
|
||||||
|
|
||||||
raw := object.NewRaw()
|
raw := object.NewRaw()
|
||||||
raw.SetContainerID(cid)
|
raw.SetContainerID(cid)
|
||||||
raw.SetOwnerID(tkn.Issuer())
|
raw.SetOwnerID(issuer)
|
||||||
raw.SetAttributes(filename, timestamp)
|
raw.SetAttributes(filename, timestamp)
|
||||||
|
|
||||||
ops := new(client.PutObjectParams).WithObject(raw.Object()).WithPayloadReader(buf)
|
ops := new(client.PutObjectParams).WithObject(raw.Object()).WithPayloadReader(bytes.NewBuffer(data))
|
||||||
oid, err := conn.PutObject(
|
oid, err := conn.PutObject(
|
||||||
ctx,
|
ctx,
|
||||||
ops,
|
ops,
|
2
go.mod
2
go.mod
|
@ -4,6 +4,7 @@ go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go v1.37.9
|
github.com/aws/aws-sdk-go v1.37.9
|
||||||
|
github.com/golang/protobuf v1.4.3
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/nspcc-dev/neofs-api-go v1.27.0
|
github.com/nspcc-dev/neofs-api-go v1.27.0
|
||||||
|
@ -21,4 +22,5 @@ require (
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 // indirect
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 // indirect
|
||||||
golang.org/x/text v0.3.5 // indirect
|
golang.org/x/text v0.3.5 // indirect
|
||||||
google.golang.org/grpc v1.36.1
|
google.golang.org/grpc v1.36.1
|
||||||
|
google.golang.org/protobuf v1.25.0
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue