github.com/ethersphere/bee/v2@v2.2.0/pkg/accesscontrol/grantee.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
     6  
     7  import (
     8  	"context"
     9  	"crypto/ecdsa"
    10  	"crypto/elliptic"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"github.com/btcsuite/btcd/btcec/v2"
    15  	"github.com/ethersphere/bee/v2/pkg/file"
    16  	"github.com/ethersphere/bee/v2/pkg/swarm"
    17  )
    18  
    19  const (
    20  	publicKeyLen = 65
    21  )
    22  
    23  var (
    24  	// ErrNothingToRemove indicates that the remove list is empty.
    25  	ErrNothingToRemove = errors.New("nothing to remove")
    26  	// ErrNoGranteeFound indicates that the grantee list is empty.
    27  	ErrNoGranteeFound = errors.New("no grantee found")
    28  	// ErrNothingToAdd indicates that the add list is empty.
    29  	ErrNothingToAdd = errors.New("nothing to add")
    30  )
    31  
    32  // GranteeList manages a list of public keys.
    33  type GranteeList interface {
    34  	// Add adds a list of public keys to the grantee list. It filters out duplicates.
    35  	Add(addList []*ecdsa.PublicKey) error
    36  	// Remove removes a list of public keys from the grantee list, if there is any.
    37  	Remove(removeList []*ecdsa.PublicKey) error
    38  	// Get simply returns the list of public keys.
    39  	Get() []*ecdsa.PublicKey
    40  	// Save saves the grantee list to the underlying storage and returns the reference.
    41  	Save(ctx context.Context) (swarm.Address, error)
    42  }
    43  
    44  // GranteeListStruct represents a list of grantee public keys.
    45  type GranteeListStruct struct {
    46  	grantees []*ecdsa.PublicKey
    47  	loadSave file.LoadSaver
    48  }
    49  
    50  var _ GranteeList = (*GranteeListStruct)(nil)
    51  
    52  // Get simply returns the list of public keys.
    53  func (g *GranteeListStruct) Get() []*ecdsa.PublicKey {
    54  	return g.grantees
    55  }
    56  
    57  // Add adds a list of public keys to the grantee list. It filters out duplicates.
    58  func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error {
    59  	if len(addList) == 0 {
    60  		return ErrNothingToAdd
    61  	}
    62  	filteredList := make([]*ecdsa.PublicKey, 0, len(addList))
    63  	for _, addkey := range addList {
    64  		add := true
    65  		for _, granteekey := range g.grantees {
    66  			if granteekey.Equal(addkey) {
    67  				add = false
    68  				break
    69  			}
    70  		}
    71  		for _, filteredkey := range filteredList {
    72  			if filteredkey.Equal(addkey) {
    73  				add = false
    74  				break
    75  			}
    76  		}
    77  		if add {
    78  			filteredList = append(filteredList, addkey)
    79  		}
    80  	}
    81  	g.grantees = append(g.grantees, filteredList...)
    82  
    83  	return nil
    84  }
    85  
    86  // Save saves the grantee list to the underlying storage and returns the reference.
    87  func (g *GranteeListStruct) Save(ctx context.Context) (swarm.Address, error) {
    88  	data := serialize(g.grantees)
    89  	refBytes, err := g.loadSave.Save(ctx, data)
    90  	if err != nil {
    91  		return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err)
    92  	}
    93  
    94  	return swarm.NewAddress(refBytes), nil
    95  }
    96  
    97  // Remove removes a list of public keys from the grantee list, if there is any.
    98  func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error {
    99  	if len(keysToRemove) == 0 {
   100  		return ErrNothingToRemove
   101  	}
   102  
   103  	if len(g.grantees) == 0 {
   104  		return ErrNoGranteeFound
   105  	}
   106  	grantees := g.grantees
   107  
   108  	for _, remove := range keysToRemove {
   109  		for i := 0; i < len(grantees); i++ {
   110  			if grantees[i].Equal(remove) {
   111  				grantees[i] = grantees[len(grantees)-1]
   112  				grantees = grantees[:len(grantees)-1]
   113  			}
   114  		}
   115  	}
   116  	g.grantees = grantees
   117  
   118  	return nil
   119  }
   120  
   121  // NewGranteeList creates a new (and empty) grantee list.
   122  func NewGranteeList(ls file.LoadSaver) *GranteeListStruct {
   123  	return &GranteeListStruct{
   124  		grantees: []*ecdsa.PublicKey{},
   125  		loadSave: ls,
   126  	}
   127  }
   128  
   129  // NewGranteeListReference loads an existing grantee list.
   130  func NewGranteeListReference(ctx context.Context, ls file.LoadSaver, reference swarm.Address) (*GranteeListStruct, error) {
   131  	data, err := ls.Load(ctx, reference.Bytes())
   132  	if err != nil {
   133  		return nil, fmt.Errorf("failed to load grantee list reference, %w", err)
   134  	}
   135  	grantees := deserialize(data)
   136  
   137  	return &GranteeListStruct{
   138  		grantees: grantees,
   139  		loadSave: ls,
   140  	}, nil
   141  }
   142  
   143  func serialize(publicKeys []*ecdsa.PublicKey) []byte {
   144  	b := make([]byte, 0, len(publicKeys)*publicKeyLen)
   145  	for _, key := range publicKeys {
   146  		b = append(b, serializePublicKey(key)...)
   147  	}
   148  	return b
   149  }
   150  
   151  func serializePublicKey(pub *ecdsa.PublicKey) []byte {
   152  	return elliptic.Marshal(pub.Curve, pub.X, pub.Y)
   153  }
   154  
   155  func deserialize(data []byte) []*ecdsa.PublicKey {
   156  	if len(data) == 0 {
   157  		return []*ecdsa.PublicKey{}
   158  	}
   159  
   160  	p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen)
   161  	for i := 0; i < len(data); i += publicKeyLen {
   162  		pubKey := deserializeBytes(data[i : i+publicKeyLen])
   163  		if pubKey == nil {
   164  			return []*ecdsa.PublicKey{}
   165  		}
   166  		p = append(p, pubKey)
   167  	}
   168  	return p
   169  }
   170  
   171  func deserializeBytes(data []byte) *ecdsa.PublicKey {
   172  	key, err := btcec.ParsePubKey(data)
   173  	if err != nil {
   174  		return nil
   175  	}
   176  	return key.ToECDSA()
   177  }