From 364257c5fdf7445067114bf8926b070c0e9a5c9d Mon Sep 17 00:00:00 2001 From: Angira Kekteeva Date: Mon, 14 Jun 2021 16:20:49 +0300 Subject: [PATCH 1/2] [#48] creda: Add accessbox in protobuf format Signed-off-by: Angira Kekteeva --- Makefile | 9 ++ creds/accessbox/accessbox.pb.go | 250 ++++++++++++++++++++++++++++++++ creds/accessbox/accessbox.proto | 19 +++ go.mod | 2 + 4 files changed, 280 insertions(+) create mode 100644 creds/accessbox/accessbox.pb.go create mode 100644 creds/accessbox/accessbox.proto diff --git a/Makefile b/Makefile index 6609e4862..ed43e5884 100644 --- a/Makefile +++ b/Makefile @@ -109,3 +109,12 @@ help: # Clean up clean: 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 diff --git a/creds/accessbox/accessbox.pb.go b/creds/accessbox/accessbox.pb.go new file mode 100644 index 000000000..b457d8eeb --- /dev/null +++ b/creds/accessbox/accessbox.pb.go @@ -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 +} diff --git a/creds/accessbox/accessbox.proto b/creds/accessbox/accessbox.proto new file mode 100644 index 000000000..8906c9263 --- /dev/null +++ b/creds/accessbox/accessbox.proto @@ -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"]; +} + diff --git a/go.mod b/go.mod index 2f1ee8eb5..7896b055c 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( 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/gorilla/mux v1.8.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/text v0.3.5 // indirect google.golang.org/grpc v1.36.1 + google.golang.org/protobuf v1.25.0 ) From fe2d507121fe56f70c2f0b680920e41946d2134a Mon Sep 17 00:00:00 2001 From: Angira Kekteeva Date: Mon, 14 Jun 2021 16:39:25 +0300 Subject: [PATCH 2/2] [#48] creds,authmate:Replace old accessbox by new Removed encoder, decoder wraps. Made changes in api, authmate and creds via new accessbox. Updated bearer_token_tests via new accessbox. Signed-off-by: Angira Kekteeva --- api/auth/center.go | 8 +- authmate/authmate.go | 18 ++- creds/accessbox/accessbox.go | 205 +++++++++++++++++++++--- creds/accessbox/bearer_token.go | 43 ----- creds/accessbox/bearer_token_test.go | 176 +++++++++----------- creds/accessbox/decoder.go | 88 ---------- creds/accessbox/encoder.go | 85 ---------- creds/hcs/public.go | 9 +- creds/{bearer => tokens}/credentials.go | 68 +++++--- 9 files changed, 324 insertions(+), 376 deletions(-) delete mode 100644 creds/accessbox/bearer_token.go delete mode 100644 creds/accessbox/decoder.go delete mode 100644 creds/accessbox/encoder.go rename creds/{bearer => tokens}/credentials.go (64%) diff --git a/api/auth/center.go b/api/auth/center.go index 565f11f3d..eda6481ba 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -15,8 +15,8 @@ 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/bearer" "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" ) @@ -31,7 +31,7 @@ type ( center struct { reg *regexpSubmatcher - cli bearer.Credentials + cli tokens.Credentials } // Params stores node connection parameters. @@ -57,7 +57,7 @@ var _ io.ReadSeeker = prs(0) // New creates an instance of AuthCenter. func New(conns pool.Pool, key hcs.PrivateKey) Center { return ¢er{ - cli: bearer.New(conns, key), + cli: tokens.New(conns, key), reg: ®expSubmatcher{re: authorizationFieldRegexp}, } } @@ -96,7 +96,7 @@ func (c *center) Authenticate(r *http.Request) (*token.BearerToken, error) { 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 { return nil, err } diff --git a/authmate/authmate.go b/authmate/authmate.go index b994c3f5a..dd90c3177 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -20,8 +20,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/token" "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/tokens" "github.com/nspcc-dev/neofs-sdk-go/pkg/pool" "go.uber.org/zap" ) @@ -129,6 +130,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr var ( err error cid *cid.ID + box accessbox.AccessBox ) 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) } + 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", zap.Stringer("owner_tkn", tkn.Issuer())) - address, err := bearer. + address, err := tokens. New(a.pool, options.OwnerPrivateKey). - Put(ctx, cid, tkn, options.GatesPublicKeys...) + Put(ctx, cid, tkn.Issuer(), &box, options.GatesPublicKeys...) if err != nil { 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 // writes to io.Writer the secret access key. 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() if err := address.Parse(options.SecretAddress); err != nil { 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 { return fmt.Errorf("failed to get bearer token: %w", err) } diff --git a/creds/accessbox/accessbox.go b/creds/accessbox/accessbox.go index 66bcd367f..0fc2b7db8 100644 --- a/creds/accessbox/accessbox.go +++ b/creds/accessbox/accessbox.go @@ -1,29 +1,184 @@ package accessbox -import "github.com/nspcc-dev/neofs-api-go/pkg/token" +import ( + "bytes" + "crypto/rand" + "fmt" -type ( - // Box provides marshalling/unmarshalling for the token. - Box interface { - Marshal() ([]byte, error) - Unmarshal([]byte) error - } - - // Encoder provides encoding method. - Encoder interface { - Encode(Box) error - } - - // Decoder provides decoding method. - Decoder interface { - Decode(Box) error - } - - // BearerTokenBox is a marshalling/unmarshalling bearer token wrapper. - BearerTokenBox interface { - Box - - Token() *token.BearerToken - SetToken(*token.BearerToken) - } + "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" + "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 +} + +// Marshal returns the wire-format of AccessBox. +func (x *AccessBox) Marshal() ([]byte, error) { + return proto.Marshal(x) +} + +// Unmarshal parses the wire-format message and put data to x. +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") + } + // 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) +} diff --git a/creds/accessbox/bearer_token.go b/creds/accessbox/bearer_token.go deleted file mode 100644 index 8551a52bd..000000000 --- a/creds/accessbox/bearer_token.go +++ /dev/null @@ -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 -} diff --git a/creds/accessbox/bearer_token_test.go b/creds/accessbox/bearer_token_test.go index bcaba33fc..51f30b8d1 100644 --- a/creds/accessbox/bearer_token_test.go +++ b/creds/accessbox/bearer_token_test.go @@ -1,12 +1,9 @@ package accessbox import ( - "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" - "encoding/binary" - "strconv" "testing" "github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl" @@ -15,9 +12,34 @@ import ( "github.com/stretchr/testify/require" ) -func Test_encrypt_decrypt(t *testing.T) { - tkn := token.NewBearerToken() - box := NewBearerBox(tkn) +func Test_tokens_encode_decode(t *testing.T) { + var ( + 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) require.NoError(t, err) @@ -28,21 +50,28 @@ func Test_encrypt_decrypt(t *testing.T) { 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) + data, err := box.Marshal() require.NoError(t, err) - encrypted, err := encrypt(cred.PrivateKey(), cred.PublicKey(), data) + err = box2.Unmarshal(data) require.NoError(t, err) - decrypted, err := decrypt(cred.PrivateKey(), cred.PublicKey(), encrypted) + tkn2, err := box2.GetBearerToken(cred.PrivateKey()) require.NoError(t, err) - require.Equal(t, data, decrypted) + require.Equal(t, tkn, tkn2) } -func Test_encrypt_decrypt_step_by_step(t *testing.T) { - tkn := token.NewBearerToken() - box := NewBearerBox(tkn) +func Test_accessbox_multiple_keys(t *testing.T) { + var ( + box AccessBox + tkn = token.NewBearerToken() + ) sec, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) require.NoError(t, err) @@ -53,75 +82,6 @@ func Test_encrypt_decrypt_step_by_step(t *testing.T) { tkn.SetEACLTable(eacl.NewTable()) 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 pubs := make([]hcs.PublicKey, 0, count) keys := make([]hcs.PrivateKey, 0, count) @@ -135,27 +95,41 @@ func TestBearerToken_AccessBox(t *testing.T) { } } - buf := new(bytes.Buffer) - require.NoError(t, NewEncoder(buf, cred.PrivateKey(), pubs...).Encode(box)) + box.SetOwnerPublicKey(cred.PublicKey()) - data := buf.Bytes() + err = box.AddBearerToken(tkn, cred.PrivateKey(), pubs...) + require.NoError(t, err) - 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()) - }) + for i, k := range keys { + tkn2, err := box.GetBearerToken(k) + require.NoError(t, err, "key #%d: %s failed", i, k) + require.Equal(t, tkn2, tkn) } - - t.Run("should fail for unknown key", func(t *testing.T) { - cred, err = hcs.Generate(rand.Reader) - require.NoError(t, err) - - r := bytes.NewReader(data) - nbx := NewBearerBox(nil) - require.EqualError(t, NewDecoder(r, cred.PrivateKey()).Decode(nbx), "chacha20poly1305: message authentication failed") - }) +} + +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) } diff --git a/creds/accessbox/decoder.go b/creds/accessbox/decoder.go deleted file mode 100644 index 059a2972b..000000000 --- a/creds/accessbox/decoder.go +++ /dev/null @@ -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) -} diff --git a/creds/accessbox/encoder.go b/creds/accessbox/encoder.go deleted file mode 100644 index d46b875b5..000000000 --- a/creds/accessbox/encoder.go +++ /dev/null @@ -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 -} diff --git a/creds/hcs/public.go b/creds/hcs/public.go index 9a72551c5..2ef2c4f25 100644 --- a/creds/hcs/public.go +++ b/creds/hcs/public.go @@ -26,7 +26,8 @@ func (p *public) WriteTo(w io.Writer) (int64, error) { 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) return &pub, nil } @@ -37,7 +38,7 @@ func publicKeyFromString(val string) (PublicKey, error) { return nil, err } - return publicKeyFromBytes(v) + return PublicKeyFromBytes(v) } // NewPublicKeyFromReader reads new public key from given reader. @@ -47,7 +48,7 @@ func NewPublicKeyFromReader(r io.Reader) (PublicKey, error) { return nil, err } - return publicKeyFromBytes(data) + return PublicKeyFromBytes(data) } // LoadPublicKey loads public key from given file or (serialized) string. @@ -61,5 +62,5 @@ func LoadPublicKey(val string) (PublicKey, error) { return nil, err } - return publicKeyFromBytes(data) + return PublicKeyFromBytes(data) } diff --git a/creds/bearer/credentials.go b/creds/tokens/credentials.go similarity index 64% rename from creds/bearer/credentials.go rename to creds/tokens/credentials.go index ffaf5509b..2d39667c6 100644 --- a/creds/bearer/credentials.go +++ b/creds/tokens/credentials.go @@ -1,4 +1,4 @@ -package bearer +package tokens import ( "bytes" @@ -11,6 +11,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg/client" cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" "github.com/nspcc-dev/neofs-api-go/pkg/object" + "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-s3-gw/creds/accessbox" "github.com/nspcc-dev/neofs-s3-gw/creds/hcs" @@ -20,8 +22,9 @@ import ( type ( // Credentials is a bearer token get/put interface. Credentials interface { - Get(context.Context, *object.Address) (*token.BearerToken, error) - Put(context.Context, *cid.ID, *token.BearerToken, ...hcs.PublicKey) (*object.Address, error) + 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) } cred struct { @@ -59,11 +62,39 @@ func (c *cred) releaseBuffer(buf *bytes.Buffer) { bufferPool.Put(buf) } -func (c *cred) Get(ctx context.Context, address *object.Address) (*token.BearerToken, error) { - buf := c.acquireBuffer() - defer c.releaseBuffer(buf) +func (c *cred) GetBearerToken(ctx context.Context, address *object.Address) (*token.BearerToken, error) { + box, err := c.getAccessBox(ctx, address) + 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() if err != nil { @@ -80,30 +111,25 @@ func (c *cred) Get(ctx context.Context, address *object.Address) (*token.BearerT return nil, err } - err = accessbox.NewDecoder(buf, c.key).Decode(box) - if err != nil { + if err = box.Unmarshal(buf.Bytes()); err != nil { return nil, err } - - return box.Token(), nil + return &box, 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 ( err error - buf = c.acquireBuffer() - box = accessbox.NewBearerBox(tkn) - created = strconv.FormatInt(time.Now().Unix(), 10) ) - defer c.releaseBuffer(buf) - if len(keys) == 0 { return nil, ErrEmptyPublicKeys - } else if tkn == nil { + } else if box == nil { 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 } @@ -121,10 +147,10 @@ func (c *cred) Put(ctx context.Context, cid *cid.ID, tkn *token.BearerToken, key raw := object.NewRaw() raw.SetContainerID(cid) - raw.SetOwnerID(tkn.Issuer()) + raw.SetOwnerID(issuer) 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( ctx, ops,