Skip to content

Commit

Permalink
Persist grantee list on swarm (#30)
Browse files Browse the repository at this point in the history
* Persist grantee list on swarm
* accesslogic refactor
* Refactor grantee list tests

Co-authored-by: Roland Seres <[email protected]>
  • Loading branch information
2 people authored and aranyia committed Apr 9, 2024
1 parent 37e29d0 commit 9a95e91
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 131 deletions.
5 changes: 3 additions & 2 deletions pkg/dynamicaccess/accesslogic.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ func (al ActLogic) AddGrantee(storage kvs.KeyValueStore, publisherPubKey, grante
return err
}
lookupKey := keys[0]
accessKeyEncryptionKey := keys[1]
// accessKeyDecryptionKey is used for encryption of the access key
accessKeyDecryptionKey := keys[1]

// Encrypt the access key for the new Grantee
cipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc)
cipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, uint32(0), hashFunc)
granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey)
if err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions pkg/dynamicaccess/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

type Controller interface {
DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error)
UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error)
UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error)
}

type defaultController struct {
Expand All @@ -27,7 +27,7 @@ func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.A
return addr, err
}

func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) {
func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) {
kvs, err := c.history.Lookup(0)
if err != nil {
return swarm.EmptyAddress, err
Expand All @@ -36,7 +36,7 @@ func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.Pu
// new feed
// TODO: putter session to create kvs
kvs = kvsmock.New()
_, err = c.granteeManager.Publish(kvs, publisher, topic)
_, err = c.granteeManager.Publish(kvs, publisher)
if err != nil {
return swarm.EmptyAddress, err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/dynamicaccess/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ func TestEncrypt(t *testing.T) {
eref, ref := prepareEncryptedChunkReference(ak)

key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
gm.Add("topic", []*ecdsa.PublicKey{&key1.PublicKey})
gm.Add([]*ecdsa.PublicKey{&key1.PublicKey})

addr, _ := c.UploadHandler(ref, &pk.PublicKey, "topic")
addr, _ := c.UploadHandler(ref, &pk.PublicKey)
if !addr.Equal(eref) {
t.Fatalf("Decrypted chunk address: %s is not the expected: %s", addr, eref)
}
Expand Down
130 changes: 110 additions & 20 deletions pkg/dynamicaccess/grantee.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,134 @@
package dynamicaccess

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"fmt"

"github.com/ethersphere/bee/pkg/file"
"github.com/ethersphere/bee/pkg/storer"
"github.com/ethersphere/bee/pkg/swarm"
)

const (
publicKeyLen = 65
)

type GranteeList interface {
Add(topic string, addList []*ecdsa.PublicKey) error
Remove(topic string, removeList []*ecdsa.PublicKey) error
Get(topic string) []*ecdsa.PublicKey
Add(addList []*ecdsa.PublicKey) error
Remove(removeList []*ecdsa.PublicKey) error
Get() []*ecdsa.PublicKey
Save() (swarm.Address, error)
}

type GranteeListStruct struct {
grantees map[string][]*ecdsa.PublicKey
grantees []byte
loadSave file.LoadSaver
putter storer.PutterSession
}

var _ GranteeList = (*GranteeListStruct)(nil)

func (g *GranteeListStruct) Get() []*ecdsa.PublicKey {
return g.deserialize(g.grantees)
}

func (g *GranteeListStruct) serialize(publicKeys []*ecdsa.PublicKey) []byte {
b := make([]byte, 0, len(publicKeys)*publicKeyLen)
for _, key := range publicKeys {
b = append(b, g.serializePublicKey(key)...)
}
return b
}

func (g *GranteeListStruct) serializePublicKey(pub *ecdsa.PublicKey) []byte {
return elliptic.Marshal(pub.Curve, pub.X, pub.Y)
}

func (g *GranteeListStruct) Get(topic string) []*ecdsa.PublicKey {
grantees := g.grantees[topic]
keys := make([]*ecdsa.PublicKey, len(grantees))
copy(keys, grantees)
return keys
func (g *GranteeListStruct) deserialize(data []byte) []*ecdsa.PublicKey {
if len(data) == 0 {
return nil
}

p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen)
for i := 0; i < len(data); i += publicKeyLen {
pubKey := g.deserializeBytes(data[i : i+publicKeyLen])
if pubKey == nil {
return nil
}
p = append(p, pubKey)
}
return p
}

func (g *GranteeListStruct) Add(topic string, addList []*ecdsa.PublicKey) error {
g.grantees[topic] = append(g.grantees[topic], addList...)
func (g *GranteeListStruct) deserializeBytes(data []byte) *ecdsa.PublicKey {
curve := elliptic.P256()
x, y := elliptic.Unmarshal(curve, data)
return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}
}

func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error {
if len(addList) == 0 {
return fmt.Errorf("no public key provided")
}

data := g.serialize(addList)
g.grantees = append(g.grantees, data...)
return nil
}

func (g *GranteeListStruct) Remove(topic string, removeList []*ecdsa.PublicKey) error {
for _, remove := range removeList {
for i, grantee := range g.grantees[topic] {
if *grantee == *remove {
g.grantees[topic][i] = g.grantees[topic][len(g.grantees[topic])-1]
g.grantees[topic] = g.grantees[topic][:len(g.grantees[topic])-1]
func (g *GranteeListStruct) Save() (swarm.Address, error) {
refBytes, err := g.loadSave.Save(context.Background(), g.grantees)
if err != nil {
return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err)
}
address := swarm.NewAddress(refBytes)
err = g.putter.Done(address)
if err != nil {
return swarm.ZeroAddress, err
}
return address, nil
}

func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error {
if len(keysToRemove) == 0 {
return fmt.Errorf("nothing to remove")
}
grantees := g.deserialize(g.grantees)
if grantees == nil {
return fmt.Errorf("no grantee found")
}

for _, remove := range keysToRemove {
for i, grantee := range grantees {
if grantee.Equal(remove) {
grantees[i] = grantees[len(grantees)-1]
grantees = grantees[:len(grantees)-1]
}
}
}

g.grantees = g.serialize(grantees)
return nil
}

func NewGrantee() *GranteeListStruct {
return &GranteeListStruct{grantees: make(map[string][]*ecdsa.PublicKey)}
func NewGranteeList(ls file.LoadSaver, putter storer.PutterSession, reference swarm.Address) GranteeList {
var (
data []byte
err error
)
if swarm.ZeroAddress.Equal(reference) || swarm.EmptyAddress.Equal(reference) {
data = []byte{}
} else {
data, err = ls.Load(context.Background(), reference.Bytes())
}
if err != nil {
return nil
}

return &GranteeListStruct{
grantees: data,
loadSave: ls,
putter: putter,
}
}
25 changes: 13 additions & 12 deletions pkg/dynamicaccess/grantee_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ package dynamicaccess
import (
"crypto/ecdsa"

"github.com/ethersphere/bee/pkg/dynamicaccess/mock"
"github.com/ethersphere/bee/pkg/kvs"
"github.com/ethersphere/bee/pkg/swarm"
)

type GranteeManager interface {
Get(topic string) []*ecdsa.PublicKey
Add(topic string, addList []*ecdsa.PublicKey) error
Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error)
Get() []*ecdsa.PublicKey
Add(addList []*ecdsa.PublicKey) error
Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey) (swarm.Address, error)

// HandleGrantees(topic string, addList, removeList []*ecdsa.PublicKey) *Act
// HandleGrantees(addList, removeList []*ecdsa.PublicKey) *Act

// Load(grantee Grantee)
// Save()
Expand All @@ -22,24 +23,24 @@ var _ GranteeManager = (*granteeManager)(nil)

type granteeManager struct {
accessLogic ActLogic
granteeList GranteeList
granteeList *mock.GranteeListStructMock
}

func NewGranteeManager(al ActLogic) *granteeManager {
return &granteeManager{accessLogic: al, granteeList: NewGrantee()}
return &granteeManager{accessLogic: al, granteeList: mock.NewGranteeList()}
}

func (gm *granteeManager) Get(topic string) []*ecdsa.PublicKey {
return gm.granteeList.Get(topic)
func (gm *granteeManager) Get() []*ecdsa.PublicKey {
return gm.granteeList.Get()
}

func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error {
return gm.granteeList.Add(topic, addList)
func (gm *granteeManager) Add(addList []*ecdsa.PublicKey) error {
return gm.granteeList.Add(addList)
}

func (gm *granteeManager) Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) {
func (gm *granteeManager) Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey) (swarm.Address, error) {
err := gm.accessLogic.AddPublisher(kvs, publisher)
for _, grantee := range gm.granteeList.Get(topic) {
for _, grantee := range gm.granteeList.Get() {
err = gm.accessLogic.AddGrantee(kvs, publisher, grantee, nil)
}
return swarm.EmptyAddress, err
Expand Down
6 changes: 3 additions & 3 deletions pkg/dynamicaccess/grantee_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ func TestAdd(t *testing.T) {

id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
err := m.Add("topic", []*ecdsa.PublicKey{&id1.PublicKey})
err := m.Add([]*ecdsa.PublicKey{&id1.PublicKey})
if err != nil {
t.Errorf("Add() returned an error")
}
err = m.Add("topic", []*ecdsa.PublicKey{&id2.PublicKey})
err = m.Add([]*ecdsa.PublicKey{&id2.PublicKey})
if err != nil {
t.Errorf("Add() returned an error")
}
s := kvsmock.New()
m.Publish(s, &pub.PublicKey, "topic")
m.Publish(s, &pub.PublicKey)
fmt.Println("")
}
Loading

0 comments on commit 9a95e91

Please sign in to comment.