github.com/ethersphere/bee/v2@v2.2.0/pkg/accesscontrol/controller.go (about)

     1  // Copyright 2024 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package accesscontrol provides functionalities needed
     6  // for managing access control on Swarm
     7  package accesscontrol
     8  
     9  import (
    10  	"context"
    11  	"crypto/ecdsa"
    12  	"fmt"
    13  	"io"
    14  	"time"
    15  
    16  	"github.com/ethersphere/bee/v2/pkg/accesscontrol/kvs"
    17  	"github.com/ethersphere/bee/v2/pkg/encryption"
    18  	"github.com/ethersphere/bee/v2/pkg/file"
    19  	"github.com/ethersphere/bee/v2/pkg/swarm"
    20  )
    21  
    22  // Grantees represents an interface for managing and retrieving grantees for a publisher.
    23  type Grantees interface {
    24  	// UpdateHandler manages the grantees for the given publisher, updating the list based on provided public keys to add or remove.
    25  	// Only the publisher can make changes to the grantee list.
    26  	UpdateHandler(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeRef swarm.Address, historyRef swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error)
    27  	// Get returns the list of grantees for the given publisher.
    28  	// The list is accessible only by the publisher.
    29  	Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglRef swarm.Address) ([]*ecdsa.PublicKey, error)
    30  }
    31  
    32  // Controller represents an interface for managing access control on Swarm.
    33  // It provides methods for handling downloads, uploads and updates for grantee lists and references.
    34  type Controller interface {
    35  	Grantees
    36  	// DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp.
    37  	DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRef swarm.Address, timestamp int64) (swarm.Address, error)
    38  	// UploadHandler encrypts the reference and stores it in the history as the latest update.
    39  	UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRef swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error)
    40  	io.Closer
    41  }
    42  
    43  // ControllerStruct represents a controller for access control logic.
    44  type ControllerStruct struct {
    45  	access ActLogic
    46  }
    47  
    48  var _ Controller = (*ControllerStruct)(nil)
    49  
    50  // NewController creates a new access controller with the given access logic.
    51  func NewController(access ActLogic) *ControllerStruct {
    52  	return &ControllerStruct{
    53  		access: access,
    54  	}
    55  }
    56  
    57  // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp.
    58  func (c *ControllerStruct) DownloadHandler(
    59  	ctx context.Context,
    60  	ls file.LoadSaver,
    61  	encryptedRef swarm.Address,
    62  	publisher *ecdsa.PublicKey,
    63  	historyRef swarm.Address,
    64  	timestamp int64,
    65  ) (swarm.Address, error) {
    66  	_, act, err := c.getHistoryAndAct(ctx, ls, historyRef, publisher, timestamp)
    67  	if err != nil {
    68  		return swarm.ZeroAddress, err
    69  	}
    70  
    71  	return c.access.DecryptRef(ctx, act, encryptedRef, publisher)
    72  }
    73  
    74  // UploadHandler encrypts the reference and stores it in the history as the latest update.
    75  func (c *ControllerStruct) UploadHandler(
    76  	ctx context.Context,
    77  	ls file.LoadSaver,
    78  	reference swarm.Address,
    79  	publisher *ecdsa.PublicKey,
    80  	historyRef swarm.Address,
    81  ) (swarm.Address, swarm.Address, swarm.Address, error) {
    82  	history, act, err := c.getHistoryAndAct(ctx, ls, historyRef, publisher, time.Now().Unix())
    83  	if err != nil {
    84  		return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
    85  	}
    86  	actRef := swarm.ZeroAddress
    87  	newHistoryRef := historyRef
    88  	if historyRef.IsZero() {
    89  		newHistoryRef, actRef, err = c.saveHistoryAndAct(ctx, history, nil, act)
    90  		if err != nil {
    91  			return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
    92  		}
    93  	}
    94  
    95  	encryptedRef, err := c.access.EncryptRef(ctx, act, publisher, reference)
    96  	return actRef, newHistoryRef, encryptedRef, err
    97  }
    98  
    99  // UpdateHandler manages the grantees for the given publisher, updating the list based on provided public keys to add or remove.
   100  // Only the publisher can make changes to the grantee list.
   101  // Limitation: If an update is called again within a second from the latest upload/update then mantaray save fails with ErrInvalidInput,
   102  // because the key (timestamp) is already present, hence a new fork is not created.
   103  func (c *ControllerStruct) UpdateHandler(
   104  	ctx context.Context,
   105  	ls file.LoadSaver,
   106  	gls file.LoadSaver,
   107  	encryptedglRef swarm.Address,
   108  	historyRef swarm.Address,
   109  	publisher *ecdsa.PublicKey,
   110  	addList []*ecdsa.PublicKey,
   111  	removeList []*ecdsa.PublicKey,
   112  ) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) {
   113  	history, act, err := c.getHistoryAndAct(ctx, ls, historyRef, publisher, time.Now().Unix())
   114  	if err != nil {
   115  		return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   116  	}
   117  
   118  	gl, err := c.getGranteeList(ctx, gls, encryptedglRef, publisher)
   119  	if err != nil {
   120  		return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   121  	}
   122  	if len(addList) != 0 {
   123  		err = gl.Add(addList)
   124  		if err != nil {
   125  			return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   126  		}
   127  	}
   128  	granteesToAdd := addList
   129  	if len(removeList) != 0 {
   130  		err = gl.Remove(removeList)
   131  		if err != nil {
   132  			return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   133  		}
   134  		// generate new access key and new act, only if history was not newly created
   135  		if !historyRef.IsZero() {
   136  			act, err = c.newActWithPublisher(ctx, ls, publisher)
   137  			if err != nil {
   138  				return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   139  			}
   140  		}
   141  		granteesToAdd = gl.Get()
   142  	}
   143  
   144  	for _, grantee := range granteesToAdd {
   145  		err := c.access.AddGrantee(ctx, act, publisher, grantee)
   146  		if err != nil {
   147  			return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   148  		}
   149  	}
   150  
   151  	granteeRef, err := gl.Save(ctx)
   152  	if err != nil {
   153  		return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   154  	}
   155  
   156  	egranteeRef, err := c.encryptRefForPublisher(publisher, granteeRef)
   157  	if err != nil {
   158  		return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   159  	}
   160  	// need to re-initialize history, because Lookup loads the forks causing the manifest save to skip the root node
   161  	if !historyRef.IsZero() {
   162  		history, err = NewHistoryReference(ls, historyRef)
   163  		if err != nil {
   164  			return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   165  		}
   166  	}
   167  
   168  	mtdt := map[string]string{"encryptedglref": egranteeRef.String()}
   169  	hRef, actRef, err := c.saveHistoryAndAct(ctx, history, &mtdt, act)
   170  	if err != nil {
   171  		return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err
   172  	}
   173  
   174  	return granteeRef, egranteeRef, hRef, actRef, nil
   175  }
   176  
   177  // Get returns the list of grantees for the given publisher.
   178  // The list is accessible only by the publisher.
   179  func (c *ControllerStruct) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglRef swarm.Address) ([]*ecdsa.PublicKey, error) {
   180  	gl, err := c.getGranteeList(ctx, ls, encryptedglRef, publisher)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	return gl.Get(), nil
   185  }
   186  
   187  func (c *ControllerStruct) newActWithPublisher(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey) (kvs.KeyValueStore, error) {
   188  	act, err := kvs.New(ls)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	err = c.access.AddGrantee(ctx, act, publisher, publisher)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	return act, nil
   198  }
   199  
   200  func (c *ControllerStruct) getHistoryAndAct(ctx context.Context, ls file.LoadSaver, historyRef swarm.Address, publisher *ecdsa.PublicKey, timestamp int64) (history History, act kvs.KeyValueStore, err error) {
   201  	if historyRef.IsZero() {
   202  		history, err = NewHistory(ls)
   203  		if err != nil {
   204  			return nil, nil, err
   205  		}
   206  		act, err = c.newActWithPublisher(ctx, ls, publisher)
   207  		if err != nil {
   208  			return nil, nil, err
   209  		}
   210  	} else {
   211  		history, err = NewHistoryReference(ls, historyRef)
   212  		if err != nil {
   213  			return nil, nil, err
   214  		}
   215  		entry, err := history.Lookup(ctx, timestamp)
   216  		if err != nil {
   217  			return nil, nil, err
   218  		}
   219  		act, err = kvs.NewReference(ls, entry.Reference())
   220  		if err != nil {
   221  			return nil, nil, err
   222  		}
   223  	}
   224  
   225  	return history, act, nil
   226  }
   227  
   228  func (c *ControllerStruct) saveHistoryAndAct(ctx context.Context, history History, mtdt *map[string]string, act kvs.KeyValueStore) (swarm.Address, swarm.Address, error) {
   229  	actRef, err := act.Save(ctx)
   230  	if err != nil {
   231  		return swarm.ZeroAddress, swarm.ZeroAddress, err
   232  	}
   233  	err = history.Add(ctx, actRef, nil, mtdt)
   234  	if err != nil {
   235  		return swarm.ZeroAddress, swarm.ZeroAddress, err
   236  	}
   237  	historyRef, err := history.Store(ctx)
   238  	if err != nil {
   239  		return swarm.ZeroAddress, swarm.ZeroAddress, err
   240  	}
   241  
   242  	return historyRef, actRef, nil
   243  }
   244  
   245  func (c *ControllerStruct) getGranteeList(ctx context.Context, ls file.LoadSaver, encryptedglRef swarm.Address, publisher *ecdsa.PublicKey) (gl GranteeList, err error) {
   246  	if encryptedglRef.IsZero() {
   247  		gl = NewGranteeList(ls)
   248  	} else {
   249  		granteeref, err := c.decryptRefForPublisher(publisher, encryptedglRef)
   250  		if err != nil {
   251  			return nil, err
   252  		}
   253  
   254  		gl, err = NewGranteeListReference(ctx, ls, granteeref)
   255  		if err != nil {
   256  			return nil, err
   257  		}
   258  	}
   259  
   260  	return gl, nil
   261  }
   262  
   263  func (c *ControllerStruct) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) {
   264  	keys, err := c.access.Session.Key(publisherPubKey, [][]byte{oneByteArray})
   265  	if err != nil {
   266  		return swarm.ZeroAddress, err
   267  	}
   268  	refCipher := encryption.New(keys[0], 0, 0, hashFunc)
   269  	encryptedRef, err := refCipher.Encrypt(ref.Bytes())
   270  	if err != nil {
   271  		return swarm.ZeroAddress, fmt.Errorf("failed to encrypt reference: %w", err)
   272  	}
   273  
   274  	return swarm.NewAddress(encryptedRef), nil
   275  }
   276  
   277  func (c *ControllerStruct) decryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, encryptedRef swarm.Address) (swarm.Address, error) {
   278  	keys, err := c.access.Session.Key(publisherPubKey, [][]byte{oneByteArray})
   279  	if err != nil {
   280  		return swarm.ZeroAddress, err
   281  	}
   282  	refCipher := encryption.New(keys[0], 0, 0, hashFunc)
   283  	ref, err := refCipher.Decrypt(encryptedRef.Bytes())
   284  	if err != nil {
   285  		return swarm.ZeroAddress, fmt.Errorf("failed to decrypt reference: %w", err)
   286  	}
   287  
   288  	return swarm.NewAddress(ref), nil
   289  }
   290  
   291  // Close simply returns nil
   292  func (c *ControllerStruct) Close() error {
   293  	return nil
   294  }